Your comments

Alejandro,

The server is not currently able to receive any data except requests. The API used by the browser components could also be used under node.js to access data. It is documented in the code. You can start by instantiating an OpenICE object with the website URL "https://www.openice.info". From there you can create tables to gain access to data. You can also check out our usage of that API to build the web demo on openice.info.

Thanks
The errors indicate an inability to receive libraries from our artifact server either due to network connectivity issues or issues with the server. Often such errors are transient. Do you ever retry the build? I've tried rebuilding several times successfully and cannot reproduce the problem.

Also, the hello-openice project runs the program as its default target. So it will show that the "build" is at 75% when it runs the program. This is expected behavior. From that point the program will print out any OpenICE traffic it detects. If there is no traffic present nothing will be output. The intent of the "hello-openice" project is for people to examine the code and see how they can access data in the OpenICE system.

Thank you
I haven't been able to reproduce this problem. The error indicated is a bad HTTP response from a server hosting libraries used in the OpenICE software. Have you retried the build (and thus the HTTP request) since then? Often network errors are transient. If the problem persists it may have to do with your network connection or the performance of the hosting server. I've built several times here and received correct responses from all artifact servers each time.

Thanks
Jeff
Brad

It's not just the onion tears obscuring what's at work here. You are right that TimeManager is somewhat monolithic. It's a place where a lot of the magic lives to keep it safe. I'll try to explain some of the magic .. specifically how TimeManager tracks the liveliness of HeartBeat instances. But first to clarify Participants contain Subscribers (1:n) and Subscribers contain DataReaders (1:n). In DDS most of the "action" happens at the DataReader level. A Subscribers biggest job is to contain DataReaders. One other important job is that all the DataReaders inherit their "partition" from the containing Subscriber. There's a picture of it in RTI's documentation.

