opcua 0.11 → 0.12

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.
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/ruby
2
+ require_relative '../lib/opcua/client'
3
+ #require 'opcua/client'
4
+
5
+ ### username & pass in url (e.g. siemens)
6
+ # client = OPCUA::Client.new("opc.tcp://OpcUaClient:SUNRISE@localhost:4840")
7
+
8
+ client = OPCUA::Client.new("opc.tcp://localhost:4840")
9
+ client.subscription_interval = 100 # default 500
10
+
11
+ node = client.get 2, '/KalimatC34/Tools/Tool1/ToolNumber' # get node from nodeid
12
+ p node.value
13
+ node.on_value_change do
14
+ puts 'now'
15
+ end
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/ruby
2
+ require_relative '../lib/opcua/client'
3
+ #require 'opcua/client'
4
+
5
+ ### username & pass in url (e.g. siemens)
6
+ # client = OPCUA::Client.new("opc.tcp://OpcUaClient:SUNRISE@localhost:4840")
7
+
8
+ client = OPCUA::Client.new("opc.tcp://localhost:4840")
9
+ client.subscription_interval = 100 # default 500
10
+
11
+ if (node = client.get 2, '/KalimatC34/Tools/Tool1/ToolNumber') # get node from nodeid
12
+ p node
13
+ p node.value
14
+ else
15
+ p 'invalid nodeid'
16
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/ruby
2
+ require_relative '../lib/opcua/client'
3
+ #require 'opcua/client'
4
+
5
+ ### username & pass in url (e.g. siemens)
6
+ # client = OPCUA::Client.new("opc.tcp://OpcUaClient:SUNRISE@localhost:4840")
7
+
8
+ client = OPCUA::Client.new("opc.tcp://localhost:4840")
9
+ client.default_ns = 2
10
+
11
+ if (node = client.get '/KalimatC34/Tools/Tool1/ToolNumber')
12
+ node.value = [:a, :b]
13
+ else
14
+ p 'invalid nodeid'
15
+ end
@@ -16,9 +16,9 @@ Daemonite.new do
16
16
  t.add_variable :SollWertX
17
17
  t.add_variable :SollWertY
18
18
  t.add_variable :SollWertZ
19
- t.add_variable :ToolNumber
19
+ t.add_variable_rw :ToolNumber
20
20
  t.add_variable :DuploNumber
21
- t.add_variable :testValue1
21
+ t.add_property :testValue1
22
22
  t.add_method :testMethod, test1: OPCUA::TYPES::STRING, test2: OPCUA::TYPES::DATETIME do |node, test1, test2|
23
23
  ns, nid = node.id
24
24
  puts '-' * 10
@@ -45,18 +45,30 @@ Daemonite.new do
45
45
  t3 = tools.manifest(:Tool3,tt)
46
46
 
47
47
  opts[:tn] = t1.find(:ToolNumber)
48
+ opts[:tn].description = 'test test'
49
+ opts[:tn].value = [0,1]
50
+ p opts[:tn].description
48
51
 
49
52
  measurments_t1 = t1.find(:Measurements)
50
53
  measurments_t1.manifest(:M1,mt)
51
54
  measurments_t1.manifest(:M2,mt)
55
+
56
+ p opts['server'].namespaces
52
57
  rescue => e
53
58
  puts e.message
54
59
  end
55
60
 
61
+
62
+ counter = 0
56
63
  run do |opts|
57
64
  GC.start
58
65
  sleep opts['server'].run
59
- opts[:tn].value = Time.now
66
+ # if counter % 100 == 0
67
+ # opts[:tn].value = [counter, counter]
68
+ # # opts[:tn].value = 1
69
+ # p opts[:tn].value
70
+ # end
71
+ # counter += 1
60
72
  rescue => e
61
73
  puts e.message
62
74
  end
@@ -2,6 +2,7 @@
2
2
  #include "../../../cert/cert.h"
3
3
  #include "../../../cert/cert_key.h"
4
4
 
5
+
5
6
  VALUE mOPCUA = Qnil;
6
7
  VALUE cClient = Qnil;
7
8
  VALUE cVarNode = Qnil;
@@ -10,6 +11,11 @@ VALUE cNode = Qnil;
10
11
 
11
12
  #include "../values.h"
12
13
 
14
+ #include <signal.h>
15
+
16
+ static volatile bool keepRunning = true;
17
+ void intHandler(int dummy) { keepRunning = false; }
18
+
13
19
  /* -- */
