RTI Connext DDS Micro C++ API  Version 3.0.3
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
FooDataWriter Struct Reference

Declares the interface required to support a user data type-specific data writer. More...

#include <dds_cpp_data.hxx>

Inherits DDSDataWriter_impl.

List of all members.

Public Member Functions

DDSDataWriteras_datawriter ()
 Widen the given FooDataWriter pointer to a DDSDataWriter pointer.
DDS_ReturnCode_t write (const Foo &instance_data, const DDS_InstanceHandle_t &handle)
 Modifies the value of a data instance.
DDS_InstanceHandle_t register_instance (const Foo &instance_data)
 Informs RTI Connext DDS Micro that the application will be modifying a particular instance.
DDS_InstanceHandle_t register_instance_w_timestamp (const Foo &instance_data, const DDS_Time_t &source_timestamp)
 Performs the same functions as register_instance except that the application provides the value for the source_timestamp.
DDS_ReturnCode_t unregister_instance (const Foo &instance_data, const DDS_InstanceHandle_t &handle)
 Reverses the action of FooDataWriter::register_instance.
DDS_ReturnCode_t unregister_instance_w_timestamp (const Foo &instance_data, const DDS_InstanceHandle_t &handle, const DDS_Time_t &source_timestamp)
 Performs the same function as FooDataWriter::unregister_instance except that it also provides the value for the source_timestamp.
DDS_ReturnCode_t dispose (const Foo &instance_data, const DDS_InstanceHandle_t &handle)
 Requests the middleware to delete the data.
DDS_ReturnCode_t dispose_w_timestamp (const Foo &instance_data, const DDS_InstanceHandle_t &handle, const DDS_Time_t &source_timestamp)
 Performs the same functions as dispose except that the application provides the value for the source_timestamp that is made available to DDSDataReader objects by means of the source_timestamp attribute inside the DDS_SampleInfo.
DDS_ReturnCode_t write_w_timestamp (const Foo &instance_data, const DDS_InstanceHandle_t &handle, const DDS_Time_t &source_timestamp)
 Performs the same function as FooDataWriter::write except that it also provides the value for the source_timestamp.
DDS_ReturnCode_t get_loan (Foo *&sample)
 Gets a sample managed by the DataWriter.
DDS_ReturnCode_t discard_loan (Foo &sample)
 Returns a loaned sample back to the DataWriter.
Foo * create_data ()
 Creates a data sample and initializes it.
bool delete_data (Foo *sample)
 Destroys a user data type instance.

Static Public Member Functions

static FooDataWriternarrow (DDSDataWriter *writer)
 Narrow the given DDSDataWriter pointer to a FooDataWriter pointer.

Detailed Description

Declares the interface required to support a user data type-specific data writer.


Member Function Documentation

static FooDataWriter* FooDataWriter::narrow ( DDSDataWriter writer)
static

Narrow the given DDSDataWriter pointer to a FooDataWriter pointer.

Check if the given writer is of type FooDataWriter.

Parameters:
writer<<in>> Base-class DDSDataWriter to be converted to the auto-generated class FooDataWriter that extends DDSDataWriter.
Returns:
FooDataWriter if writer is of type Foo. Return NULL otherwise.
DDSDataWriter* FooDataWriter::as_datawriter ( )

Widen the given FooDataWriter pointer to a DDSDataWriter pointer.

Returns:
DDSDataWriter.
DDS_ReturnCode_t FooDataWriter::write ( const Foo &  instance_data,
const DDS_InstanceHandle_t handle 
)

Modifies the value of a data instance.

When this operation is used, RTI Connext DDS Micro will automatically supply the value of the source_timestamp that is made available to DDSDataReader objects by means of the source_timestamp attribute inside the DDS_SampleInfo. (Refer to DDS_SampleInfo details).

As a side effect, this operation asserts liveliness on the DDSDataWriter itself, the DDSPublisher and the DDSDomainParticipant.

Note that the special value DDS_HANDLE_NIL can be used for the parameter handle. This indicates the identity of the instance should be automatically deduced from the instance_data (by means of the key).

If handle is any value other than DDS_HANDLE_NIL, then it must correspond to an instance that has been registered. If there is no correspondence, the operation will fail with DDS_RETCODE_BAD_PARAMETER.

