Vortex Café : Power + Portability combined for “write once, run anywhere” data connectivity.

PrismTech features Vortex Café on its latest video. The only pure Java™ implementation of the OMG’s Data Distribution Service (DDS) that enables seamless, efficient and timely data sharing across multi-core machines, mobile and embedded devices, Vortex Café is optimized for Android OS and J2SE Java™ Virtual Machines (JVMs). It combines the benefits of the Java™ programming model with real-time data sharing enabling users to bring next generation Internet of Things (IoT) applications to market more quickly. Vortex Café is the first real-time peer-to-peer middleware infrastructure designed for mobility whilst enabling seamless interoperability with existing DDS systems. Vortex Café brings the benefits and productivity advantages of the Java™ programming language such as “write once, run anywhere” portability and programming efficiency, combined with the real-time data sharing performance and Quality-of-Service (QoS) control required by industrial and business-critical IoT systems.

Find out more about Vortex Café and download our latest resources.

Applying the Data Distribution Service in an IoT Healthcare System

In my last post, I described how the Data Distribution Service (DDS) standard is used to provide ubiquitous access to clinical measurements. In this post, I will be introducing the foundations of this enabling technology that can allow healthcare systems to reap the full potential of the Internet of Things.

The abstraction at the foundation of DDS is a global data space. Think of it as the virtual area where data on a patient is going to be gathered, shared, analyzed and acted upon. DDS applications—sensors, devices, applications, etc.—autonomously, anonymously, securely and efficiently cooperate by reading and writing Topics on this global data space – where a unique name, a type and a set of qualities of service (QoS) identify a Topic. Applications that join the global data space automatically discover their mutual existence as well as their interest (i.e. Topics produced and consumed). This information is then used to optimize the protocol and communication path used for efficient and high performance data sharing.

Conceptually the global data space is active, in the sense that it can retain data for late joiners and make it available even if the producer is no longer available. For example, a doctor can take advantage of this feature to see the history of clinical measurements produced by a given device. This feature is commonly called temporal decoupling as the producer and the consumer don’t need to be running at the same time in order to share data.

DDS’ global data space implementations are required to be fully distributed – no single point of failure or single point of bottleneck. This makes it possible for DDS-based system to scale out well and to experience very low latencies and high throughput. As an example, on contemporary hardware connected by a 1 Gbps Ethernet network, DDS has an inter-node latency of ~30 usec and can easily distributed several millions samples per second.

Anatomy of a DDS Application
Structurally a DDS application is composed by different entities (see picture below). The DomainParticipant provide access to a DDS domain. A domain represents a global data space instance and is identified by a natural number, 0, 1, 2, …, n. A domain can be further organized into partitions – partitions are used to organize data within the domain. Publishers and Subscribers control access to the partitions. Finally, the DataWriter and DataReader are used by applications to write and read data into the partitions associated with their respective Publisher and Subscriber.

As DDS is independent of the programming language and hardware and software platform, it allows applications written in different languages and deployed on different OS to exchange data – in other terms DDS takes care of representing data in a programming language and hardware independent manner, dealing with endianness and data layout. This means that a device developed using C++ and running Linux on an ARM processor is going to be able to send and receive data from a device developed using C# and running Windows Embedded on an x86 processor—and it won’t matter if the devices are in the same room, same facility, same state or same country.

Understanding DDS Topics
As I mentioned before, DDS uses Topics to represent the information shared between applications. So far, I’ve told you that a Topic has (1) a unique name, (2) a type used to represent the information associated with the Topic in a programming language, and (3) a set of qualities of service – but there is more. Topic can be keyed or keyless. When available, they key is used to identify a specific topic instances. Let’s try to understand this with an example. Imagine that we want to make available on DDS the data produced by an oxymeter. In this case the first thing we need to decide is how to model the data with a Topic type. Below you see the Topic type that may represent the information provided by a hypothetical oxymeter. The topic type is defined using IDL, but XML or Java could also be used.

struct Oxymetry {
string deviceId;
long spO2;
long bpm;
long pleth;
};
#pragma keylist Oximetry deviceId

Notice that with the #pragma directive I’ve defined the attribute deviceId as the key for the type. This attribute allows to uniquely identifying the device that produces the data. In DDS terms, each unique value of the deviceId attribute identifies a Topic instance. DDS provides specific life cycle information associated to instances, such as informing you when a new instance is available as well as when is no more in the system. When Topic instances are tied to device, the life-cycle management is particularly useful in learning about the operational status of the device itself.

The values assumed by a specific instance are called samples. Thus, DDS applications write samples belonging to an instance of a specific topic.

Reading and Writing Data
As mentioned above, in DDS data is read and written using DataWriter and DataReader. Specifically, a Datareader will receive in its local cache (a local projection of the global data space) the data written by the DataWriter if the two match. A Datareader and a DataWriter match if they have been defined for the same Topic and with compatible QoS. The set of data received by a Datareader can be controlled through content filters. Content filters are expressions defined with SQL WHERE syntax that predicate Topic attributes. For instance, the filter expression “spO2 < 85” could be used to receive the oxymeter data only when the saturation is below 85percent.

