opcua_client 0.0.1

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