RTI Connext DDS Micro will not detect the error when the handle is any value other than DDS_HANDLE_NIL, corresponds to an instance that has been registered, but does not correspond to the instance deduced from the instance_data (by means of the key). RTI Connext DDS Micro will treat as if the write() operation is for the instance as indicated by the handle.

This operation may block if the RELIABILITY kind is set to DDS_RELIABLE_RELIABILITY_QOS and the modification would cause data to be lost or else cause one of the limits specified in the RESOURCE_LIMITS to be exceeded.

Specifically, this operation may block in the following situations (note that the list may not be exhaustive), even if its DDS_HistoryQosPolicyKind is DDS_KEEP_LAST_HISTORY_QOS:

If this operation does block for any of the above reasons, the RELIABILITY max_blocking_time configures the maximum time the write operation may block (waiting for space to become available). If max_blocking_time elapses before the DDSDataWriter is able to store the modification without exceeding the limits, the operation will time out (DDS_RETCODE_TIMEOUT).

If there are no instance resources left, this operation may fail with DDS_RETCODE_OUT_OF_RESOURCES. Calling FooDataWriter::unregister_instance may help freeing up some resources.

Parameters:
instance_data<<in>> The data to write.
handle<<in>> Either the handle returned by a previous call to FooDataWriter::register_instance, or else the special value DDS_HANDLE_NIL. If Foo has a key and handle is not DDS_HANDLE_NIL, handle must represent a registered instance of type Foo. Otherwise, this method may fail with DDS_RETCODE_BAD_PARAMETER.
Returns:
One of the Standard Return Codes, DDS_RETCODE_TIMEOUT, DDS_RETCODE_OUT_OF_RESOURCES or DDS_RETCODE_NOT_ENABLED.
See also:
DDSDataReader
FooDataWriter::write_w_timestamp
DDS_InstanceHandle_t FooDataWriter::register_instance ( const Foo &  instance_data)

Informs RTI Connext DDS Micro that the application will be modifying a particular instance.

This operation is only useful for keyed data types. Using it for non-keyed types causes no effect and returns DDS_HANDLE_NIL. The operation takes as a parameter an instance (of which only the key value is examined) and returns a handle that can be used in successive write() or dispose() operations.

The operation gives RTI Connext DDS Micro an opportunity to pre-configure itself to improve performance.

The use of this operation by an application is optional even for keyed types. If an instance has not been pre-registered, the application can use the special value DDS_HANDLE_NIL as the DDS_InstanceHandle_t paramater to the write or dispose operation and RTI Connext DDS Micro will auto-register the instance.

For best performance, the operation should be invoked prior to calling any operation that modifies the instance, such as FooDataWriter::write, FooDataWriter::write_w_timestamp, FooDataWriter::dispose and FooDataWriter::dispose_w_timestamp and the handle used in conjunction with the data for those calls.

When this operation is used, RTI Connext DDS Micro will automatically supply the value of the source_timestamp that is used.

This operation may fail and return DDS_HANDLE_NIL if DDS_ResourceLimitsQosPolicy::max_instances limit has been exceeded.

The operation is idempotent. If it is called for an already registered instance, it just returns the already allocated handle. This may be used to lookup and retrieve the handle allocated to a given instance.

This operation can only be called after DDSDataWriter has been enabled. Otherwise, DDS_HANDLE_NIL will be returned.

Parameters:
instance_data<<in>> The instance that should be registered. Of this instance, only the fields that represent the key are examined by the function. .
Returns:
For keyed data type, a handle that can be used in the calls that take a DDS_InstanceHandle_t, such as write, dispose, unregister_instance, or return DDS_HANDLE_NIL on failure. If the instance_data is of a data type that has no keys, this function always return DDS_HANDLE_NIL.
See also:
FooDataWriter::unregister_instance
DDS_InstanceHandle_t FooDataWriter::register_instance_w_timestamp ( const Foo &  instance_data,
const DDS_Time_t source_timestamp 
)

Performs the same functions as register_instance except that the application provides the value for the source_timestamp.

The provided source_timestamp potentially affects the relative order in which readers observe events from multiple writers.

This operation may fail and return DDS_HANDLE_NIL if DDS_ResourceLimitsQosPolicy::max_instances limit has been exceeded.

This operation can only be called after DDSDataWriter has been enabled. Otherwise, DDS_HANDLE_NIL will be returned.

