opcua_client 0.0.4 → 0.0.5

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