DDS tracks "liveliness" of data instances in accordance with the Liveliness QoS policy. Currently in OpenICE readers of the HeartBeat topic should use the "heartbeat" named QoS profile. In the code use of this profile looks like this:
hbReader = (HeartBeatDataReader) subscriber.create_datareader_with_profile(cfHbTopic, QosProfiles.ice_library, QosProfiles.heartbeat, null, StatusKind.STATUS_MASK_NONE);
and the profile specifies a 5 second lease duration (instances are marked "NOT ALIVE" when no new samples have arrived or the writer of the instance hasn't responded before the lease times out). The next line creates a ReadCondition which can be added to a WaitSet to get notified whenever an unread sample state exists in the reader.
hbReadCond = hbReader.create_readcondition(SampleStateKind.NOT_READ_SAMPLE_STATE, ViewStateKind.ANY_VIEW_STATE, InstanceStateKind.ANY_INSTANCE_STATE);
That condition is registered in the event loop with a handler.
eventLoop.addHandler(hbReadCond, hbReadHandler);
The handler then reads from the DataReader when the collection is active
hbReader.read(hb_seq, sa_seq, ResourceLimitsQosPolicy.LENGTH_UNLIMITED, SampleStateKind.NOT_READ_SAMPLE_STATE, ViewStateKind.ANY_VIEW_STATE, InstanceStateKind.ANY_INSTANCE_STATE);
and checks the SampleInfo.instance_state flag for the current liveliness state of an instance. So if the HeartBeat is no longer alive
if(0!=(InstanceStateKind.NOT_ALIVE_INSTANCE_STATE&sampleInfo.instance_state)) {
Or if the Heartbeat instance is alive
if(0!=(InstanceStateKind.ALIVE_INSTANCE_STATE&sampleInfo.instance_state)) {
different action can be taken.

In this way the current "live" and "not alive" instances of HeartBeat can be tracked. DDS is stateful so you could also do this without events periodically with calls to read_next_instance but somehow the API there gets even more obtuse.

Cheers!
Jeff Plourde
Brad

Yet another great question and I hope this is useful to others.

Before I attempt to explain some of the DDS APIs I'd like to explain the underlying requirements. OpenICE is envisioned as allowing the user to interact with a shard of a larger distributed data store which is automatically coordinated with other nodes of the system. In other more modern systems "reactivity" is built in at quite a low level to such a system. In other words libraries and APIs acknowledge certain components as "reactive". This enables concepts like declarative data binding where the relationship between two changing data elements can be "bound". Actually a lot of OpenICE code is devoted to adapting DDS structures to the JavaFX version of reactive collections and data elements which can then be "bound" to UI elements. The pattern is actually useful beyond UIs and declarative data binding code can be, in many cases, more expressive and predictable.

The rest of my response will focus on the DDS APIs which do not work in this way. Each DDS entity may emit status changes as an event. Readers also emit changes to contained data in a similar fashion. The EventLoop is an "event loop" in the traditional sense but in OpenICE it is used most often to handle DDS events. As opposed to considering "events" equivalent to "messages" in message-oriented middleware it's better to think of them more generically because the same suite of events are always generated by DDS related to the underlying user data. "Events" here are not composed by the user as in a message oriented middleware. Therefore the creation/disposal of keyed data instances are two types of events. The new availability of data samples for an instance is another type. Each DDS "Entity" can emit a variety of events.

There are three options for the handling of events in "user level" code. Perhaps the simplest strategy is polling. If ultra low latency is prioritized well ahead of resource usage you can poll a DDS entity for status changes with get_status_changes() Or if the DATA_AVAILABLE status is what you are interested in you could poll with repeated calls to read or take.

Another simple strategy is to receive callbacks from the DDS libraries when data or events are available to process. This is a simple way to get started either by passing a listener into a constructor at entity creation time or utilizing set_listener calls. While this is super convenient it's best for rapid handling of events only (perhaps enqueuing them into another processing system) because internal DDS threads are being used to deliver events to you. Any blocking of that callback thread could change the behavior and performance of DDS.

The third option is communication via condition variables and WaitSets. These tools allow you to block your thread until any of a set of conditions becomes active. For instance a single thread could service several DataReaders by adding ReadConditions from those readers to a WaitSet. The EventLoop is something I built to manage this in a manner similar to existing event handling loops (like glib). It allows one to register a variety of conditions and callbacks for handling those conditions. The EventLoop is driven by an external thread (see EventLoopHandler). In a more complex system this might be a thread pool but for now it's a single thread so that handlers don't require reentrancy (which simplifies a lot of things greatly).

So in essence the EventLoop is a way to centralize handling of various DDS events onto a particular thread. This is less wasteful of CPU cycles than polling and it is less likely to affect the operation of internal DDS threads. It also ensures that the handling of events is synchronized in one thread simplifying concurrency.

I hope this is helpful. An EventLoop is NOT required to utilize DDS but with de-coupled component-based software all interacting with DDS in-process this is a helpful piece of infrastructure.

Keep the great questions coming!

Thanks
Jeff



1.) I posed this on the other thread as well but conceptually how would *you* detect that a device is off or on? I don't intend to be facetious but it can become a philosophical question. A future OpenICE aware medical device would publish its heartbeats to indicate it's presence on the network. To accommodate device adapter surrogates for legacy medical devices we created the DeviceConnectivity topic. (as an aside there is a gap here because nothing in HeartBeat or DeviceIdentity indicates whether the device is a surrogate and whether or not DeviceConnectivity should be expected. Currently all our devices and simulators publish DeviceConnectivity for consistency). In the existing apps and code a medical device is considered "on" the network if its HeartBeat instance is ALIVE AND its DeviceConnectivity is in the Connected state.

2.) Again I'd refer you to the DeviceListModelImpl and accessory classes thereof. In the Supervisory UI devices with live HeartBeats are admitted to the UI for display. If their DeviceConnectivity doesn't exist (so far unreported) or is NOT in the Connected state they are represented with a circle/slash indicating that the device is unavailable.

But this is a great discussion and we should continue to discuss the edge cases.

Thank you
Jeff
Hi Brad

To the best of my knowledge it's similar to POSIX struct timespec. So you're absolutely correct that the signed 32 bits since the UNIX epoch means it inherits the year 2038 problem. For Time_t as it is used by DDS (mainly in SampleInfo) a monotonic clock might eventually be preferable; although it could be confusing that "Time_t" encodes only a counter if configured that way. A discussion of monotonic versus realtime clock usage appears in RTI's documentation. The availability of the monotonic clock is platform-specific

The Time_t defined in ice.idl (used in device_time and presentation_time) could be changed at the "OpenICE" level. We could easily convert the seconds component to a 'long long' 64-bit value in a later version of OpenICE for the benefit of our descendants in 2038. I think this makes sense. What do you think?

Thank you
Jeff
I'll look forward to reading the other post but I'm curious how one could detect the power state of a device reliably short of monitoring electrical current? The DeviceConnectivity samples will indicate whether a connection has been established/detected on the port but you're absolutely right that it's not necessarily indicative of the power state of the remote device. A powered down device will never establish a connection or transmit data and therefore remain in the "Connecting" state. A powered up device would, if its interface is functioning correctly, progress to the Connected state on the DeviceConnectivity topic. If an interface shuts down (maybe because the device powered down or maybe just because the interface stopped functioning) a timeout will occur reverting the DeviceConnectivity state back to Connecting.

It's also intentional that DeviceIdentity fields are often populated progressively. For instance for some device 'driver' software the Manufacturer is known initially (because the driver itself is manufacturer-specific) but the Model will be received later in the course of communication with the device. Similarly the serial number may subsequently be populated as communication with the device progresses. Also some devices never emit some of the data (especially something like serial number) so it's impossible to "hold up" publication until the entire DeviceIdentity object can be filled out. While some day manufacturers may agree to a small subset of these fields being mandatory there will always be optional fields. For instance the latest revision to the Device resource in HL7 FHIR also specifies 0..1 cardinality for descriptive fields.

Without sounding like a buzzword machine a more reactive approach to handling these data might serve better. For instance in the OpenICE UIs many DDS topics are peered to JavaFX collections of enriched JavaFX objects. Transforming from a paradigm of "handling events" to one of "binding data". The ultimate flexibility of this approach is exemplified in the existing code by the VitalModelImpl where basic data properties received from DDS are combined into ever more complex dynamic (reactive) properties which are easily bound to the UI declaratively. I hope that future iterations of the DDS infrastructure allow this to occur more natively. The classes currently generated by IDL are not even valid java beans much less reactive objects and I hope that can change in the future.
Jeff Peterson is absolutely right. I'll add further detail as well. For an overview of why the "HeartBeat" topic exists check this post out. For the existing OpenICE code that tracks HeartBeat, DeviceIdentity, and DeviceConnectivity please check out DeviceListModelImpl.

The DDS Writer/Reader construct is a type of distributed data cache (albeit with 'unique' APIs). When a writer and reader are matched (on topic name, partition, and QoS request/offer compatibility) the samples in the writer are synchronized with the reader. Controlled by QoS, the "Writer" is a collection and the "Reader" is a remote peer collection. By entering samples into a Writer you are expressing the desire that those samples make their way into all matched Readers. The paradigm is data-centric and not message oriented. Provided that a writer is configured to maintain a history of samples, transmit reliably, and maintain samples at least transiently then a "late joiner" will automatically receive extant samples when matched with a writer that already contains samples (OpenICE uses this type of QoS). Depending on your point of view you could call it "confusing" or maybe "flexible" that with different QoS settings (not the ones used in OpenICE) the same middleware becomes message-oriented.

Transient detachment from and reattachment to the network is a challenge. Specifically in this case
1.) Writer has synchronized all its samples to Reader
2.) Detachment -- Reader marks samples as NOT_ALIVE
3.) Re-attachment -- Writer and Reader verify that they are already at the *same* sequence number .. so no sample information is exchanged .. and samples remain in the "NOT_ALIVE" state
I would have thought in this case that existing samples would be re-marked ALIVE when liveliness is regained but it's underspecified in the standard and not implemented in this way by RTI (still within the bounds of the standard).

In theory, one approach could be to maintain a DeviceIdentity Reader and interrogate it on-demand for the most recent sample of a given instance. Populating a DeviceIdentity instance with only its keys (unique_device_identifier) would allow invocation of lookup_instance after which the returned InstanceHandle_t could be used in read_instance with max_samples of 1 to retrieve the most recent sample. I've never found this to be stable. And since auto-generated (from IDL) objects cannot be decorated for JavaFX anyway I'm already forced to maintain peer collections to my readers which I tend to use for this purpose. Maintaining all most recent samples without regard to liveliness. This is the strategy in DeviceListModelImpl.

I hope this helps.