Skip to content
68 changes: 51 additions & 17 deletions docs/data.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,18 +129,6 @@ To set any numeric type, including enumerations:
output.instance.setNumber('my_double', 2.14)
output.instance.setNumber('my_enum', 2)

.. warning::
The range of values for a numeric field is determined by the type
used to define that field in the configuration file. However,
``setNumber`` and ``getNumber`` can't handle 64-bit integers
(*int64* and *uint64*) whose absolute values are larger than 2^53.
This is a *Connector* limitation due to the use of *double* as an
intermediate representation.

When ``setNumber`` or ``getNumber`` detect this situation, they will raise
an :class:`DDSError`. ``getJson`` and ``setJson`` do not have this
limitation and can handle any 64-bit integer.

To set booleans:

.. code-block::
Expand Down Expand Up @@ -170,7 +158,7 @@ Similarly, to get a field in a :class:`Input` sample, use the appropriate
getter: :meth:`SampleIterator.getNumber()`, :meth:`SampleIterator.getBoolean()`,
:meth:`SampleIterator.getString()`, or the type-independent
:meth:`SampleIterator.get()`.
``getString`` also works with numeric fields, returning the number as a string:
:meth:`SampleIterator.getString` also works with numeric fields, returning the number as a string:

.. code-block::

Expand All @@ -191,17 +179,63 @@ getter: :meth:`SampleIterator.getNumber()`, :meth:`SampleIterator.getBoolean()`,


.. note::
The typed getters and setters perform better than ``set``
and ``get`` in applications that write or read at high rates.
Also prefer ``getJson`` and ``setFromJson`` over ``set``
and ``get`` when accessing all or most of the fields of a sample
The typed getters and setters perform better than :meth:`Instance.set`
and :meth:`SampleIterator.get` in applications that write or read at high rates.
Also, prefer :meth:`SampleIterator.getJson` and :meth:`Instance.setFromJson`
over :meth:`Instance.set` and :meth:`SampleIterator.get` when accessing all
or most of the fields of a sample
(see previous section).

.. note::
If a field ``my_string``, defined as a string in the configuration file, contains
a value that can be interpreted as a number, ``sample.get('my_string')`` returns
a number, not a string.

Accessing 64-bit integers
^^^^^^^^^^^^^^^^^^^^^^^^^
Internally, *Connector* relies on a framework that only contains a single number
type, an IEEE-754 floating-point number. Additionally, *Connector* does not use
JavaScript's BigInt representation for numbers, meaning JavaScript has this same limitation.
As a result, not all 64-bit integers can be represented with exact precision using all
APIs.
If your type contains uint64 or int64 members, and you expect them to be larger
Comment thread
samuelraeburn marked this conversation as resolved.
Outdated
than ``Number.MAX_SAFE_INTEGER`` (or smaller than ``Number.MIN_SAFE_INTEGER``),
then you must take the following into account.

64-bit values with an absolute value greater or equal to 2^53 can be set via:
- The type-agnostic setter, :meth:`Instance.set`. The values must be supplied
as strings, e.g., ``the_output.instance.set('my_uint64', '18446744073709551615')``.
- :meth:`Instance.setString`, e.g., ``the_output.instance.setString('my_uint64', '18446744073709551615')``.
- :meth:`Instance.setFromJson`, if the values are provided as strings, e.g., ``the_output.instance.setFromJson({my_uint64: '18446744073709551615'})``.

