
0
Under review
Reading a DeviceIdentity that has a blank model field
I'm writing an OpenICE app designed to report the on/off state of various Devices. So far I've successfully modified Hello OpenICE code to Subscribe to HeartBeat and DeviceIdentity Topics. The HeartBeat data seems to arrive fine, but the DeviceIdentity data has problems.
In the received DeviceIdentity, the model name is blank, rather than being something like "Pulse Ox (simulated)". I've seen some symptoms that suggest that the model string gets filled in later, but I'm not certain of that.
I wanted to double-check with you that I'm using the correct pattern to read the DeviceIdentity data: I substituted class names in Hello OpenICE code based on some guesswork, so something may be incorrect. Based on the symptom I'm wondering whether I'm not properly telling DDS that I want to read the data contents. I haven't found an example of reading DeviceIdentity in the mdpnp git.
Here is the relevant code (adapted from HelloICE.java) - I apologize for the formatting. Note that on reception of a Pulse Ox (simulated) DeviceIdentity, the model field is blank.
Thanks for your help,
Brad
---------------------------------
public void on_data_available(DataReader reader) {
In the received DeviceIdentity, the model name is blank, rather than being something like "Pulse Ox (simulated)". I've seen some symptoms that suggest that the model string gets filled in later, but I'm not certain of that.
I wanted to double-check with you that I'm using the correct pattern to read the DeviceIdentity data: I substituted class names in Hello OpenICE code based on some guesswork, so something may be incorrect. Based on the symptom I'm wondering whether I'm not properly telling DDS that I want to read the data contents. I haven't found an example of reading DeviceIdentity in the mdpnp git.
Here is the relevant code (adapted from HelloICE.java) - I apologize for the formatting. Note that on reception of a Pulse Ox (simulated) DeviceIdentity, the model field is blank.
Thanks for your help,
Brad
---------------------------------
public void on_data_available(DataReader reader) {
ice.DeviceIdentitySeq deviceIdentitySeq = new ice.DeviceIdentitySeq();
SampleInfoSeq infoSeq = new SampleInfoSeq();
SampleInfoSeq infoSeq = new SampleInfoSeq();
DeviceIdentityDataReader diReader = (DeviceIdentityDataReader) reader;
try {
diReader.read(deviceIdentitySeq, infoSeq, ResourceLimitsQosPolicy.LENGTH_UNLIMITED, SampleStateKind.NOT_READ_SAMPLE_STATE, ViewStateKind.ANY_VIEW_STATE, InstanceStateKind.ALIVE_INSTANCE_STATE);
/*
* Bug: the model number info seems blank. Example output:
* Bug: the model number info seems blank. Example output:
* 2015-09-24 13:50:11 DEBUG IceQos:50 - Loaded default ice_library QoS
* 2015-09-24 13:51:30 ERROR DeviceIdentityListener:75 - Empty model in received DeviceIdentity
* 2015-09-24 13:51:30 INFO HelloIce:75 - ON: manuf: , model: , serial:
* 2015-09-24 13:51:47 INFO HelloIce:75 - OFF: manuf: , model: , serial:
*/
* 2015-09-24 13:51:30 ERROR DeviceIdentityListener:75 - Empty model in received DeviceIdentity
* 2015-09-24 13:51:30 INFO HelloIce:75 - ON: manuf: , model: , serial:
* 2015-09-24 13:51:47 INFO HelloIce:75 - OFF: manuf: , model: , serial:
*/
Date now = new Date();
for(int i = 0; i < infoSeq.size(); i++) {
SampleInfo si = (SampleInfo) infoSeq.get(i);
ice.DeviceIdentity data = (ice.DeviceIdentity) deviceIdentitySeq.get(i);
if(si.valid_data) {
if (data.model.length() == 0) {
logger.error("Empty model in received DeviceIdentity");
}
tracker.sawIdentity(data, now);
}
}
} catch (RETCODE_NO_DATA noData) {
// No Data was available to the read call
} finally {
// the objects provided by "read" are owned by the reader and we must return them
// so the reader can control their lifecycle
diReader.return_loan(deviceIdentitySeq, infoSeq);
}
}
Customer support service by UserEcho
To reproduce, I start my code, then start OpenICE Supervisor on the same PC, then use the Supervisor to create a simulated Pulse Oximeter. The code I show above receives the Pulse Oximeter's DeviceIdentity, but often (not always) that DeviceIdentity has a blank (0 length; not null) model String rather than a string like "Pulse Ox (Simulated)".
The workaround is to ignore received DeviceIdentity objects whose .model field is blank (not null; length == 0).
The bug is that the AbstractSimulatedConnectedDevice constructor calls writeDeviceIdentity() before the subclass has a chance to fill in that DeviceIdentity. Then the subclass constructor, for example SimPulseOximeter(), eventually gets control, fills in the fields of the DeviceIdentity, then (appropriately) calls writeDeviceIdentity().
Naively, it looks like the fix would be to remove the call to writeDeviceIdentity() from the constructor for AbstractSimulatedConnectedDevice. That's assuming that all of its subclasses' constructors call writeDeviceIdentity once they've filled in the DeviceIdentity fields.
This is fantastic work! Thank you for taking the time to track down the solution. Is there a way we can share this fix or workaround with others? You mentioned that your code is adapted from HelloICE.java, can we alter that to avoid this?
Thanks,
Jeff
On a more practical note: HelloICE.java doesn't have the problem, because it doesn't Subscribe to the DeviceIdentity Topic. I think the right thing would be to write an app that detects what devices are on or off. ...but as I mentioned here, I'm not sure we really know how to do that yet.
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.