14
20
  static void node_free(node_struct *ns) { //{{{
15
21
  if (ns != NULL) {
@@ -26,6 +32,7 @@ static node_struct * node_alloc(client_struct *client, UA_NodeId nodeid) { //{{{
26
32
  ns->master = client;
27
33
  ns->id = nodeid;
28
34
  ns->on_change = Qnil;
35
+ ns->waiting = 0;
29
36
 
30
37
  return ns;
31
38
  } //}}}
@@ -51,6 +58,28 @@ static VALUE node_value(VALUE self) { //{{{
51
58
  UA_Variant_clear(&value);
52
59
  return rb_ary_entry(ret,0);
53
60
  } //}}}
61
+ static VALUE node_value_set(VALUE self, VALUE value) { //{{{
62
+ node_struct *ns;
63
+ Data_Get_Struct(self, node_struct, ns);
64
+ if (!ns->master->started) rb_raise(rb_eRuntimeError, "Client disconnected.");
65
+
66
+ UA_Variant variant;
67
+ if (value_to_variant(value,&variant)) {
68
+ // printf("-----------------------------------------%ld\n",variant.arrayDimensionsSize);
69
+ if (variant.arrayDimensionsSize > 0) {
70
+ UA_Int32 ads = (UA_Int32) variant.arrayDimensionsSize;
71
+ UA_Client_writeValueRankAttribute(ns->master->master, ns->id, &ads);
72
+ UA_Client_writeArrayDimensionsAttribute(ns->master->master, ns->id, variant.arrayDimensionsSize, variant.arrayDimensions);
73
+ }
74
+
75
+ UA_StatusCode retval = UA_Client_writeValueAttribute(ns->master->master, ns->id, &variant);
76
+ if (retval != UA_STATUSCODE_GOOD) {
77
+ rb_raise(rb_eRuntimeError, "Can't set value: %s.", UA_StatusCode_name(retval));
78
+ }
79
+ }
80
+
81
+ return self;
82
+ } //}}}
54
83
  static VALUE node_on_change(VALUE self) { //{{{
55
84
  node_struct *ns;
56
85
  Data_Get_Struct(self, node_struct, ns);
@@ -67,6 +96,82 @@ static VALUE node_on_change(VALUE self) { //{{{
67
96
 
68
97
  return self;
69
98
  } //}}}
99
+ static void node_on_value_change_handler(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, UA_DataValue *value) {
100
+ VALUE ins = (VALUE)monContext;
101
+ VALUE blk = RARRAY_AREF(ins,1);
102
+
103
+ node_struct *ns;
104
+ Data_Get_Struct(RARRAY_AREF(ins,0), node_struct, ns);
105
+ ns->waiting = ns->waiting + 1;
106
+
107
+ if (ns->waiting == 2 && (NIL_P(blk) || TYPE(blk) != T_NIL)) {
108
+ VALUE args = rb_ary_new2(3);
109
+ VALUE ret = extract_value(value->value);
110
+ rb_ary_store(args,0,rb_ary_entry(ret,0));
111
+ if (value->hasSourceTimestamp) {
112
+ rb_ary_store(args,1,rb_time_new(UA_DateTime_toUnixTime(value->sourceTimestamp),0));
113
+ } else {
114
+ if (value->hasServerTimestamp) {
115
+ rb_ary_store(args,1,rb_time_new(UA_DateTime_toUnixTime(value->serverTimestamp),0));
116
+ } else {
117
+ rb_ary_store(args,1,Qnil);
118
+ }
119
+ }
120
+ rb_ary_store(args,2,rb_ary_entry(ret,1));
121
+ rb_proc_call(blk,args);
122
+ }
123
+ }
124
+ static VALUE node_on_value_change(VALUE self) {
125
+ node_struct *ns;
126
+ Data_Get_Struct(self, node_struct, ns);
127
+ if (!ns->master->started) rb_raise(rb_eRuntimeError, "Client disconnected.");
128
+
129
+ if (!rb_block_given_p())
130
+ rb_raise(rb_eArgError, "you need to supply a block with #on_change");
131
+
132
+ UA_CreateSubscriptionRequest sreq; UA_CreateSubscriptionRequest_init(&sreq);
133
+ sreq.requestedPublishingInterval = 100;
134
+ sreq.requestedLifetimeCount = 10000;
135
+ sreq.requestedMaxKeepAliveCount = 10;
136
+ sreq.maxNotificationsPerPublish = 0;
137
+ sreq.publishingEnabled = true;
138
+ sreq.priority = 0;
139
+
140
+ UA_CreateSubscriptionResponse sres = UA_Client_Subscriptions_create(ns->master->master, sreq, NULL, NULL, NULL);
141
+ if (sres.responseHeader.serviceResult != UA_STATUSCODE_GOOD)
142
+ rb_raise(rb_eRuntimeError, "Subscription could not be created.");
143
+
144
+ VALUE ins = rb_ary_new2(2);
145
+ rb_ary_store(ins,0,self);
146
+ rb_ary_store(ins,1,rb_block_proc());
147
+ ns->waiting = 0;
148
+
149
+ UA_MonitoredItemCreateRequest monRequest = UA_MonitoredItemCreateRequest_default(ns->id);
150
+ UA_MonitoredItemCreateResult monResponse =
151
+ UA_Client_MonitoredItems_createDataChange(ns->master->master, sres.subscriptionId,
152
+ UA_TIMESTAMPSTORETURN_BOTH,
153
+ monRequest, (void *)ins, node_on_value_change_handler, NULL);
154
+
155
+ if(monResponse.statusCode != UA_STATUSCODE_GOOD) {
156
+ rb_raise(rb_eRuntimeError, "Monitoring item failed: %s\n", UA_StatusCode_name(monResponse.statusCode));
157
+ }
158
+
159
+ keepRunning = true;
160
+ signal(SIGINT, intHandler);
161
+ while (ns->waiting < 2 && keepRunning) {
162
+ UA_Client_run_iterate(ns->master->master, 100);
163
+ }
164
+ signal(SIGINT, SIG_DFL); // reset the disposition for SIGINT to the default
165
+
166
+ UA_MonitoredItemCreateResult_clear(&monResponse);
167
+ UA_MonitoredItemCreateRequest_clear(&monRequest);
168
+
169
+ UA_Client_Subscriptions_deleteSingle(ns->master->master, sres.subscriptionId);
170
+ UA_CreateSubscriptionResponse_clear(&sres);
171
+ UA_CreateSubscriptionRequest_clear(&sreq);
172
+
173
+ return self;
174
+ }
70
175
 
71
176
  /* -- */
72
177
  static void client_free(client_struct *pss) { //{{{
@@ -238,28 +343,31 @@ static VALUE client_get(int argc, VALUE* argv, VALUE self) { //{{{
238
343
  if (NIL_P(ns) || TYPE(ns) != T_FIXNUM)
239
344
  rb_raise(rb_eTypeError, "ns is not a valid (numeric) namespace id");
240
345
 
241
- node_struct *res;
346
+ UA_NodeId it;
347
+
242
348
  if (TYPE(id) == T_FIXNUM) {
243
- res = node_alloc(pss, UA_NODEID_NUMERIC(NUM2INT(ns), NUM2INT(id)));
349
+ it = UA_NODEID_NUMERIC(NUM2INT(ns), NUM2INT(id));
244
350
  } else {
245
351
  VALUE str = rb_obj_as_string(id);
246
352
  if (NIL_P(str) || TYPE(str) != T_STRING)
247
353
  rb_raise(rb_eTypeError, "cannot convert url to string");
248
354
  char *nstr = (char *)StringValuePtr(str);
249
355
 
250
- res = node_alloc(pss, UA_NODEID_STRING(NUM2INT(ns), nstr));
356
+ it = UA_NODEID_STRING(NUM2INT(ns), nstr);
251
357
  }
252
358
 
253
359
  UA_NodeClass nc;UA_NodeClass_init(&nc);
254
- UA_Client_readNodeClassAttribute(pss->master, res->id, &nc);
360
+ UA_Client_readNodeClassAttribute(pss->master, it, &nc);
255
361
 
256
362
  VALUE node;
257
363
  if (nc == UA_NODECLASS_VARIABLE) {
258
- node = node_wrap(cVarNode,res);
364
+ node = node_wrap(cVarNode,node_alloc(pss, it));
259
365
  } else if (nc == UA_NODECLASS_METHOD) {
260
- node = node_wrap(cMethodNode,res);
366
+ node = node_wrap(cMethodNode,node_alloc(pss, it));
367
+ } else if (nc == UA_NODECLASS_UNSPECIFIED) {
368
+ node = Qnil;
261
369
  } else {
262
- node = node_wrap(cNode,res);
370
+ node = node_wrap(cNode,node_alloc(pss, it));
263
371
  }
264
372
  UA_NodeClass_clear(&nc);
265
373
 
@@ -323,6 +431,25 @@ static VALUE client_debug_set(VALUE self, VALUE val) { //{{{
323
431
  return self;
324
432
  } //}}}
325
433
 
434
+ static VALUE client_namespaces(VALUE self) { //{{{
435
+ client_struct *pss;
436
+ Data_Get_Struct(self, client_struct, pss);
437
+ if (!pss->started) rb_raise(rb_eRuntimeError, "Client disconnected.");
438
+
439
+ UA_Variant value;
440
+ UA_Variant_init(&value);
441
+ UA_StatusCode retval = UA_Client_readValueAttribute(pss->master, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY), &value);
442
+
443
+ VALUE ret = Qnil;
444
+ if (retval == UA_STATUSCODE_GOOD) {
445
+ ret = extract_value(value);
446
+ }
447
+
448
+ UA_Variant_clear(&value);
449
+ RB_OBJ_FREEZE(ret);
450
+ return rb_ary_entry(ret,0);
451
+ } //}}}
452
+
326
453
  static VALUE node_id(VALUE self) { //{{{
327
454
  node_struct *ns;
328
455
 
@@ -348,7 +475,7 @@ static VALUE node_to_s(VALUE self) { //{{{
348
475
  Data_Get_Struct(self, node_struct, ns);
349
476
 
350
477
  if (ns->id.identifierType == UA_NODEIDTYPE_NUMERIC) {
351
- ret = rb_sprintf("ns=%d;n=%d", ns->id.namespaceIndex, ns->id.identifier.numeric);
478
+ ret = rb_sprintf("ns=%d;i=%d", ns->id.namespaceIndex, ns->id.identifier.numeric);
352
479
  } else if(ns->id.identifierType == UA_NODEIDTYPE_STRING) {
353
480
  ret = rb_sprintf("ns=%d;s=%.*s", ns->id.namespaceIndex, (int)ns->id.identifier.string.length, ns->id.identifier.string.data);
354
481
  } else {
@@ -357,7 +484,7 @@ static VALUE node_to_s(VALUE self) { //{{{
357
484
  return ret;
358
485
  } //}}}
359
486
 
360
- static VALUE node_call(int argc, VALUE* argv, VALUE self) {
487
+ static VALUE node_call(int argc, VALUE* argv, VALUE self) { //{{{
361
488
  node_struct *ns;
362
489
 
363
490
  VALUE splat;
@@ -389,7 +516,7 @@ static VALUE node_call(int argc, VALUE* argv, VALUE self) {
389
516
  } else {
390
517
  return Qfalse;
391
518
  }
392
- }
519
+ } //}}}
393
520
 
394
521
  static void client_run_handler(UA_Client *client, UA_UInt32 subId, void *subContext, UA_UInt32 monId, void *monContext, UA_DataValue *value) { //{{{
395
522
  VALUE val = (VALUE)monContext;
@@ -465,9 +592,9 @@ void Init_client(void) {
465
592
  mOPCUA = rb_define_module("OPCUA");
466
593
 
467
594
  cClient = rb_define_class_under(mOPCUA, "Client", rb_cObject);
468
- cNode = rb_define_class_under(mOPCUA, "cNode", rb_cObject);
469
- cMethodNode = rb_define_class_under(mOPCUA, "cMethodNode", rb_cObject);
470
- cVarNode = rb_define_class_under(mOPCUA, "cVarNode", rb_cObject);
595
+ cNode = rb_define_class_under(cClient, "cNode", rb_cObject);
596
+ cMethodNode = rb_define_class_under(cClient, "cMethodNode", cNode);
597
+ cVarNode = rb_define_class_under(cClient, "cVarNode", cNode);
471
598
 
472
599
  Init_types();
473
600
 
@@ -480,18 +607,17 @@ void Init_client(void) {
480
607
  rb_define_method(cClient, "subscription_interval=", client_subscription_interval_set, 1);
481
608
  rb_define_method(cClient, "default_ns", client_default_ns, 0);
482
609
  rb_define_method(cClient, "default_ns=", client_default_ns_set, 1);
610
+ rb_define_method(cClient, "namespaces", client_namespaces, 0);
483
611
  rb_define_method(cClient, "debug", client_debug, 0);
484
612
  rb_define_method(cClient, "debug=", client_debug_set, 1);
485
613
 
486
614
  rb_define_method(cNode, "to_s", node_to_s, 0);
487
615
  rb_define_method(cNode, "id", node_id, 0);
488
616
 
489
- rb_define_method(cMethodNode, "to_s", node_to_s, 0);
490
- rb_define_method(cMethodNode, "id", node_id, 0);
491
617
  rb_define_method(cMethodNode, "call", node_call, -1);
492
618
 
493
- rb_define_method(cVarNode, "to_s", node_to_s, 0);
494
- rb_define_method(cVarNode, "id", node_id, 0);
495
619
  rb_define_method(cVarNode, "value", node_value, 0);
620
+ rb_define_method(cVarNode, "value=", node_value_set, 1);
496
621
  rb_define_method(cVarNode, "on_change", node_on_change, 0);
622
+ rb_define_method(cVarNode, "on_value_change", node_on_value_change, 0);
497
623
  }
@@ -22,4 +22,5 @@ typedef struct node_struct {
22
22
  client_struct *master;
23
23
  VALUE on_change;
24
24
  UA_NodeId id;
25
+ int waiting;
25
26
  } node_struct;
@@ -5,5 +5,6 @@ spec = eval(File.read(__dir__ + '/../../../opcua.gemspec'))
5
5
  $CFLAGS = '-g -Wall ' + $CFLAGS
6
6
  $CFLAGS << ''
7
7
  $LDFLAGS << ' -lopen62541'
8
+
8
9
  dir_config('server')
9
10
  create_makefile('server')
@@ -2,9 +2,13 @@
2
2
 
3
3
  VALUE mOPCUA = Qnil;
4
4
  VALUE cServer = Qnil;
5
- VALUE cObjectsNode = Qnil;
6
- VALUE cTypesTopNode = Qnil;
7
- VALUE cTypesSubNode = Qnil;
5
+ VALUE cNode = Qnil;
6
+ VALUE cObjectNode = Qnil;
7
+ VALUE cTypeTopNode = Qnil;
8
+ VALUE cTypeSubNode = Qnil;
9
+ VALUE cReferenceTopNode = Qnil;
10
+ VALUE cReferenceSubNode = Qnil;
11
+ VALUE cReferenceNode = Qnil;
8
12
  VALUE cVarNode = Qnil;
9
13
  VALUE cMethodNode = Qnil;
10
14
 
@@ -41,7 +45,7 @@ static VALUE node_wrap(VALUE klass, node_struct *ns) { //{{{
41
45
  static VALUE node_type_folder(VALUE self) { //{{{
42
46
  node_struct *ns;
43
47
  Data_Get_Struct(self, node_struct, ns);
44
- return node_wrap(cTypesTopNode, node_alloc(ns->master, UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE)));
48
+ return node_wrap(cTypeTopNode, node_alloc(ns->master, UA_NODEID_NUMERIC(0, UA_NS0ID_FOLDERTYPE)));
45
49
  } //}}}
46
50
  static VALUE node_add_object_type(VALUE self, VALUE name) { //{{{
47
51
  node_struct *ns;
@@ -66,7 +70,34 @@ static VALUE node_add_object_type(VALUE self, VALUE name) { //{{{
66
70
  NULL,
67
71
  NULL);
68
72
 
69
- return node_wrap(cTypesSubNode,node_alloc(ns->master,n));
73
+ return node_wrap(cTypeSubNode,node_alloc(ns->master,n));
74
+ } //}}}
75
+ static VALUE node_add_reference_type(VALUE self, VALUE name, VALUE type) { //{{{
76
+ node_struct *ns;
77
+
78
+ Data_Get_Struct(self, node_struct, ns);
79
+
80
+ VALUE str = rb_obj_as_string(name);
81
+ if (NIL_P(str) || TYPE(str) != T_STRING)
82
+ rb_raise(rb_eTypeError, "cannot convert arg 1 to string");
83
+ char *nstr = (char *)StringValuePtr(str);
84
+ if (TYPE(type) != T_FIXNUM)
85
+ rb_raise(rb_eTypeError, "cannot convert arg 2 to integer");
86
+
87
+ UA_NodeId n = UA_NODEID_NUMERIC(ns->master->default_ns, nodecounter++);
88
+
89
+ UA_ReferenceTypeAttributes rtAttr = UA_ReferenceTypeAttributes_default;
90
+ rtAttr.displayName = UA_LOCALIZEDTEXT("en-US", nstr);
91
+ UA_Server_addReferenceTypeNode(ns->master->master,
92
+ n,
93
+ ns->id,
94
+ UA_NODEID_NUMERIC(0, NUM2INT(type)),
95
+ UA_QUALIFIEDNAME(ns->master->default_ns, nstr),
96
+ rtAttr,
97
+ NULL,
98
+ NULL);
99
+
100
+ return node_wrap(cReferenceSubNode,node_alloc(ns->master,n));
70
101
  } //}}}
71
102
 
72
103
  static VALUE node_id(VALUE self) { //{{{
@@ -94,7 +125,7 @@ static VALUE node_to_s(VALUE self) { //{{{
94
125
  Data_Get_Struct(self, node_struct, ns);
95
126
 
96
127
  if (ns->id.identifierType == UA_NODEIDTYPE_NUMERIC) {
97
- ret = rb_sprintf("ns=%d;n=%d", ns->id.namespaceIndex, ns->id.identifier.numeric);
128
+ ret = rb_sprintf("ns=%d;i=%d", ns->id.namespaceIndex, ns->id.identifier.numeric);
98
129
  } else if(ns->id.identifierType == UA_NODEIDTYPE_STRING) {
99
130
  ret = rb_sprintf("ns=%d;s=%.*s", ns->id.namespaceIndex, (int)ns->id.identifier.string.length, ns->id.identifier.string.data);
100
131
  } else {
@@ -102,6 +133,33 @@ static VALUE node_to_s(VALUE self) { //{{{
102
133
  }
103
134
  return ret;
104
135
  } //}}}
136
+ static VALUE node_add_reference(VALUE self, VALUE to, VALUE type) { //{{{
137
+ node_struct *ns;
138
+ node_struct *tos;
139
+ node_struct *tys;
140
+
141
+ Data_Get_Struct(self, node_struct, ns);
142
+
143
+ if (!(rb_obj_is_kind_of(type,cReferenceSubNode) || rb_obj_is_kind_of(to,cTypeSubNode))) {
144
+ rb_raise(rb_eArgError, "arguments have to be NodeIDs.");
145
+ }
146
+ Data_Get_Struct(to, node_struct, tos);
147
+ Data_Get_Struct(type, node_struct, tys);
148
+ UA_NodeId n = UA_NODEID_NUMERIC(ns->master->default_ns, nodecounter++);
149
+
150
+ UA_ExpandedNodeId toNodeId;
151
+ toNodeId.serverIndex = 0;
152
+ toNodeId.namespaceUri = UA_STRING_NULL;
153
+ toNodeId.nodeId = tos->id;
154
+
155
+ UA_Server_addReference(ns->master->master,
156
+ n,
157
+ tys->id,
158
+ toNodeId,
159
+ true);
160
+
161
+ return node_wrap(cReferenceNode,node_alloc(ns->master,n));
162
+ } //}}}
105
163
 
106
164
  static UA_StatusCode node_add_method_callback( //{{{
107
165
  UA_Server *server,
@@ -121,7 +179,7 @@ static UA_StatusCode node_add_method_callback( //{{{
121
179
  // );
122
180
 
123
181
  VALUE args = rb_ary_new();
124
- rb_ary_push(args, Data_Wrap_Struct(cObjectsNode,NULL,NULL,me));
182
+ rb_ary_push(args, Data_Wrap_Struct(cObjectNode,NULL,NULL,me));
125
183
  for (int i = 0; i < inputSize; i++) {
126
184
  VALUE ret = extract_value(input[i]);
127
185
  rb_ary_push(args,rb_ary_entry(ret,0));
@@ -220,10 +278,9 @@ static VALUE node_add_method(int argc, VALUE* argv, VALUE self) { //{{{
220
278
  return node_wrap(CLASS_OF(self),node_alloc(parent->master,node_add_method_ua_simple(nstr,parent,opts,blk)));
221
279
  } //}}}
222
280
 
223
- static UA_NodeId node_add_variable_ua(UA_Int32 type, UA_NodeId n, UA_LocalizedText dn, UA_QualifiedName qn, node_struct *parent, VALUE ref, UA_Byte accesslevelmask) { //{{{
281
+ static UA_NodeId node_add_variable_ua(UA_Int32 type, UA_NodeId n, UA_LocalizedText dn, UA_QualifiedName qn, node_struct *parent, UA_Byte accesslevelmask) { //{{{
224
282
  UA_VariableAttributes vAttr = UA_VariableAttributes_default;
225
283
  vAttr.displayName = dn;
226
-
227
284
  vAttr.accessLevel = accesslevelmask;
228
285
 
229
286
  UA_Server_addVariableNode(parent->master->master,
@@ -231,34 +288,30 @@ static UA_NodeId node_add_variable_ua(UA_Int32 type, UA_NodeId n, UA_LocalizedTe
231
288
  parent->id,
232
289
  UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
233
290
  qn,
234
- UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
291
+ UA_NODEID_NUMERIC(0, type),
235
292
  vAttr,
236
293
  NULL,
237
294
  NULL);
238
295
 
239
- if (ref != Qnil) {
240
- UA_Server_addReference(parent->master->master,
241
- n,
242
- UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
243
- UA_EXPANDEDNODEID_NUMERIC(0, type),
244
- true);
245
- }
296
+ UA_Server_addReference(parent->master->master,
297
+ n,
298
+ UA_NODEID_NUMERIC(0, UA_NS0ID_HASMODELLINGRULE),
299
+ UA_EXPANDEDNODEID_NUMERIC(0, UA_NS0ID_MODELLINGRULE_MANDATORY),
300
+ true);
246
301
 
247
302
  return n;
248
-
249
303
  } //}}}
250
- static UA_NodeId node_add_variable_ua_simple(UA_Int32 type, char* nstr, node_struct *parent, VALUE ref, UA_Byte accesslevelmask,bool numeric) { //{{{
304
+ static UA_NodeId node_add_variable_ua_simple(UA_Int32 type, char* nstr, node_struct *parent, UA_Byte accesslevelmask, bool numeric) { //{{{
251
305
  return node_add_variable_ua(
252
306
  type,
253
307
  numeric ? UA_NODEID_NUMERIC(parent->master->default_ns,nodecounter++) : UA_NODEID_STRING(parent->master->default_ns,nstr),
254
308
  UA_LOCALIZEDTEXT("en-US", nstr),
255
309
  UA_QUALIFIEDNAME(parent->master->default_ns, nstr),
256
310
  parent,
257
- ref,
258
311
  accesslevelmask
259
312
  );
260
313
  } //}}}
261
- static VALUE node_add_variable_wrap(int argc, VALUE* argv, VALUE self, UA_Byte accesslevelmask,bool numeric) { //{{{
314
+ static VALUE node_add_variable_wrap(int argc, VALUE* argv, VALUE self, UA_Byte accesslevelmask, bool numeric) { //{{{
262
315
  node_struct *parent;
263
316
 
264
317
  if (argc > 2 || argc == 0) { // there should only be 1 or 2 arguments
@@ -269,7 +322,7 @@ static VALUE node_add_variable_wrap(int argc, VALUE* argv, VALUE self, UA_Byte a
269
322
  if (argc == 2 && argv[1] != Qnil) {
270
323
  type = NUM2INT(argv[1]);
271
324
  } else {
272
- type = UA_NS0ID_MODELLINGRULE_MANDATORY;
325
+ type = UA_NS0ID_BASEDATAVARIABLETYPE;
273
326
  }
274
327
 
275
328
  Data_Get_Struct(self, node_struct, parent);
@@ -279,7 +332,7 @@ static VALUE node_add_variable_wrap(int argc, VALUE* argv, VALUE self, UA_Byte a
279
332
  rb_raise(rb_eTypeError, "cannot convert obj to string");
280
333
  char *nstr = (char *)StringValuePtr(str);
281
334
 
282
- return node_wrap(cVarNode,node_alloc(parent->master,node_add_variable_ua_simple(type,nstr,parent,argv[1],accesslevelmask,numeric)));
335
+ return node_wrap(cVarNode,node_alloc(parent->master,node_add_variable_ua_simple(type,nstr,parent,accesslevelmask,numeric)));
283
336
  } //}}}
284
337
  static VALUE node_add_variable(int argc, VALUE* argv, VALUE self) { //{{{
285
338
  return node_add_variable_wrap(argc,argv,self,UA_ACCESSLEVELMASK_READ,true);
@@ -364,7 +417,7 @@ static VALUE node_add_object(int argc, VALUE* argv, VALUE self) { //{{{
364
417
  type = UA_NS0ID_MODELLINGRULE_MANDATORY;
365
418
  }
366
419
 
367
- if (!(rb_obj_is_kind_of(argv[1],cTypesTopNode) || rb_obj_is_kind_of(argv[1],cTypesSubNode))) {
420
+ if (!(rb_obj_is_kind_of(argv[1],cTypeTopNode) || rb_obj_is_kind_of(argv[1],cTypeSubNode))) {
368
421
  rb_raise(rb_eArgError, "argument 2 has to be a type.");
369
422
  }
370
423
 
@@ -401,12 +454,24 @@ static bool node_get_reference(UA_Server *server, UA_NodeId parent, UA_NodeId *r
401
454
  UA_BrowseDescription_init(&bDes);
402
455
  bDes.nodeId = parent;
403
456
  bDes.resultMask = UA_BROWSERESULTMASK_ALL;
404
- UA_BrowseResult bRes = UA_Server_browse(server, 3, &bDes);
457
+ UA_BrowseResult bRes = UA_Server_browse(server, 999, &bDes);
405
458
 
406
459
  if (bRes.referencesSize > 0) {
407
460
  UA_ReferenceDescription *ref = &(bRes.references[0]);
408
461
 
409
- *result = ref->nodeId.nodeId;
462
+ UA_NodeId_copy(&ref->nodeId.nodeId,result);
463
+
464
+ UA_QualifiedName qn; UA_QualifiedName_init(&qn);
465
+ UA_Server_readBrowseName(server, ref->nodeId.nodeId, &qn);
466
+
467
+ // printf("NS: %d ---> NodeId %u; %-16.*s\n",
468
+ // ref->nodeId.nodeId.namespaceIndex,
469
+ // ref->nodeId.nodeId.identifier.numeric,
470
+ // (int)qn.name.length,
471
+ // qn.name.data
472
+ // );
473
+
474
+ UA_BrowseResult_deleteMembers(&bRes);
410
475
  UA_BrowseResult_clear(&bRes);
411
476
  return true;
412
477
  }
@@ -480,7 +545,18 @@ static UA_StatusCode node_manifest_iter(UA_NodeId child_id, UA_Boolean is_invers
480
545
  free(newparent);
481
546
  }
482
547
  if(nc == UA_NODECLASS_VARIABLE) {
483
- node_add_variable_ua(UA_NS0ID_MODELLINGRULE_MANDATORY,UA_NODEID_STRING(parent->master->default_ns,buffer),dn,qn,newnode,Qtrue,al);
548
+ UA_QualifiedName pqn;UA_QualifiedName_init(&pqn);
549
+ UA_Server_readBrowseName(parent->master->master, UA_NODEID_NUMERIC(0, UA_NS0ID_PROPERTYTYPE), &pqn);
550
+ UA_BrowsePathResult property = node_browse_path(parent->master->master, child_id, UA_NODEID_NUMERIC(0, UA_NS0ID_HASTYPEDEFINITION), pqn);
551
+
552
+ if (property.statusCode == UA_STATUSCODE_GOOD) {
553
+ node_add_variable_ua(UA_NS0ID_PROPERTYTYPE,UA_NODEID_STRING(parent->master->default_ns,buffer),dn,qn,newnode,al);
554
+ } else {
555
+ node_add_variable_ua(UA_NS0ID_BASEDATAVARIABLETYPE,UA_NODEID_STRING(parent->master->default_ns,buffer),dn,qn,newnode,al);
556
+ }
557
+
558
+ UA_BrowsePathResult_clear(&property);
559
+ UA_QualifiedName_clear(&pqn);
484
560
  }
485
561
  if(nc == UA_NODECLASS_METHOD) {
486
562
  UA_NodeId ttt;
@@ -512,7 +588,7 @@ static VALUE node_manifest(VALUE self, VALUE name, VALUE parent) { //{{{
512
588
  node_struct *ns;
513
589
  node_struct *ts;
514
590
 
515
- if (!(rb_obj_is_kind_of(parent,cTypesTopNode) || rb_obj_is_kind_of(parent,cTypesSubNode))) {
591
+ if (!(rb_obj_is_kind_of(parent,cTypeTopNode) || rb_obj_is_kind_of(parent,cTypeSubNode))) {
516
592
  rb_raise(rb_eArgError, "argument 2 has to be a type.");
517
593
  }
518
594
 
@@ -569,13 +645,64 @@ static VALUE node_find(VALUE self, VALUE qname) { //{{{
569
645
  } else if (nc == UA_NODECLASS_METHOD) {
570
646
  node = node_wrap(cMethodNode,node_alloc(ns->master,ret));
571
647
  } else {
572
- node = node_wrap(cObjectsNode,node_alloc(ns->master,ret));
648
+ node = node_wrap(cObjectNode,node_alloc(ns->master,ret));
573
649
  }
574
650
  UA_NodeClass_clear(&nc);
575
651
 
576
652
  return node;
577
653
  }
578
654
  } //}}}
655
+ static VALUE server_get(int argc, VALUE* argv, VALUE self) { //{{{
656
+ if (argc > 2 || argc < 1) { // there should only be 1 or 2 arguments
657
+ rb_raise(rb_eArgError, "wrong number of arguments");
658
+ }
659
+
660
+ server_struct *pss;
661
+ Data_Get_Struct(self, server_struct, pss);
662
+
663
+ VALUE ns = UINT2NUM(pss->default_ns);
664
+ VALUE id;
665
+
666
+ if (argc == 1) {
667
+ id = argv[0];
668
+ } else {
669
+ ns = argv[0];
670
+ id = argv[1];
671
+ }
672
+
673
+ if (NIL_P(ns) || TYPE(ns) != T_FIXNUM)
674
+ rb_raise(rb_eTypeError, "ns is not a valid (numeric) namespace id");
675
+
676
+ UA_NodeId it;
677
+
678
+ if (TYPE(id) == T_FIXNUM) {
679
+ it = UA_NODEID_NUMERIC(NUM2INT(ns), NUM2INT(id));
680
+ } else {
681
+ VALUE str = rb_obj_as_string(id);
682
+ if (NIL_P(str) || TYPE(str) != T_STRING)
683
+ rb_raise(rb_eTypeError, "cannot convert url to string");
684
+ char *nstr = (char *)StringValuePtr(str);
685
+
686
+ it = UA_NODEID_STRING(NUM2INT(ns), nstr);
687
+ }
688
+
689
+ UA_NodeClass nc;UA_NodeClass_init(&nc);
690
+ UA_Server_readNodeClass(pss->master, it, &nc);
691
+
692
+ VALUE node;
693
+ if (nc == UA_NODECLASS_VARIABLE) {
694
+ node = node_wrap(cVarNode,node_alloc(pss, it));
695
+ } else if (nc == UA_NODECLASS_METHOD) {
696
+ node = node_wrap(cNode,node_alloc(pss, it));
697
+ } else if (nc == UA_NODECLASS_UNSPECIFIED) {
698
+ node = Qnil;
699
+ } else {
700
+ node = node_wrap(cNode,node_alloc(pss, it));
701
+ }
702
+ UA_NodeClass_clear(&nc);
703
+
704
+ return node;
705
+ } //}}}
579
706
 
580
707
  static VALUE node_value_set(VALUE self, VALUE value) { //{{{
581
708
  node_struct *ns;
@@ -583,6 +710,14 @@ static VALUE node_value_set(VALUE self, VALUE value) { //{{{
583
710
 
584
711
  UA_Variant variant;
585
712
  if (value_to_variant(value,&variant)) {
713
+ // printf("-----------------------------------------%ld\n",variant.arrayDimensionsSize);
714
+ if (variant.arrayDimensionsSize > 0) {
715
+ UA_Server_writeValueRank(ns->master->master, ns->id, variant.arrayDimensionsSize);
716
+ UA_Variant uaArrayDimensions;
717
+ UA_Variant_setArray(&uaArrayDimensions, variant.arrayDimensions, variant.arrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]);
718
+ UA_Server_writeArrayDimensions(ns->master->master, ns->id, uaArrayDimensions);
719
+ }
720
+
586
721
  UA_Server_writeValue(ns->master->master, ns->id, variant);
587
722
  }
588
723
  return self;
@@ -603,6 +738,36 @@ static VALUE node_value(VALUE self) { //{{{
603
738
  }
604
739
 
605
740
  UA_Variant_clear(&value);
741
+ return rb_ary_entry(ret,0);
742
+ } //}}}
743
+ static VALUE node_description_set(VALUE self, VALUE value) { //{{{
744
+ node_struct *ns;
745
+ Data_Get_Struct(self, node_struct, ns);
746
+
747
+ VALUE str = rb_obj_as_string(value);
748
+ if (NIL_P(str) || TYPE(str) != T_STRING)
749
+ rb_raise(rb_eTypeError, "cannot convert obj to string");
750
+ char *nstr = (char *)StringValuePtr(str);
751
+ UA_LocalizedText lt = UA_LOCALIZEDTEXT("en-US",nstr);
752
+
753
+ UA_Server_writeDescription(ns->master->master, ns->id, lt);
754
+ return self;
755
+ } //}}}
756
+ static VALUE node_description(VALUE self) { //{{{
757
+ node_struct *ns;
758
+
759
+ Data_Get_Struct(self, node_struct, ns);
760
+
761
+ UA_LocalizedText value;
762
+ UA_LocalizedText_init(&value);
763
+ UA_StatusCode retval = UA_Server_readDescription(ns->master->master, ns->id, &value);
764
+
765
+ VALUE ret = Qnil;
766
+ if (retval == UA_STATUSCODE_GOOD) {
767
+ ret = rb_str_export_locale(rb_str_new((char *)(value.text.data),value.text.length));
768
+ }
769
+
770
+ UA_LocalizedText_clear(&value);
606
771
  return ret;
607
772
  } //}}}
608
773
 
@@ -668,12 +833,17 @@ static VALUE server_add_namespace(VALUE self, VALUE name) { //{{{
668
833
  static VALUE server_types(VALUE self) { //{{{
669
834
  server_struct *pss;
670
835
  Data_Get_Struct(self, server_struct, pss);
671
- return node_wrap(cTypesTopNode, node_alloc(pss, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE)));
836
+ return node_wrap(cTypeTopNode, node_alloc(pss, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE)));
837
+ } //}}}
838
+ static VALUE server_references(VALUE self) { //{{{
839
+ server_struct *pss;
840
+ Data_Get_Struct(self, server_struct, pss);
841
+ return node_wrap(cReferenceTopNode, node_alloc(pss, UA_NODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES)));
672
842
  } //}}}
673
843
  static VALUE server_objects(VALUE self) { //{{{
674
844
  server_struct *pss;
675
845
  Data_Get_Struct(self, server_struct, pss);
676
- return node_wrap(cObjectsNode, node_alloc(pss, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER)));
846
+ return node_wrap(cObjectNode, node_alloc(pss, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER)));
677
847
  } //}}}
678
848
  static VALUE server_debug(VALUE self) { //{{{
679
849
  server_struct *pss;
@@ -694,6 +864,23 @@ static VALUE server_debug_set(VALUE self, VALUE val) { //{{{
694
864
  }
695
865
  return self;
696
866
  } //}}}
867
+ static VALUE server_namespaces(VALUE self) { //{{{
868
+ server_struct *pss;
869
+ Data_Get_Struct(self, server_struct, pss);
870
+
871
+ UA_Variant value;
872
+ UA_Variant_init(&value);
873
+ UA_StatusCode retval = UA_Server_readValue(pss->master, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY), &value);
874
+
875
+ VALUE ret = Qnil;
876
+ if (retval == UA_STATUSCODE_GOOD) {
877
+ ret = extract_value(value);
878
+ }
879
+
880
+ UA_Variant_clear(&value);
881
+ RB_OBJ_FREEZE(ret);
882
+ return rb_ary_entry(ret,0);
883
+ } //}}}
697
884
 
698
885
  void Init_server(void) {
699
886
  mOPCUA = rb_define_module("OPCUA");
@@ -701,45 +888,53 @@ void Init_server(void) {
701
888
  rb_define_const(mOPCUA, "MANDATORYPLACEHOLDER", INT2NUM(UA_NS0ID_MODELLINGRULE_MANDATORYPLACEHOLDER));
702
889
  rb_define_const(mOPCUA, "OPTIONAL", INT2NUM(UA_NS0ID_MODELLINGRULE_OPTIONAL));
703
890
  rb_define_const(mOPCUA, "OPTIONALPLACEHOLDER", INT2NUM(UA_NS0ID_MODELLINGRULE_OPTIONALPLACEHOLDER));
891
+ rb_define_const(mOPCUA, "BASEDATAVARIABLETYPE", INT2NUM(UA_NS0ID_BASEDATAVARIABLETYPE));
892
+ rb_define_const(mOPCUA, "PROPERTYTYPE", INT2NUM(UA_NS0ID_PROPERTYTYPE));
704
893
 
705
894
  Init_types();
706
895
 
707
- cServer = rb_define_class_under(mOPCUA, "Server", rb_cObject);
708
- cObjectsNode = rb_define_class_under(mOPCUA, "cObjectsNode", rb_cObject);
709
- cTypesTopNode = rb_define_class_under(mOPCUA, "cTypesTopNode", rb_cObject);
710
- cTypesSubNode = rb_define_class_under(mOPCUA, "cTypesSubNode", rb_cObject);
711
- cVarNode = rb_define_class_under(mOPCUA, "cVarNode", rb_cObject);
712
- cMethodNode = rb_define_class_under(mOPCUA, "cMethodNode", rb_cObject);
896
+ cServer = rb_define_class_under(mOPCUA, "Server", rb_cObject);
897
+ cNode = rb_define_class_under(cServer, "Node", rb_cObject);
898
+ cObjectNode = rb_define_class_under(cServer, "ObjectNode", cNode);
899
+ cTypeTopNode = rb_define_class_under(cServer, "TypeTopNode", cNode);
900
+ cTypeSubNode = rb_define_class_under(cServer, "TypeSubNode", cNode);
901
+ cReferenceTopNode = rb_define_class_under(cServer, "ReferenceTopNode", cNode);
902
+ cReferenceSubNode = rb_define_class_under(cServer, "ReferenceSubNode", cNode);
903
+ cReferenceNode = rb_define_class_under(cServer, "ObjectReferenceNode", cNode);
904
+ cVarNode = rb_define_class_under(cServer, "ObjectVarNode", cNode);
905
+ cMethodNode = rb_define_class_under(cServer, "ObjectMethodNode", cNode);
713
906
 
714
907
  rb_define_alloc_func(cServer, server_alloc);
715
908
  rb_define_method(cServer, "initialize", server_init, 0);
716
909
  rb_define_method(cServer, "run", server_run, 0);
717
910
  rb_define_method(cServer, "add_namespace", server_add_namespace, 1);
718
911
  rb_define_method(cServer, "types", server_types, 0);
912
+ rb_define_method(cServer, "references", server_references, 0);
719
913
  rb_define_method(cServer, "objects", server_objects, 0);
720
914
  rb_define_method(cServer, "debug", server_debug, 0);
721
915
  rb_define_method(cServer, "debug=", server_debug_set, 1);
916
+ rb_define_method(cServer, "namespaces", server_namespaces, 0);
917
+ rb_define_method(cServer, "get", server_get, -1);
918
+
919
+ rb_define_method(cNode, "to_s", node_to_s, 0);
920
+ rb_define_method(cNode, "id", node_id, 0);
921
+ rb_define_method(cNode, "description", node_description, 0);
922
+ rb_define_method(cNode, "description=", node_description_set, 1);
923
+
924
+ rb_define_method(cTypeTopNode, "add_object_type", node_add_object_type, 1);
925
+ rb_define_method(cTypeTopNode, "add_reference_type", node_add_reference_type, 1);
926
+ rb_define_method(cTypeTopNode, "folder", node_type_folder, 0);
927
+
928
+ rb_define_method(cTypeSubNode, "add_object_type", node_add_object_type, 1);
929
+ rb_define_method(cTypeSubNode, "add_variable", node_add_variable, -1);
930
+ rb_define_method(cTypeSubNode, "add_variable_rw", node_add_variable_rw, -1);
931
+ rb_define_method(cTypeSubNode, "add_object", node_add_object, -1);
932
+ rb_define_method(cTypeSubNode, "add_method", node_add_method, -1);
933
+ rb_define_method(cTypeSubNode, "add_reference", node_add_reference, 2);
934
+
935
+ rb_define_method(cObjectNode, "manifest", node_manifest, 2);
936
+ rb_define_method(cObjectNode, "find", node_find, 1);
722
937
 
723
- rb_define_method(cTypesTopNode, "add_object_type", node_add_object_type, 1);
724
- rb_define_method(cTypesTopNode, "folder", node_type_folder, 0);
725
- rb_define_method(cTypesSubNode, "add_object_type", node_add_object_type, 1);
726
- rb_define_method(cTypesSubNode, "add_variable", node_add_variable, -1);
727
- rb_define_method(cTypesSubNode, "add_variable_rw", node_add_variable_rw, -1);
728
- rb_define_method(cTypesSubNode, "add_object", node_add_object, -1);
729
- rb_define_method(cTypesSubNode, "add_method", node_add_method, -1);
730
- rb_define_method(cTypesSubNode, "to_s", node_to_s, 0);
731
- rb_define_method(cTypesSubNode, "id", node_id, 0);
732
-
733
- rb_define_method(cObjectsNode, "manifest", node_manifest, 2);
734
- rb_define_method(cObjectsNode, "find", node_find, 1);
735
- rb_define_method(cObjectsNode, "to_s", node_to_s, 0);
736
- rb_define_method(cObjectsNode, "id", node_id, 0);
737
-
738
- rb_define_method(cVarNode, "to_s", node_to_s, 0);
739
- rb_define_method(cVarNode, "id", node_id, 0);
740
938
  rb_define_method(cVarNode, "value", node_value, 0);
741
939
  rb_define_method(cVarNode, "value=", node_value_set, 1);
742
-
743
- rb_define_method(cMethodNode, "to_s", node_to_s, 0);
744
- rb_define_method(cMethodNode, "id", node_id, 0);
745
940
  }