C++ Based OPC UA Client/Server/PubSub SDK  1.7.6.537
Lesson 2: Create DataMonitoredItem

This lesson describes how to monitor data changes of a node.

For an introduction to the concept of subscriptions and monitored items see OPC UA Client/Server Subscription Concept.

Step 1: Introducing UaSubscription and UaSubscriptionCallback

To create monitored items, we need a subscription first.

Add a new header file samplesubscription.h containing the following code to your project:

#ifndef SAMPLESUBSCRIPTION_H
#define SAMPLESUBSCRIPTION_H
#include "uabase.h"
#include "uaclientsdk.h"
using namespace UaClientSdk;
class SampleSubscription :
{
UA_DISABLE_COPY(SampleSubscription);
public:
SampleSubscription();
virtual ~SampleSubscription();
// UaSubscriptionCallback implementation ----------------------------------------------------
virtual void subscriptionStatusChanged(
OpcUa_UInt32 clientSubscriptionHandle,
const UaStatus& status);
virtual void dataChange(
OpcUa_UInt32 clientSubscriptionHandle,
const UaDataNotifications& dataNotifications,
const UaDiagnosticInfos& diagnosticInfos);
virtual void newEvents(
OpcUa_UInt32 clientSubscriptionHandle,
UaEventFieldLists& eventFieldList);
// UaSubscriptionCallback implementation ------------------------------------------------------
private:
UaSession* m_pSession;
UaSubscription* m_pSubscription;
};
#endif // SAMPLESUBSCRIPTION_H

UaSubscriptionCallback defines the callback interface for the UaSubscription class.

This callback interface needs to be implemented by the user of the UaSubscription class to receive data change, event and subscription status change callbacks from the Client SDK.

Add a new source file samplesubscription.cpp containing the following code to your project:

#include "samplesubscription.h"
#include "uasubscription.h"
#include "uasession.h"
SampleSubscription::SampleSubscription()
: m_pSession(NULL),
m_pSubscription(NULL)
{
}
SampleSubscription::~SampleSubscription()
{
}

The constructor and destructor remain empty so far.

Implement subscriptionStatusChanged:

void SampleSubscription::subscriptionStatusChanged(
OpcUa_UInt32 clientSubscriptionHandle,
const UaStatus& status)
{
OpcUa_ReferenceParameter(clientSubscriptionHandle); // We use the callback only for this subscription
printf("Subscription not longer valid - failed with status %s\n", status.toString().toUtf8());
}

Implement dataChange:

void SampleSubscription::dataChange(
OpcUa_UInt32 clientSubscriptionHandle,
const UaDataNotifications& dataNotifications,
const UaDiagnosticInfos& diagnosticInfos)
{
OpcUa_ReferenceParameter(clientSubscriptionHandle); // We use the callback only for this subscription
OpcUa_ReferenceParameter(diagnosticInfos);
OpcUa_UInt32 i = 0;
printf("-- DataChange Notification ---------------------------------\n");
for ( i=0; i<dataNotifications.length(); i++ )
{
if ( OpcUa_IsGood(dataNotifications[i].Value.StatusCode) )
{
UaVariant tempValue = dataNotifications[i].Value.Value;
printf(" Variable %d value = %s\n", dataNotifications[i].ClientHandle, tempValue.toString().toUtf8());
}
else
{
UaStatus itemError(dataNotifications[i].Value.StatusCode);
printf(" Variable %d failed with status %s\n", dataNotifications[i].ClientHandle, itemError.toString().toUtf8());
}
}
printf("------------------------------------------------------------\n");
}

Implement newEvents:

void SampleSubscription::newEvents(
OpcUa_UInt32 clientSubscriptionHandle,
UaEventFieldLists& eventFieldList)
{
OpcUa_ReferenceParameter(clientSubscriptionHandle);
OpcUa_ReferenceParameter(eventFieldList);
}

Step 2: Creating and Deleting a Subscription

In a next step we are implementing the methods to create and delete a subscription.

Add the following code to samplesubscription.h

...
// New code begins
// Create / delete a subscription on the server
UaStatus createSubscription(UaSession* pSession);
UaStatus deleteSubscription();
// New code ends
private:
UaSession* m_pSession;
UaSubscription* m_pSubscription;
};

Implement createSubscription:

UaStatus SampleSubscription::createSubscription(UaSession* pSession)
{
if ( m_pSubscription )
{
printf("\nError: Subscription already created\n");
return OpcUa_BadInvalidState;
}
m_pSession = pSession;
UaStatus result;
ServiceSettings serviceSettings;
SubscriptionSettings subscriptionSettings;
subscriptionSettings.publishingInterval = 100;
printf("\nCreating subscription ...\n");
result = pSession->createSubscription(
serviceSettings,
this,
1,
subscriptionSettings,
OpcUa_True,
&m_pSubscription);
if (result.isGood())
{
printf("CreateSubscription succeeded\n");
}
else
{
m_pSubscription = NULL;
printf("CreateSubscription failed with status %s\n", result.toString().toUtf8());
}
return result;
}

See SubscriptionSettings for possible settings.

Implement deleteSubscription:

UaStatus SampleSubscription::deleteSubscription()
{
if ( m_pSubscription == NULL )
{
printf("\nError: No Subscription created\n");
return OpcUa_BadInvalidState;
}
UaStatus result;
ServiceSettings serviceSettings;
// let the SDK cleanup the resources for the existing subscription
printf("\nDeleting subscription ...\n");
result = m_pSession->deleteSubscription(
serviceSettings,
&m_pSubscription);
if (result.isGood())
{
printf("DeleteSubscription succeeded\n");
}
else
{
printf("DeleteSubscription failed with status %s\n", result.toString().toUtf8());
}
m_pSubscription = NULL;
return result;
}

