-
Notifications
You must be signed in to change notification settings - Fork 52
API v3.0.0 Changes
Changes from api v2.0 to v3.0 here
- AsyncOperation class replaced with Task class from the Bolts Framework
- MetaWearBleService class renamed to BtleService and moved to
android
package
<service android:name="com.mbientlab.metawear.android.BtleService" />
- Sensor data represented by AsyncDataProducer or ForcedDataProducer
-
getModule
no longer throws checked exception, returns null if failed- Checked exception variant renamed to
getModuleOrThrow
- Checked exception variant renamed to
-
MessageHandler
renamed toSubscriber
- Interacts with the outer variables through an environment parameter which is set by its managing Route
- Need to use the environment to avoid references to unseriazable variables
-
process
function renamed toapply
-
process
function replaced with explicitly named data processing functions -
monitor
renamed toreact
-
stream
andlog
functions directly take aSubscriber
object rather than a key- Instead of assigning a
Subscriber
to a key, you now set a Subscriber's environment
- Instead of assigning a
-
split
component now breaks data into its component pieces, replacing functions such as fromXAxis and fromYAxis - Behavior of the old splitter now handled by the
multicast
component -
name
component assigns a user defined name to a data processor - Feedback & feedforward loops expressed by setting the desired value to the string representing the desired data output, either from another data processor or sensor
- Use
startRecord
andendRecord
to mark where to start and stop recording a macro instead of wrapping the code in an anonymous function
Using lambda expressions, streaming data is easily expressed in a compact statement:
AccelerationDataProducer accel = board.getModule(Accelerometer.class).acceleration();
ArrayList<CartesianFloat> received = new ArrayList<>();
accel.addRoute(source -> source.stream((msg, env) -> {
received.add(msg.data(CartesianFloat.class));
Log.i("test", msg.data(CartesianFloat.class).toString());
}));
If you are using anonymous classes, the code is written instead as follows:
final ArrayList<CartesianFloat> received = new ArrayList<>();
accel.addRoute(new RouteBuilder() {
@Override
public void configure(RouteElement source) {
source.stream(new Subscriber() {
@Override
public void apply(Message msg, Object... env) {
received.add(msg.data(CartesianFloat.class));
Log.i("test", msg.data(CartesianFloat.class).toString());
}
});
}
});
If you are planning on serializing a route, you will need to be mindful of the outer variables the Subscriber object refers to as they may not be serializable i.e. a GUI object. This is especially truth for developers using anonymous classes as all non-static anonymous classes refer to their enclosing class with a hidden this$0
variable. To work around the references issue, Subscribers have an environment parameter passed into the apply
function along with the data object. This lets the Subscriber access outer variables without explicitly referring them resulting in their being ignored when serializing Subscribers. When deserializing routes, you do not need to reset the Subscriber however you will need to reset the environment.
To serialize the anonymous class version of the accelerometer stream, the code needs to be refactored such that the Subscriber is static and the owning route sets the Subscriber's environment.
private static Subscriber DATA_HANDLER = new Subscriber() {
@Override
public void apply(Message msg, Object... env) {
((ArrayList<CartesianFloat>) env[0]).add(msg.data(CartesianFloat.class));
Log.i("test", msg.data(CartesianFloat.class).toString());
}
};
final ArrayList<CartesianFloat> received = new ArrayList<>();
accel.addRoute(new RouteBuilder() {
@Override
public void configure(RouteElement source) {
source.stream(DATA_HANDLER);
}
}).continueWith(new Continuation<Route, Void>() {
@Override
public Void then(Task<Route> task) throws Exception {
task.getResult().setEnvironment(0, received);
return null;
}
});
Consider the LED controller example code from the Android documentation which uses the counter, math, and comparison processors, and signal monitors. With API v3.0, the same code now looks as follows:
final Led led= mwBoard.getModule(Led.class);
mwBoard.getModule(Switch.class).addRoute(new RouteBuilder() {
@Override
public void configure(RouteElement source) {
source.count().map(Function2.MODULUS, 2)
.multicast()
.to().filter(Comparison.EQ, 1).react(new RouteElement.Action() {
@Override
public void execute(DataToken token) {
led.editPattern(Led.Color.BLUE)
.highIntensity((byte) 16).lowIntensity((byte) 16)
.pulseDuration((short) 1000)
.highTime((short) 500)
.repeatCount(Led.PATTERN_REPEAT_INDEFINITELY)
.commit();
led.play();
}
})
.to().filter(Comparison.EQ, 0).react(new RouteElement.Action() {
@Override
public void execute(DataToken token) {
led.stop(true);
}
})
.end();
}
})
If you use the Jack compiler, you can compact the code by replacing all of the anonymous functions with lambda expressions:
final Led led= board.getModule(Led.class);
board.getModule(Switch.class).addRoute(source -> source.count().map(Function2.MODULUS, 2)
.multicast()
.to().filter(Comparison.EQ, 1).react(token -> {
led.editPattern(Led.Color.BLUE)
.highIntensity((byte) 16).lowIntensity((byte) 16)
.pulseDuration((short) 1000)
.highTime((short) 500)
.repeatCount(Led.PATTERN_REPEAT_INDEFINITELY)
.commit();
led.play();
})
.to().filter(Comparison.EQ, 0).react(token -> led.stop(true))
.end());
To program the above led controller code with the macro, add calls to startRecord
and endRecord
to the code.
final Led led= mwBoard.getModule(Led.class);
final Macro macro= mwBoard.getModule(Macro.class);
macro.startRecord();
mwBoard.getModule(Switch.class).addRoute(new RouteBuilder() {
@Override
public void configure(RouteElement source) {
source.count().map(Function1.MODULUS, 2)
.multicast()
.to().filter(Comparison.EQ, 1).react(new RouteElement.Action() {
@Override
public void execute(DataToken token) {
led.editPattern(Led.Color.BLUE)
.highIntensity((byte) 16).lowIntensity((byte) 16)
.pulseDuration((short) 1000)
.highTime((short) 500)
.repeatCount(Led.PATTERN_REPEAT_INDEFINITELY)
.commit();
led.play();
}
})
.to().filter(Comparison.EQ, 0).react(new RouteElement.Action() {
@Override
public void execute(DataToken token) {
led.stop(true);
}
})
.end();
}
}).continueWithTask(new Continuation<Route, Task<Byte>>() {
@Override
public Task<Byte> then(Task<Route> task) throws Exception {
return macro.endRecord();
}
}).continueWith(new Continuation<Byte, Void>() {
@Override
public Void then(Task<Byte> task) throws Exception {
Log.i("test", "Macro id " + task.getResult());
}
});
With lambda support enabled:
final Led led= board.getModule(Led.class);
final Macro macro = board.getModule(Macro.class);
macro.startRecord();
board.getModule(Switch.class).addRoute(source ->
source.accumulate().map(Function2.MODULUS, 2).multicast()
.to().filter(Comparison.EQ, 1).react(token -> {
led.editPattern(Led.Color.GREEN)
.highIntensity((byte) 16).lowIntensity((byte) 16)
.pulseDuration((short) 1000)
.highTime((short) 500)
.repeatCount(Led.PATTERN_REPEAT_INDEFINITELY)
.commit();
led.play();
})
.to().filter(Comparison.EQ, 0).react(token -> led.stop(true))
).continueWithTask(ignored -> macro.endRecord())
.continueWith(task -> Log.i("test", "Macro id " + task.getResult()));