≡ Menu

Smart Grids, part tres: Data!

This is the third part of the Smart Grid article.

In the first part, we talked about the concepts that went into smart grids.

In the second part, we talked about requirements for an application that would demonstrate many of the concepts that went into a smart application.

Now let’s talk a little about data.

However, before we can talk about data, we need to be aware of our project structure.

We’re going to organize our project as a single Maven parent project, with various subprojects making up the artifacts that comprise our application. Therefore, we’re going to use the directory of the parent project as “home,” and our references to projects as part of the parent project will be relative to that home directory.

Well, that was simple, wasn’t it? So let’s dive in.

So what does our data object need? It tracks light levels, so obviously a light level is required; an identifier that indicates the source would be appropriate, as well as a geolocation that indicates where that source resides. We don’t need to track data over time, but a timestamp that indicates the “liveness” of the data would be nice.

So our presumptive data object might look like this:

class DataPoint {
   var identifier;
   var geolocation;
   var lightLevel;
   var timestamp;
}

Looks a little like Scala, doesn’t it? That’s okay. It’s good, actually, but we’re going to be using Java, instead; we’re going to eventually translate this to standard Java (speeding up our simplicity and compile times by quite a bit along the way.) We’re just using something Scala-like to do our modeling.

One other thing that stands out that may or may not be obvious: what is the “light level?” How is it represented? What’s the scale?

Is a lightLevel of “1.0” the maximum light level? (“Full bright?”) Is it an integer, or an RGB color?

Well, a light sensor is based on a photoresistor, which allows more electricity through its circuit as the light level increases. That indicates that it’s not a composite reading at its base, and could represent a percentage – “the amount of current allowed through is 75%,” for example.

The chances of an API providing that kind of reading? Very low, as it turns out – and the Sensor object that Android provides gives us a bit of a guide.

android.hardware.Sensor provides a method to get the maximum range for a given sensor, called (oddly enough) getMaximumRange().

So the sensor event will pass back a floating point number (as that’s the value type the SensorEvent contains, and that number will be between 0.0 and the value returned from getMaximumRange().

We can’t assume that every device is going to have the same scale or photoresistor. Therefore, we’re going to…

Add a value to our data object.

Wait, why the emphasis? Is that a big deal?

It could be, actually. One of the things that kills networked applications (grid applications) is data transfer, back and forth. Adding a data item needs to be considered very carefully.

At this point, adding one data item probably isn’t an issue. Our standard packet size is 1500 bytes, which gives us some wiggle room in terms of laying out our data.

If we actually start pushing that single packet size, we might want to encode the data somehow (whether with a data structure a la protocol buffers or by simply compressing the data).

We could also change how the data is exchanged.

Putting the maximum range into the data point itself works when our data is very simple – but it’s also a constant for a given data provider.

We have the option of using a sort of “registration process,” where a device notifies the application before it sends sensor data. That registration could be as simple as saying “device XYZ has a maximum range of A,” and then the application could use that to gauge the light sensor data.

Is that necessary?

Hmm. For this application, probably not; it’s conceivable that we might add more data elements, but not likely, and even if we did, we wouldn’t be pushing the packet size limit.

Let’s opt for simplicity in this revision, and perhaps in a future branch we’ll demonstrate the registration mechanism for the sake of completeness.

One other thought: our geolocation is comprised of a longitude and latitude. From an object-oriented standpoint, representing it as a single object is appropriate, but for simplicity’s sake (again) we’ll break it into its component parts. Therefore, our object will look like this class, with a number of accessor and utility methods eliminated for brevity:

class DataPoint {
   String deviceId;
   Double longitude;
   Double latitude;
   Long level;
   Long maxLevel;
   Long timestamp;
}

You’ll notice a few oddities in this class description.

For one thing, I’m using object references instead of primitives: Double over double, for example.

The simplest explanation is “that’s the way I’ve done it,” but there is a reason: query by example.

If you use query-by-example, you need a logical wildcard value. Most such systems use null as a wildcard (although many can specify other values as wildcard values).

It also means we can indicate that a given data point is lacking data: if the geolocation hasn’t been determined, for example, we can leave those fields empty without having a default geolocation of an uninhabited spot in the Atlantic Ocean (which would correspond to longitude 0, latitude 0.)

Another change is the use of an integral for the levels, instead of a floating point. This one’s harder to defend, and may change – but it just so happens that in testing the light levels are integral in nature. That doesn’t mean every device will respond the same way, and therefore this might change in the future.

The code for DataPoint as it exists is on github; feel free to take a look. We’re going to hop over to our actual data broker soon.