Elektron SDK - C/C++

API Family: Elektron

EMA NI Provider - Listening to the connection state

Download tutorial source code

Click here to download

Last update June 2018
Compilers

Visual Studio 2015

For other compilers settings, please check out the EMA C++ Compiler Settings tutorial.

Prerequisites EMA NI Provider - Connecting to the ADH

Declare the NI_PUB and TEST_NI_PUB services in your TREP (see Before you start).

Tutorial purpose

In the previous tutorial we demonstrated how to connect a NI Provider application to a TREP infrastructure. In order to verify if the provider was actually connected, we used a manual procedure that involved running a TREP consumer application, eg: Eikon. In this tutorial step, we will see how the NI Provider application can directly receive the connection status from the EMA library.

To this aim we will go through the following sections:

Introduction

In order for our NI Provider application to successfully publish data to TREP, we must ensure we have successfully connected and logged in.  The manual procedure we used in the previous tutorial to verify our connection status was tedious and not very practical. We manually checked the connection state by using a consuming application we connected into the TREP infrastructure.  While this allowed us to visually determine connection status, this obviously doesn't help our Provider application.

In this tutorial we demonstrate how the NI Provider application can leverage EMA to capture our connection status, decode it and determine if we can successfully publish data.

Listening to login messages

When your application creates the OmmProvider object, the EMA library takes care of initiating the connection to the infrastructure. Once the provider is connected and properly logged in, the EMA library also takes care of automatically reconnecting and logging in upon connection failure. All this happens under the hood and makes your developer’s life a lot easier. Naturally, your NI Provider application may need to know when these connections and disconnections actually happen. To do so, it has to register for messages of the MMT_LOGIN domain type:

  • MMT_LOGIN refresh messages are returned by the infrastructure in response to the Login request that the EMA library sends when you create an OmmProvider object. 
  • MMT_LOGIN status messages are sent when the state of the connection changes, due to disconnections and automatic reconnections.

These messages convey information that indicates if the connection stream has been successfully established or if the stream state has changed. This information may also indicate that the login request has been accepted, rejected or closed.

For more details about Login messages, please consult the RDM Usage Guide (see References) .

In order to receive these messages, the NI Provider application must register to the MMT_LOGIN domain messages by calling the OmmProvider::registerClient() method in this way:

 

    // Register the _connectionStateListener to receive login messages from EMA
    _provider->registerClient(
                          ReqMsg()
                              .domainType(MMT_LOGIN)
                              .name(username), 
                          _connectionStateListener, 
                          (void*)0);
  • The first parameter (the ReqMsg) indicates the domain type you want to register to (MMT_LOGIN in our case) and the user name used to login your application.
  • The second parameter is a reference to an object that will be called by the EMA library when a new message is received. The class of this object must inherit from the thomsonreuters::ema::access::OmmProviderClient class and define the onRefreshMsg() and onStatusMsg() callback methods.
  • The third parameter can be used to convey closure data. It is set to 0 as we do not need it in this tutorial.

Note: The method returns a handle that can be preserved for later use. For example to unregister our interest for the MMT_LOGIN messages. In our case we want to receive these messages during the whole life time of the application. As there’s no real need to unregister, we do not preserve the handle. The unregistration will be done automatically when the OmmProvider is deleted. 

In the tutorial source code, we added the registerClient() method call to the NIProvider::connect() method, just after the OmmProvider is created. 

Decoding login messages

The MMT_LOGIN messages will be received by the onRefreshMsg() and onStatusMsg() callback methods of a ConnectionStateListener class that we added to the project. An instance of this class has been made a member of the NiProvider class so that the NiProvider can delegate login messages listening and decoding.

Note: As update messages are not used on a Login stream, there's no need to implement the UpdateMsg() callback method of the ConnectionStateListener class (see the EMA C++ RDM Usage Guide in References).

The onRefreshMsg() method just extracts the OmmState object embedded in the message it receives, and sends it to the decodeConnectionState() method for decoding.

 

void ConnectionStateListener::onRefreshMsg(const RefreshMsg& refreshMsg, const OmmProviderEvent& ommEvent)
{
    decodeConnectionState(refreshMsg.getState());
}

The onStatusMsg() method tests if the received message transports an OmmState object. No state means that the connection is ok. If there is a state, onStatusMsg() extracts it and sends it to the decodeConnectionState() method for decoding.

void ConnectionStateListener::onStatusMsg(const StatusMsg& statusMsg, const OmmProviderEvent& ommEvent)
{
    if (statusMsg.hasState())
    {
        decodeConnectionState(statusMsg.getState());
    }
    else
    {
        cout << "  Provider is connected" << endl;
        _isConnected = true;
    }
}

decodeConnectionState() is a helper method that we defined in the ConnectionStateListener class to factorize the decoding of the connection states. The OmmState actually holds two states, the stream state and the data state. If the stream state is open, and the data state is ok, the provider is properly connected and logged in. Any other combination means the connection is not correct. Based on this information, the decodeConnectionState() method prints the connection state on the console.