64-bit values with an absolute value greater than 2^53 can be retrieved via:
Comment thread
samuelraeburn marked this conversation as resolved.
- The type-agnostic getter, :meth:`SampleIterator.get`. If the absolute value of
the field is less than ``2^53`` it will be returned as a number; otherwise it will be
returned as a string, e.g., ``sample.get(0).get('my_int64') // '9223372036854775807' OR 1234``.
- Using :meth:`SampleIterator.getString`. The value will be returned as a string,
e.g., ``sample.getString(my_int64') // '9223372036854775807' OR '1234'``.

.. warning::

If :meth:`SampleIterator.getNumber()` is used to retrieve a value > 2^53 it will
throw a :class:`DDSError`.

.. warning::

If :meth:`Instance.setNumber()` is used to set a value >= 2^53 it will throw a
:class:`DDSError`.

.. warning::

The :meth:`SampleIterator.getJson()` method should not be used to retrieve integer
values larger than ``Number.MAX_SAFE_INTEGER`` (or smaller than ``Number.MIN_SAFE_INTEGER``).
The values returned may be corrupted **but no error will be thrown**.

.. note::

The :meth:`Instance.setNumber()` operation can safely handle ``abs(value) < 2^53``,
whereas the :meth:`SampleIterator.getNumber()` operation can safely handle ``abs(value) <= 2^53``.

Accessing structs
^^^^^^^^^^^^^^^^^

Expand Down
31 changes: 25 additions & 6 deletions rticonnextdds-connector.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,13 +321,17 @@ function _getAnyValue (getter, connector, inputName, index, fieldName) {
return !!boolVal.deref()
} else if (selection === _AnyValueKind.connector_string) {
const nodeStr = _moveCString(stringVal.deref())
// Try to convert the returned string to a JSON object. We can now return
// one of two things:
// If this is NOT a numeric string, try to convert the returned string to a
// JSON object. We can now return one of two things:
// - An actual string (if the JSON.parse call fails)
// - A JSON object (if the JSON.parse call succeeds)
try {
return JSON.parse(nodeStr)
} catch (err) {
if (isNaN(nodeStr)) {
try {
return JSON.parse(nodeStr)
} catch (err) {
return nodeStr
}
} else {
return nodeStr
}
} else {
Expand Down Expand Up @@ -496,6 +500,11 @@ class SampleIterator {
/**
* Gets the value of a numeric field in this sample.
*
* .. note::
* This operation should not be used with values with an aboslute value
* larger than `Number.MAX_SAFE_INTEGER`. See :ref:`Accessing 64-bit integers`
* for more information.
*
* @param {string} fieldName - The name of the field.
* @returns {number} The numeric value of the field.
*/
Expand Down Expand Up @@ -1279,6 +1288,11 @@ class Instance {
/**
* Sets a numeric field.
*
* .. note::
* This operation should not be used with values with an aboslute value
* larger than `Number.MAX_SAFE_INTEGER`. See :ref:`Accessing 64-bit integers`
* for more information.
*
* @param {string} fieldName - The name of the field.
* @param {number} value - A numeric value, or null, to unset an
* optional member.
Expand Down Expand Up @@ -1331,7 +1345,7 @@ class Instance {
* Sets a string field.
*
* @param {string} fieldName - The name of the field.
* @param {number} value - A string value, or null, to unset an
* @param {string|null} value - A string value, or null, to unset an
* optional member.
*/
setString (fieldName, value) {
Expand Down Expand Up @@ -1420,6 +1434,11 @@ class Instance {
/**
* Retrives the value of this instance as a JSON object.
*
* .. note::
* This operation should not be used with values with an aboslute value
* larger than `Number.MAX_SAFE_INTEGER`. See :ref:`Accessing 64-bit integers`
* for more information.
*
* @returns {JSON} The value of this instance as a JSON object.
*/
getJson () {
Expand Down
39 changes: 20 additions & 19 deletions test/nodejs/test_rticonnextdds_connector.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,45 +42,46 @@ describe('Connector Tests', function () {
})

it('Connector should get instantiated for valid' +
'xml and participant profile', function () {
'xml and participant profile', async function () {
const participantProfile = 'MyParticipantLibrary::Zero'
const xmlProfile = path.join(__dirname, '/../xml/TestConnector.xml')
const connector = new rti.Connector(participantProfile, xmlProfile)
expect(connector).to.exist
expect(connector).to.be.instanceOf(rti.Connector)
connector.close()
await connector.close()
})

it('Multiple Connector objects can be instantiated', () => {
it('Multiple Connector objects can be instantiated', async () => {
const participantProfile = 'MyParticipantLibrary::Zero'
const xmlProfile = path.join(__dirname, '/../xml/TestConnector.xml')
const connectors = []
for (let i = 0; i < 3; i++) {
connectors.push(new rti.Connector(participantProfile, xmlProfile))
}
connectors.forEach((connector) => {

connectors.forEach(async (connector) => {
expect(connector).to.exist
expect(connector).to.be.instanceOf(rti.Connector)
connector.close()
await connector.close()
})
})

// Test for CON-163
it('Multiple Connector obejcts can be instantiated without participant QoS', () => {
it('Multiple Connector obejcts can be instantiated without participant QoS', async () => {
const participantProfile = 'MyParticipantLibrary::MyParticipant'
const xmlProfile = path.join(__dirname, '/../xml/TestConnector3.xml')
const connectors = []
for (let i = 0; i < 2; i++) {
connectors.push(new rti.Connector(participantProfile, xmlProfile))
}
connectors.forEach((connector) => {
connectors.forEach(async (connector) => {
expect(connector).to.exist
expect(connector).to.be.instanceOf(rti.Connector)
connector.close()
await connector.close()
})
})

it('Load two XML files using the url group syntax', function () {
it('Load two XML files using the url group syntax', async function () {
const xmlProfile1 = path.join(__dirname, '/../xml/TestConnector.xml')
const xmlProfile2 = path.join(__dirname, '/../xml/TestConnector2.xml')
const fullXmlPath = xmlProfile1 + ';' + xmlProfile2
Expand All @@ -89,17 +90,18 @@ describe('Connector Tests', function () {
expect(connector).to.be.instanceOf(rti.Connector)
const output = connector.getOutput('MyPublisher2::MySquareWriter2')
expect(output).to.exist
connector.close()
await connector.close()
})

it('Should be possible to create a Connector with participant qos', function () {
const xmlProfile = path.join(__dirname, '/../xml/TestConnector.xml')
const connector = new rti.Connector(
'MyParticipantLibrary::ConnectorWithParticipantQos',
xmlProfile)
expect(connector).to.exist
expect(connector).to.be.instanceOf(rti.Connector)
})
// it('Should be possible to create a Connector with participant qos', async function () {
// const xmlProfile = path.join(__dirname, '/../xml/TestConnector.xml')
// const connector = new rti.Connector(
// 'MyParticipantLibrary::ConnectorWithParticipantQos',
// xmlProfile)
// expect(connector).to.exist
// expect(connector).to.be.instanceOf(rti.Connector)
// await connector.close()
// })

it('is possible to obtain the current version of Connector', function () {
const version = rti.Connector.getVersion()
Expand Down Expand Up @@ -133,7 +135,6 @@ describe('Connector Tests', function () {
expect(connector).to.exist
expect(connector).to.be.instanceOf(rti.Connector)
await connector.close()
await connector.close()
})

describe('Connector callback test', function () {
Expand Down
Loading