tutorial_pubsub_subscribe.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
  2. * See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
  3. *
  4. * Copyright (c) 2019 Kalycito Infotech Private Limited
  5. */
  6. /**
  7. * IMPORTANT ANNOUNCEMENT
  8. * The PubSub Subscriber API is currently not finished. This example can be used
  9. * to receive and display values that are published by tutorial_pubsub_publish
  10. * example in the TargetVariables of Subscriber Information Model .
  11. */
  12. #include <open62541/plugin/log_stdout.h>
  13. #include <open62541/plugin/pubsub_udp.h>
  14. #include <open62541/server.h>
  15. #include <open62541/server_config_default.h>
  16. #include <open62541/types_generated.h>
  17. #include "ua_pubsub.h"
  18. #ifdef UA_ENABLE_PUBSUB_ETH_UADP
  19. #include <open62541/plugin/pubsub_ethernet.h>
  20. #endif
  21. #include <stdio.h>
  22. #include <signal.h>
  23. #include <stdlib.h>
  24. UA_NodeId connectionIdentifier;
  25. UA_NodeId readerGroupIdentifier;
  26. UA_NodeId readerIdentifier;
  27. UA_DataSetReaderConfig readerConfig;
  28. static void fillTestDataSetMetaData(UA_DataSetMetaDataType *pMetaData);
  29. /* Add new connection to the server */
  30. static UA_StatusCode
  31. addPubSubConnection(UA_Server *server, UA_String *transportProfile,
  32. UA_NetworkAddressUrlDataType *networkAddressUrl) {
  33. if((server == NULL) || (transportProfile == NULL) ||
  34. (networkAddressUrl == NULL)) {
  35. return UA_STATUSCODE_BADINTERNALERROR;
  36. }
  37. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  38. /* Configuration creation for the connection */
  39. UA_PubSubConnectionConfig connectionConfig;
  40. memset (&connectionConfig, 0, sizeof(UA_PubSubConnectionConfig));
  41. connectionConfig.name = UA_STRING("UDPMC Connection 1");
  42. connectionConfig.transportProfileUri = *transportProfile;
  43. connectionConfig.enabled = UA_TRUE;
  44. UA_Variant_setScalar(&connectionConfig.address, networkAddressUrl,
  45. &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
  46. connectionConfig.publisherId.numeric = UA_UInt32_random ();
  47. retval |= UA_Server_addPubSubConnection (server, &connectionConfig, &connectionIdentifier);
  48. if (retval != UA_STATUSCODE_GOOD) {
  49. return retval;
  50. }
  51. retval |= UA_PubSubConnection_regist(server, &connectionIdentifier);
  52. return retval;
  53. }
  54. /* Add ReaderGroup to the created connection */
  55. static UA_StatusCode
  56. addReaderGroup(UA_Server *server) {
  57. if(server == NULL) {
  58. return UA_STATUSCODE_BADINTERNALERROR;
  59. }
  60. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  61. UA_ReaderGroupConfig readerGroupConfig;
  62. memset (&readerGroupConfig, 0, sizeof(UA_ReaderGroupConfig));
  63. readerGroupConfig.name = UA_STRING("ReaderGroup1");
  64. retval |= UA_Server_addReaderGroup(server, connectionIdentifier, &readerGroupConfig,
  65. &readerGroupIdentifier);
  66. return retval;
  67. }
  68. /* Add DataSetReader to the ReaderGroup */
  69. static UA_StatusCode
  70. addDataSetReader(UA_Server *server) {
  71. if(server == NULL) {
  72. return UA_STATUSCODE_BADINTERNALERROR;
  73. }
  74. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  75. memset (&readerConfig, 0, sizeof(UA_DataSetReaderConfig));
  76. readerConfig.name = UA_STRING("DataSet Reader 1");
  77. /* Parameters to filter which DataSetMessage has to be processed
  78. * by the DataSetReader */
  79. /* The following parameters are used to show that the data published by
  80. * tutorial_pubsub_publish.c is being subscribed and is being updated in
  81. * the information model */
  82. UA_UInt16 publisherIdentifier = 2234;
  83. readerConfig.publisherId.type = &UA_TYPES[UA_TYPES_UINT16];
  84. readerConfig.publisherId.data = &publisherIdentifier;
  85. readerConfig.writerGroupId = 100;
  86. readerConfig.dataSetWriterId = 62541;
  87. /* Setting up Meta data configuration in DataSetReader */
  88. fillTestDataSetMetaData(&readerConfig.dataSetMetaData);
  89. retval |= UA_Server_addDataSetReader(server, readerGroupIdentifier, &readerConfig,
  90. &readerIdentifier);
  91. return retval;
  92. }
  93. /* Set SubscribedDataSet type to TargetVariables data type
  94. * Add subscribedvariables to the DataSetReader */
  95. static UA_StatusCode
  96. addSubscribedVariables (UA_Server *server, UA_NodeId dataSetReaderId) {
  97. if(server == NULL) {
  98. return UA_STATUSCODE_BADINTERNALERROR;
  99. }
  100. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  101. UA_NodeId folderId;
  102. UA_String folderName = readerConfig.dataSetMetaData.name;
  103. UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
  104. UA_QualifiedName folderBrowseName;
  105. if(folderName.length > 0) {
  106. oAttr.displayName.locale = UA_STRING ("en-US");
  107. oAttr.displayName.text = folderName;
  108. folderBrowseName.namespaceIndex = 1;
  109. folderBrowseName.name = folderName;
  110. }
  111. else {
  112. oAttr.displayName = UA_LOCALIZEDTEXT ("en-US", "Subscribed Variables");
  113. folderBrowseName = UA_QUALIFIEDNAME (1, "Subscribed Variables");
  114. }
  115. UA_Server_addObjectNode (server, UA_NODEID_NULL,
  116. UA_NODEID_NUMERIC (0, UA_NS0ID_OBJECTSFOLDER),
  117. UA_NODEID_NUMERIC (0, UA_NS0ID_ORGANIZES),
  118. folderBrowseName, UA_NODEID_NUMERIC (0,
  119. UA_NS0ID_BASEOBJECTTYPE), oAttr, NULL, &folderId);
  120. retval |= UA_Server_DataSetReader_addTargetVariables (server, &folderId,
  121. dataSetReaderId,
  122. UA_PUBSUB_SDS_TARGET);
  123. UA_free(readerConfig.dataSetMetaData.fields);
  124. return retval;
  125. }
  126. /* Define MetaData for TargetVariables */
  127. static void fillTestDataSetMetaData(UA_DataSetMetaDataType *pMetaData) {
  128. if(pMetaData == NULL) {
  129. return;
  130. }
  131. UA_DataSetMetaDataType_init (pMetaData);
  132. pMetaData->name = UA_STRING ("DataSet 1");
  133. /* Static definition of number of fields size to 4 to create four different
  134. * targetVariables of distinct datatype
  135. * Currently the publisher sends only DateTime data type */
  136. pMetaData->fieldsSize = 4;
  137. pMetaData->fields = (UA_FieldMetaData*)UA_Array_new (pMetaData->fieldsSize,
  138. &UA_TYPES[UA_TYPES_FIELDMETADATA]);
  139. /* DateTime DataType */
  140. UA_FieldMetaData_init (&pMetaData->fields[0]);
  141. UA_NodeId_copy (&UA_TYPES[UA_TYPES_DATETIME].typeId,
  142. &pMetaData->fields[0].dataType);
  143. pMetaData->fields[0].builtInType = UA_NS0ID_DATETIME;
  144. pMetaData->fields[0].name = UA_STRING ("DateTime");
  145. pMetaData->fields[0].valueRank = -1; /* scalar */
  146. /* Int32 DataType */
  147. UA_FieldMetaData_init (&pMetaData->fields[1]);
  148. UA_NodeId_copy(&UA_TYPES[UA_TYPES_INT32].typeId,
  149. &pMetaData->fields[1].dataType);
  150. pMetaData->fields[1].builtInType = UA_NS0ID_INT32;
  151. pMetaData->fields[1].name = UA_STRING ("Int32");
  152. pMetaData->fields[1].valueRank = -1; /* scalar */
  153. /* Int64 DataType */
  154. UA_FieldMetaData_init (&pMetaData->fields[2]);
  155. UA_NodeId_copy(&UA_TYPES[UA_TYPES_INT64].typeId,
  156. &pMetaData->fields[2].dataType);
  157. pMetaData->fields[2].builtInType = UA_NS0ID_INT64;
  158. pMetaData->fields[2].name = UA_STRING ("Int64");
  159. pMetaData->fields[2].valueRank = -1; /* scalar */
  160. /* Boolean DataType */
  161. UA_FieldMetaData_init (&pMetaData->fields[3]);
  162. UA_NodeId_copy (&UA_TYPES[UA_TYPES_BOOLEAN].typeId,
  163. &pMetaData->fields[3].dataType);
  164. pMetaData->fields[3].builtInType = UA_NS0ID_BOOLEAN;
  165. pMetaData->fields[3].name = UA_STRING ("BoolToggle");
  166. pMetaData->fields[3].valueRank = -1; /* scalar */
  167. }
  168. UA_Boolean running = true;
  169. static void stopHandler(int sign) {
  170. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
  171. running = false;
  172. }
  173. static int
  174. run(UA_String *transportProfile, UA_NetworkAddressUrlDataType *networkAddressUrl) {
  175. signal(SIGINT, stopHandler);
  176. signal(SIGTERM, stopHandler);
  177. /* Return value initialized to Status Good */
  178. UA_StatusCode retval = UA_STATUSCODE_GOOD;
  179. UA_Server *server = UA_Server_new();
  180. UA_ServerConfig *config = UA_Server_getConfig(server);
  181. UA_ServerConfig_setMinimal(config, 4801, NULL);
  182. /* Add the PubSub network layer implementation to the server config.
  183. * The TransportLayer is acting as factory to create new connections
  184. * on runtime. Details about the PubSubTransportLayer can be found inside the
  185. * tutorial_pubsub_connection */
  186. config->pubsubTransportLayers = (UA_PubSubTransportLayer *)
  187. UA_calloc(2, sizeof(UA_PubSubTransportLayer));
  188. if(!config->pubsubTransportLayers) {
  189. UA_Server_delete(server);
  190. return EXIT_FAILURE;
  191. }
  192. config->pubsubTransportLayers[0] = UA_PubSubTransportLayerUDPMP();
  193. config->pubsubTransportLayersSize++;
  194. #ifdef UA_ENABLE_PUBSUB_ETH_UADP
  195. config->pubsubTransportLayers[1] = UA_PubSubTransportLayerEthernet();
  196. config->pubsubTransportLayersSize++;
  197. #endif
  198. /* API calls */
  199. /* Add PubSubConnection */
  200. retval |= addPubSubConnection(server, transportProfile, networkAddressUrl);
  201. if (retval != UA_STATUSCODE_GOOD)
  202. return EXIT_FAILURE;
  203. /* Add ReaderGroup to the created PubSubConnection */
  204. retval |= addReaderGroup(server);
  205. if (retval != UA_STATUSCODE_GOOD)
  206. return EXIT_FAILURE;
  207. /* Add DataSetReader to the created ReaderGroup */
  208. retval |= addDataSetReader(server);
  209. if (retval != UA_STATUSCODE_GOOD)
  210. return EXIT_FAILURE;
  211. /* Add SubscribedVariables to the created DataSetReader */
  212. retval |= addSubscribedVariables(server, readerIdentifier);
  213. if (retval != UA_STATUSCODE_GOOD)
  214. return EXIT_FAILURE;
  215. retval = UA_Server_run(server, &running);
  216. UA_Server_delete(server);
  217. return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
  218. }
  219. static void
  220. usage(char *progname) {
  221. printf("usage: %s <uri> [device]\n", progname);
  222. }
  223. int main(int argc, char **argv) {
  224. UA_String transportProfile = UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
  225. UA_NetworkAddressUrlDataType networkAddressUrl = {UA_STRING_NULL , UA_STRING("opc.udp://224.0.0.22:4840/")};
  226. if(argc > 1) {
  227. if(strcmp(argv[1], "-h") == 0) {
  228. usage(argv[0]);
  229. return EXIT_SUCCESS;
  230. } else if(strncmp(argv[1], "opc.udp://", 10) == 0) {
  231. networkAddressUrl.url = UA_STRING(argv[1]);
  232. } else if(strncmp(argv[1], "opc.eth://", 10) == 0) {
  233. transportProfile =
  234. UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-eth-uadp");
  235. if(argc < 3) {
  236. printf("Error: UADP/ETH needs an interface name\n");
  237. return EXIT_FAILURE;
  238. }
  239. networkAddressUrl.networkInterface = UA_STRING(argv[2]);
  240. networkAddressUrl.url = UA_STRING(argv[1]);
  241. } else {
  242. printf ("Error: unknown URI\n");
  243. return EXIT_FAILURE;
  244. }
  245. }
  246. return run(&transportProfile, &networkAddressUrl);
  247. }