Add the following code to the destructor of SampleSubscription:

SampleSubscription::~SampleSubscription()
{
if ( m_pSubscription )
{
deleteSubscription();
}
}

Add the marked lines to sampleclient.h

#ifndef SAMPLECLIENT_H
#define SAMPLECLIENT_H
#include "uabase.h"
#include "uaclientsdk.h"
class SampleSubscription; // Add this line
using namespace UaClientSdk;
class SampleClient : public UaSessionCallback
{
UA_DISABLE_COPY(SampleClient);
public:
SampleClient();
virtual ~SampleClient();
// UaSessionCallback implementation ----------------------------------------------------
virtual void connectionStatusChanged(OpcUa_UInt32 clientConnectionId, UaClient::ServerStatus serverStatus);
// UaSessionCallback implementation ------------------------------------------------------
// OPC UA service calls
UaStatus connect();
UaStatus disconnect();
UaStatus read();
UaStatus subscribe(); // Add this line
UaStatus unsubscribe(); // Add this line
private:
UaSession* m_pSession;
SampleSubscription* m_pSampleSubscription; // Add this line
};
#endif // SAMPLECLIENT_H

Add the following code to sampleclient.cpp

#include "sampleclient.h"
#include "uasession.h"
#include "samplesubscription.h" // Add this line
SampleClient::SampleClient()
{
m_pSession = new UaSession();
m_pSampleSubscription = new SampleSubscription(); // Add this line
}
SampleClient::~SampleClient()
{
// New code begins
if (m_pSampleSubscription)
{
// delete local subscription object
delete m_pSampleSubscription;
m_pSampleSubscription = NULL;
}
// New code ends
if (m_pSession)
{
...

Implement subscribe:

UaStatus SampleClient::subscribe()
{
UaStatus result;
result = m_pSampleSubscription->createSubscription(m_pSession);
return result;
}

Implement unsubscribe:

UaStatus SampleClient::unsubscribe()
{
return m_pSampleSubscription->deleteSubscription();
}

Complete main to create and delete a subscription.

...
// Connect succeeded
if (status.isGood())
{
// Read values
status = pMyClient->read();
// New code begins
// Wait for user command.
printf("\nPress Enter to create subscription\n");
printf("Press Enter again to stop subscription\n");
getchar();
// Create subscription
status = pMyClient->subscribe();
if (status.isGood())
{
// Wait for user command.
getchar();
// Delete subscription
status = pMyClient->unsubscribe();
}
// New code ends
// Wait for user command.
printf("\nPress Enter to disconnect\n");
getchar();
// Disconnect from OPC UA Server
status = pMyClient->disconnect();
}
...

Step 3: Adding MonitoredItems to a Subscription

Now that we have a subcription, we can add monitored items to this subscription. In this example, the value ServerStatus → CurrentTime is monitored.

Add the following code to samplesubscription.h

...
// Create / delete a subscription on the server
UaStatus createSubscription(UaSession* pSession);
UaStatus deleteSubscription();
// New code begins
// Create monitored items in the subscription
UaStatus createMonitoredItems();
// New code ends
...

Implement createMonitoredItems:

UaStatus SampleSubscription::createMonitoredItems()
{
if ( m_pSubscription == NULL )
{
printf("\nError: No Subscription created\n");
return OpcUa_BadInvalidState;
}
UaStatus result;
OpcUa_UInt32 i;
ServiceSettings serviceSettings;
// Configure one item to add to subscription
// We monitor the value of the ServerStatus -> CurrentTime
itemsToCreate.create(1);
itemsToCreate[0].ItemToMonitor.AttributeId = OpcUa_Attributes_Value;
itemsToCreate[0].ItemToMonitor.NodeId.Identifier.Numeric = OpcUaId_Server_ServerStatus_CurrentTime;
itemsToCreate[0].RequestedParameters.ClientHandle = 1;
itemsToCreate[0].RequestedParameters.SamplingInterval = 100;
itemsToCreate[0].RequestedParameters.QueueSize = 1;
itemsToCreate[0].RequestedParameters.DiscardOldest = OpcUa_True;
itemsToCreate[0].MonitoringMode = OpcUa_MonitoringMode_Reporting;
printf("\nAdding monitored items to subscription ...\n");
result = m_pSubscription->createMonitoredItems(
serviceSettings,
itemsToCreate,
createResults);
if (result.isGood())
{
// check individual results
for (i = 0; i < createResults.length(); i++)
{
if (OpcUa_IsGood(createResults[i].StatusCode))
{
printf("CreateMonitoredItems succeeded for item: %s\n",
UaNodeId(itemsToCreate[i].ItemToMonitor.NodeId).toXmlString().toUtf8());
}
else
{
printf("CreateMonitoredItems failed for item: %s - Status %s\n",
UaNodeId(itemsToCreate[i].ItemToMonitor.NodeId).toXmlString().toUtf8(),
UaStatus(createResults[i].StatusCode).toString().toUtf8());
}
}
}
// service call failed
else
{
printf("CreateMonitoredItems failed with status %s\n", result.toString().toUtf8());
}
return result;
}

First, the item to monitor and the corresponding settings are stored in itemsToCreate.

Then the method calls UaSubscription::createMonitoredItems.

Finally, we create the monitored item in the subscribe method:

UaStatus SampleClient::subscribe()
{
UaStatus result;
result = m_pSampleSubscription->createSubscription(m_pSession);
// New code begins
if ( result.isGood() )
{
result = m_pSampleSubscription->createMonitoredItems();
}
// New code ends
return result;
}