Parameters:
instance_data<<in>> The instance that should be registered. Of this instance, only the fields that represent the key are examined by the function.
source_timestamp<<in>> The timestamp value must be greater than or equal to the timestamp value used in the last writer operation (used in a register, unregister, dispose, or write, with either the automatically supplied timestamp or the application provided timestamp). This timestamp may potentially affect the order in which readers observe events from multiple writers.
Returns:
For keyed data type, return a handle that can be used in the calls that take a DDS_InstanceHandle_t, such as write, dispose, unregister_instance, or return DDS_HANDLE_NIL on failure. If the instance_data is of a data type that has no keys, this function always return DDS_HANDLE_NIL.
See also:
FooDataWriter::unregister_instance
DDS_ReturnCode_t FooDataWriter::unregister_instance ( const Foo &  instance_data,
const DDS_InstanceHandle_t handle 
)

Reverses the action of FooDataWriter::register_instance.

This operation is useful only for keyed data types. Using it for non-keyed types causes no effect and reports no error. The operation takes as a parameter an instance (of which only the key value is examined) and a handle.

This operation should only be called on an instance that is currently registered. This includes instances that have been auto-registered by calling operations such as write or dispose as described in FooDataWriter::register_instance. Otherwise, this operation may fail with DDS_RETCODE_BAD_PARAMETER.

This only need be called just once per instance, regardless of how many times register_instance was called for that instance.

When this operation is used, RTI Connext DDS Micro will automatically supply the value of the source_timestamp that is used.

This operation informs RTI Connext DDS Micro that the DDSDataWriter is no longer going to provide any information about the instance. This operation also indicates that RTI Connext DDS Micro can locally remove all information regarding that instance. The application should not attempt to use the handle previously allocated to that instance after calling FooDataWriter::unregister_instance().

The special value DDS_HANDLE_NIL can be used for the parameter handle. This indicates that the identity of the instance should be automatically deduced from the instance_data (by means of the key).

If handle is any value other than DDS_HANDLE_NIL, then it must correspond to an instance that has been registered. If there is no correspondence, the operation will fail with DDS_RETCODE_BAD_PARAMETER.

RTI Connext DDS Micro will not detect the error when the handle is any value other than DDS_HANDLE_NIL, corresponds to an instance that has been registered, but does not correspond to the instance deduced from the instance_data (by means of the key). RTI Connext DDS Micro will treat as if the unregister_instance() operation is for the instance as indicated by the handle.

If after a FooDataWriter::unregister_instance, the application wants to modify (FooDataWriter::write or FooDataWriter::dispose) an instance, it has to register it again, or else use the special handle value DDS_HANDLE_NIL.

This operation does not indicate that the instance is deleted (that is the purpose of FooDataWriter::dispose). The operation FooDataWriter::unregister_instance just indicates that the DDSDataWriter no longer has anything to say about the instance. DDSDataReader entities that are reading the instance may receive a sample with DDS_NOT_ALIVE_NO_WRITERS_INSTANCE_STATE for the instance, unless there are other DDSDataWriter objects writing that same instance.

This operation can affect the ownership of the data instance (see OWNERSHIP). If the DDSDataWriter was the exclusive owner of the instance, then calling unregister_instance() will relinquish that ownership.

If DDS_ReliabilityQosPolicy::kind is set to DDS_RELIABLE_RELIABILITY_QOS and the unregistration would overflow the resource limits of this writer or of a reader, this operation may block for up to DDS_ReliabilityQosPolicy::max_blocking_time; if this writer is still unable to unregister after that period, this method will fail with DDS_RETCODE_TIMEOUT.

Parameters:
instance_data<<in>> The instance that should be unregistered. If Foo has a key and instance_handle is DDS_HANDLE_NIL, only the fields that represent the key are examined by the function. Otherwise, instance_data is not used. If instance_data is used, it must represent an instance that has been registerd. Otherwise, this method may fail with DDS_RETCODE_BAD_PARAMETER .
handle<<in>> represents the instance to be unregistered. If Foo has a key and handle is DDS_HANDLE_NIL, handle is not used and instance is deduced from instance_data. If Foo has no key, handle is not used. If handle is used, it must represent an instance that has been registered. Otherwise, this method may fail with DDS_RETCODE_BAD_PARAMETER.
Returns:
One of the Standard Return Codes, DDS_RETCODE_TIMEOUT or DDS_RETCODE_NOT_ENABLED
See also:
FooDataWriter::register_instance
FooDataWriter::unregister_instance_w_timestamp
DDS_ReturnCode_t FooDataWriter::unregister_instance_w_timestamp ( const Foo &  instance_data,
const DDS_InstanceHandle_t handle,
const DDS_Time_t source_timestamp 
)

