opcua 0.12 → 0.13
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|