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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93f9f3a4464f5273ae87fb6e0d3c75ec902db88a659e48d40dd6da7bd75b0168
4
- data.tar.gz: bff1a7a86aca728eb200099d62d594b79cc7ae4800c4337ab0b8370e50c6d435
3
+ metadata.gz: ae12c435f33aa7b57384562385a22dafb562fe3620573697b5d3bc57e1e58798
4
+ data.tar.gz: 29ac2ff75077f30b9c614103033c43e53186db66a428352f2bdfc369203715ae
5
5
  SHA512:
6
- metadata.gz: 83ed7b449e8e3203750d48a8e1352035931f35515d91d85c2be52ebc38b5293a04444c449d25ec850026c8edfd3184ef1f83cd2a8ab632b8718f85f5ba62815f
7
- data.tar.gz: 2fa13a2d7d99b6534a6db547b8a0419a1bd1b5488b881d80d1b2d3d9537628c5c97257aa56420cc0d739cbe47d16e74b0bd52d01f05d91562f4e735357439267
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
+ ![ci-badge](https://github.com/mak-it/opcua-client-ruby/actions/workflows/build.yml/badge.svg)
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 == UA_TYPES_INT16) {
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[UA_TYPES_INT16];
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[UA_TYPES_INT32];
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[UA_TYPES_FLOAT];
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
- // TODO: multi UINT16
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
- // TODO: multi UINT32
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
 
@@ -1,3 +1,3 @@
1
1
  module OPCUAClient
2
- VERSION = "0.0.4".freeze
2
+ VERSION = "0.0.5".freeze
3
3
  end
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
@@ -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
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: 2020-03-13 00:00:00.000000000 Z
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
- rubyforge_project:
50
- rubygems_version: 2.7.9
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