Performs the same function as FooDataWriter::unregister_instance except that it also provides the value for the source_timestamp.

The provided source_timestamp potentially affects the relative order in which readers observe events from multiple writers.

The constraints on the values of the handle parameter and the corresponding error behavior are the same specified for the FooDataWriter::unregister_instance operation.

This operation may block and may time out (DDS_RETCODE_TIMEOUT) under the same circumtances described for the unregister_instance operation.

Parameters:
instance_data<<in>> The instance that should be unregistered. If Foo has a key and instance_handle is DDS_HANDLE_NIL, only the fields that represent the key are examined by the function. Otherwise, instance_data is not used. If instance_data is used, it must represent an instance that has been registerd. Otherwise, this method may fail with DDS_RETCODE_BAD_PARAMETER.
handle<<in>> represents the instance to be unregistered. If Foo has a key and handle is DDS_HANDLE_NIL, handle is not used and instance is deduced from instance_data. If Foo has no key, handle is not used. If handle is used, it must represent an instance that has been registered. Otherwise, this method may fail with DDS_RETCODE_BAD_PARAMETER.
source_timestamp<<in>> The timestamp value must be greater than or equal to the timestamp value used in the last writer operation (used in a register, unregister, dispose, or write, with either the automatically supplied timestamp or the application provided timestamp). This timestamp may potentially affect the order in which readers observe events from multiple writers.
Returns:
One of the Standard Return Codes, DDS_RETCODE_TIMEOUT or DDS_RETCODE_NOT_ENABLED.
See also:
FooDataWriter::register_instance
FooDataWriter::unregister_instance
DDS_ReturnCode_t FooDataWriter::dispose ( const Foo &  instance_data,
const DDS_InstanceHandle_t handle 
)

Requests the middleware to delete the data.

This operation is useful only for keyed data types. Using it for non-keyed types has no effect and reports no error.

The actual deletion is postponed until there is no more use for that data in the whole system.

Applications are made aware of the deletion by means of operations on the DDSDataReader objects that already knew that instance. DDSDataReader objects that didn't know the instance will never see it.

This operation does not modify the value of the instance. The instance_data parameter is passed just for the purposes of identifying the instance.

When this operation is used, RTI Connext DDS Micro will automatically supply the value of the source_timestamp that is made available to DDSDataReader objects by means of the source_timestamp attribute inside the DDS_SampleInfo.

The constraints on the values of the handle parameter and the corresponding error behavior are the same specified for the FooDataWriter::unregister_instance operation.

The special value DDS_HANDLE_NIL can be used for the parameter instance_handle. This indicates the identity of the instance should be automatically deduced from the instance_data (by means of the key).

If handle is any value other than DDS_HANDLE_NIL, then it must correspond to an instance that has been registered. If there is no correspondence, the operation will fail with DDS_RETCODE_BAD_PARAMETER.

RTI Connext DDS Micro will not detect the error when the handle is any value other than DDS_HANDLE_NIL, corresponds to an instance that has been registered, but does not correspond to the instance deduced from the instance_data (by means of the key). RTI Connext DDS Micro will treat as if the dispose() operation is for the instance as indicated by the handle.

This operation may block and time out (DDS_RETCODE_TIMEOUT) under the same circumtances described for FooDataWriter::write().

If there are no instance resources left, this operation may fail with DDS_RETCODE_OUT_OF_RESOURCES. Calling FooDataWriter::unregister_instance may help freeing up some resources.