Quality of Service
DDS provides 22 Quality of Service policies that give very fine control over local resource, network utilization, traffic prioritization, and data management. For the complete list refer to the DDS Tutorial.

Your First DDS-Based Pulse Oxymeter
At this point we are ready to write our first oxymeter application. We will write the application that would run on the device and also another application that would display the readings on the console. Below I’ll provide you with the C++ code, but you can also check the Java and Scala version online.

Oxymeter
int main(int argc, char* argv[]) {
if (argc < 2) {
std::cout << “USAGE:\n\t oxymeter <device-id>” << std::endl;
return 1;
}

std::string deviceId(argv[1]);
DomainParticipant dp(0);
Topic<Oxymetry> topic(dp, “TOxymetry”);
Publisher pub(dp);

DataWriter<Oxymetry> dw(dp, topic);

// isActive() becomes false when the device is turning off.
while (isActive()) {
auto o = get_oximetry(deviceId);
// write data
dw.write(o);
std::this_thread::sleep_for(std::chrono::seconds(UPDATE_PERIOD));
}
return 0;
}

Logger
int main(int argc, char* argv[]) {
DomainParticipant dp(0);
Topic<Oxymetry> topic(dp, “TOxymetry”);
Subscriber sub(dp);

DataReader<Oxymetry> dr(dp, topic);

// Create the waitset
WaitSet ws;
// Create a ReadCondition for our data reader
// and configure it for new data
ReadCondition rc(dr, DataState::new_data());
// attache the condition
ws += rc;

while (true) {
ws.wait();
auto samples = dr.read();

std::for_each(samples.begin(),
samples.end(),
[](const dds::sub::Sample<Oxymetry>& s) {
std::cout << s.data() << std::endl;
});
}
return 0;
}

This example reveals how simple is to use DDS to share data. Beside the simplicity, as explained in my last blog post, once in DDS, the data can be made available across any device and shared at any scale. DDS-based platforms such as PrismTech’s Vortex provide a good example of how this can be done.

If you want to learn more about DDS, feel free to read the DDS Tutorial or check out the various educational slides.

OpenSplice Mobile – DDS has never been so much fun

Today we have released OpenSplice Mobile v1.1 and, as I’ll describe in this post, it brings DDS to a new level of usability and coolness!

To begin with, OpenSplice Mobile is a pure-Java implementation of DDS, meaning that you can run it on anything that has a Java VM, an embedded Java VM, or an Android VM. In essence, you can run OpenSplice Mobile on your Android phone / tablet, on your Raspberry Pi, on your Panda Board, etc., or simply on a more traditional computing platform, e.g. a server, a workstation or laptop. BTW, I have actually already demonstrated this as some of you may have seen at the Berlin OpenSplice User Meeting a couple of weeks ago.

At this point you may wonder how the same DDS implementation may be a good fit for both resource constrained embedded devices as well as high-end servers with multiple cores — the trick is OpenSplice Mobile SEDA (Staged Event Driven Architecture). By configuring the stages in the SEDA you can optimize OpenSplice Mobile for latency and embedded devices or for throughput and high-end servers. The configuration can be controlled via a property file or a series of “-D” options for the JVM.

Those of you that can’t wait to start hacking with OpenSplice Mobile, will be happy to know that it provides the new Java 5 API for DDS. This new API removes quite a bit of nonsense of the previous Java API and enforces a disciplined class loading — in essence you can control the class loader that will be loading classes for the DDS types. If that was not enough to wet your appetite, we have added a few cool features such as the ability to express queries and filters using Java and JavaScript.  This means that to query/filter DDS data you are not limited to what can be expressed by the WHERE clause of a SQL statement, you have the power of a predicate written in Java or in JavaScript! If you wonder about lambdas, we will obviously add support for the JDK8 version, yet at the present time OpenSplice Mobile requires a JDK6.

At this point , assuming you’ve written your first OpenSplice Mobile application and you would like to see its runtime structure —  you’d love to explore the jungle of DDS entities. How can you do that? OpenSplice Mobile has a Monitor that gives access to all information concerning the DDS entities as well as the protocol entities. With the Monitor you can browse DDS entities, verify QoS settings, check the status of reliability queues etc. (see snapshot below). If you have looked at the Mobile distribution you may wonder how you start it. Things could not be any simpler, you just have to make sure that the monitor Jar is in the Java classpath and then simply point your browser to http://localhost:8080 (or the URL of the machine where you want to inspect the DDS app). If the port 8080 is taken the monitor will automatically take the first free, e.g. 8081, or 8082 etc.

OpenSplice Mobile Graphic

Now, if you have not done so already, go and get OpenSplice Mobile from our Software Downloads and start hacking some code!

Good Hacking,
Angelo

P.S. If you want to play with the OpenSplice Mobile iShapes application on your Android device you can get it from the Google Play Store