doc(pubsub): Add documentation for PubSub

- Tutorial subscribe example
 - Pubsub realtime examples
 - Pubsub nodeset realtime examples

Change-Id: I75b2581f87ccd0179d817c124b3e85c9de9b4d3a
This commit is contained in:
Selva Suba Jenifer Joseph 2020-12-09 12:59:07 +05:30 committed by Julius Pfrommer
parent b219820258
commit 563b150def
5 changed files with 174 additions and 28 deletions

View File

@ -60,7 +60,11 @@ generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_server_object.c ${DOC_SRC_D
generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_server_method.c ${DOC_SRC_DIR}/tutorial_server_method.rst)
generate_rst(${PROJECT_SOURCE_DIR}/examples/tutorial_client_firststeps.c ${DOC_SRC_DIR}/tutorial_client_firststeps.rst)
generate_rst(${PROJECT_SOURCE_DIR}/examples/pubsub/tutorial_pubsub_publish.c ${DOC_SRC_DIR}/tutorial_pubsub_publish.rst)
generate_rst(${PROJECT_SOURCE_DIR}/examples/pubsub/tutorial_pubsub_subscribe.c ${DOC_SRC_DIR}/tutorial_pubsub_subscribe.rst)
generate_rst(${PROJECT_SOURCE_DIR}/examples/pubsub_realtime/pubsub_TSN_publisher.c ${DOC_SRC_DIR}/pubsub_TSN_publisher.rst)
generate_rst(${PROJECT_SOURCE_DIR}/examples/pubsub_realtime/pubsub_TSN_loopback.c ${DOC_SRC_DIR}/pubsub_TSN_loopback.rst)
generate_rst(${PROJECT_SOURCE_DIR}/examples/pubsub_realtime/nodeset/pubsub_nodeset_rt_publisher.c ${DOC_SRC_DIR}/pubsub_nodeset_rt_publisher.rst)
generate_rst(${PROJECT_SOURCE_DIR}/examples/pubsub_realtime/nodeset/pubsub_nodeset_rt_subscriber.c ${DOC_SRC_DIR}/pubsub_nodeset_rt_subscriber.rst)
# Doc targets

View File

@ -17,3 +17,8 @@ Tutorials
tutorial_server_alarms_conditions.rst
tutorial_client_firststeps.rst
tutorial_pubsub_publish.rst
tutorial_pubsub_subscribe.rst
pubsub_TSN_publisher.rst
pubsub_TSN_loopback.rst
pubsub_nodeset_rt_publisher.rst
pubsub_nodeset_rt_subscriber.rst

View File

