WP7 Code: Mocking Event Streams with IEnumerable

A few weeks ago I showed how to represent computations involving geo locations or accelerometer readings as queries over RxLINQ event streams (i.e., IObservable collections). In both instances the event source was one of the sensors that is on board of every Windows Phone 7 device. What happens when applications querying position changed or acceleration reading changed event streams run in the emulator? As there are no physical sensors the events don’t fire. For example, the 2-D bubble level application from my previous post shows a stationary bubble. Does that mean that one cannot write location or accelerometer code and experiment with it in the emulator? Likewise, is testing in the emulator a dead end? In light of the above it would seem so. However, the duality between  IObservable and IEnumerable provides an elegant way to mock event streams with any IEnumerable source. This blog post shows how to exploit the duality in the context of the 2-D bubble application. With just a few changes you should be able to run and test the application without an actual accelerometer sensor, in the emulator.

To illustrate how trivial the changes are let me start top-down rather than bottom up. Recall that the first query in the 2-D bubble level pulled the accelerometer readings into a local variable named as such; here’s a snippet from that code with the key part highlighted:

{

    var accelerometerReadings = accelerometer.GetAccEventStream();

    var x0 = (double)ellipse1.GetValue(Canvas.TopProperty) + ellipse1.Width / 2;
var y0 = (double)ellipse1.GetValue(Canvas.LeftProperty) + ellipse1.Height / 2;

    // snip

}

The only change in RxLINQ queries entails sourcing the event stream from a different object (an instance of DataLoader). Here’s the modified code; there are no other changes to this class:

{
var dl = new DataLoader();

     var accelerometerReadings = dl.Readings();

     var x0 = (double)ellipse1.GetValue(Canvas.TopProperty) + ellipse1.Width / 2;
var y0 = (double)ellipse1.GetValue(Canvas.LeftProperty) + ellipse1.Height / 2;

    // snip

}

The Readings method of DataLoader provides an IObservable collection. For this example the collection represents actual accelerometer readings that have been saved to isolated storage. However this is not a requirement; the events could also represent synthetic data. As long as the information used by the RxLINQ queries is there (in this case the X and Y accelerations, as well as the timestamps) the events could be synthetic, as well as recordings of sensor readings. Here’s the DataLoader class:

public class DataLoader
{
private IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication();
private Stream stream;
private StreamReader reader;
private const string FileName = "accelerations.txt";

    public DataLoader()
{
stream = App.GetResourceStream(new Uri(FileName, UriKind.Relative)).Stream;
reader = new StreamReader(stream);
}

    public IObservable<AccelerometerReading> Readings()
{

        // Read saved data into a list (IEnumerable)
var readings = new List<AccelerometerReading>();
        for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
readings.Add(new AccelerometerReading(line));

        // Convert the IEnumerable into IObservable
var rs = readings.ToObservable(Scheduler.NewThread);

        // Sync via Thread.Sleep on a different thread
return rs.Zip(rs.Skip(1), (l, r) =>
{
Thread.Sleep(TimeSpan.FromTicks(r.Timestamp.Ticks - l.Timestamp.Ticks));
return r;
}).ObserveOnDispatcher();
}
}

The Readings() method populates a List<AccelerometerReading> with AccelerometerReading objects which have been read from a text file. RxLINQ adds the ToObservable extension method to IEnumerable such that once read, the list of recorded readings can be converted into an observable collection. The argument signals that the conversion should take place on a different thread, leaving the main thread for UI updates.

Sensor readings have a temporal element; if the code would blindly dole out readings from the IEnumerable then the query computing the motion (which used time deltas) will produce garbage. Consequently, after emitting an event, the code needs to wait until it is time to emit the next event. RxLINQ comes again to the rescue. First, the combination of Zip and Skip allows the code to Sleep between two consecutive events. (As this computation takes place on a different thread the Sleep doesn’t interfere with the UI thread.) Second, ObserveOnDispatcher() brings the (now temporally-spaced) events to the main thread, which is where the accelerometer events show up when using the actual sensor.

AccelerometerReading is a helper class that simplifies storing the accelerometer events by capturing only the X/Y/Z readings and the Timestamp in ticks. Here’s the code:

public class AccelerometerReading
{
public DateTimeOffset Timestamp { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }

        public AccelerometerReading()
{
//
}

        public AccelerometerReading(AccelerometerReadingEventArgs ea)
{
Timestamp = ea.Timestamp;
X = ea.X;
Y = ea.Y;
Z = ea.Z;
}

        public AccelerometerReading(string line)
{
var elements = line.Split(';');
if (elements.Length == 4)
{
Timestamp = new DateTimeOffset(new DateTime(long.Parse(elements[0])));
X = Double.Parse(elements[1]);
Y = Double.Parse(elements[2]);
Z = Double.Parse(elements[3]);
}
}
}

For completeness here’s a sample from the contents of the accelerations.txt file:

634262369734340000;0.00390625;-0.01171875;-1.02734375
634262369734550000;0.0078125;-0.01171875;-1.04296875
634262369734790000;0.00390625;-0.00390625;-1.03125
634262369735000000;0.00390625;-0.0078125;-1.03125
634262369735210000;-0.00390625;-0.00390625;-1.03125
634262369735440000;0.00390625;-0.0078125;-1.03125
634262369735660000;-0.00390625;-0.00390625;-1.0390625
634262369735870000;0.00390625;0.0078125;-1.03515625
634262369736100000;0.00390625;-0.00390625;-1.03125
634262369736310000;0.00390625;0;-1.02734375
634262369736540000;0;-0.00390625;-1.0390625
634262369736750000;0;0;-1.0390625
634262369736960000;0;0;-1.03125
634262369737170000;-0.00390625;0.00390625;-1.0234375
634262369737380000;-0.00390625;0.00390625;-1.0234375
634262369737610000;0;0.00390625;-1.02734375
634262369737830000;0;-0.00390625;-1.05078125
634262369738040000;-0.00390625;0.00390625;-1.03125
634262369738260000;0.00390625;0.0078125;-1.0234375
634262369738470000;0.00390625;0.0078125;-1.01953125
634262369738680000;0;0.0078125;-1.0234375
634262369738890000;0;0.00390625;-1.03125
634262369739100000;0.00390625;0.0078125;-1.0234375
634262369739310000;0.0078125;0.0078125;-1.0234375

In summary, this blog post has shown:

  • How to mock an IObservable (i.e., event stream) with an IEnumerable by leveraging the IObservable/IEnumerable duality,
  • How to read application data from isolated storage, and
  • How to reconstruct the temporal dimension of sensory data with RxLINQ combinators. (There are other ways of implementing the temporal spacing; for now the combination of Zip and Skip reinforces a pattern I used in previous posts.)