opcua_client 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +12 -1
- data/ext/opcua_client/opcua_client.c +140 -119
- data/lib/opcua_client/version.rb +1 -1
- data/spec/core_spec.rb +15 -0
- data/spec/spec_helper.rb +22 -0
- metadata +12 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae12c435f33aa7b57384562385a22dafb562fe3620573697b5d3bc57e1e58798
|
4
|
+
data.tar.gz: 29ac2ff75077f30b9c614103033c43e53186db66a428352f2bdfc369203715ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96f3ec5185c7072203f003ec35a3db4a984277333fe413f600248e7ffadcf92c815fbb6fc593f0b2b3d5fffd7983b71e0685188a0909ec826f63ad56ddb6a09a
|
7
|
+
data.tar.gz: f11e9e5101902d4a4715741c455762ba8bcd4eb28715e80eb8644319267d66d7e52d6c850d29234fd72a6938b18eaface6f20788bd613b3052fc96489c2ce2dd
|
data/README.md
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
Incomplete OPC-UA client library for Ruby. Wraps open62541: <https://open62541.org>.
|
4
4
|
|
5
|
+

|
6
|
+
|
5
7
|
## Installation
|
6
8
|
|
7
9
|
Add it to your Gemfile:
|
@@ -65,7 +67,9 @@ All methods raise OPCUAClient::Error if unsuccessful.
|
|
65
67
|
* ```client.write_float(Fixnum ns, String name, Float value)```
|
66
68
|
* ```client.write_boolean(Fixnum ns, String name, bool value)```
|
67
69
|
* ```client.multi_write_int16(Fixnum ns, Array[String] names, Array[Fixnum] values)```
|
70
|
+
* ```client.multi_write_uint16(Fixnum ns, Array[String] names, Array[Fixnum] values)```
|
68
71
|
* ```client.multi_write_int32(Fixnum ns, Array[String] names, Array[Fixnum] values)```
|
72
|
+
* ```client.multi_write_uint32(Fixnum ns, Array[String] names, Array[Fixnum] values)```
|
69
73
|
* ```client.multi_write_float(Fixnum ns, Array[String] names, Array[Float] values)```
|
70
74
|
* ```client.multi_write_boolean(Fixnum ns, Array[String] names, Array[bool] values)```
|
71
75
|
|
@@ -122,8 +126,15 @@ bundle
|
|
122
126
|
### Try out changes
|
123
127
|
|
124
128
|
```console
|
125
|
-
$ rake compile
|
129
|
+
$ bin/rake compile
|
126
130
|
$ bin/console
|
127
131
|
pry> client = OPCUAClient::Client.new
|
128
132
|
pry> client.connect("opc.tcp://127.0.0.1:4840")
|
129
133
|
```
|
134
|
+
|
135
|
+
### Test it
|
136
|
+
|
137
|
+
```console
|
138
|
+
$ bin/rake compile
|
139
|
+
$ bin/rake spec
|
140
|
+
```
|
@@ -34,29 +34,29 @@ handler_dataChanged(UA_Client *client, UA_UInt32 subId, void *subContext,
|
|
34
34
|
struct OpcuaClientContext *ctx = UA_Client_getContext(client);
|
35
35
|
VALUE self = ctx->rubyClientInstance;
|
36
36
|
VALUE callback = rb_ivar_get(self, rb_intern("@callback_after_data_changed"));
|
37
|
-
|
37
|
+
|
38
38
|
if (NIL_P(callback)) {
|
39
39
|
return;
|
40
40
|
}
|
41
|
-
|
41
|
+
|
42
42
|
VALUE v_serverTime = Qnil;
|
43
43
|
if (value->hasServerTimestamp) {
|
44
44
|
v_serverTime = toRubyTime(value->serverTimestamp);
|
45
45
|
}
|
46
|
-
|
46
|
+
|
47
47
|
VALUE v_sourceTime = Qnil;
|
48
48
|
if (value->hasSourceTimestamp) {
|
49
49
|
v_sourceTime = toRubyTime(value->sourceTimestamp);
|
50
50
|
}
|
51
|
-
|
51
|
+
|
52
52
|
VALUE params = rb_ary_new();
|
53
53
|
rb_ary_push(params, UINT2NUM(subId));
|
54
54
|
rb_ary_push(params, UINT2NUM(monId));
|
55
55
|
rb_ary_push(params, v_serverTime);
|
56
56
|
rb_ary_push(params, v_sourceTime);
|
57
|
-
|
57
|
+
|
58
58
|
VALUE v_newValue = Qnil;
|
59
|
-
|
59
|
+
|
60
60
|
if(UA_Variant_hasScalarType(&value->value, &UA_TYPES[UA_TYPES_DATETIME])) {
|
61
61
|
UA_DateTime raw_date = *(UA_DateTime *) value->value.data;
|
62
62
|
v_newValue = toRubyTime(raw_date);
|
@@ -73,7 +73,7 @@ handler_dataChanged(UA_Client *client, UA_UInt32 subId, void *subContext,
|
|
73
73
|
UA_Float dbl = *(UA_Float *) value->value.data;
|
74
74
|
v_newValue = DBL2NUM(dbl);
|
75
75
|
}
|
76
|
-
|
76
|
+
|
77
77
|
rb_ary_push(params, v_newValue);
|
78
78
|
rb_proc_call(callback, params);
|
79
79
|
}
|
@@ -91,7 +91,7 @@ subscriptionInactivityCallback(UA_Client *client, UA_UInt32 subscriptionId, void
|
|
91
91
|
static void
|
92
92
|
stateCallback (UA_Client *client, UA_ClientState clientState) {
|
93
93
|
struct OpcuaClientContext *ctx = UA_Client_getContext(client);
|
94
|
-
|
94
|
+
|
95
95
|
switch(clientState) {
|
96
96
|
case UA_CLIENTSTATE_DISCONNECTED:
|
97
97
|
; // printf("%s\n", "The client is disconnected");
|
@@ -105,14 +105,14 @@ stateCallback (UA_Client *client, UA_ClientState clientState) {
|
|
105
105
|
case UA_CLIENTSTATE_SESSION:
|
106
106
|
; // printf("%s\n", "A new session was created!");
|
107
107
|
VALUE self = ctx->rubyClientInstance;
|
108
|
-
|
108
|
+
|
109
109
|
VALUE callback = rb_ivar_get(self, rb_intern("@callback_after_session_created"));
|
110
110
|
if (!NIL_P(callback)) {
|
111
111
|
VALUE params = rb_ary_new();
|
112
112
|
rb_ary_push(params, self);
|
113
113
|
rb_proc_call(callback, params); // rescue?
|
114
114
|
}
|
115
|
-
|
115
|
+
|
116
116
|
break;
|
117
117
|
case UA_CLIENTSTATE_SESSION_RENEWED:
|
118
118
|
/* The session was renewed. We don't need to recreate the subscription. */
|
@@ -134,7 +134,7 @@ static VALUE raise_ua_status_error(UA_StatusCode status) {
|
|
134
134
|
static void UA_Client_free(void *self) {
|
135
135
|
// printf("free client\n");
|
136
136
|
struct UninitializedClient *uclient = self;
|
137
|
-
|
137
|
+
|
138
138
|
if (uclient->client) {
|
139
139
|
struct OpcuaClientContext *ctx = UA_Client_getContext(uclient->client);
|
140
140
|
xfree(ctx);
|
@@ -154,26 +154,26 @@ static VALUE allocate(VALUE klass) {
|
|
154
154
|
// printf("allocate client\n");
|
155
155
|
struct UninitializedClient *uclient = ALLOC(struct UninitializedClient);
|
156
156
|
*uclient = (const struct UninitializedClient){ 0 };
|
157
|
-
|
157
|
+
|
158
158
|
return TypedData_Wrap_Struct(klass, &UA_Client_Type, uclient);
|
159
159
|
}
|
160
160
|
|
161
161
|
static VALUE rb_initialize(VALUE self) {
|
162
162
|
struct UninitializedClient * uclient;
|
163
163
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
164
|
-
|
164
|
+
|
165
165
|
UA_ClientConfig customConfig = UA_ClientConfig_default;
|
166
166
|
customConfig.stateCallback = stateCallback;
|
167
167
|
customConfig.subscriptionInactivityCallback = subscriptionInactivityCallback;
|
168
|
-
|
168
|
+
|
169
169
|
struct OpcuaClientContext *ctx = ALLOC(struct OpcuaClientContext);
|
170
170
|
*ctx = (const struct OpcuaClientContext){ 0 };
|
171
|
-
|
171
|
+
|
172
172
|
ctx->rubyClientInstance = self;
|
173
173
|
customConfig.clientContext = ctx;
|
174
|
-
|
174
|
+
|
175
175
|
uclient->client = UA_Client_new(customConfig);
|
176
|
-
|
176
|
+
|
177
177
|
return Qnil;
|
178
178
|
}
|
179
179
|
|
@@ -181,15 +181,15 @@ static VALUE rb_connect(VALUE self, VALUE v_connectionString) {
|
|
181
181
|
if (RB_TYPE_P(v_connectionString, T_STRING) != 1) {
|
182
182
|
return raise_invalid_arguments_error();
|
183
183
|
}
|
184
|
-
|
184
|
+
|
185
185
|
char *connectionString = StringValueCStr(v_connectionString);
|
186
|
-
|
186
|
+
|
187
187
|
struct UninitializedClient * uclient;
|
188
188
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
189
189
|
UA_Client *client = uclient->client;
|
190
|
-
|
190
|
+
|
191
191
|
UA_StatusCode status = UA_Client_connect(client, connectionString);
|
192
|
-
|
192
|
+
|
193
193
|
if (status == UA_STATUSCODE_GOOD) {
|
194
194
|
return Qnil;
|
195
195
|
} else {
|
@@ -201,10 +201,10 @@ static VALUE rb_createSubscription(VALUE self) {
|
|
201
201
|
struct UninitializedClient * uclient;
|
202
202
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
203
203
|
UA_Client *client = uclient->client;
|
204
|
-
|
204
|
+
|
205
205
|
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
|
206
206
|
UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request, NULL, NULL, deleteSubscriptionCallback);
|
207
|
-
|
207
|
+
|
208
208
|
if (response.responseHeader.serviceResult == UA_STATUSCODE_GOOD) {
|
209
209
|
UA_UInt32 subscriptionId = response.subscriptionId;
|
210
210
|
return UINT2NUM(subscriptionId);
|
@@ -217,13 +217,13 @@ static VALUE rb_addMonitoredItem(VALUE self, VALUE v_subscriptionId, VALUE v_mon
|
|
217
217
|
struct UninitializedClient * uclient;
|
218
218
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
219
219
|
UA_Client *client = uclient->client;
|
220
|
-
|
220
|
+
|
221
221
|
UA_UInt32 subscriptionId = NUM2UINT(v_subscriptionId); // TODO: check type
|
222
222
|
UA_UInt16 monNsIndex = NUM2USHORT(v_monNsIndex); // TODO: check type
|
223
223
|
char* monNsName = StringValueCStr(v_monNsName); // TODO: check type
|
224
224
|
|
225
225
|
UA_MonitoredItemCreateRequest monRequest = UA_MonitoredItemCreateRequest_default(UA_NODEID_STRING(monNsIndex, monNsName));
|
226
|
-
|
226
|
+
|
227
227
|
UA_MonitoredItemCreateResult monResponse =
|
228
228
|
UA_Client_MonitoredItems_createDataChange(client, subscriptionId,
|
229
229
|
UA_TIMESTAMPSTORETURN_BOTH,
|
@@ -242,27 +242,27 @@ static VALUE rb_disconnect(VALUE self) {
|
|
242
242
|
struct UninitializedClient * uclient;
|
243
243
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
244
244
|
UA_Client *client = uclient->client;
|
245
|
-
|
245
|
+
|
246
246
|
UA_StatusCode status = UA_Client_disconnect(client);
|
247
247
|
return RB_UINT2NUM(status);
|
248
248
|
}
|
249
249
|
|
250
250
|
static UA_StatusCode multiRead(UA_Client *client, const UA_NodeId *nodeId, UA_Variant *out, const long varsCount) {
|
251
|
-
|
251
|
+
|
252
252
|
UA_UInt16 rvSize = UA_TYPES[UA_TYPES_READVALUEID].memSize;
|
253
253
|
UA_ReadValueId *rValues = UA_calloc(varsCount, rvSize);
|
254
|
-
|
254
|
+
|
255
255
|
for (int i=0; i<varsCount; i++) {
|
256
256
|
UA_ReadValueId *readItem = &rValues[i];
|
257
257
|
readItem->nodeId = nodeId[i];
|
258
258
|
readItem->attributeId = UA_ATTRIBUTEID_VALUE;
|
259
259
|
}
|
260
|
-
|
260
|
+
|
261
261
|
UA_ReadRequest request;
|
262
262
|
UA_ReadRequest_init(&request);
|
263
263
|
request.nodesToRead = rValues;
|
264
264
|
request.nodesToReadSize = varsCount;
|
265
|
-
|
265
|
+
|
266
266
|
UA_ReadResponse response = UA_Client_Service_read(client, request);
|
267
267
|
UA_StatusCode retval = response.responseHeader.serviceResult;
|
268
268
|
if(retval == UA_STATUSCODE_GOOD) {
|
@@ -271,23 +271,23 @@ static UA_StatusCode multiRead(UA_Client *client, const UA_NodeId *nodeId, UA_Va
|
|
271
271
|
else
|
272
272
|
retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
|
273
273
|
}
|
274
|
-
|
274
|
+
|
275
275
|
if(retval != UA_STATUSCODE_GOOD) {
|
276
276
|
UA_ReadResponse_deleteMembers(&response);
|
277
277
|
UA_free(rValues);
|
278
278
|
return retval;
|
279
279
|
}
|
280
|
-
|
280
|
+
|
281
281
|
/* Set the StatusCode */
|
282
282
|
UA_DataValue *results = response.results;
|
283
|
-
|
283
|
+
|
284
284
|
if (response.resultsSize != varsCount) {
|
285
285
|
retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
|
286
286
|
UA_ReadResponse_deleteMembers(&response);
|
287
287
|
UA_free(rValues);
|
288
288
|
return retval;
|
289
289
|
}
|
290
|
-
|
290
|
+
|
291
291
|
for (int i=0; i<varsCount; i++) {
|
292
292
|
if ((results[i].hasStatus && results[i].status != UA_STATUSCODE_GOOD) || !results[i].hasValue) {
|
293
293
|
retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
|
@@ -296,12 +296,12 @@ static UA_StatusCode multiRead(UA_Client *client, const UA_NodeId *nodeId, UA_Va
|
|
296
296
|
return retval;
|
297
297
|
}
|
298
298
|
}
|
299
|
-
|
299
|
+
|
300
300
|
for (int i=0; i<varsCount; i++) {
|
301
301
|
out[i] = results[i].value;
|
302
302
|
UA_Variant_init(&results[i].value);
|
303
303
|
}
|
304
|
-
|
304
|
+
|
305
305
|
UA_ReadResponse_deleteMembers(&response);
|
306
306
|
UA_free(rValues);
|
307
307
|
return retval;
|
@@ -309,11 +309,11 @@ static UA_StatusCode multiRead(UA_Client *client, const UA_NodeId *nodeId, UA_Va
|
|
309
309
|
|
310
310
|
static UA_StatusCode multiWrite(UA_Client *client, const UA_NodeId *nodeId, const UA_Variant *in, const long varsSize) {
|
311
311
|
UA_AttributeId attributeId = UA_ATTRIBUTEID_VALUE;
|
312
|
-
|
312
|
+
|
313
313
|
UA_UInt16 wvSize = UA_TYPES[UA_TYPES_WRITEVALUE].memSize;
|
314
|
-
|
314
|
+
|
315
315
|
UA_WriteValue *wValues = UA_calloc(varsSize, wvSize);
|
316
|
-
|
316
|
+
|
317
317
|
for (int i=0; i<varsSize; i++) {
|
318
318
|
UA_WriteValue *wValue = &wValues[i];
|
319
319
|
wValue->attributeId = attributeId;
|
@@ -321,7 +321,7 @@ static UA_StatusCode multiWrite(UA_Client *client, const UA_NodeId *nodeId, cons
|
|
321
321
|
wValue->value.value = in[i];
|
322
322
|
wValue->value.hasValue = true;
|
323
323
|
}
|
324
|
-
|
324
|
+
|
325
325
|
UA_WriteRequest wReq;
|
326
326
|
UA_WriteRequest_init(&wReq);
|
327
327
|
wReq.nodesToWrite = wValues;
|
@@ -333,7 +333,7 @@ static UA_StatusCode multiWrite(UA_Client *client, const UA_NodeId *nodeId, cons
|
|
333
333
|
if(retval == UA_STATUSCODE_GOOD) {
|
334
334
|
if(wResp.resultsSize == varsSize) {
|
335
335
|
retval = wResp.results[0];
|
336
|
-
|
336
|
+
|
337
337
|
for (int i=0; i<wResp.resultsSize; i++) {
|
338
338
|
if (wResp.results[i] != UA_STATUSCODE_GOOD) {
|
339
339
|
retval = wResp.results[i];
|
@@ -341,7 +341,7 @@ static UA_StatusCode multiWrite(UA_Client *client, const UA_NodeId *nodeId, cons
|
|
341
341
|
break;
|
342
342
|
}
|
343
343
|
}
|
344
|
-
|
344
|
+
|
345
345
|
if (retval == UA_STATUSCODE_GOOD) {
|
346
346
|
// printf("%s\n", "multiWrite: all vars written");
|
347
347
|
}
|
@@ -355,7 +355,7 @@ static UA_StatusCode multiWrite(UA_Client *client, const UA_NodeId *nodeId, cons
|
|
355
355
|
|
356
356
|
UA_WriteResponse_deleteMembers(&wResp);
|
357
357
|
UA_free(wValues);
|
358
|
-
|
358
|
+
|
359
359
|
return retval;
|
360
360
|
}
|
361
361
|
|
@@ -363,47 +363,47 @@ static VALUE rb_readUaValues(VALUE self, VALUE v_nsIndex, VALUE v_aryNames) {
|
|
363
363
|
if (RB_TYPE_P(v_nsIndex, T_FIXNUM) != 1) {
|
364
364
|
return raise_invalid_arguments_error();
|
365
365
|
}
|
366
|
-
|
366
|
+
|
367
367
|
Check_Type(v_aryNames, T_ARRAY);
|
368
368
|
const long namesCount = RARRAY_LEN(v_aryNames);
|
369
|
-
|
369
|
+
|
370
370
|
int nsIndex = FIX2INT(v_nsIndex);
|
371
|
-
|
371
|
+
|
372
372
|
struct UninitializedClient * uclient;
|
373
373
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
374
374
|
UA_Client *client = uclient->client;
|
375
|
-
|
375
|
+
|
376
376
|
UA_UInt16 nidSize = UA_TYPES[UA_TYPES_NODEID].memSize;
|
377
377
|
UA_UInt16 variantSize = UA_TYPES[UA_TYPES_VARIANT].memSize;
|
378
|
-
|
378
|
+
|
379
379
|
UA_NodeId *nodes = UA_calloc(namesCount, nidSize);
|
380
380
|
UA_Variant *readValues = UA_calloc(namesCount, variantSize);
|
381
|
-
|
381
|
+
|
382
382
|
for (int i=0; i<namesCount; i++) {
|
383
383
|
VALUE v_name = rb_ary_entry(v_aryNames, i);
|
384
|
-
|
384
|
+
|
385
385
|
if (RB_TYPE_P(v_name, T_STRING) != 1) {
|
386
386
|
return raise_invalid_arguments_error();
|
387
387
|
}
|
388
|
-
|
388
|
+
|
389
389
|
char *name = StringValueCStr(v_name);
|
390
390
|
nodes[i] = UA_NODEID_STRING(nsIndex, name);
|
391
391
|
}
|
392
|
-
|
392
|
+
|
393
393
|
UA_StatusCode status = multiRead(client, nodes, readValues, namesCount);
|
394
|
-
|
394
|
+
|
395
395
|
VALUE resultArray = Qnil;
|
396
|
-
|
396
|
+
|
397
397
|
if (status == UA_STATUSCODE_GOOD) {
|
398
398
|
// printf("%s\n", "value read successful");
|
399
|
-
|
399
|
+
|
400
400
|
resultArray = rb_ary_new2(namesCount);
|
401
|
-
|
401
|
+
|
402
402
|
for (int i=0; i<namesCount; i++) {
|
403
403
|
// printf("the value is: %i\n", val);
|
404
|
-
|
404
|
+
|
405
405
|
VALUE rubyVal = Qnil;
|
406
|
-
|
406
|
+
|
407
407
|
if (UA_Variant_hasScalarType(&readValues[i], &UA_TYPES[UA_TYPES_INT16])) {
|
408
408
|
UA_Int16 val = *(UA_Int16*)readValues[i].data;
|
409
409
|
rubyVal = INT2FIX(val);
|
@@ -425,7 +425,7 @@ static VALUE rb_readUaValues(VALUE self, VALUE v_nsIndex, VALUE v_aryNames) {
|
|
425
425
|
} else {
|
426
426
|
rubyVal = Qnil; // unsupported
|
427
427
|
}
|
428
|
-
|
428
|
+
|
429
429
|
rb_ary_push(resultArray, rubyVal);
|
430
430
|
}
|
431
431
|
} else {
|
@@ -435,17 +435,17 @@ static VALUE rb_readUaValues(VALUE self, VALUE v_nsIndex, VALUE v_aryNames) {
|
|
435
435
|
}
|
436
436
|
UA_free(nodes);
|
437
437
|
UA_free(readValues);
|
438
|
-
|
438
|
+
|
439
439
|
return raise_ua_status_error(status);
|
440
440
|
}
|
441
|
-
|
441
|
+
|
442
442
|
/* Clean up */
|
443
443
|
for (int i=0; i<namesCount; i++) {
|
444
444
|
UA_Variant_deleteMembers(&readValues[i]);
|
445
445
|
}
|
446
446
|
UA_free(nodes);
|
447
447
|
UA_free(readValues);
|
448
|
-
|
448
|
+
|
449
449
|
return resultArray;
|
450
450
|
}
|
451
451
|
|
@@ -453,58 +453,70 @@ static VALUE rb_writeUaValues(VALUE self, VALUE v_nsIndex, VALUE v_aryNames, VAL
|
|
453
453
|
if (RB_TYPE_P(v_nsIndex, T_FIXNUM) != 1) {
|
454
454
|
return raise_invalid_arguments_error();
|
455
455
|
}
|
456
|
-
|
456
|
+
|
457
457
|
Check_Type(v_aryNames, T_ARRAY);
|
458
458
|
Check_Type(v_aryNewValues, T_ARRAY);
|
459
|
-
|
459
|
+
|
460
460
|
const long namesCount = RARRAY_LEN(v_aryNames);
|
461
461
|
const long valuesCount = RARRAY_LEN(v_aryNewValues);
|
462
|
-
|
462
|
+
|
463
463
|
if (namesCount != valuesCount) {
|
464
464
|
return raise_invalid_arguments_error();
|
465
465
|
}
|
466
|
-
|
466
|
+
|
467
467
|
int nsIndex = FIX2INT(v_nsIndex);
|
468
|
-
|
468
|
+
|
469
469
|
struct UninitializedClient * uclient;
|
470
470
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
471
471
|
UA_Client *client = uclient->client;
|
472
|
-
|
472
|
+
|
473
473
|
UA_UInt16 nidSize = UA_TYPES[UA_TYPES_NODEID].memSize;
|
474
474
|
UA_UInt16 variantSize = UA_TYPES[UA_TYPES_VARIANT].memSize;
|
475
|
-
|
475
|
+
|
476
476
|
UA_NodeId *nodes = UA_calloc(namesCount, nidSize);
|
477
477
|
UA_Variant *values = UA_calloc(namesCount, variantSize);
|
478
|
-
|
478
|
+
|
479
479
|
for (int i=0; i<namesCount; i++) {
|
480
480
|
VALUE v_name = rb_ary_entry(v_aryNames, i);
|
481
481
|
VALUE v_newValue = rb_ary_entry(v_aryNewValues, i);
|
482
|
-
|
482
|
+
|
483
483
|
if (RB_TYPE_P(v_name, T_STRING) != 1) {
|
484
484
|
return raise_invalid_arguments_error();
|
485
485
|
}
|
486
|
-
|
486
|
+
|
487
487
|
char *name = StringValueCStr(v_name);
|
488
488
|
nodes[i] = UA_NODEID_STRING(nsIndex, name);
|
489
|
-
|
490
|
-
if (uaType ==
|
489
|
+
|
490
|
+
if (uaType == UA_TYPES_UINT16) {
|
491
|
+
Check_Type(v_newValue, T_FIXNUM);
|
492
|
+
UA_UInt16 newValue = NUM2USHORT(v_newValue);
|
493
|
+
values[i].data = UA_malloc(sizeof(UA_UInt16));
|
494
|
+
*(UA_UInt16*)values[i].data = newValue;
|
495
|
+
values[i].type = &UA_TYPES[uaType];
|
496
|
+
} else if (uaType == UA_TYPES_INT16) {
|
491
497
|
Check_Type(v_newValue, T_FIXNUM);
|
492
498
|
UA_Int16 newValue = NUM2SHORT(v_newValue);
|
493
499
|
values[i].data = UA_malloc(sizeof(UA_Int16));
|
494
500
|
*(UA_Int16*)values[i].data = newValue;
|
495
|
-
values[i].type = &UA_TYPES[
|
501
|
+
values[i].type = &UA_TYPES[uaType];
|
502
|
+
} else if (uaType == UA_TYPES_UINT32) {
|
503
|
+
Check_Type(v_newValue, T_FIXNUM);
|
504
|
+
UA_UInt32 newValue = NUM2UINT(v_newValue);
|
505
|
+
values[i].data = UA_malloc(sizeof(UA_UInt32));
|
506
|
+
*(UA_UInt32*)values[i].data = newValue;
|
507
|
+
values[i].type = &UA_TYPES[uaType];
|
496
508
|
} else if (uaType == UA_TYPES_INT32) {
|
497
509
|
Check_Type(v_newValue, T_FIXNUM);
|
498
510
|
UA_Int32 newValue = NUM2INT(v_newValue);
|
499
511
|
values[i].data = UA_malloc(sizeof(UA_Int32));
|
500
512
|
*(UA_Int32*)values[i].data = newValue;
|
501
|
-
values[i].type = &UA_TYPES[
|
513
|
+
values[i].type = &UA_TYPES[uaType];
|
502
514
|
} else if (uaType == UA_TYPES_FLOAT) {
|
503
515
|
Check_Type(v_newValue, T_FLOAT);
|
504
516
|
UA_Float newValue = NUM2DBL(v_newValue);
|
505
517
|
values[i].data = UA_malloc(sizeof(UA_Float));
|
506
518
|
*(UA_Float*)values[i].data = newValue;
|
507
|
-
values[i].type = &UA_TYPES[
|
519
|
+
values[i].type = &UA_TYPES[uaType];
|
508
520
|
} else if (uaType == UA_TYPES_BOOLEAN) {
|
509
521
|
if (RB_TYPE_P(v_newValue, T_TRUE) != 1 && RB_TYPE_P(v_newValue, T_FALSE) != 1) {
|
510
522
|
return raise_invalid_arguments_error();
|
@@ -517,9 +529,9 @@ static VALUE rb_writeUaValues(VALUE self, VALUE v_nsIndex, VALUE v_aryNames, VAL
|
|
517
529
|
rb_raise(cError, "Unsupported type");
|
518
530
|
}
|
519
531
|
}
|
520
|
-
|
532
|
+
|
521
533
|
UA_StatusCode status = multiWrite(client, nodes, values, namesCount);
|
522
|
-
|
534
|
+
|
523
535
|
if (status == UA_STATUSCODE_GOOD) {
|
524
536
|
// printf("%s\n", "value write successful");
|
525
537
|
} else {
|
@@ -529,17 +541,17 @@ static VALUE rb_writeUaValues(VALUE self, VALUE v_nsIndex, VALUE v_aryNames, VAL
|
|
529
541
|
}
|
530
542
|
UA_free(nodes);
|
531
543
|
UA_free(values);
|
532
|
-
|
544
|
+
|
533
545
|
return raise_ua_status_error(status);
|
534
546
|
}
|
535
|
-
|
547
|
+
|
536
548
|
/* Clean up */
|
537
549
|
for (int i=0; i<namesCount; i++) {
|
538
550
|
UA_Variant_deleteMembers(&values[i]);
|
539
551
|
}
|
540
552
|
UA_free(nodes);
|
541
553
|
UA_free(values);
|
542
|
-
|
554
|
+
|
543
555
|
return Qnil;
|
544
556
|
}
|
545
557
|
|
@@ -547,25 +559,25 @@ static VALUE rb_writeUaValue(VALUE self, VALUE v_nsIndex, VALUE v_name, VALUE v_
|
|
547
559
|
if (RB_TYPE_P(v_name, T_STRING) != 1) {
|
548
560
|
return raise_invalid_arguments_error();
|
549
561
|
}
|
550
|
-
|
562
|
+
|
551
563
|
if (RB_TYPE_P(v_nsIndex, T_FIXNUM) != 1) {
|
552
564
|
return raise_invalid_arguments_error();
|
553
565
|
}
|
554
|
-
|
566
|
+
|
555
567
|
if (uaType == UA_TYPES_INT16 && RB_TYPE_P(v_newValue, T_FIXNUM) != 1) {
|
556
568
|
return raise_invalid_arguments_error();
|
557
569
|
}
|
558
|
-
|
570
|
+
|
559
571
|
char *name = StringValueCStr(v_name);
|
560
572
|
int nsIndex = FIX2INT(v_nsIndex);
|
561
|
-
|
573
|
+
|
562
574
|
struct UninitializedClient * uclient;
|
563
575
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
564
576
|
UA_Client *client = uclient->client;
|
565
|
-
|
577
|
+
|
566
578
|
UA_Variant value;
|
567
579
|
UA_Variant_init(&value);
|
568
|
-
|
580
|
+
|
569
581
|
if (uaType == UA_TYPES_INT16) {
|
570
582
|
UA_Int16 newValue = NUM2SHORT(v_newValue);
|
571
583
|
value.data = UA_malloc(sizeof(UA_Int16));
|
@@ -601,7 +613,7 @@ static VALUE rb_writeUaValue(VALUE self, VALUE v_nsIndex, VALUE v_name, VALUE v_
|
|
601
613
|
}
|
602
614
|
|
603
615
|
UA_StatusCode status = UA_Client_writeValueAttribute(client, UA_NODEID_STRING(nsIndex, name), &value);
|
604
|
-
|
616
|
+
|
605
617
|
if (status == UA_STATUSCODE_GOOD) {
|
606
618
|
// printf("%s\n", "value write successful");
|
607
619
|
} else {
|
@@ -609,10 +621,10 @@ static VALUE rb_writeUaValue(VALUE self, VALUE v_nsIndex, VALUE v_name, VALUE v_
|
|
609
621
|
UA_Variant_deleteMembers(&value);
|
610
622
|
return raise_ua_status_error(status);
|
611
623
|
}
|
612
|
-
|
624
|
+
|
613
625
|
/* Clean up */
|
614
626
|
UA_Variant_deleteMembers(&value);
|
615
|
-
|
627
|
+
|
616
628
|
return Qnil;
|
617
629
|
}
|
618
630
|
|
@@ -620,7 +632,9 @@ static VALUE rb_writeUInt16Value(VALUE self, VALUE v_nsIndex, VALUE v_name, VALU
|
|
620
632
|
return rb_writeUaValue(self, v_nsIndex, v_name, v_newValue, UA_TYPES_UINT16);
|
621
633
|
}
|
622
634
|
|
623
|
-
|
635
|
+
static VALUE rb_writeUInt16Values(VALUE self, VALUE v_nsIndex, VALUE v_aryNames, VALUE v_aryNewValues) {
|
636
|
+
return rb_writeUaValues(self, v_nsIndex, v_aryNames, v_aryNewValues, UA_TYPES_UINT16);
|
637
|
+
}
|
624
638
|
|
625
639
|
static VALUE rb_writeInt16Value(VALUE self, VALUE v_nsIndex, VALUE v_name, VALUE v_newValue) {
|
626
640
|
return rb_writeUaValue(self, v_nsIndex, v_name, v_newValue, UA_TYPES_INT16);
|
@@ -642,7 +656,9 @@ static VALUE rb_writeUInt32Value(VALUE self, VALUE v_nsIndex, VALUE v_name, VALU
|
|
642
656
|
return rb_writeUaValue(self, v_nsIndex, v_name, v_newValue, UA_TYPES_UINT32);
|
643
657
|
}
|
644
658
|
|
645
|
-
|
659
|
+
static VALUE rb_writeUInt32Values(VALUE self, VALUE v_nsIndex, VALUE v_aryNames, VALUE v_aryNewValues) {
|
660
|
+
return rb_writeUaValues(self, v_nsIndex, v_aryNames, v_aryNewValues, UA_TYPES_UINT32);
|
661
|
+
}
|
646
662
|
|
647
663
|
static VALUE rb_writeBooleanValue(VALUE self, VALUE v_nsIndex, VALUE v_name, VALUE v_newValue) {
|
648
664
|
return rb_writeUaValue(self, v_nsIndex, v_name, v_newValue, UA_TYPES_BOOLEAN);
|
@@ -664,22 +680,22 @@ static VALUE rb_readUaValue(VALUE self, VALUE v_nsIndex, VALUE v_name, int type)
|
|
664
680
|
if (RB_TYPE_P(v_name, T_STRING) != 1) {
|
665
681
|
return raise_invalid_arguments_error();
|
666
682
|
}
|
667
|
-
|
683
|
+
|
668
684
|
if (RB_TYPE_P(v_nsIndex, T_FIXNUM) != 1) {
|
669
685
|
return raise_invalid_arguments_error();
|
670
686
|
}
|
671
|
-
|
687
|
+
|
672
688
|
char *name = StringValueCStr(v_name);
|
673
689
|
int nsIndex = FIX2INT(v_nsIndex);
|
674
|
-
|
690
|
+
|
675
691
|
struct UninitializedClient * uclient;
|
676
692
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
677
693
|
UA_Client *client = uclient->client;
|
678
|
-
|
694
|
+
|
679
695
|
UA_Variant value;
|
680
696
|
UA_Variant_init(&value);
|
681
697
|
UA_StatusCode status = UA_Client_readValueAttribute(client, UA_NODEID_STRING(nsIndex, name), &value);
|
682
|
-
|
698
|
+
|
683
699
|
if (status == UA_STATUSCODE_GOOD) {
|
684
700
|
// printf("%s\n", "value read successful");
|
685
701
|
} else {
|
@@ -687,9 +703,9 @@ static VALUE rb_readUaValue(VALUE self, VALUE v_nsIndex, VALUE v_name, int type)
|
|
687
703
|
UA_Variant_deleteMembers(&value);
|
688
704
|
return raise_ua_status_error(status);
|
689
705
|
}
|
690
|
-
|
706
|
+
|
691
707
|
VALUE result = Qnil;
|
692
|
-
|
708
|
+
|
693
709
|
if (type == UA_TYPES_INT16 && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_INT16])) {
|
694
710
|
UA_Int16 val =*(UA_Int16*)value.data;
|
695
711
|
// printf("the value is: %i\n", val);
|
@@ -714,10 +730,10 @@ static VALUE rb_readUaValue(VALUE self, VALUE v_nsIndex, VALUE v_name, int type)
|
|
714
730
|
rb_raise(cError, "UA type mismatch");
|
715
731
|
return Qnil;
|
716
732
|
}
|
717
|
-
|
733
|
+
|
718
734
|
/* Clean up */
|
719
735
|
UA_Variant_deleteMembers(&value);
|
720
|
-
|
736
|
+
|
721
737
|
return result;
|
722
738
|
}
|
723
739
|
|
@@ -759,7 +775,7 @@ static VALUE rb_run_single_monitoring_cycle(VALUE self) {
|
|
759
775
|
struct UninitializedClient * uclient;
|
760
776
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
761
777
|
UA_Client *client = uclient->client;
|
762
|
-
|
778
|
+
|
763
779
|
UA_StatusCode status = UA_Client_runAsync(client, 1000);
|
764
780
|
return UINT2NUM(status);
|
765
781
|
}
|
@@ -768,13 +784,13 @@ static VALUE rb_run_single_monitoring_cycle_bang(VALUE self) {
|
|
768
784
|
struct UninitializedClient * uclient;
|
769
785
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
770
786
|
UA_Client *client = uclient->client;
|
771
|
-
|
787
|
+
|
772
788
|
UA_StatusCode status = UA_Client_runAsync(client, 1000);
|
773
|
-
|
789
|
+
|
774
790
|
if (status != UA_STATUSCODE_GOOD) {
|
775
791
|
return raise_ua_status_error(status);
|
776
792
|
}
|
777
|
-
|
793
|
+
|
778
794
|
return Qnil;
|
779
795
|
}
|
780
796
|
|
@@ -782,7 +798,7 @@ static VALUE rb_state(VALUE self) {
|
|
782
798
|
struct UninitializedClient * uclient;
|
783
799
|
TypedData_Get_Struct(self, struct UninitializedClient, &UA_Client_Type, uclient);
|
784
800
|
UA_Client *client = uclient->client;
|
785
|
-
|
801
|
+
|
786
802
|
UA_ClientState state = UA_Client_getState(client);
|
787
803
|
return INT2NUM(state);
|
788
804
|
}
|
@@ -800,29 +816,32 @@ void Init_opcua_client()
|
|
800
816
|
#ifdef UA_ENABLE_SUBSCRIPTIONS
|
801
817
|
// printf("%s\n", "ok! opcua-client-ruby built with subscriptions enabled.");
|
802
818
|
#endif
|
803
|
-
|
819
|
+
|
804
820
|
mOPCUAClient = rb_const_get(rb_cObject, rb_intern("OPCUAClient"));
|
821
|
+
rb_global_variable(&mOPCUAClient);
|
805
822
|
defineStateContants(mOPCUAClient);
|
806
|
-
|
823
|
+
|
807
824
|
cError = rb_define_class_under(mOPCUAClient, "Error", rb_eStandardError);
|
825
|
+
rb_global_variable(&cError);
|
808
826
|
cClient = rb_define_class_under(mOPCUAClient, "Client", rb_cObject);
|
809
|
-
|
827
|
+
rb_global_variable(&cClient);
|
828
|
+
|
810
829
|
rb_define_alloc_func(cClient, allocate);
|
811
|
-
|
830
|
+
|
812
831
|
rb_define_method(cClient, "initialize", rb_initialize, 0);
|
813
|
-
|
832
|
+
|
814
833
|
rb_define_method(cClient, "run_single_monitoring_cycle", rb_run_single_monitoring_cycle, 0);
|
815
834
|
rb_define_method(cClient, "run_mon_cycle", rb_run_single_monitoring_cycle, 0);
|
816
835
|
rb_define_method(cClient, "do_mon_cycle", rb_run_single_monitoring_cycle, 0);
|
817
|
-
|
836
|
+
|
818
837
|
rb_define_method(cClient, "run_single_monitoring_cycle!", rb_run_single_monitoring_cycle_bang, 0);
|
819
838
|
rb_define_method(cClient, "run_mon_cycle!", rb_run_single_monitoring_cycle_bang, 0);
|
820
839
|
rb_define_method(cClient, "do_mon_cycle!", rb_run_single_monitoring_cycle_bang, 0);
|
821
|
-
|
840
|
+
|
822
841
|
rb_define_method(cClient, "connect", rb_connect, 1);
|
823
842
|
rb_define_method(cClient, "disconnect", rb_disconnect, 0);
|
824
843
|
rb_define_method(cClient, "state", rb_state, 0);
|
825
|
-
|
844
|
+
|
826
845
|
rb_define_method(cClient, "read_int16", rb_readInt16Value, 2);
|
827
846
|
rb_define_method(cClient, "read_uint16", rb_readUInt16Value, 2);
|
828
847
|
rb_define_method(cClient, "read_int32", rb_readInt32Value, 2);
|
@@ -830,7 +849,7 @@ void Init_opcua_client()
|
|
830
849
|
rb_define_method(cClient, "read_float", rb_readFloatValue, 2);
|
831
850
|
rb_define_method(cClient, "read_boolean", rb_readBooleanValue, 2);
|
832
851
|
rb_define_method(cClient, "read_bool", rb_readBooleanValue, 2);
|
833
|
-
|
852
|
+
|
834
853
|
rb_define_method(cClient, "write_int16", rb_writeInt16Value, 3);
|
835
854
|
rb_define_method(cClient, "write_uint16", rb_writeUInt16Value, 3);
|
836
855
|
rb_define_method(cClient, "write_int32", rb_writeInt32Value, 3);
|
@@ -838,15 +857,17 @@ void Init_opcua_client()
|
|
838
857
|
rb_define_method(cClient, "write_float", rb_writeFloatValue, 3);
|
839
858
|
rb_define_method(cClient, "write_boolean", rb_writeBooleanValue, 3);
|
840
859
|
rb_define_method(cClient, "write_bool", rb_writeBooleanValue, 3);
|
841
|
-
|
860
|
+
|
842
861
|
rb_define_method(cClient, "multi_write_int16", rb_writeInt16Values, 3);
|
862
|
+
rb_define_method(cClient, "multi_write_uint16", rb_writeUInt16Values, 3);
|
843
863
|
rb_define_method(cClient, "multi_write_int32", rb_writeInt32Values, 3);
|
864
|
+
rb_define_method(cClient, "multi_write_uint32", rb_writeUInt32Values, 3);
|
844
865
|
rb_define_method(cClient, "multi_write_float", rb_writeFloatValues, 3);
|
845
866
|
rb_define_method(cClient, "multi_write_boolean", rb_writeBooleanValues, 3);
|
846
867
|
rb_define_method(cClient, "multi_write_bool", rb_writeBooleanValues, 3);
|
847
|
-
|
868
|
+
|
848
869
|
rb_define_method(cClient, "multi_read", rb_readUaValues, 2);
|
849
|
-
|
870
|
+
|
850
871
|
rb_define_method(cClient, "create_subscription", rb_createSubscription, 0);
|
851
872
|
rb_define_method(cClient, "add_monitored_item", rb_addMonitoredItem, 3);
|
852
873
|
|
data/lib/opcua_client/version.rb
CHANGED
data/spec/core_spec.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe OPCUAClient::Client do
|
4
|
+
context "unconnected" do
|
5
|
+
it "allows disconnect for unconnected clients" do
|
6
|
+
result = new_client(connect: false).disconnect
|
7
|
+
expect(result).to eq(0)
|
8
|
+
end
|
9
|
+
|
10
|
+
it "returns 0 state" do
|
11
|
+
state = new_client(connect: false).state
|
12
|
+
expect(state).to eq(0)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'opcua_client'
|
3
|
+
|
4
|
+
# https://github.com/brianmario/mysql2/commit/0ee20536501848a354f1c3a007333167120c7457
|
5
|
+
if GC.respond_to?(:verify_compaction_references)
|
6
|
+
# This method was added in Ruby 3.0.0. Calling it this way asks the GC to
|
7
|
+
# move objects around, helping to find object movement bugs.
|
8
|
+
GC.verify_compaction_references(double_heap: true, toward: :empty)
|
9
|
+
end
|
10
|
+
|
11
|
+
def new_client(connect: true)
|
12
|
+
client = OPCUAClient::Client.new
|
13
|
+
|
14
|
+
if connect
|
15
|
+
# TODO
|
16
|
+
end
|
17
|
+
|
18
|
+
client
|
19
|
+
end
|
20
|
+
|
21
|
+
RSpec.configure do |config|
|
22
|
+
end
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: opcua_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ritvars Rundzans
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
13
|
+
description:
|
14
14
|
email:
|
15
15
|
- ritvars.rundzans@makit.lv
|
16
16
|
executables: []
|
@@ -26,11 +26,13 @@ files:
|
|
26
26
|
- lib/opcua_client.rb
|
27
27
|
- lib/opcua_client/client.rb
|
28
28
|
- lib/opcua_client/version.rb
|
29
|
+
- spec/core_spec.rb
|
30
|
+
- spec/spec_helper.rb
|
29
31
|
homepage: https://github.com/mak-it/opcua-client-ruby
|
30
32
|
licenses:
|
31
33
|
- MIT
|
32
34
|
metadata: {}
|
33
|
-
post_install_message:
|
35
|
+
post_install_message:
|
34
36
|
rdoc_options:
|
35
37
|
- "--charset=UTF-8"
|
36
38
|
require_paths:
|
@@ -46,10 +48,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
48
|
- !ruby/object:Gem::Version
|
47
49
|
version: '0'
|
48
50
|
requirements: []
|
49
|
-
|
50
|
-
|
51
|
-
signing_key:
|
51
|
+
rubygems_version: 3.3.7
|
52
|
+
signing_key:
|
52
53
|
specification_version: 4
|
53
54
|
summary: Basic OPC-UA client library for Ruby. Uses open62541 (https://open62541.org)
|
54
55
|
under the hood.
|
55
|
-
test_files:
|
56
|
+
test_files:
|
57
|
+
- spec/core_spec.rb
|
58
|
+
- spec/spec_helper.rb
|