@ -5,11 +5,30 @@
*/
/**
* IMPORTANT ANNOUNCEMENT
* The PubSub Subscriber API is currently not finished. This example can be used
* to receive and display values that are published by tutorial_pubsub_publish
* example in the TargetVariables of Subscriber Information Model .
* .. _pubsub-tutorial:
*
* **IMPORTANT ANNOUNCEMENT**
*
* The PubSub Subscriber API is currently not finished. This Tutorial will be
* continuously extended during the next PubSub batches. More details about the
* PubSub extension and corresponding open62541 API are located here: :ref:`pubsub`.
*
* Subscribing Fields
* ^^^^^^^^^^^^^^^^^^
* The PubSub subscribe example demonstrates the simplest way to receive information over two transport layers such as
* UDP and Ethernet, that are published by tutorial_pubsub_publish example and update values in the TargetVariables
* of Subscriber Information Model.
*
* Run step of the application is as mentioned below:
*
* ./bin/examples/tutorial_pubsub_subscribe
*
* **Connection handling**
*
* PubSubConnections can be created and deleted on runtime. More details about
* the system preconfiguration and connection can be found in ``tutorial_pubsub_connection.c``.
*/
#include <open62541/plugin/log_stdout.h>
#include <open62541/plugin/pubsub_udp.h>
#include <open62541/server.h>
@ -18,7 +37,7 @@
#include "ua_pubsub.h"
#ifdef UA_ENABLE_PUBSUB_ETH_UADP
#if defined (UA_ENABLE_PUBSUB_ETH_UADP)
#include <open62541/plugin/pubsub_ethernet.h>
#endif
@ -61,6 +80,12 @@ addPubSubConnection(UA_Server *server, UA_String *transportProfile,
return retval;
}
/**
* **ReaderGroup**
*
* ReaderGroup is used to group a list of DataSetReaders. All ReaderGroups are
* created within a PubSubConnection and automatically deleted if the connection
* is removed. All network message related filters are only available in the DataSetReader. */
/* Add ReaderGroup to the created connection */
static UA_StatusCode
addReaderGroup(UA_Server *server) {
@ -78,6 +103,14 @@ addReaderGroup(UA_Server *server) {
return retval;
}
/**
* **DataSetReader**
*
* DataSetReader can receive NetworkMessages with the DataSetMessage
* of interest sent by the Publisher. DataSetReader provides
* the configuration necessary to receive and process DataSetMessages
* on the Subscriber side. DataSetReader must be linked with a
* SubscribedDataSet and be contained within a ReaderGroup. */
/* Add DataSetReader to the ReaderGroup */
static UA_StatusCode
addDataSetReader(UA_Server *server) {
@ -107,7 +140,10 @@ addDataSetReader(UA_Server *server) {
return retval;
}
/* Set SubscribedDataSet type to TargetVariables data type
/**
* **SubscribedDataSet**
*
* Set SubscribedDataSet type to TargetVariables data type.
* Add subscribedvariables to the DataSetReader */
static UA_StatusCode
addSubscribedVariables (UA_Server *server, UA_NodeId dataSetReaderId) {
@ -136,6 +172,12 @@ addSubscribedVariables (UA_Server *server, UA_NodeId dataSetReaderId) {
folderBrowseName, UA_NODEID_NUMERIC (0,
UA_NS0ID_BASEOBJECTTYPE), oAttr, NULL, &folderId);
/**
* **TargetVariables**
*
* The SubscribedDataSet option TargetVariables defines a list of Variable mappings between
* received DataSet fields and target Variables in the Subscriber AddressSpace.
* The values subscribed from the Publisher are updated in the value field of these variables */
/* Create the TargetVariables with respect to DataSetMetaData fields */
UA_FieldTargetVariable *targetVars = (UA_FieldTargetVariable *)
UA_calloc(readerConfig.dataSetMetaData.fieldsSize, sizeof(UA_FieldTargetVariable));
@ -172,6 +214,13 @@ addSubscribedVariables (UA_Server *server, UA_NodeId dataSetReaderId) {
return retval;
}
/**
* **DataSetMetaData**
*
* The DataSetMetaData describes the content of a DataSet. It provides the information necessary to decode
* DataSetMessages on the Subscriber side. DataSetMessages received from the Publisher are decoded into
* DataSet and each field is updated in the Subscriber based on datatype match of TargetVariable fields of Subscriber
* and PublishedDataSetFields of Publisher */
/* Define MetaData for TargetVariables */
static void fillTestDataSetMetaData(UA_DataSetMetaDataType *pMetaData) {
if(pMetaData == NULL) {
@ -221,6 +270,8 @@ static void fillTestDataSetMetaData(UA_DataSetMetaDataType *pMetaData) {
pMetaData->fields[3].valueRank = -1; /* scalar */
}
/**
* Followed by the main server code, making use of the above definitions */
UA_Boolean running = true;
static void stopHandler(int sign) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
@ -250,7 +301,7 @@ run(UA_String *transportProfile, UA_NetworkAddressUrlDataType *networkAddressUrl
config->pubsubTransportLayers[0] = UA_PubSubTransportLayerUDPMP();
config->pubsubTransportLayersSize++;
#ifdef UA_ENABLE_PUBSUB_ETH_UADP
#if defined (UA_ENABLE_PUBSUB_ETH_UADP)
config->pubsubTransportLayers[1] = UA_PubSubTransportLayerEthernet();
config->pubsubTransportLayersSize++;
#endif

View File

@ -2,14 +2,23 @@
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
/**
* .. _pubsub-tutorial:
*
* Publisher Realtime example using custom nodes
* ---------------------------------------------
*
* The purpose of this example file is to use the custom nodes of the XML
* file(pubDataModel.xml) for publisher
* file(pubDataModel.xml) for publisher.
* This Publisher example uses the two custom nodes (PublisherCounterVariable and Pressure)
* created using the XML file(pubDataModel.xml) for publishing the packet
* created using the XML file(pubDataModel.xml) for publishing the packet.
* The pubDataModel.csv will contain the nodeids of custom nodes(object and variables) and
* the nodeids of the custom nodes are harcoded inside the addDataSetField API
* the nodeids of the custom nodes are harcoded inside the addDataSetField API.
* This example uses two threads namely the Publisher and UserApplication. The Publisher thread is used to publish data at every cycle.
* The UserApplication thread serves the functionality of the Control loop, which increments the counterdata to be published
* by the Publisher and also writes the published data in a csv along with transmission timestamp.
*
* Run steps of the Publisher application as mentioned below:
*
* ./bin/examples/pubsub_nodeset_rt_publisher -i <iface>
* For more information run ./bin/examples/pubsub_nodeset_rt_publisher -h */
@ -147,7 +156,6 @@ static void stopHandler(int sign) {
* Nanosecond field in timespec is checked for overflowing and one second
* is added to seconds field and nanosecond field is set to zero
*/
static void nanoSecondFieldConversion(struct timespec *timeSpecValue) {
/* Check if ns field is greater than '1 ns less than 1sec' */
while (timeSpecValue->tv_nsec > (SECONDS -1)) {
@ -158,6 +166,11 @@ static void nanoSecondFieldConversion(struct timespec *timeSpecValue) {
}
/**
* **Custom callback handling**
*
* Custom callback thread handling overwrites the default timer based
* callback function with the custom (user-specified) callback interval. */
/* Add a callback for cyclic repetition */
static UA_StatusCode
addPubSubApplicationCallback(UA_Server *server, UA_NodeId identifier,
@ -195,7 +208,10 @@ removePubSubApplicationCallback(UA_Server *server, UA_NodeId identifier, UA_UInt
"Pthread Join Failed thread: %ld\n", callbackId);
}
/* If the external data source is written over the information model, the
/**
* **External data source handling**
*
* If the external data source is written over the information model, the
* externalDataWriteCallback will be triggered. The user has to take care and assure
* that the write leads not to synchronization issues and race conditions. */
static UA_StatusCode
@ -392,12 +408,14 @@ updateMeasurementsPublisher(struct timespec start_time,
measurementsPublisher++;
}
/**
* **Publisher thread routine**
*
* The Publisher thread sleeps for 60% of the cycletime (250us) and prepares the tranmission packet within 40% of
* cycletime. The data published by this thread in one cycle is subscribed by the subscriber thread of pubsub_nodeset_rt_subscriber in the
* next cycle (two cycle timing model).
*
* The publisherETF function is the routine used by the publisher thread.
* This routine publishes the data at a cycle time of 250us.
*/
void *publisherETF(void *arg) {
struct timespec nextnanosleeptime;
@ -453,6 +471,8 @@ void *publisherETF(void *arg) {
/**
* **UserApplication thread routine**
*
* The userapplication thread will wakeup at 30% of cycle time and handles the userdata in the Information Model.
* This thread is used to increment the counterdata that will be published by the Publisher thread and also writes the published data in a csv.
*/
void *userApplicationPub(void *arg) {
struct timespec nextnanosleeptimeUserApplication;
@ -477,6 +497,12 @@ void *userApplicationPub(void *arg) {
return (void*)NULL;
}
/**
* **Thread creation**
*
* The threadcreation functionality creates thread with given threadpriority, coreaffinity. The function returns the threadID of the newly
* created thread.
*/
static pthread_t threadCreation(UA_Int32 threadPriority, UA_Int32 coreAffinity, void *(*thread) (void *), char *applicationName, void *serverConfig){
/* Core affinity set */
@ -517,6 +543,13 @@ static pthread_t threadCreation(UA_Int32 threadPriority, UA_Int32 coreAffinity,
}
/**
* **Usage function**
*
* The usage function gives the list of options that can be configured in the application.
*
* ./bin/examples/pubsub_nodeset_rt_publisher -h gives the list of options for running the application.
*/
static void usage(char *appname)
{
fprintf(stderr,
@ -540,9 +573,9 @@ static void usage(char *appname)
}
/**
* ***Main Server code**
* **Main Server code**
*
* The main function contains publisher threads running
* The main function contains publisher threads running
*/
int main(int argc, char **argv) {
signal(SIGINT, stopHandler);

View File

@ -1,13 +1,22 @@
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
/**
* .. _pubsub-tutorial:
*
* Subscriber Realtime example using custom nodes
* ---------------------------------------------
*
* The purpose of this example file is to use the custom nodes of the XML
* file(subDataModel.xml) for subscriber
* file(subDataModel.xml) for subscriber.
* This Subscriber example uses the two custom nodes (SubscriberCounterVariable and Pressure)
* created using the XML file(subDataModel.xml) for subscribing the packet
* created using the XML file(subDataModel.xml) for subscribing the packet.
* The subDataModel.csv will contain the nodeids of custom nodes(object and variables) and
* the nodeids of the custom nodes are harcoded inside the addSubscribedVariables API
*
* This example uses two threads namely the Subscriber and UserApplication. The Subscriber thread is used to subscribe to data at every cycle.
* The UserApplication thread serves the functionality of the Control loop, which reads the Information Model of the Subscriber and
* the new counterdata will be written in the csv along with received timestamp.
*
* Run steps of the Subscriber application as mentioned below:
* ./bin/examples/pubsub_nodeset_rt_subscriber -i <iface>
* For more information run ./bin/examples/pubsub_nodeset_rt_subscriber -h */
@ -72,9 +81,7 @@ static UA_Int32 userAppCore = DEFAULT_USER_APP_CORE;
/* user data write in Information model */
/* After 60% is left for publisher */
static UA_Double userAppWakeupPercentage = 0.3;
/* Publisher will sleep for 60% of cycle time and then prepares the */
/* transmission packet within 40% */
/* after some prototyping and analyzing it */
/* Subscriber will wake up at the start of cycle time and then receives the packet */
static UA_Double subWakeupPercentage = 0;
static UA_Boolean fileWrite = UA_FALSE;
@ -139,7 +146,6 @@ static void stopHandler(int sign) {
* Nanosecond field in timespec is checked for overflowing and one second
* is added to seconds field and nanosecond field is set to zero
*/
static void nanoSecondFieldConversion(struct timespec *timeSpecValue) {
/* Check if ns field is greater than '1 ns less than 1sec' */
while (timeSpecValue->tv_nsec > (SECONDS -1)) {
@ -150,6 +156,11 @@ static void nanoSecondFieldConversion(struct timespec *timeSpecValue) {
}
/**
* **Custom callback handling**
*
* Custom callback thread handling overwrites the default timer based
* callback function with the custom (user-specified) callback interval. */
/* Add a callback for cyclic repetition */
static UA_StatusCode
addPubSubApplicationCallback(UA_Server *server, UA_NodeId identifier, UA_ServerCallback callback,
@ -186,7 +197,10 @@ removePubSubApplicationCallback(UA_Server *server, UA_NodeId identifier, UA_UInt
"Pthread Join Failed thread: %ld\n", callbackId);
}
/* If the external data source is written over the information model, the
/**
* **External data source handling**
*
* If the external data source is written over the information model, the
* externalDataWriteCallback will be triggered. The user has to take care and assure
* that the write leads not to synchronization issues and race conditions. */
static UA_StatusCode
@ -206,6 +220,12 @@ externalDataReadNotificationCallback(UA_Server *server, const UA_NodeId *session
//allow read without any preparation
return UA_STATUSCODE_GOOD;
}
/**
* **Subscriber Connection Creation**
*
* Create Subscriber connection for the Subscriber thread
*/
static void
addPubSubConnectionSubscriber(UA_Server *server, UA_NetworkAddressUrlDataType *networkAddressUrlSubscriber){
UA_StatusCode retval = UA_STATUSCODE_GOOD;
@ -224,6 +244,12 @@ addPubSubConnectionSubscriber(UA_Server *server, UA_NetworkAddressUrlDataType *n
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,"The PubSub Connection was created successfully!");
}
/**
* **ReaderGroup**
*
* ReaderGroup is used to group a list of DataSetReaders. All ReaderGroups are
* created within a PubSubConnection and automatically deleted if the connection
* is removed. */
/* Add ReaderGroup to the created connection */
static void
addReaderGroup(UA_Server *server) {
@ -242,7 +268,10 @@ addReaderGroup(UA_Server *server) {
&readerGroupIdentifier);
}
/* Set SubscribedDataSet type to TargetVariables data type
/**
* **SubscribedDataSet**
*
* Set SubscribedDataSet type to TargetVariables data type
* Add SubscriberCounter variable to the DataSetReader */
static void addSubscribedVariables (UA_Server *server) {
if (server == NULL) {
@ -294,6 +323,14 @@ static void addSubscribedVariables (UA_Server *server) {
readerConfig.subscribedDataSet.subscribedDataSetTarget.targetVariablesSize = 2;
}
/**
* **DataSetReader**
*
* DataSetReader can receive NetworkMessages with the DataSetMessage
* of interest sent by the Publisher. DataSetReader provides
* the configuration necessary to receive and process DataSetMessages
* on the Subscriber side. DataSetReader must be linked with a
* SubscribedDataSet and be contained within a ReaderGroup. */
/* Add DataSetReader to the ReaderGroup */
static void
addDataSetReader(UA_Server *server) {
@ -356,7 +393,8 @@ addDataSetReader(UA_Server *server) {
}
/**
* Subscribed data handling**
* **Subscribed data handling**
*
* The subscribed data is updated in the array using this function Subscribed data handling**
*/
static void
@ -370,9 +408,9 @@ updateMeasurementsSubscriber(struct timespec receive_time, UA_UInt64 counterValu
/**
* **Subscriber thread routine**
*
* Subscriber thread will wakeup during the start of cycle at 250us interval and check if the packets are received.
* The subscriber function is the routine used by the subscriber thread.
*/
void *subscriber(void *arg) {
UA_Server* server;
UA_ReaderGroup* currentReaderGroup;
@ -406,6 +444,8 @@ void *subscriber(void *arg) {
/**
* **UserApplication thread routine**
*
* The userapplication thread will wakeup at 30% of cycle time and handles the userdata in the Information Model.
* This thread is used to write the counterdata that is subscribed by the Subscriber thread in a csv.
*/
void *userApplicationSub(void *arg) {
struct timespec nextnanosleeptimeUserApplication;
@ -430,6 +470,12 @@ void *userApplicationSub(void *arg) {
return (void*)NULL;
}
/**
* **Thread creation**
*
* The threadcreation functionality creates thread with given threadpriority, coreaffinity. The function returns the threadID of the newly
* created thread.
*/
static pthread_t threadCreation(UA_Int32 threadPriority, UA_Int32 coreAffinity, void *(*thread) (void *), \
char *applicationName, void *serverConfig){
@ -471,6 +517,13 @@ static pthread_t threadCreation(UA_Int32 threadPriority, UA_Int32 coreAffinity,
}
/**
* **Usage function**
*
* The usage function gives the list of options that can be configured in the application.
*
* ./bin/examples/pubsub_nodeset_rt_subscriber -h gives the list of options for running the application.
*/
static void usage(char *appname)
{
fprintf(stderr,
@ -492,7 +545,7 @@ static void usage(char *appname)
}
/**
* ***Main Server code**
* **Main Server code**
*
* The main function contains subscriber threads running
*/