tutorial_pubsub_subscribe.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  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. /**
  5. * IMPORTANT ANNOUNCEMENT
  6. * The PubSub subscriber API is currently not finished. This examples can be used to receive
  7. * and print the values, which are published by the tutorial_pubsub_publish example.
  8. * The following code uses internal API which will be later replaced by the higher-level
  9. * PubSub subscriber API.
  10. */
  11. #include <signal.h>
  12. #include "ua_pubsub_networkmessage.h"
  13. #include "ua_log_stdout.h"
  14. #include "ua_server.h"
  15. #include "ua_config_default.h"
  16. #include "ua_pubsub.h"
  17. #include "ua_network_pubsub_udp.h"
  18. #include "src_generated/ua_types_generated.h"
  19. UA_Boolean running = true;
  20. static void stopHandler(int sign) {
  21. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
  22. running = false;
  23. }
  24. static void
  25. subscriptionPollingCallback(UA_Server *server, UA_PubSubConnection *connection) {
  26. UA_ByteString buffer;
  27. if (UA_ByteString_allocBuffer(&buffer, 512) != UA_STATUSCODE_GOOD) {
  28. UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
  29. "Message buffer allocation failed!");
  30. return;
  31. }
  32. /* Receive the message. Blocks for 5ms */
  33. UA_StatusCode retval =
  34. connection->channel->receive(connection->channel, &buffer, NULL, 5);
  35. if(retval != UA_STATUSCODE_GOOD || buffer.length == 0) {
  36. /* Workaround!! Reset buffer length. Receive can set the length to zero.
  37. * Then the buffer is not deleted because no memory allocation is
  38. * assumed.
  39. * TODO: Return an error code in 'receive' instead of setting the buf
  40. * length to zero. */
  41. buffer.length = 512;
  42. UA_ByteString_deleteMembers(&buffer);
  43. return;
  44. }
  45. /* Decode the message */
  46. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  47. "Message length: %zu", buffer.length);
  48. UA_NetworkMessage networkMessage;
  49. memset(&networkMessage, 0, sizeof(UA_NetworkMessage));
  50. size_t currentPosition = 0;
  51. UA_NetworkMessage_decodeBinary(&buffer, &currentPosition, &networkMessage);
  52. UA_ByteString_deleteMembers(&buffer);
  53. /* Is this the correct message type? */
  54. if(networkMessage.networkMessageType != UA_NETWORKMESSAGE_DATASET)
  55. goto cleanup;
  56. /* At least one DataSetMessage in the NetworkMessage? */
  57. if(networkMessage.payloadHeaderEnabled &&
  58. networkMessage.payloadHeader.dataSetPayloadHeader.count < 1)
  59. goto cleanup;
  60. /* Is this a KeyFrame-DataSetMessage? */
  61. UA_DataSetMessage *dsm = &networkMessage.payload.dataSetPayload.dataSetMessages[0];
  62. if(dsm->header.dataSetMessageType != UA_DATASETMESSAGE_DATAKEYFRAME)
  63. goto cleanup;
  64. /* Loop over the fields and print well-known content types */
  65. for(int i = 0; i < dsm->data.keyFrameData.fieldCount; i++) {
  66. const UA_DataType *currentType = dsm->data.keyFrameData.dataSetFields[i].value.type;
  67. if(currentType == &UA_TYPES[UA_TYPES_BYTE]) {
  68. UA_Byte value = *(UA_Byte *)dsm->data.keyFrameData.dataSetFields[i].value.data;
  69. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  70. "Message content: [Byte] \tReceived data: %i", value);
  71. } else if (currentType == &UA_TYPES[UA_TYPES_DATETIME]) {
  72. UA_DateTime value = *(UA_DateTime *)dsm->data.keyFrameData.dataSetFields[i].value.data;
  73. UA_DateTimeStruct receivedTime = UA_DateTime_toStruct(value);
  74. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
  75. "Message content: [DateTime] \t"
  76. "Received date: %02i-%02i-%02i Received time: %02i:%02i:%02i",
  77. receivedTime.year, receivedTime.month, receivedTime.day,
  78. receivedTime.hour, receivedTime.min, receivedTime.sec);
  79. }
  80. }
  81. cleanup:
  82. UA_NetworkMessage_deleteMembers(&networkMessage);
  83. }
  84. int main(void) {
  85. signal(SIGINT, stopHandler);
  86. signal(SIGTERM, stopHandler);
  87. UA_ServerConfig *config = UA_ServerConfig_new_minimal(4801, NULL);
  88. /* Details about the PubSubTransportLayer can be found inside the
  89. * tutorial_pubsub_connection */
  90. config->pubsubTransportLayers = (UA_PubSubTransportLayer *)
  91. UA_malloc(sizeof(UA_PubSubTransportLayer));
  92. if (!config->pubsubTransportLayers) {
  93. UA_ServerConfig_delete(config);
  94. return -1;
  95. }
  96. config->pubsubTransportLayers[0] = UA_PubSubTransportLayerUDPMP();
  97. config->pubsubTransportLayersSize++;
  98. UA_Server *server = UA_Server_new(config);
  99. UA_PubSubConnectionConfig connectionConfig;
  100. memset(&connectionConfig, 0, sizeof(connectionConfig));
  101. connectionConfig.name = UA_STRING("UDP-UADP Connection 1");
  102. connectionConfig.transportProfileUri =
  103. UA_STRING("http://opcfoundation.org/UA-Profile/Transport/pubsub-udp-uadp");
  104. connectionConfig.enabled = UA_TRUE;
  105. UA_NetworkAddressUrlDataType networkAddressUrl =
  106. {UA_STRING_NULL , UA_STRING("opc.udp://224.0.0.22:4840/")};
  107. UA_Variant_setScalar(&connectionConfig.address, &networkAddressUrl,
  108. &UA_TYPES[UA_TYPES_NETWORKADDRESSURLDATATYPE]);
  109. UA_NodeId connectionIdent;
  110. UA_StatusCode retval =
  111. UA_Server_addPubSubConnection(server, &connectionConfig, &connectionIdent);
  112. if(retval == UA_STATUSCODE_GOOD)
  113. UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
  114. "The PubSub Connection was created successfully!");
  115. /* The following lines register the listening on the configured multicast
  116. * address and configure a repeated job, which is used to handle received
  117. * messages. */
  118. UA_PubSubConnection *connection =
  119. UA_PubSubConnection_findConnectionbyId(server, connectionIdent);
  120. if(connection != NULL) {
  121. UA_StatusCode rv = connection->channel->regist(connection->channel, NULL);
  122. if (rv == UA_STATUSCODE_GOOD) {
  123. UA_UInt64 subscriptionCallbackId;
  124. UA_Server_addRepeatedCallback(server, (UA_ServerCallback)subscriptionPollingCallback,
  125. connection, 100, &subscriptionCallbackId);
  126. } else {
  127. UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "register channel failed: %s!",
  128. UA_StatusCode_name(rv));
  129. }
  130. }
  131. retval |= UA_Server_run(server, &running);
  132. UA_Server_delete(server);
  133. UA_ServerConfig_delete(config);
  134. return (int)retval;
  135. }