opcua_client 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7ece180c3aa8255995c232d04ab6edd8d698534a2e7a07f95feb67aee06dce5c
4
+ data.tar.gz: 1478ab14003bdc8c6da023fc53eef15b5d8e0e5f76850c1773b087563e0ae80d
5
+ SHA512:
6
+ metadata.gz: 39fcbe3246e1f1c416e3d4258e395d0d12da62dfd4c7c64b5151a6bc8b308c243baa880519161b8fb13e891bdcacad6f5da3e3fa9875973526ae1cfa300376c0
7
+ data.tar.gz: 3f0057136a51a9a7c08df5be42df6b95291810dea18cfc7e2369712e05fdf40ffba3d2ead4d159f94994d6995abcf6e96e81f1b3a5c6cfe9933b30e6641d1500
data/README.md ADDED
@@ -0,0 +1,117 @@
1
+ # opcua-client-ruby
2
+
3
+ Incomplete OPC-UA client library for Ruby. Wraps open62541: <https://open62541.org>.
4
+
5
+ ## Installation
6
+
7
+ Add it to your Gemfile:
8
+
9
+ ```ruby
10
+ gem 'opcua_client'
11
+ ```
12
+
13
+ ## Basic usage
14
+
15
+ Use `start` helper to automatically close connections:
16
+
17
+ ```ruby
18
+ require 'opcua_client'
19
+
20
+ OPCUAClient.start("opc.tcp://127.0.0.1:4840") do |client|
21
+ # write to ns=2;s=1
22
+ client.write_int16(2, "1", 888)
23
+ puts client.read_int16(2, "1")
24
+ end
25
+ ```
26
+
27
+ Or handle connections manually:
28
+
29
+ ```ruby
30
+ require 'opcua_client'
31
+
32
+ client = OPCUAClient::Client.new
33
+ begin
34
+ client.connect("opc.tcp://127.0.0.1:4840")
35
+ # write to ns=2;s=1
36
+ client.write_int16(2, "1", 888)
37
+ puts client.read_int16(2, "1")
38
+ ensure
39
+ client.disconnect
40
+ end
41
+ ```
42
+
43
+ ### Available methods - connection:
44
+
45
+ * ```client.connect(String url)``` - raises OPCUAClient::Error if unsuccessful
46
+ * ```client.disconnect => Fixnum``` - returns status
47
+
48
+ ### Available methods - reads and writes:
49
+
50
+ All methods raise OPCUAClient::Error if unsuccessful.
51
+
52
+ * ```client.read_int16(Fixnum ns, String name) => Fixnum```
53
+ * ```client.read_int32(Fixnum ns, String name) => Fixnum```
54
+ * ```client.read_float(Fixnum ns, String name) => Float```
55
+ * ```client.read_boolean(Fixnum ns, String name) => true/false```
56
+ * ```client.write_int16(Fixnum ns, String name, Fixnum value)```
57
+ * ```client.write_int32(Fixnum ns, String name, Fixnum value)```
58
+ * ```client.write_float(Fixnum ns, String name, Float value)```
59
+ * ```client.write_boolean(Fixnum ns, String name, bool value)```
60
+
61
+ ### Available methods - misc:
62
+
63
+ * ```client.state => Fixnum``` - client internal state
64
+ * ```client.human_state => String``` - human readable client internal state
65
+ * ```OPCUAClient::Client.human_status_code(Fixnum status) => String``` - returns human status for status
66
+
67
+ ## Subscriptions and monitoring
68
+
69
+ ```ruby
70
+ cli = OPCUAClient::Client.new
71
+
72
+ cli.after_session_created do |cli|
73
+ subscription_id = cli.create_subscription
74
+ ns_index = 1
75
+ node_name = "the.answer"
76
+ cli.add_monitored_item(subscription_id, ns_index, node_name)
77
+ end
78
+
79
+ cli.after_data_changed do |subscription_id, monitor_id, server_time, source_time, new_value|
80
+ puts("data changed: " + [subscription_id, monitor_id, server_time, source_time, new_value].inspect)
81
+ end
82
+
83
+ cli.connect("opc.tcp://127.0.0.1:4840")
84
+
85
+ loop do
86
+ cli.connect("opc.tcp://127.0.0.1:4840") # no-op if connected
87
+ cli.run_mon_cycle
88
+ sleep(0.2)
89
+ end
90
+ ```
91
+
92
+ ### Available methods:
93
+
94
+ * ```client.create_subscription => Fixnum``` - nil if error
95
+ * ```client.add_monitored_item(Fixnum subscription, Fixnum ns, String name) => Fixnum``` - nil if error
96
+ * ```client.run_mon_cycle``` - returns status
97
+ * ```client.run_mon_cycle!``` - raises OPCUAClient::Error if unsuccessful
98
+
99
+ ### Available callbacks:
100
+ * ```after_session_created```
101
+ * ```after_data_changed```
102
+
103
+ ## Contribute
104
+
105
+ ### Set up
106
+
107
+ ```console
108
+ bundle
109
+ ```
110
+
111
+ ### Try out changes
112
+
113
+ ```console
114
+ $ rake compile
115
+ $ bin/console
116
+ pry> client = OPCUAClient::Client.new
117
+ ```
@@ -0,0 +1,2 @@
1
+ require 'mkmf'
2
+ create_makefile 'opcua_client/opcua_client'
@@ -0,0 +1,493 @@
1
+ #include <ruby.h>
2
+ #include "open62541.h"
3
+
4
+ VALUE cClient;
5
+ VALUE cError;
6
+ VALUE mOPCUAClient;
7
+
8
+ struct UninitializedClient {
9
+ UA_Client *client;
10
+ };
11
+
12
+ struct OpcuaClientContext {
13
+ VALUE rubyClientInstance;
14
+ };
15
+
16
+ static VALUE toRubyTime(UA_DateTime raw_date) {
17
+ UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);
18
+ VALUE year = UINT2NUM(dts.year);
19
+ VALUE month = UINT2NUM(dts.month);
20
+ VALUE day = UINT2NUM(dts.day);
21
+ VALUE hour = UINT2NUM(dts.hour);
22
+ VALUE min = UINT2NUM(dts.min);
23
+ VALUE sec = UINT2NUM(dts.sec);
24
+ VALUE millis = UINT2NUM(dts.milliSec);
25
+ VALUE cDate = rb_const_get(rb_cObject, rb_intern("Time"));
26
+ VALUE rb_date = rb_funcall(cDate, rb_intern("gm"), 7, year, month, day, hour, min, sec, millis);
27
+ return rb_date;
28
+ }
29
+
30
+ static void
31
+ handler_dataChanged(UA_Client *client, UA_UInt32 subId, void *subContext,
32
+ UA_UInt32 monId, void *monContext, UA_DataValue *value) {
33
+
34
+ struct OpcuaClientContext *ctx = UA_Client_getContext(client);
35
+ VALUE self = ctx->rubyClientInstance;
36
+ VALUE callback = rb_ivar_get(self, rb_intern("@callback_after_data_changed"));
37
+
38
+ if (NIL_P(callback)) {
39
+ return;
40
+ }
41
+
42
+ VALUE v_serverTime = Qnil;
43
+ if (value->hasServerTimestamp) {
44
+ v_serverTime = toRubyTime(value->serverTimestamp);
45
+ }
46
+
47
+ VALUE v_sourceTime = Qnil;
48
+ if (value->hasSourceTimestamp) {
49
+ v_sourceTime = toRubyTime(value->sourceTimestamp);
50
+ }
51
+
52
+ VALUE params = rb_ary_new();
53
+ rb_ary_push(params, UINT2NUM(subId));
54
+ rb_ary_push(params, UINT2NUM(monId));
55
+ rb_ary_push(params, v_serverTime);
56
+ rb_ary_push(params, v_sourceTime);
57
+
58
+ VALUE v_newValue = Qnil;
59
+
60
+ if(UA_Variant_hasScalarType(&value->value, &UA_TYPES[UA_TYPES_DATETIME])) {
61
+ UA_DateTime raw_date = *(UA_DateTime *) value->value.data;
62
+ v_newValue = toRubyTime(raw_date);
63
+ } else if (UA_Variant_hasScalarType(&value->value, &UA_TYPES[UA_TYPES_INT32])) {
64
+ UA_Int32 number = *(UA_Int32 *) value->value.data;
65
+ v_newValue = INT2NUM(number);
66
+ } else if (UA_Variant_hasScalarType(&value->value, &UA_TYPES[UA_TYPES_INT16])) {
67
+ UA_Int16 number = *(UA_Int16 *) value->value.data;
68
+ v_newValue = INT2NUM(number);
69
+ } else if (UA_Variant_hasScalarType(&value->value, &UA_TYPES[UA_TYPES_BOOLEAN])) {
70
+ UA_Boolean b = *(UA_Boolean *) value->value.data;
71
+ v_newValue = b ? Qtrue : Qfalse;
72
+ } else if (UA_Variant_hasScalarType(&value->value, &UA_TYPES[UA_TYPES_FLOAT])) {
73
+ UA_Float dbl = *(UA_Float *) value->value.data;
74
+ v_newValue = DBL2NUM(dbl);
75
+ }
76
+
77
+ rb_ary_push(params, v_newValue);
78
+ rb_proc_call(callback, params);
79
+ }
80
+
81
+ static void
82
+ deleteSubscriptionCallback(UA_Client *client, UA_UInt32 subscriptionId, void *subscriptionContext) {
83
+ // printf("Subscription Id %u was deleted\n", subscriptionId);
84
+ }
85
+
86
+ static void
87
+ subscriptionInactivityCallback(UA_Client *client, UA_UInt32 subscriptionId, void *subContext) {
88
+ // printf("Inactivity for subscription %u", subscriptionId);
89
+ }
90
+
91
+ static void
92
+ stateCallback (UA_Client *client, UA_ClientState clientState) {
93
+ struct OpcuaClientContext *ctx = UA_Client_getContext(client);
94
+
95
+ switch(clientState) {
96
+ case UA_CLIENTSTATE_DISCONNECTED:
97
+ ; // printf("%s\n", "The client is disconnected");
98
+ break;
99
+ case UA_CLIENTSTATE_CONNECTED:
100
+ ; // printf("%s\n", "A TCP connection to the server is open");
101
+ break;
102
+ case UA_CLIENTSTATE_SECURECHANNEL:
103
+ ; // printf("%s\n", "A SecureChannel to the server is open");
104
+ break;
105
+ case UA_CLIENTSTATE_SESSION:
106
+ ; // printf("%s\n", "A new session was created!");
107
+ VALUE self = ctx->rubyClientInstance;
108
+
109
+ VALUE callback = rb_ivar_get(self, rb_intern("@callback_after_session_created"));
110
+ if (!NIL_P(callback)) {
111
+ VALUE params = rb_ary_new();
112
+ rb_ary_push(params, self);
113
+ rb_proc_call(callback, params); // rescue?
114
+ }
115
+
116
+ break;
117
+ case UA_CLIENTSTATE_SESSION_RENEWED:
118
+ /* The session was renewed. We don't need to recreate the subscription. */
119
+ break;
120
+ }
121
+ return;
122
+ }
123
+
124
+ static VALUE raise_invalid_arguments_error() {
125
+ rb_raise(cError, "Invalid arguments");
126
+ return Qnil;
127
+ }
128
+
129
+ static VALUE raise_ua_status_error(UA_StatusCode status) {
130
+ rb_raise(cError, "%u: %s", status, UA_StatusCode_name(status));
131
+ return Qnil;
132
+ }
133
+
134
+ static void UA_Client_free(void *self) {
135
+ // printf("free client\n");
136
+ struct UninitializedClient *uclient = self;
137
+
138
+ if (uclient->client) {
139
+ struct OpcuaClientContext *ctx = UA_Client_getContext(uclient->client);
140
+ xfree(ctx);
141
+ UA_Client_delete(uclient->client);
142
+ }
143
+
144
+ xfree(self);
145
+ }
146
+
147
+ static const rb_data_type_t UA_Client_Type = {
148
+ "UA_Uninitialized_Client",
149
+ { 0, UA_Client_free, 0 },
150
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
151
+ };
152
+
153
+ static VALUE allocate(VALUE klass) {
154
+ // printf("allocate client\n");
155
+ struct UninitializedClient *uclient = ALLOC(struct UninitializedClient);
156
+ *uclient = (const struct UninitializedClient){ 0 };
157
+
158
+ return TypedData_Wrap_Struct(klass, &UA_Client_Type, uclient);
159
+ }
160
+
161
+ static VALUE rb_initialize(VALUE self) {
162
+ struct UninitializedClient * uclient;
163
+ TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
164
+
165
+ UA_ClientConfig customConfig = UA_ClientConfig_default;
166
+ customConfig.stateCallback = stateCallback;
167
+ customConfig.subscriptionInactivityCallback = subscriptionInactivityCallback;
168
+
169
+ struct OpcuaClientContext *ctx = ALLOC(struct OpcuaClientContext);
170
+ *ctx = (const struct OpcuaClientContext){ 0 };
171
+
172
+ ctx->rubyClientInstance = self;
173
+ customConfig.clientContext = ctx;
174
+
175
+ uclient->client = UA_Client_new(customConfig);
176
+
177
+ return Qnil;
178
+ }
179
+
180
+ static VALUE rb_connect(VALUE self, VALUE v_connectionString) {
181
+ if (RB_TYPE_P(v_connectionString, T_STRING) != 1) {
182
+ return raise_invalid_arguments_error();
183
+ }
184
+
185
+ char *connectionString = StringValueCStr(v_connectionString);
186
+
187
+ struct UninitializedClient * uclient;
188
+ TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
189
+ UA_Client *client = uclient->client;
190
+
191
+ UA_StatusCode status = UA_Client_connect(client, connectionString);
192
+
193
+ if (status == UA_STATUSCODE_GOOD) {
194
+ return Qnil;
195
+ } else {
196
+ return raise_ua_status_error(status);
197
+ }
198
+ }
199
+
200
+ static VALUE rb_createSubscription(VALUE self) {
201
+ struct UninitializedClient * uclient;
202
+ TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
203
+ UA_Client *client = uclient->client;
204
+
205
+ UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
206
+ UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request, NULL, NULL, deleteSubscriptionCallback);
207
+
208
+ if (response.responseHeader.serviceResult == UA_STATUSCODE_GOOD) {
209
+ UA_UInt32 subscriptionId = response.subscriptionId;
210
+ return UINT2NUM(subscriptionId);
211
+ } else {
212
+ return Qnil;
213
+ }
214
+ }
215
+
216
+ static VALUE rb_addMonitoredItem(VALUE self, VALUE v_subscriptionId, VALUE v_monNsIndex, VALUE v_monNsName) {
217
+ struct UninitializedClient * uclient;
218
+ TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
219
+ UA_Client *client = uclient->client;
220
+
221
+ UA_UInt32 subscriptionId = NUM2UINT(v_subscriptionId); // TODO: check type
222
+ UA_UInt16 monNsIndex = NUM2USHORT(v_monNsIndex); // TODO: check type
223
+ char* monNsName = StringValueCStr(v_monNsName); // TODO: check type
224
+
225
+ UA_MonitoredItemCreateRequest monRequest = UA_MonitoredItemCreateRequest_default(UA_NODEID_STRING(monNsIndex, monNsName));
226
+
227
+ UA_MonitoredItemCreateResult monResponse =
228
+ UA_Client_MonitoredItems_createDataChange(client, subscriptionId,
229
+ UA_TIMESTAMPSTORETURN_BOTH,
230
+ monRequest, NULL, handler_dataChanged, NULL);
231
+ if (monResponse.statusCode == UA_STATUSCODE_GOOD) {
232
+ // printf("Request to monitor field %hu:%s successful, id %u\n", monNsIndex, monNsName, monResponse.monitoredItemId);
233
+ UA_UInt32 monitoredItemId = monResponse.monitoredItemId;
234
+ return UINT2NUM(monitoredItemId);
235
+ } else {
236
+ // printf("Request to monitor field failed: %s\n", UA_StatusCode_name(monResponse.statusCode));
237
+ return Qnil;
238
+ }
239
+ }
240
+
241
+ static VALUE rb_disconnect(VALUE self) {
242
+ struct UninitializedClient * uclient;
243
+ TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
244
+ UA_Client *client = uclient->client;
245
+
246
+ UA_StatusCode status = UA_Client_disconnect(client);
247
+ return RB_UINT2NUM(status);
248
+ }
249
+
250
+ static VALUE rb_writeUaValue(VALUE self, VALUE v_nsIndex, VALUE v_name, VALUE v_newValue, int uaType) {
251
+ if (RB_TYPE_P(v_name, T_STRING) != 1) {
252
+ return raise_invalid_arguments_error();
253
+ }
254
+
255
+ if (RB_TYPE_P(v_nsIndex, T_FIXNUM) != 1) {
256
+ return raise_invalid_arguments_error();
257
+ }
258
+
259
+ if (uaType == UA_TYPES_INT16 && RB_TYPE_P(v_newValue, T_FIXNUM) != 1) {
260
+ return raise_invalid_arguments_error();
261
+ }
262
+
263
+ char *name = StringValueCStr(v_name);
264
+ int nsIndex = FIX2INT(v_nsIndex);
265
+
266
+ struct UninitializedClient * uclient;
267
+ TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
268
+ UA_Client *client = uclient->client;
269
+
270
+ UA_Variant value;
271
+ UA_Variant_init(&value);
272
+
273
+ if (uaType == UA_TYPES_INT16) {
274
+ UA_Int16 newValue = NUM2SHORT(v_newValue);
275
+ value.data = UA_malloc(sizeof(UA_Int16));
276
+ *(UA_Int16*)value.data = newValue;
277
+ value.type = &UA_TYPES[UA_TYPES_INT16];
278
+ } else if (uaType == UA_TYPES_INT32) {
279
+ UA_Int32 newValue = NUM2INT(v_newValue);
280
+ value.data = UA_malloc(sizeof(UA_Int32));
281
+ *(UA_Int32*)value.data = newValue;
282
+ value.type = &UA_TYPES[UA_TYPES_INT32];
283
+ } else if (uaType == UA_TYPES_FLOAT) {
284
+ UA_Float newValue = NUM2DBL(v_newValue);
285
+ value.data = UA_malloc(sizeof(UA_Float));
286
+ *(UA_Float*)value.data = newValue;
287
+ value.type = &UA_TYPES[UA_TYPES_FLOAT];
288
+ } else if (uaType == UA_TYPES_BOOLEAN) {
289
+ UA_Boolean newValue = RTEST(v_newValue);
290
+ value.data = UA_malloc(sizeof(UA_Boolean));
291
+ *(UA_Boolean*)value.data = newValue;
292
+ value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
293
+ } else {
294
+ rb_raise(cError, "Unsupported type");
295
+ }
296
+
297
+ UA_StatusCode status = UA_Client_writeValueAttribute(client, UA_NODEID_STRING(nsIndex, name), &value);
298
+
299
+ if (status == UA_STATUSCODE_GOOD) {
300
+ // printf("%s\n", "value write successful");
301
+ } else {
302
+ /* Clean up */
303
+ UA_Variant_deleteMembers(&value);
304
+ return raise_ua_status_error(status);
305
+ }
306
+
307
+ /* Clean up */
308
+ UA_Variant_deleteMembers(&value);
309
+
310
+ return Qnil;
311
+ }
312
+
313
+ static VALUE rb_writeInt16Value(VALUE self, VALUE v_nsIndex, VALUE v_name, VALUE v_newValue) {
314
+ return rb_writeUaValue(self, v_nsIndex, v_name, v_newValue, UA_TYPES_INT16);
315
+ }
316
+
317
+ static VALUE rb_writeInt32Value(VALUE self, VALUE v_nsIndex, VALUE v_name, VALUE v_newValue) {
318
+ return rb_writeUaValue(self, v_nsIndex, v_name, v_newValue, UA_TYPES_INT32);
319
+ }
320
+
321
+ static VALUE rb_writeBooleanValue(VALUE self, VALUE v_nsIndex, VALUE v_name, VALUE v_newValue) {
322
+ return rb_writeUaValue(self, v_nsIndex, v_name, v_newValue, UA_TYPES_BOOLEAN);
323
+ }
324
+
325
+ static VALUE rb_writeFloatValue(VALUE self, VALUE v_nsIndex, VALUE v_name, VALUE v_newValue) {
326
+ return rb_writeUaValue(self, v_nsIndex, v_name, v_newValue, UA_TYPES_FLOAT);
327
+ }
328
+
329
+ static VALUE rb_readUaValue(VALUE self, VALUE v_nsIndex, VALUE v_name, int type) {
330
+ if (RB_TYPE_P(v_name, T_STRING) != 1) {
331
+ return raise_invalid_arguments_error();
332
+ }
333
+
334
+ if (RB_TYPE_P(v_nsIndex, T_FIXNUM) != 1) {
335
+ return raise_invalid_arguments_error();
336
+ }
337
+
338
+ char *name = StringValueCStr(v_name);
339
+ int nsIndex = FIX2INT(v_nsIndex);
340
+
341
+ struct UninitializedClient * uclient;
342
+ TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
343
+ UA_Client *client = uclient->client;
344
+
345
+ UA_Variant value;
346
+ UA_Variant_init(&value);
347
+ UA_StatusCode status = UA_Client_readValueAttribute(client, UA_NODEID_STRING(nsIndex, name), &value);
348
+
349
+ if (status == UA_STATUSCODE_GOOD) {
350
+ // printf("%s\n", "value read successful");
351
+ } else {
352
+ /* Clean up */
353
+ UA_Variant_deleteMembers(&value);
354
+ return raise_ua_status_error(status);
355
+ }
356
+
357
+ VALUE result = Qnil;
358
+
359
+ if (type == UA_TYPES_INT16 && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_INT16])) {
360
+ UA_Int16 val =*(UA_Int16*)value.data;
361
+ // printf("the value is: %i\n", val);
362
+ result = INT2FIX(val);
363
+ } else if (type == UA_TYPES_INT32 && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_INT32])) {
364
+ UA_Int32 val =*(UA_Int32*)value.data;
365
+ result = INT2FIX(val);
366
+ } else if (type == UA_TYPES_BOOLEAN && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_BOOLEAN])) {
367
+ UA_Boolean val =*(UA_Boolean*)value.data;
368
+ result = val ? Qtrue : Qfalse;
369
+ } else if (type == UA_TYPES_FLOAT && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_FLOAT])) {
370
+ UA_Float val =*(UA_Float*)value.data;
371
+ result = DBL2NUM(val);
372
+ } else {
373
+ rb_raise(cError, "Not an int16");
374
+ return Qnil;
375
+ }
376
+
377
+ /* Clean up */
378
+ UA_Variant_deleteMembers(&value);
379
+
380
+ return result;
381
+ }
382
+
383
+ static VALUE rb_readInt16Value(VALUE self, VALUE v_nsIndex, VALUE v_name) {
384
+ return rb_readUaValue(self, v_nsIndex, v_name, UA_TYPES_INT16);
385
+ }
386
+
387
+ static VALUE rb_readInt32Value(VALUE self, VALUE v_nsIndex, VALUE v_name) {
388
+ return rb_readUaValue(self, v_nsIndex, v_name, UA_TYPES_INT32);
389
+ }
390
+
391
+ static VALUE rb_readBooleanValue(VALUE self, VALUE v_nsIndex, VALUE v_name) {
392
+ return rb_readUaValue(self, v_nsIndex, v_name, UA_TYPES_BOOLEAN);
393
+ }
394
+
395
+ static VALUE rb_readFloatValue(VALUE self, VALUE v_nsIndex, VALUE v_name) {
396
+ return rb_readUaValue(self, v_nsIndex, v_name, UA_TYPES_FLOAT);
397
+ }
398
+
399
+ static VALUE rb_get_human_UA_StatusCode(VALUE self, VALUE v_code) {
400
+ if (RB_TYPE_P(v_code, T_FIXNUM) == 1) {
401
+ unsigned int code = FIX2UINT(v_code);
402
+ const char* name = UA_StatusCode_name(code);
403
+ return rb_str_export_locale(rb_str_new_cstr(name));
404
+ } else {
405
+ return raise_invalid_arguments_error();
406
+ }
407
+ }
408
+
409
+ static VALUE rb_run_single_monitoring_cycle(VALUE self) {
410
+ struct UninitializedClient * uclient;
411
+ TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
412
+ UA_Client *client = uclient->client;
413
+
414
+ UA_StatusCode status = UA_Client_runAsync(client, 1000);
415
+ return UINT2NUM(status);
416
+ }
417
+
418
+ static VALUE rb_run_single_monitoring_cycle_bang(VALUE self) {
419
+ struct UninitializedClient * uclient;
420
+ TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
421
+ UA_Client *client = uclient->client;
422
+
423
+ UA_StatusCode status = UA_Client_runAsync(client, 1000);
424
+
425
+ if (status != UA_STATUSCODE_GOOD) {
426
+ return raise_ua_status_error(status);
427
+ }
428
+
429
+ return Qnil;
430
+ }
431
+
432
+ static VALUE rb_state(VALUE self) {
433
+ struct UninitializedClient * uclient;
434
+ TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
435
+ UA_Client *client = uclient->client;
436
+
437
+ UA_ClientState state = UA_Client_getState(client);
438
+ return INT2NUM(state);
439
+ }
440
+
441
+ static void defineStateContants(VALUE mOPCUAClient) {
442
+ rb_define_const(mOPCUAClient, "UA_CLIENTSTATE_DISCONNECTED", INT2NUM(UA_CLIENTSTATE_DISCONNECTED));
443
+ rb_define_const(mOPCUAClient, "UA_CLIENTSTATE_CONNECTED", INT2NUM(UA_CLIENTSTATE_CONNECTED));
444
+ rb_define_const(mOPCUAClient, "UA_CLIENTSTATE_SECURECHANNEL", INT2NUM(UA_CLIENTSTATE_SECURECHANNEL));
445
+ rb_define_const(mOPCUAClient, "UA_CLIENTSTATE_SESSION", INT2NUM(UA_CLIENTSTATE_SESSION));
446
+ rb_define_const(mOPCUAClient, "UA_CLIENTSTATE_SESSION_RENEWED", INT2NUM(UA_CLIENTSTATE_SESSION_RENEWED));
447
+ }
448
+
449
+ void Init_opcua_client()
450
+ {
451
+ #ifdef UA_ENABLE_SUBSCRIPTIONS
452
+ // printf("%s\n", "ok! opcua-client-ruby built with subscriptions enabled.");
453
+ #endif
454
+
455
+ mOPCUAClient = rb_const_get(rb_cObject, rb_intern("OPCUAClient"));
456
+ defineStateContants(mOPCUAClient);
457
+
458
+ cError = rb_define_class_under(mOPCUAClient, "Error", rb_eStandardError);
459
+ cClient = rb_define_class_under(mOPCUAClient, "Client", rb_cObject);
460
+
461
+ rb_define_alloc_func(cClient, allocate);
462
+
463
+ rb_define_method(cClient, "initialize", rb_initialize, 0);
464
+
465
+ rb_define_method(cClient, "run_single_monitoring_cycle", rb_run_single_monitoring_cycle, 0);
466
+ rb_define_method(cClient, "run_mon_cycle", rb_run_single_monitoring_cycle, 0);
467
+ rb_define_method(cClient, "do_mon_cycle", rb_run_single_monitoring_cycle, 0);
468
+
469
+ rb_define_method(cClient, "run_single_monitoring_cycle!", rb_run_single_monitoring_cycle_bang, 0);
470
+ rb_define_method(cClient, "run_mon_cycle!", rb_run_single_monitoring_cycle_bang, 0);
471
+ rb_define_method(cClient, "do_mon_cycle!", rb_run_single_monitoring_cycle_bang, 0);
472
+
473
+ rb_define_method(cClient, "connect", rb_connect, 1);
474
+ rb_define_method(cClient, "disconnect", rb_disconnect, 0);
475
+ rb_define_method(cClient, "state", rb_state, 0);
476
+
477
+ rb_define_method(cClient, "read_int16", rb_readInt16Value, 2);
478
+ rb_define_method(cClient, "read_int32", rb_readInt32Value, 2);
479
+ rb_define_method(cClient, "read_float", rb_readFloatValue, 2);
480
+ rb_define_method(cClient, "read_boolean", rb_readBooleanValue, 2);
481
+ rb_define_method(cClient, "read_bool", rb_readBooleanValue, 2);
482
+
483
+ rb_define_method(cClient, "write_int16", rb_writeInt16Value, 3);
484
+ rb_define_method(cClient, "write_int32", rb_writeInt32Value, 3);
485
+ rb_define_method(cClient, "write_float", rb_writeFloatValue, 3);
486
+ rb_define_method(cClient, "write_boolean", rb_writeBooleanValue, 3);
487
+ rb_define_method(cClient, "write_bool", rb_writeBooleanValue, 3);
488
+
489
+ rb_define_method(cClient, "create_subscription", rb_createSubscription, 0);
490
+ rb_define_method(cClient, "add_monitored_item", rb_addMonitoredItem, 3);
491
+
492
+ rb_define_singleton_method(mOPCUAClient, "human_status_code", rb_get_human_UA_StatusCode, 1);
493
+ }