Parameters:
instance_data<<in>> The data to dispose. If Foo has a key and instance_handle is DDS_HANDLE_NIL, only the fields that represent the key are examined by the function. Otherwise, instance_data is not used. If Foo has a key, instance_data can be NULL only if instance_handle is not DDS_HANDLE_NIL. Otherwise, this method will fail with DDS_RETCODE_BAD_PARAMETER.
handle<<in>> Either the handle returned by a previous call to FooDataWriter::register_instance, or else the special value DDS_HANDLE_NIL. If Foo has a key and instance_handle is DDS_HANDLE_NIL, instance_handle is not used and instance is deduced from instance_data. If Foo has no key, instance_handle is not used. If handle is used, it must represent a registered instance of type Foo. Otherwise, this method fail with DDS_RETCODE_BAD_PARAMETER.
Returns:
One of the Standard Return Codes, DDS_RETCODE_TIMEOUT, DDS_RETCODE_OUT_OF_RESOURCES or DDS_RETCODE_NOT_ENABLED.
See also:
FooDataWriter::dispose_w_timestamp
DDS_ReturnCode_t FooDataWriter::dispose_w_timestamp ( const Foo &  instance_data,
const DDS_InstanceHandle_t handle,
const DDS_Time_t source_timestamp 
)

Performs the same functions as dispose except that the application provides the value for the source_timestamp that is made available to DDSDataReader objects by means of the source_timestamp attribute inside the DDS_SampleInfo.

The constraints on the values of the handle parameter and the corresponding error behavior are the same specified for the FooDataWriter::dispose operation.

This operation may block and time out (DDS_RETCODE_TIMEOUT) under the same circumtances described for FooDataWriter::write.

If there are no instance resources left, this operation may fail with DDS_RETCODE_OUT_OF_RESOURCES. Calling FooDataWriter::unregister_instance may help freeing up some resources.

Parameters:
instance_data<<in>> The data to dispose. If Foo has a key and instance_handle is DDS_HANDLE_NIL, only the fields that represent the key are examined by the function. Otherwise, instance_data is not used.
handle<<in>> Either the handle returned by a previous call to FooDataWriter::register_instance, or else the special value DDS_HANDLE_NIL. If Foo has a key and instance_handle is DDS_HANDLE_NIL, instance_handle is not used and instance is deduced from instance_data. If Foo has no key, instance_handle is not used. If handle is used, it must represent a registered instance of type Foo. Otherwise, this method may fail with DDS_RETCODE_BAD_PARAMETER
source_timestamp<<in>> The timestamp value must be greater than or equal to the timestamp value used in the last writer operation (used in a register, unregister, dispose, or write, with either the automatically supplied timestamp or the application provided timestamp). This timestamp may potentially affect the order in which readers observe events from multiple writers. This timestamp will be available to the DDSDataReader objects by means of the source_timestamp attribute inside the DDS_SampleInfo.
Returns:
One of the Standard Return Codes, DDS_RETCODE_TIMEOUT, DDS_RETCODE_OUT_OF_RESOURCES or DDS_RETCODE_NOT_ENABLED.
See also:
FooDataWriter::dispose
DDS_ReturnCode_t FooDataWriter::write_w_timestamp ( const Foo &  instance_data,
const DDS_InstanceHandle_t handle,
const DDS_Time_t source_timestamp 
)

Performs the same function as FooDataWriter::write except that it also provides the value for the source_timestamp.

Explicitly provides the timestamp that will be available to the DDSDataReader objects by means of the source_timestamp attribute inside the DDS_SampleInfo. (Refer to DDS_SampleInfo details)

The constraints on the values of the handle parameter and the corresponding error behavior are the same specified for the FooDataWriter::write operation.

This operation may block and time out (DDS_RETCODE_TIMEOUT) under the same circumtances described for FooDataWriter::write.

If there are no instance resources left, this operation may fail with DDS_RETCODE_OUT_OF_RESOURCES. Calling FooDataWriter::unregister_instance may help free up some resources.

This operation may fail with DDS_RETCODE_BAD_PARAMETER under the same circumstances described for the write operation.

Parameters:
instance_data<<in>> The data to write.
handle<<in>> Either the handle returned by a previous call to FooDataWriter::register_instance, or else the special value DDS_HANDLE_NIL. If Foo has a key and handle is not DDS_HANDLE_NIL, handle must represent a registered instance of type Foo. Otherwise, this method may fail with DDS_RETCODE_BAD_PARAMETER.
source_timestamp<<in>> The timestamp value must be greater than or equal to the timestamp value used in the last writer operation (used in a register, unregister, dispose, or write, with either the automatically supplied timestamp or the application provided timestamp). This timestamp may potentially affect the order in which readers observe events from multiple writers. This timestamp will be available to the DDSDataReader objects by means of the source_timestamp attribute inside the DDS_SampleInfo.
Returns:
One of the Standard Return Codes, DDS_RETCODE_TIMEOUT, DDS_RETCODE_OUT_OF_RESOURCES or DDS_RETCODE_NOT_ENABLED.
See also:
FooDataWriter::write
DDSDataReader
DDS_ReturnCode_t FooDataWriter::get_loan ( Foo *&  sample)