void ConnectionStateListener::decodeConnectionState(const thomsonreuters::ema::access::OmmState & state)
{
    if (state.getStreamState() == OmmState::OpenEnum
        &&
        state.getDataState() == OmmState::OkEnum)
    {
		cout << "  Provider is connected. OmmState:" << state << endl; 
		_isConnected = true;
	}
	else
	{
		cout << "  Provider is disconnected. OmmState:" << state << endl;
        _isConnected = false;
    }
}

The connection state is preserved in the _isConnected member of the ConnectionStateListener class. This state can be retrieved thanks to the isConnected() methods we added to the ConnectionStateListener and NiProvider classes as shown below:

bool ConnectionStateListener::isConnected() const
{
    return _isConnected;
}
bool NiProvider::isConnected() const
{
    return _connectionStateListener.isConnected();
}

Stop listening to login messages

Your application will stop receiving MMT_LOGIN messages, as soon as you delete the OmmProvider object. So basically, we do not do anything explicit to unregister our interest in these messages. However, if for any reason you want to stop receiving MMT_LOGIN messages but you want to keep your provider connected, you must call the OmmProvider::unregisterClient() method with the handle that was returned by the OmmProvider::registerClient() method. Obviously, you have to preserve this handle somewhere in your application.

​void NiProvider::disconnect()
{
    // Exit the method if already disconnected
    if (_provider == 0)
      return;

    cout << "  Disconnecting..." << endl;

    // Disconnect from the infrastructure and automatically unregister the 
    // _connectionStateListener we registered in the connect() method to 
    // receive login messages from EMA
    delete _provider;
    _provider = 0;
}

The main workflow

The main workflow did not really change as the connection messages management does not impact it. We just removed the 10 seconds wait that happened after the disconnection. 

int main(int argc, char* argv[])
{
    .
    .
    .
        NiProvider provider;

        provider.connect();

        waitFor(60);

        provider.disconnect();
    .
    .
    .
}

Build and run the application

Build the application and start it. Please refer to the Build and Run section within the first tutorial of this series (A barebones EMA NIP application shell) for detailed instructions.

Then, a console application should open and display something like:

-------------------------------------------------------------------------------
|                    Non Interactive Provider EMA Tutorial                    |
|                                                                             |
|                Tutorial 3 - Listening to the connection state               |
-------------------------------------------------------------------------------
  Provider created
  Connecting Provider to ADH 10.2.43.49:14003 as nip-user
  Waiting for 30 seconds...
  Provider is connected. OmmState:Open / Ok / None / 'Refresh Completed'
  Disconnecting...
  Provider is disconnected. OmmState:Open / Suspect / None / 'channel down'
  Exiting the application
Press any key to continue . . .

As you may have noticed, the “Provider is connected” message is displayed in the middle of the “Waiting for 30 seconds...” message. This happens because the two messages are printed from different threads. The “Waiting for 30 seconds...” message is printed by the application main thread, whereas the “Provider is connected” message is printed by a background EMA thread that calls the onRefreshMsg() and onUpdateMsg() methods. This is not really an issue for this tutorial because we just print the connection state, thanks to the C++ std output streams that are thread safe. However, this may be an issue if in your application the two threads need to concurrently access the same data in memory. In that case, you will either need to control the concurrent access, with mutexes for example, or to move to a mono-threaded application, instead of multi-threaded which is the default for EMA applications.

Troubleshooting

Q: When I build the tutorial project, I get errors like:

error MSB3073: The command "copy "\Ema\Libs\WIN_64_VS140\Debug_MDd\Shared\*.*" .\Debug_WIN_64_VS140_Shared\

A: The ElektronSDKInstallPath environment variable is not set or set to the wrong path. See Setup the development environment.

 

Q: The application is stuck after the "Connecting Provider to ADH…" message is displayed.

After a while the application displays an error like: 

Exception Type='OmmInvalidUsageException', 
          Text='login failed (timed out after waiting 45000 milliseconds) for 10.2.43.149:14003)'

A: Verify that the ADH of your TREP infrastructure is up and that you properly set the hardcoded host variable of the NiProvider::connect() method. 

You can also use the telnet command tool to verify that your NIP application machine can connect to the ADH (telnet <ADH host> <port>). If the telnet succeeds but you still can’t connect, verify that you don’t have any firewall blocking the messages sent/received by the application.  

Ultimately, ask your TREP administrator to help you to investigate with TREP monitoring tools like adhmon.

 

Q: The “Waiting for 30 seconds...” message and the “Provider is connected” message get mixed up when displayed in the console.

A: This is perfectly normal and actually caused by the EMA background thread that prints the “Provider is connected” message at the same time the main application thread prints the “Waiting for 30 seconds...” message. This can be fixed either by choosing a mono threaded application model (see the Message Processing - dispatch section of the Requesting MarketPrice data EMA Consumer tutorial) or by printing these messages atomically (use critical sections or a single cout << call to print the whole message).

Tutorial Group: 
EMA NI Provider