opcua 0.12 → 0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +71 -4
- data/cert/cert.h +52 -52
- data/cert/cert_key.h +101 -101
- data/example/array/Makefile +3 -0
- data/example/array/clienttest.c +168 -0
- data/example/array/open62541.h +27464 -0
- data/example/array/servertest.c +142 -0
- data/example/bug5.rb +22 -0
- data/example/client_method.rb +7 -3
- data/example/server.rb +3 -3
- data/ext/opcua/client/client.c +38 -13
- data/ext/opcua/client/client.h +2 -1
- data/ext/opcua/client/finders.c +1 -0
- data/ext/opcua/client/finders.h +1 -0
- data/ext/opcua/client/log_none.c +1 -0
- data/ext/opcua/client/log_none.h +1 -0
- data/ext/opcua/client/strnautocat.c +1 -0
- data/ext/opcua/client/strnautocat.h +1 -0
- data/ext/opcua/client/values.c +1 -0
- data/ext/opcua/client/values.h +1 -0
- data/ext/opcua/helpers/finders.c +142 -0
- data/ext/opcua/helpers/finders.h +13 -0
- data/ext/opcua/{log_none.h → helpers/log_none.c} +2 -0
- data/ext/opcua/helpers/log_none.h +12 -0
- data/ext/opcua/{strnautocat.h → helpers/strnautocat.c} +2 -0
- data/ext/opcua/helpers/strnautocat.h +8 -0
- data/ext/opcua/{values.h → helpers/values.c} +64 -25
- data/ext/opcua/helpers/values.h +12 -0
- data/ext/opcua/server/finders.c +1 -0
- data/ext/opcua/server/finders.h +1 -0
- data/ext/opcua/server/log_none.c +1 -0
- data/ext/opcua/server/log_none.h +1 -0
- data/ext/opcua/server/server.c +70 -57
- data/ext/opcua/server/server.h +4 -2
- data/ext/opcua/server/strnautocat.c +1 -0
- data/ext/opcua/server/strnautocat.h +1 -0
- data/ext/opcua/server/values.c +1 -0
- data/ext/opcua/server/values.h +1 -0
- data/opcua.gemspec +1 -1
- metadata +32 -6
|
@@ -0,0 +1,142 @@
|
|
|
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
|
+
* Adding Variables to a Server
|
|
6
|
+
* ----------------------------
|
|
7
|
+
*
|
|
8
|
+
* This tutorial shows how to work with data types and how to add variable nodes
|
|
9
|
+
* to a server. First, we add a new variable to the server. Take a look at the
|
|
10
|
+
* definition of the ``UA_VariableAttributes`` structure to see the list of all
|
|
11
|
+
* attributes defined for VariableNodes.
|
|
12
|
+
*
|
|
13
|
+
* Note that the default settings have the AccessLevel of the variable value as
|
|
14
|
+
* read only. See below for making the variable writable.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
#include <open62541.h>
|
|
18
|
+
|
|
19
|
+
#include <signal.h>
|
|
20
|
+
#include <stdlib.h>
|
|
21
|
+
|
|
22
|
+
static void
|
|
23
|
+
addVariable(UA_Server *server) {
|
|
24
|
+
/* Define the attribute of the myInteger variable node */
|
|
25
|
+
UA_VariableAttributes attr = UA_VariableAttributes_default;
|
|
26
|
+
UA_Int32 myInteger = 42;
|
|
27
|
+
UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
|
|
28
|
+
attr.description = UA_LOCALIZEDTEXT("en-US","the answer");
|
|
29
|
+
attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer");
|
|
30
|
+
attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
|
|
31
|
+
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
|
32
|
+
|
|
33
|
+
/* Add the variable node to the information model */
|
|
34
|
+
UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
|
|
35
|
+
UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
|
|
36
|
+
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
|
|
37
|
+
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
|
|
38
|
+
UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
|
|
39
|
+
parentReferenceNodeId, myIntegerName,
|
|
40
|
+
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static UA_NodeId pointTypeId;
|
|
44
|
+
static void addVariableType2DPoint(UA_Server *server) {
|
|
45
|
+
UA_VariableAttributes vtAttr = UA_VariableAttributes_default;
|
|
46
|
+
vtAttr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
|
|
47
|
+
vtAttr.valueRank = UA_VALUERANK_ONE_DIMENSION;
|
|
48
|
+
UA_UInt32 arrayDims[1] = {5};
|
|
49
|
+
vtAttr.arrayDimensions = arrayDims;
|
|
50
|
+
vtAttr.arrayDimensionsSize = 1;
|
|
51
|
+
vtAttr.displayName = UA_LOCALIZEDTEXT("en-US", "2DPoint Type");
|
|
52
|
+
vtAttr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
|
|
53
|
+
UA_NodeId myArrayNodeId = UA_NODEID_STRING(1, "MyArray");
|
|
54
|
+
/* a matching default value is required */
|
|
55
|
+
UA_Double zero[2] = {0.0, 0.0};
|
|
56
|
+
UA_Variant_setArray(&vtAttr.value, zero, 5, &UA_TYPES[UA_TYPES_DOUBLE]);
|
|
57
|
+
|
|
58
|
+
UA_Server_addVariableNode(server, myArrayNodeId,
|
|
59
|
+
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
|
|
60
|
+
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
|
|
61
|
+
UA_QUALIFIEDNAME(1, "2DPoint Type"), UA_NODEID_NULL,
|
|
62
|
+
vtAttr, NULL, &pointTypeId);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Now we change the value with the write service. This uses the same service
|
|
66
|
+
* implementation that can also be reached over the network by an OPC UA client.
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
static void writeVariable(UA_Server *server) {
|
|
70
|
+
UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
|
|
71
|
+
|
|
72
|
+
/* Write a different integer value */
|
|
73
|
+
UA_Int32 myInteger = 43;
|
|
74
|
+
UA_Variant myVar;
|
|
75
|
+
UA_Variant_init(&myVar);
|
|
76
|
+
UA_Variant_setScalar(&myVar, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
|
|
77
|
+
UA_Server_writeValue(server, myIntegerNodeId, myVar);
|
|
78
|
+
|
|
79
|
+
/* Set the status code of the value to an error code. The function
|
|
80
|
+
* UA_Server_write provides access to the raw service. The above
|
|
81
|
+
* UA_Server_writeValue is syntactic sugar for writing a specific node
|
|
82
|
+
* attribute with the write service. */
|
|
83
|
+
UA_WriteValue wv;
|
|
84
|
+
UA_WriteValue_init(&wv);
|
|
85
|
+
wv.nodeId = myIntegerNodeId;
|
|
86
|
+
wv.attributeId = UA_ATTRIBUTEID_VALUE;
|
|
87
|
+
wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
|
|
88
|
+
wv.value.hasStatus = true;
|
|
89
|
+
UA_Server_write(server, &wv);
|
|
90
|
+
|
|
91
|
+
/* Reset the variable to a good statuscode with a value */
|
|
92
|
+
wv.value.hasStatus = false;
|
|
93
|
+
wv.value.value = myVar;
|
|
94
|
+
wv.value.hasValue = true;
|
|
95
|
+
UA_Server_write(server, &wv);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Note how we initially set the DataType attribute of the variable node to the
|
|
100
|
+
* NodeId of the Int32 data type. This forbids writing values that are not an
|
|
101
|
+
* Int32. The following code shows how this consistency check is performed for
|
|
102
|
+
* every write.
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
static void
|
|
106
|
+
writeWrongVariable(UA_Server *server) {
|
|
107
|
+
UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
|
|
108
|
+
|
|
109
|
+
/* Write a string */
|
|
110
|
+
UA_String myString = UA_STRING("test");
|
|
111
|
+
UA_Variant myVar;
|
|
112
|
+
UA_Variant_init(&myVar);
|
|
113
|
+
UA_Variant_setScalar(&myVar, &myString, &UA_TYPES[UA_TYPES_STRING]);
|
|
114
|
+
UA_StatusCode retval = UA_Server_writeValue(server, myIntegerNodeId, myVar);
|
|
115
|
+
printf("Writing a string returned statuscode %s\n", UA_StatusCode_name(retval));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** It follows the main server code, making use of the above definitions. */
|
|
119
|
+
|
|
120
|
+
UA_Boolean running = true;
|
|
121
|
+
static void stopHandler(int sign) {
|
|
122
|
+
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
|
|
123
|
+
running = false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
int main(void) {
|
|
127
|
+
signal(SIGINT, stopHandler);
|
|
128
|
+
signal(SIGTERM, stopHandler);
|
|
129
|
+
UA_ServerConfig *config = UA_ServerConfig_new_default();
|
|
130
|
+
UA_Server *server = UA_Server_new(config);
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
addVariable(server);
|
|
134
|
+
writeVariable(server);
|
|
135
|
+
writeWrongVariable(server);
|
|
136
|
+
addVariableType2DPoint(server);
|
|
137
|
+
|
|
138
|
+
UA_StatusCode retval = UA_Server_run(server, &running);
|
|
139
|
+
|
|
140
|
+
UA_Server_delete(server);
|
|
141
|
+
return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
142
|
+
}
|
data/example/bug5.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/ruby
|
|
2
|
+
require_relative '../lib/opcua/server'
|
|
3
|
+
|
|
4
|
+
Daemonite.new do
|
|
5
|
+
server = OPCUA::Server.new
|
|
6
|
+
server.add_namespace "https://centurio.work/kelch"
|
|
7
|
+
|
|
8
|
+
mt = server.types.add_object_type(:MeasurementType).tap{ |t|
|
|
9
|
+
t.add_variable :SollWertX
|
|
10
|
+
t.add_variable :SollWertY
|
|
11
|
+
t.add_variable :SollWertZ
|
|
12
|
+
}
|
|
13
|
+
tt = server.types.add_object_type(:ToolType).tap{ |t|
|
|
14
|
+
t.add_object :M, mt, OPCUA::MANDATORY
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
tools = server.objects.instantiate(:KalimatC34, tt)
|
|
18
|
+
|
|
19
|
+
run do
|
|
20
|
+
sleep server.run
|
|
21
|
+
end
|
|
22
|
+
end.loop!
|
data/example/client_method.rb
CHANGED
|
@@ -9,6 +9,10 @@ client = OPCUA::Client.new("opc.tcp://localhost:4840")
|
|
|
9
9
|
client.debug = false
|
|
10
10
|
client.default_ns = 2
|
|
11
11
|
|
|
12
|
-
node = client.get '/KalimatC34/Tools/Tool3/testMethod'
|
|
13
|
-
node.call 'abcde', Time.now
|
|
14
|
-
client.disconnect
|
|
12
|
+
# node = client.get '/KalimatC34/Tools/Tool3/testMethod'
|
|
13
|
+
# p node.call 'abcde', Time.now
|
|
14
|
+
# client.disconnect
|
|
15
|
+
|
|
16
|
+
node = client.get 0, 11492
|
|
17
|
+
p node
|
|
18
|
+
p node.call 2
|
data/example/server.rb
CHANGED
|
@@ -48,12 +48,12 @@ Daemonite.new do
|
|
|
48
48
|
opts[:tn].description = 'test test'
|
|
49
49
|
opts[:tn].value = [0,1]
|
|
50
50
|
p opts[:tn].description
|
|
51
|
+
p opts[:tn].to_s
|
|
52
|
+
p opts[:tn].name
|
|
51
53
|
|
|
52
54
|
measurments_t1 = t1.find(:Measurements)
|
|
53
55
|
measurments_t1.manifest(:M1,mt)
|
|
54
|
-
measurments_t1.manifest(:M2,mt)
|
|
55
|
-
|
|
56
|
-
p opts['server'].namespaces
|
|
56
|
+
m2 = measurments_t1.manifest(:M2,mt)
|
|
57
57
|
rescue => e
|
|
58
58
|
puts e.message
|
|
59
59
|
end
|
data/ext/opcua/client/client.c
CHANGED
|
@@ -9,7 +9,7 @@ VALUE cVarNode = Qnil;
|
|
|
9
9
|
VALUE cMethodNode = Qnil;
|
|
10
10
|
VALUE cNode = Qnil;
|
|
11
11
|
|
|
12
|
-
#include "
|
|
12
|
+
#include "values.h"
|
|
13
13
|
|
|
14
14
|
#include <signal.h>
|
|
15
15
|
|
|
@@ -64,7 +64,14 @@ static VALUE node_value_set(VALUE self, VALUE value) { //{{{
|
|
|
64
64
|
if (!ns->master->started) rb_raise(rb_eRuntimeError, "Client disconnected.");
|
|
65
65
|
|
|
66
66
|
UA_Variant variant;
|
|
67
|
-
|
|
67
|
+
UA_NodeId pdt;
|
|
68
|
+
UA_Client_readDataTypeAttribute(ns->master->master, ns->id, &pdt);
|
|
69
|
+
UA_UInt32 proposal = -1;
|
|
70
|
+
if (pdt.namespaceIndex == 0) {
|
|
71
|
+
proposal = pdt.identifier.numeric - 1; // what a dirty hack TODO, translate node to value
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (value_to_variant(value,&variant,proposal)) {
|
|
68
75
|
// printf("-----------------------------------------%ld\n",variant.arrayDimensionsSize);
|
|
69
76
|
if (variant.arrayDimensionsSize > 0) {
|
|
70
77
|
UA_Int32 ads = (UA_Int32) variant.arrayDimensionsSize;
|
|
@@ -96,7 +103,8 @@ static VALUE node_on_change(VALUE self) { //{{{
|
|
|
96
103
|
|
|
97
104
|
return self;
|
|
98
105
|
} //}}}
|
|
99
|
-
|
|
106
|
+
|
|
107
|
+
static void node_on_value_change_handler(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, UA_DataValue *value) { // {{{
|
|
100
108
|
VALUE ins = (VALUE)monContext;
|
|
101
109
|
VALUE blk = RARRAY_AREF(ins,1);
|
|
102
110
|
|
|
@@ -120,8 +128,8 @@ static void node_on_value_change_handler(UA_Client *client, UA_UInt32 subId, voi
|
|
|
120
128
|
rb_ary_store(args,2,rb_ary_entry(ret,1));
|
|
121
129
|
rb_proc_call(blk,args);
|
|
122
130
|
}
|
|
123
|
-
}
|
|
124
|
-
static VALUE node_on_value_change(VALUE self) {
|
|
131
|
+
} // }}}
|
|
132
|
+
static VALUE node_on_value_change(VALUE self) { // {{{
|
|
125
133
|
node_struct *ns;
|
|
126
134
|
Data_Get_Struct(self, node_struct, ns);
|
|
127
135
|
if (!ns->master->started) rb_raise(rb_eRuntimeError, "Client disconnected.");
|
|
@@ -171,7 +179,7 @@ static VALUE node_on_value_change(VALUE self) {
|
|
|
171
179
|
UA_CreateSubscriptionRequest_clear(&sreq);
|
|
172
180
|
|
|
173
181
|
return self;
|
|
174
|
-
}
|
|
182
|
+
} // }}}
|
|
175
183
|
|
|
176
184
|
/* -- */
|
|
177
185
|
static void client_free(client_struct *pss) { //{{{
|
|
@@ -486,24 +494,40 @@ static VALUE node_to_s(VALUE self) { //{{{
|
|
|
486
494
|
|
|
487
495
|
static VALUE node_call(int argc, VALUE* argv, VALUE self) { //{{{
|
|
488
496
|
node_struct *ns;
|
|
497
|
+
UA_StatusCode retval;
|
|
489
498
|
|
|
490
499
|
VALUE splat;
|
|
491
500
|
rb_scan_args(argc, argv, "*", &splat);
|
|
492
501
|
|
|
493
502
|
Data_Get_Struct(self, node_struct, ns);
|
|
494
503
|
|
|
504
|
+
UA_NodeId parent;
|
|
505
|
+
client_node_get_reference_by_type(ns->master->master, ns->id, UA_NODEID_NUMERIC(0,UA_NS0ID_HASCOMPONENT), &parent, true);
|
|
506
|
+
|
|
507
|
+
UA_NodeId ia;
|
|
508
|
+
client_node_get_reference_by_name(ns->master->master, ns->id, UA_QUALIFIEDNAME(0,"InputArguments"), &ia, false);
|
|
509
|
+
UA_Variant iaval;
|
|
510
|
+
UA_Variant_init(&iaval);
|
|
511
|
+
UA_Client_readValueAttribute(ns->master->master, ia, &iaval);
|
|
512
|
+
|
|
513
|
+
UA_UInt32 proposal[RARRAY_LEN(splat)];
|
|
514
|
+
for (int i=0; i < iaval.arrayLength; i++) {
|
|
515
|
+
UA_ExtensionObject* eoarg= (UA_ExtensionObject *)(iaval.data);
|
|
516
|
+
UA_Argument* arg = (UA_Argument *)(eoarg[i].content.decoded.data);
|
|
517
|
+
|
|
518
|
+
if (arg->dataType.namespaceIndex == 0) {
|
|
519
|
+
proposal[i] = arg->dataType.identifier.numeric - 1; // what a dirty hack TODO, translate node to value
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
495
523
|
UA_Variant inputArguments[RARRAY_LEN(splat)];
|
|
496
524
|
for (long i=0; i<RARRAY_LEN(splat); i++) {
|
|
497
|
-
value_to_variant(RARRAY_AREF(splat, i),&inputArguments[i]);
|
|
525
|
+
value_to_variant(RARRAY_AREF(splat, i),&inputArguments[i],proposal[i]);
|
|
498
526
|
}
|
|
499
527
|
|
|
500
|
-
|
|
501
|
-
VALUE pat = rb_str_new2("\\/[a-z0-9A-Z]+\\/?$");
|
|
502
|
-
VALUE obj = rb_funcall(path,rb_intern("sub"),2,rb_reg_new_str(pat, 0),rb_str_new2(""));
|
|
503
|
-
|
|
504
|
-
UA_StatusCode retval = UA_Client_call(
|
|
528
|
+
retval = UA_Client_call(
|
|
505
529
|
ns->master->master,
|
|
506
|
-
|
|
530
|
+
parent,
|
|
507
531
|
ns->id,
|
|
508
532
|
RARRAY_LEN(splat),
|
|
509
533
|
(UA_Variant *)&inputArguments,
|
|
@@ -511,6 +535,7 @@ static VALUE node_call(int argc, VALUE* argv, VALUE self) { //{{{
|
|
|
511
535
|
NULL
|
|
512
536
|
);
|
|
513
537
|
|
|
538
|
+
|
|
514
539
|
if(retval == UA_STATUSCODE_GOOD) {
|
|
515
540
|
return Qtrue;
|
|
516
541
|
} else {
|
data/ext/opcua/client/client.h
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../helpers/finders.c
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../helpers/finders.h
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../helpers/log_none.c
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../helpers/log_none.h
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../helpers/strnautocat.c
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../helpers/strnautocat.h
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../helpers/values.c
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
../helpers/values.h
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#include "finders.h"
|
|
2
|
+
|
|
3
|
+
UA_BrowsePathResult node_browse_path(UA_Server *server, UA_NodeId relative, UA_NodeId ref, UA_QualifiedName mqn, bool inverse) {
|
|
4
|
+
UA_RelativePathElement rpe;
|
|
5
|
+
UA_RelativePathElement_init(&rpe);
|
|
6
|
+
rpe.referenceTypeId = ref;
|
|
7
|
+
rpe.isInverse = inverse;
|
|
8
|
+
rpe.includeSubtypes = false;
|
|
9
|
+
rpe.targetName = mqn;
|
|
10
|
+
|
|
11
|
+
UA_BrowsePath bp;
|
|
12
|
+
UA_BrowsePath_init(&bp);
|
|
13
|
+
bp.startingNode = relative;
|
|
14
|
+
bp.relativePath.elementsSize = 1;
|
|
15
|
+
bp.relativePath.elements = &rpe;
|
|
16
|
+
|
|
17
|
+
return UA_Server_translateBrowsePathToNodeIds(server, &bp);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
bool server_node_get_reference(UA_Server *server, UA_NodeId parent, UA_NodeId *result, bool inverse) {
|
|
21
|
+
UA_BrowseDescription bDes;
|
|
22
|
+
UA_BrowseDescription_init(&bDes);
|
|
23
|
+
bDes.nodeId = parent;
|
|
24
|
+
bDes.resultMask = UA_BROWSERESULTMASK_ALL;
|
|
25
|
+
bDes.browseDirection = inverse ? 1 : 0;
|
|
26
|
+
UA_BrowseResult bRes = UA_Server_browse(server, 2, &bDes);
|
|
27
|
+
|
|
28
|
+
if (bRes.referencesSize > 0) {
|
|
29
|
+
UA_ReferenceDescription *ref = &(bRes.references[0]);
|
|
30
|
+
|
|
31
|
+
UA_NodeId_copy(&ref->nodeId.nodeId,result);
|
|
32
|
+
|
|
33
|
+
// UA_QualifiedName qn; UA_QualifiedName_init(&qn);
|
|
34
|
+
// UA_Server_readBrowseName(server, ref->nodeId.nodeId, &qn);
|
|
35
|
+
// printf("NS: %d ---> NodeId %u; %-16.*s\n",
|
|
36
|
+
// ref->nodeId.nodeId.namespaceIndex,
|
|
37
|
+
// ref->nodeId.nodeId.identifier.numeric,
|
|
38
|
+
// (int)qn.name.length,
|
|
39
|
+
// qn.name.data
|
|
40
|
+
// );
|
|
41
|
+
|
|
42
|
+
UA_BrowseResult_deleteMembers(&bRes);
|
|
43
|
+
UA_BrowseResult_clear(&bRes);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
bool client_node_get_reference_by_name(UA_Client *client, UA_NodeId parent, UA_QualifiedName name, UA_NodeId *result, bool inverse) {
|
|
50
|
+
UA_BrowseRequest bReq;
|
|
51
|
+
UA_BrowseRequest_init(&bReq);
|
|
52
|
+
bReq.requestedMaxReferencesPerNode = 0;
|
|
53
|
+
bReq.nodesToBrowse = UA_BrowseDescription_new();
|
|
54
|
+
bReq.nodesToBrowseSize = 1;
|
|
55
|
+
bReq.nodesToBrowse[0].nodeId = parent;
|
|
56
|
+
bReq.nodesToBrowse[0].browseDirection = inverse ? 1 : 0;
|
|
57
|
+
bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
|
|
58
|
+
UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
|
|
59
|
+
|
|
60
|
+
bool success = false;
|
|
61
|
+
for (int i=0; i < bResp.resultsSize && !success; i++) {
|
|
62
|
+
for (int j=0; j < bResp.results[i].referencesSize && !success; j++) {
|
|
63
|
+
UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
|
|
64
|
+
|
|
65
|
+
UA_QualifiedName qn; UA_QualifiedName_init(&qn);
|
|
66
|
+
UA_Client_readBrowseNameAttribute(client, ref->nodeId.nodeId, &qn);
|
|
67
|
+
|
|
68
|
+
if (UA_QualifiedName_equal(&qn,&name)) {
|
|
69
|
+
UA_NodeId_copy(&ref->nodeId.nodeId,result);
|
|
70
|
+
success = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
UA_BrowseResponse_deleteMembers(&bResp);
|
|
76
|
+
UA_BrowseResponse_clear(&bResp);
|
|
77
|
+
return success;
|
|
78
|
+
}
|
|
79
|
+
bool client_node_get_reference_by_type(UA_Client *client, UA_NodeId parent, UA_NodeId type, UA_NodeId *result, bool inverse) {
|
|
80
|
+
UA_BrowseRequest bReq;
|
|
81
|
+
UA_BrowseRequest_init(&bReq);
|
|
82
|
+
bReq.requestedMaxReferencesPerNode = 0;
|
|
83
|
+
bReq.nodesToBrowse = UA_BrowseDescription_new();
|
|
84
|
+
bReq.nodesToBrowseSize = 1;
|
|
85
|
+
bReq.nodesToBrowse[0].nodeId = parent;
|
|
86
|
+
bReq.nodesToBrowse[0].browseDirection = inverse ? 1 : 0;
|
|
87
|
+
bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
|
|
88
|
+
UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
|
|
89
|
+
|
|
90
|
+
bool success = false;
|
|
91
|
+
for (int i=0; i < bResp.resultsSize && !success; i++) {
|
|
92
|
+
for (int j=0; j < bResp.results[i].referencesSize && !success; j++) {
|
|
93
|
+
UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
|
|
94
|
+
if (UA_NodeId_equal(&ref->referenceTypeId,&type)) {
|
|
95
|
+
UA_NodeId_copy(&ref->nodeId.nodeId,result);
|
|
96
|
+
success = true;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
UA_BrowseResponse_deleteMembers(&bResp);
|
|
102
|
+
UA_BrowseResponse_clear(&bResp);
|
|
103
|
+
return success;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
bool client_node_list_references(UA_Client *client, UA_NodeId parent, bool inverse) {
|
|
107
|
+
UA_BrowseRequest bReq;
|
|
108
|
+
UA_BrowseRequest_init(&bReq);
|
|
109
|
+
bReq.requestedMaxReferencesPerNode = 0;
|
|
110
|
+
bReq.nodesToBrowse = UA_BrowseDescription_new();
|
|
111
|
+
bReq.nodesToBrowseSize = 1;
|
|
112
|
+
bReq.nodesToBrowse[0].nodeId = parent;
|
|
113
|
+
bReq.nodesToBrowse[0].browseDirection = inverse ? 1 : 0;
|
|
114
|
+
bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL;
|
|
115
|
+
UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
|
|
116
|
+
|
|
117
|
+
for (int i=0; i < bResp.resultsSize; i++) {
|
|
118
|
+
for (int j=0; j < bResp.results[i].referencesSize; j++) {
|
|
119
|
+
UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
|
|
120
|
+
|
|
121
|
+
UA_QualifiedName qn; UA_QualifiedName_init(&qn);
|
|
122
|
+
UA_Client_readBrowseNameAttribute(client, ref->nodeId.nodeId, &qn);
|
|
123
|
+
UA_QualifiedName tqn; UA_QualifiedName_init(&tqn);
|
|
124
|
+
UA_Client_readBrowseNameAttribute(client, ref->referenceTypeId, &tqn);
|
|
125
|
+
printf("ns=%d;i=%u - %-16.*s(ns=%d;i=%u) -> %d:%-16.*s\n",
|
|
126
|
+
ref->nodeId.nodeId.namespaceIndex,
|
|
127
|
+
ref->nodeId.nodeId.identifier.numeric,
|
|
128
|
+
(int)tqn.name.length,
|
|
129
|
+
tqn.name.data,
|
|
130
|
+
ref->referenceTypeId.namespaceIndex,
|
|
131
|
+
ref->referenceTypeId.identifier.numeric,
|
|
132
|
+
qn.namespaceIndex,
|
|
133
|
+
(int)qn.name.length,
|
|
134
|
+
qn.name.data
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
UA_BrowseResponse_deleteMembers(&bResp);
|
|
140
|
+
UA_BrowseResponse_clear(&bResp);
|
|
141
|
+
return false;
|
|
142
|
+
}
|