tutorial_firstStepsClient.rst 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. First steps with open62541-client
  2. ===================================
  3. In the previous :doc:`tutorial_firstStepsServer` tutorial, you should have gotten your build environment verified and created a first OPC UA server using the open62541 stack. The created server however doesn't do much yet and there is no client to interact with the server. We are going to remedy that in this tutorial by creating some nodes and variables.
  4. ----------------------
  5. You should already have a basic server from the previous tutorial. open62541 provides both a server- and clientside API, so creating a client is equally as easy as creating a server. We are going to use dynamic linking (libopen62541.so) from now on, because our client and server will share a lot of code. Reusing the shared library will considerably reduce the overhead. To avoid confusion, remove the amalgated open62541.c/h files from your example directory.
  6. As a recap, your directory structure should now look like this::
  7. :myServerApp> rm *.o open62541.*
  8. :myServerApp> ln -s ../open62541/build/*so ./
  9. :myServerApp> tree
  10. .
  11. ├── include
  12. │   ├── logger_stdout.h
  13. │   ├── networklayer_tcp.h
  14. │   ├── networklayer_udp.h
  15. │   ├── open62541.h
  16. │   ├── ua_client.h
  17. │   ├── ua_config.h
  18. │   ├── ua_config.h.in
  19. │   ├── ua_connection.h
  20. │   ├── ua_log.h
  21. │   ├── ua_nodeids.h
  22. │   ├── ua_server.h
  23. │   ├── ua_statuscodes.h
  24. │   ├── ua_transport_generated.h
  25. │   ├── ua_types_generated.h
  26. │   └── ua_types.h
  27. ├── libopen62541.so -> ../../open62541/build/libopen62541.so
  28. ├── myServer
  29. └── myServer.c
  30. Note that I have linked the library into the folder to spare me the trouble of copying it every time I change/rebuild the stack.
  31. To create a really basic client, navigate back into the MyServerApp folder from the previous tutorial and create a client::
  32. #include <stdio.h>
  33. #include "ua_types.h"
  34. #include "ua_server.h"
  35. #include "logger_stdout.h"
  36. #include "networklayer_tcp.h"
  37. int main(void) {
  38. UA_Client *client = UA_Client_new(UA_ClientConfig_standard, Logger_Stdout_new());
  39. UA_StatusCode retval = UA_Client_connect(client, ClientNetworkLayerTCP_connect, "opc.tcp://localhost:16664");
  40. if(retval != UA_STATUSCODE_GOOD) {
  41. UA_Client_delete(client);
  42. return retval;
  43. }
  44. UA_Client_disconnect(client);
  45. UA_Client_delete(client);
  46. return 0;
  47. }
  48. Let's recompile both server and client - if you feel up to it, you can create a Makefile for this procedure. I will show a final command line compile example and ommit the compilation directives in future examples.::
  49. :myServerApp> gcc -Wl,-rpath=./ -L./ -I ./include -o myClient myClient.c -lopen62541
  50. We will also make a slight change to our server: We want it to exit cleanly when pressing ``CTRL+C``. We will add signal handler for SIGINT and SIGTERM to accomplish that to the server::
  51. #include <stdio.h>
  52. #include <signal.h>
  53. # include "ua_types.h"
  54. # include "ua_server.h"
  55. # include "logger_stdout.h"
  56. # include "networklayer_tcp.h"
  57. UA_Boolean running;
  58. UA_Logger logger;
  59. void stopHandler(int signal) {
  60. running = 0;
  61. }
  62. int main(void) {
  63. signal(SIGINT, stopHandler);
  64. signal(SIGTERM, stopHandler);
  65. UA_Server *server = UA_Server_new(UA_ServerConfig_standard);
  66. logger = Logger_Stdout_new();
  67. UA_Server_setLogger(server, logger);
  68. UA_Server_addNetworkLayer(server, ServerNetworkLayerTCP_new(UA_ConnectionConfig_standard, 16664));
  69. running = UA_TRUE;
  70. UA_Server_run(server, 1, &running);
  71. UA_Server_delete(server);
  72. printf("Terminated\n");
  73. return 0;
  74. }
  75. And then of course, recompile it::
  76. :myServerApp> gcc -Wl,-rpath=./ -L./ -I ./include -o myServer myServer.c -lopen62541
  77. You can now start and background the server, run the client, and then terminate the server like so::
  78. :myServerApp> ./myServer &
  79. [xx/yy/zz aa:bb:cc.dd.ee] info/communication Listening on opc.tcp://localhost:16664
  80. [1] 2114
  81. :myServerApp> ./myClient && killall myServer
  82. Terminated
  83. [1]+ Done ./myServer
  84. :myServerApp>
  85. Notice how the server received the SIGTERM signal from kill and exited cleany? We also used the return value of our client by inserting the ``&&``, so kill is only called after a clean client exit (``return 0``).
  86. Asserting success/failure
  87. -------------------------
  88. Almost all functions of the open62541 API will return a ``UA_StatusCode``, which in the C world would be represented by a ``unsigned int``. OPC UA defines large number of good and bad return codes represented by this number. The constant UA_STATUSCODE_GOOD is defined as 0 in ``include/ua_statuscodes.h`` along with many other return codes. It pays off to check the return code of your function calls, as we already did implicitly in the client.