Gets a sample managed by the DataWriter.

This operation is supported while using "Zero Copy transfer over shared memory" or "FlatData language binding".

The loaned sample is obtained from a DataWriter-managed sample pool and is uninitialized by default. An initialized sample can be obtained by setting DDS_DataWriterResourceLimitsQosPolicy::initialize_writer_loaned_sample to DDS_BOOLEAN_TRUE. The DDS_DataWriterResourceLimitsQosPolicy::writer_loaned_sample_allocation settings can be used to configure the DataWriter-managed sample pool.

FooDataWriter::get_loan fails with DDS_RETCODE_OUT_OF_RESOURCES if DDS_DataWriterResourceLimitsQosPolicy::writer_loaned_sample_allocation samples have been loaned, and none of those samples has been written with FooDataWriter::write or discarded via FooDataWriter::discard_loan.

Samples returned from FooDataWriter::get_loan have an associated state. Due to the optimized nature of the write operation while using Zero Copy transfer over shared memory or FlatData language binding, this sample state is used to control when a sample is available for reuse after the write operation. The possible sample states are free, allocated, removed or serialized. A sample that has never been allocated is "free". FooDataWriter::get_loan takes a "free" or "removed" sample and makes it "allocated". When a sample is written, its state transitions from "allocated" to "serialized", and the DataWriter takes responsibility for returning the sample back to its sample pool. The sample remains in the "serialized" state until it is removed from the DataWriter queue. For a reliable DataWriter, the sample is removed from the DataWriter's queue when the sample is acknowledged by all DataReaders. For a best-effort DataWriter, the sample is removed from the queue immediately after the write operation. After the sample is removed from the DataWriter queue, the sample is put back into the sample pool, and its state transitions from "serialized" to "removed". At this time, a new call to FooDataWriter::get_loan may return the same sample.

A loaned sample should not be reused to write a new value after the first write operation. Instead, a new sample from FooDataWriter::get_loan should be used to write the new value.

A loaned sample that has not been written can be returned to the DataWriter's sample pool by using FooDataWriter::discard_loan.

If the write operation fails, then the sample can be used again with a write or discard_loan operation. Disposing or unregistering an instance with loaned samples follows the same pattern. A loaned sample used successfully with a dispose or unregister operation cannot be used again. But if the dispose or unregister operation fails, the sample is available for reuse.

A DataWriter writing a type annotated with @transfer_mode(SHMEM_REF) or @language_binding(FLAT_DATA) cannot write unmanaged samples that have not been returned from FooDataWriter::get_loan.

Parameters:
sample<<inout>> reference to a user data type pointer. The loaned sample is returned via this sample.
See also:
FooDataWriter::discard_loan
DDS_ReturnCode_t FooDataWriter::discard_loan ( Foo &  sample)

Returns a loaned sample back to the DataWriter.

This operation is supported while using "Zero Copy transfer over shared memory" or the "FlatData language binding".

A loaned sample that hasn't been written can be returned to the DataWriter with this operation.

Parameters:
sample<<in>> loaned sample to be discarded.
Returns:
One of the Standard Return Codes
See also:
FooDataWriter::get_loan
Foo* FooDataWriter::create_data ( )

Creates a data sample and initializes it.

The behavior of this API is identical to FooTypeSupport::create_data.

See also:
FooDataWriter::delete_data
bool FooDataWriter::delete_data ( Foo *  sample)

Destroys a user data type instance.

The behavior of this API is identical to FooTypeSupport::delete_data.

Parameters:
sample<<in>> Cannot be NULL.
Returns:
DDS_BOOLEAN_TRUE on success.
See also:
FooDataWriter::create_data

RTI Connext DDS Micro C++ API Version 3.0.3 Copyright © Wed Jun 24 2020 Real-Time Innovations, Inc