Check-in [416a0ae7c1]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:more changes in topcua for type handling
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 416a0ae7c1fc7fafd90830dca5d13f5c3ad51dd6
User & Date: chw 2020-07-30 04:58:24
Context
2020-08-02
16:54
heavy refactoring in topcua incl. beginning of a nodeset loader check-in: 01b7d7cc94 user: chw tags: trunk
2020-07-30
04:58
more changes in topcua for type handling check-in: 416a0ae7c1 user: chw tags: trunk
2020-07-28
04:39
update topcua for some open62541 1.x diffs in type handling etc. check-in: 15e21d4fcc user: chw tags: trunk
Changes

Changes to jni/topcua/doc/opcua.n.

484
485
486
487
488
489
490
491
492
493
494
495
496
497


498
499
500
501
502
503
504
505
\fBopcua endpoints\fR ?\fIurl\fR?
.
Queries the local OPC/UA server \fBopc.tcp://localhost:4840\fR or the server
specified by the \fIurl\fR parameter for endpoints and returns a list of
deserialized dictionaries based on the \fBUA_EndpointDescription\fR structure.
Consult the \fBopen62541\fR documentation for more information.
.TP
\fBopcua genstubs\fR \fIhandle\fR ?\fIstrip ...\fR?
.
Generates stubs for methods in the handle specific address space derived
from the client or server object \fIhandle\fR. The address space
is traversed and browse paths and node class paths are accumulated. The
resulting browse paths optionally get the prefix \fIstrip\fR stripped off
from the beginning and optionally filtered using the glob patterns following


the \fIstrip\fR parameter. For all nodes matching the node class
path pattern \fIObject/Method\fR the optional \fIInputArguments\fR and
\fIOutputArguments\fR child nodes are retrieved and stub procedures
are written using the browse path and argument information.
.TP
\fBopcua gentypes\fR \fIhandle\fR
.
Generates custom data type mappings using information obtained from analyzing







|






>
>
|







484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
\fBopcua endpoints\fR ?\fIurl\fR?
.
Queries the local OPC/UA server \fBopc.tcp://localhost:4840\fR or the server
specified by the \fIurl\fR parameter for endpoints and returns a list of
deserialized dictionaries based on the \fBUA_EndpointDescription\fR structure.
Consult the \fBopen62541\fR documentation for more information.
.TP
\fBopcua genstubs\fR \fIhandle\fR ?\fIstrip substs ...\fR?
.
Generates stubs for methods in the handle specific address space derived
from the client or server object \fIhandle\fR. The address space
is traversed and browse paths and node class paths are accumulated. The
resulting browse paths optionally get the prefix \fIstrip\fR stripped off
from the beginning and optionally filtered using the glob patterns following
the \fIsubsts\fR parameter. If \fIsubsts\fR is not empty it specifies 
pairwise regexps and substitutions which are applied on the browse paths
for the final procedure names. For all nodes matching the node class
path pattern \fIObject/Method\fR the optional \fIInputArguments\fR and
\fIOutputArguments\fR child nodes are retrieved and stub procedures
are written using the browse path and argument information.
.TP
\fBopcua gentypes\fR \fIhandle\fR
.
Generates custom data type mappings using information obtained from analyzing

Changes to jni/topcua/library/topcua.tcl.

152
153
154
155
156
157
158


159
160
161
162
163
164
165
166
167
...
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
...
197
198
199
200
201
202
203
204
205





206
207
208
209
210
211
212
...
250
251
252
253
254
255
256




257
258



259
260
261
262
263
264
265
...
284
285
286
287
288
289
290



291
292
293
294
295
296





297
298
299
300
301
302
303
...
308
309
310
311
312
313
314





315
316
317
318
319
320
321
322
323
324
325
326
327
328




329
330
331
332

333

334
335
336
337
338
339
340
	    break
	}
	return $ret
    }

    # Generate stubs for methods in sub-namespace derived from
    # client or server name. Argument "strip" is cut off from the begin


    # of browse paths, all following arguments are glob patterns for
    # matching in browse paths. The entire address space is processed
    # by using the root node as base for retrieving the tree of names.
    # Example:
    #
    #   opcua::new client Pumps
    #   opcua::connect Pumps ...
    #   opcua::genstubs Pumps /Root/Objects/2:Pumps/2:
    #
................................................................................
    # each having a Start and a Stop method with zero input arguments):
    #
    #   proc opcua::Pumps::Pump_1/Start {} ...
    #   proc opcua::Pumps::Pump_1/Stop {} ...
    #   proc opcua::Pumps::Pump_2/Start {} ...
    #   proc opcua::Pumps::Pump_2/Stop {} ...

    proc genstubs {handle {strip {}} args} {
	::namespace eval ::opcua::$handle {}
	set all [expr {[llength $args] == 0}]
	set root [root]
	if {$all && ([string first /Root/Objects $strip] == 0)} {
	    # speed up for common place
	    set strip [string range $strip 5 end]
	    set root [lindex [translate $handle $root / Objects] 0]
................................................................................
		    if {[string match $pat $b]} {
			set found 1
			break
		    }
		}
	    }
	    if {$found} {
		# procname will be method path with prefix stripped
		set b [string range $b [string length $strip] end]





		# the opcua::call object is the method's parent
		set o [parent $handle $n]
		# parameter list for proc
		set plist {}
		# arguments (type and name pairs) for opcua::call
		set alist {}
		if {![catch {translate $handle $n / InputArguments} ia]} {
................................................................................
    #   namespaces  { prefix uri ... }
    #   enums       { enumname { bit-width itemname value ... } ... }
    #   structs     { structname { type fieldname ... } ... }
    #
    # This function needs tDOM for XML processing!

    proc _readbsd {string} {




	package require tdom
	set doc [dom parse $string]



	set root [$doc documentElement]
	# process namespaces
	array set ns {}
	foreach attr [$root attributes] {
	    if {[llength $attr] == 3} {
		lassign $attr loc pfx uri
	    }
................................................................................
		# must be ua:ExtensionObject, otherwise skip
		if {[$struct getAttribute BaseType] ne "ua:ExtensionObject"} {
		    continue
		}
	    }
	    set stf {}
	    foreach field [$struct childNodes] {



		if {[$field hasAttribute SwitchField]} {
		    # cannot process this struct
		    set stf {}
		    break
		}
		# TBD: handle attribute LengthField





		set nstype [$field getAttribute TypeName]
		lassign [split $nstype :] fns type
		if {$type ne ""} {
		    switch -- $fns {
			opc - ua {
			    lappend stf $type
			}
................................................................................
			    # cannot process this struct
			    set stf {}
			    break
			}
		    }
		} else {
		    lappend stf $nstype





		}
		lappend stf [$field getAttribute Name]
	    }
	    if {[llength $stf]} {
		set st([$struct getAttribute Name]) $stf
	    }
	}
	# structs need further checking later
	set stl [array names st]
	# process enums
	array set en {}
	foreach enum [$typedict selectNodes opc:EnumeratedType] {
	    set enf {}
	    foreach value [$enum childNodes] {




		lappend enf \
		    [$value getAttribute Name] \
		    [$value getAttribute Value]
	    }

	    if {[llength $enf]} {

		set en([$enum getAttribute Name]) \
		    [concat [$enum getAttribute LengthInBits] $enf]
	    }
	}
	dict set out enums [array get en]
	set enl [array names en]
	# cross check type names in structs







>
>
|
|







 







|







 







|

>
>
>
>
>







 







>
>
>
>
|
|
>
>
>







 







>
>
>






>
>
>
>
>







 







>
>
>
>
>



|










>
>
>
>
|
|
|
|
>
|
>







152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
...
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
...
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
...
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
...
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
	    break
	}
	return $ret
    }

    # Generate stubs for methods in sub-namespace derived from
    # client or server name. Argument "strip" is cut off from the begin
    # of browse paths. Argument "substs" is a list of regexp/substitution
    # elements which are applied in order on the browse path. All following
    # arguments are glob patterns for matching in browse paths before the
    # substitutions are applied. The entire address space is processed
    # by using the root node as base for retrieving the tree of names.
    # Example:
    #
    #   opcua::new client Pumps
    #   opcua::connect Pumps ...
    #   opcua::genstubs Pumps /Root/Objects/2:Pumps/2:
    #
................................................................................
    # each having a Start and a Stop method with zero input arguments):
    #
    #   proc opcua::Pumps::Pump_1/Start {} ...
    #   proc opcua::Pumps::Pump_1/Stop {} ...
    #   proc opcua::Pumps::Pump_2/Start {} ...
    #   proc opcua::Pumps::Pump_2/Stop {} ...

    proc genstubs {handle {strip {}} {substs {}} args} {
	::namespace eval ::opcua::$handle {}
	set all [expr {[llength $args] == 0}]
	set root [root]
	if {$all && ([string first /Root/Objects $strip] == 0)} {
	    # speed up for common place
	    set strip [string range $strip 5 end]
	    set root [lindex [translate $handle $root / Objects] 0]
................................................................................
		    if {[string match $pat $b]} {
			set found 1
			break
		    }
		}
	    }
	    if {$found} {
		# procname will be method path with prefix stripped ...
		set b [string range $b [string length $strip] end]
		# ... and mangled by substitutions
		set st {}
		foreach {re st} $substs {
		    regsub -all -- $re $b $st b
		}
		# the opcua::call object is the method's parent
		set o [parent $handle $n]
		# parameter list for proc
		set plist {}
		# arguments (type and name pairs) for opcua::call
		set alist {}
		if {![catch {translate $handle $n / InputArguments} ia]} {
................................................................................
    #   namespaces  { prefix uri ... }
    #   enums       { enumname { bit-width itemname value ... } ... }
    #   structs     { structname { type fieldname ... } ... }
    #
    # This function needs tDOM for XML processing!

    proc _readbsd {string} {
	dict set out namespaces {}
	dict set out enums {}
	dict set out structs {}
	if {[catch {
	    package require tdom
	    set doc [dom parse $string]
	}]} {
	    return $out
	}
	set root [$doc documentElement]
	# process namespaces
	array set ns {}
	foreach attr [$root attributes] {
	    if {[llength $attr] == 3} {
		lassign $attr loc pfx uri
	    }
................................................................................
		# must be ua:ExtensionObject, otherwise skip
		if {[$struct getAttribute BaseType] ne "ua:ExtensionObject"} {
		    continue
		}
	    }
	    set stf {}
	    foreach field [$struct childNodes] {
		if {[$field nodeName] ne "opc:Field"} {
		    continue
		}
		if {[$field hasAttribute SwitchField]} {
		    # cannot process this struct
		    set stf {}
		    break
		}
		# TBD: handle attribute LengthField
		if {![$field hasAttribute TypeName]} {
		    # cannot process this struct
		    set stf {}
		    break
		}
		set nstype [$field getAttribute TypeName]
		lassign [split $nstype :] fns type
		if {$type ne ""} {
		    switch -- $fns {
			opc - ua {
			    lappend stf $type
			}
................................................................................
			    # cannot process this struct
			    set stf {}
			    break
			}
		    }
		} else {
		    lappend stf $nstype
		}
		if {![$field hasAttribute Name]} {
		    # cannot process this struct
		    set stf {}
		    break
		}
		lappend stf [$field getAttribute Name]
	    }
	    if {[llength $stf] && [$struct hasAttribute Name]} {
		set st([$struct getAttribute Name]) $stf
	    }
	}
	# structs need further checking later
	set stl [array names st]
	# process enums
	array set en {}
	foreach enum [$typedict selectNodes opc:EnumeratedType] {
	    set enf {}
	    foreach value [$enum childNodes] {
		if {[$value nodeName] ne "opc:EnumeratedValue"} {
		    continue
		}
		if {[$value hasAttribute Name] && [$value hasAttribute Value]} {
		    lappend enf \
			[$value getAttribute Name] \
			[$value getAttribute Value]
		}
	    }
	    if {[llength $enf] && [$enum hasAttribute Name] &&
		[$enum hasAttribute LengthInBits]} {
		set en([$enum getAttribute Name]) \
		    [concat [$enum getAttribute LengthInBits] $enf]
	    }
	}
	dict set out enums [array get en]
	set enl [array names en]
	# cross check type names in structs

Changes to jni/topcua/topcua.c.

164
165
166
167
168
169
170
171

172
173
174
175
176
177
178
....
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
....
1157
1158
1159
1160
1161
1162
1163




1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175

1176
1177
1178
1179
1180
1181
1182
....
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
....
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
....
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
....
3196
3197
3198
3199
3200
3201
3202













3203
3204
3205
3206
3207
3208
3209
....
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
....
5553
5554
5555
5556
5557
5558
5559
5560
5561
5562
5563
5564
5565
5566
5567
....
5644
5645
5646
5647
5648
5649
5650
5651
5652
5653
5654
5655
5656
5657
5658
....
6572
6573
6574
6575
6576
6577
6578
6579
6580
6581
6582
6583
6584
6585
6586
....
7669
7670
7671
7672
7673
7674
7675
7676
7677
7678
7679
7680
7681
7682
7683
....
7798
7799
7800
7801
7802
7803
7804
7805
7806
7807
7808
7809
7810
7811

7812




7813
7814
7815
7816
7817
7818
7819
....
7837
7838
7839
7840
7841
7842
7843
7844
7845
7846
7847
7848
7849
7850
7851
7852



7853
7854
7855
7856
7857





7858
7859
7860
7861
7862
7863
7864
....
7867
7868
7869
7870
7871
7872
7873
7874
7875
7876
7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
7891
7892
7893
7894
7895
7896
7897
7898
7899
7900

7901
7902
7903
7904
7905
7906
7907
....
7935
7936
7937
7938
7939
7940
7941
7942
7943
7944

7945
7946
7947
7948
7949
7950
7951
7952
7953
7954
7955
7956
7957
7958
7959
7960
7961
7962
7963
7964
7965
7966
7967
7968
7969
7970
7971
7972
7973
				    void *ctx,
				    UA_StatusChangeNotification *ntf);
static void		ReleaseSubs(UAH *uah);
static void		ReleaseMethodsEtc(UAH *uah, int killProcs);
static void		ReleaseTypes(UAH *uah, int all);
static void		DisconnectClient(UAH *uah);
static void		FreeHandle(char *clientData);
static const UA_DataType *FindType(UAI *uai, UAH *uah, const char *name);

static UA_NodeId *	ParseNodeId(Tcl_Interp *interp, UA_NodeId *nodein,
				    const char *string);
static UA_NodeId *	ParseTypeOrNodeId(Tcl_Interp *interp,
					  UAH *uah, UA_NodeId *nodein,
					  const char *string);
static UA_NodeId *	ParseRefTypeOrNodeId(Tcl_Interp *interp,
					     UAI *uai, UA_NodeId *nodein,
................................................................................
 * Side effects:
 *	None.
 *
 *-------------------------------------------------------------------------
 */

static const UA_DataType *
FindType(UAI *uai, UAH *uah, const char *name)
{
    size_t j;
    Tcl_HashEntry *hPtr;
    const UA_DataType *type = NULL;

    hPtr = Tcl_FindHashEntry(&uai->types, name);
    if (hPtr != NULL) {
................................................................................
		break;
	    }
	}
	if ((type == NULL) && (uah != NULL)) {
	    size_t typesSize;
	    UA_DataType *types;





#if (UA_OPEN62541_VER_MAJOR < 1)
	    if (uah->client != NULL) {
		UA_Client_getCustomDataTypes(uah->client,
					     &types, &typesSize);
	    } else {
		UA_Server_getCustomDataTypes(uah->server,
					     &types, &typesSize);
	    }
#else
	    types = (UA_DataType *) uah->typesArray.types;
	    typesSize = uah->typesArray.typesSize;
#endif

	    for (j = 0; j < typesSize; j++) {
		if ((types[j].typeName != NULL) &&
		    (strcmp(types[j].typeName, name) == 0)) {
		    type = &types[j];
		    break;
		}
	    }
................................................................................

static UA_NodeId *
ParseTypeOrNodeId(Tcl_Interp *interp, UAH *uah, UA_NodeId *nodein,
		  const char *string)
{
    if ((strchr(string, ';') == NULL) &&
	(strchr(string, '=') == NULL)) {
	const UA_DataType *type = FindType(uah->uai, uah, string);

	if (type != NULL) {
	    *nodein = type->typeId;
	    return nodein;
	}
    }
    return ParseNodeId(interp, nodein, string);
................................................................................
	errstr = "no result buffer";
	goto error;
    }
    namein->name = UA_STRING_NULL;
    if (len > 2) {
	char *p = NULL, *q = strchr(string, ':');

	if (q > string) {
	    nsindex = strtol(string, &p, 10);
	    if ((p == q) && (p - string < len)) {
		string = p + 1;
	    } else {
		nsindex = 0;
	    }
	}
................................................................................
	}
	if (n == 1) {
	    Tcl_SetResult(interp, "zero, two, or more elements required",
			  TCL_STATIC);
	    goto error;
	}
	if (n) {
	    vtype = FindType(uai, uah, Tcl_GetString(elem[0]));
	    if ((vtype == NULL) || (vtype == &UA_TYPES[UA_TYPES_VARIANT])) {
		Tcl_SetResult(interp, "unsupported type", TCL_STATIC);
		goto error;
	    }
	    /* Now skip type element. */
	    elem += 1;
	    n -= 1;
................................................................................
		    value = NULL;
		    goto error;
		}
		UA_delete(subval, subtype);
		data = (char *) data + subtype->memSize;
	    }
	}













    } else {
	Tcl_SetResult(interp, "unsupported type", TCL_STATIC);
    }
error:
    return value;
}
 
................................................................................
	    goto error;
	} else if (n < 2) {
	    Tcl_SetResult(interp, "need two or more elements, type and data",
			  TCL_STATIC);
	    ret = TCL_ERROR;
	    goto error;
	}
	type = FindType(uai, uah, Tcl_GetString(elem[0]));
	if (type == NULL) {
	    Tcl_SetResult(interp, "unsupported type", TCL_STATIC);
	    ret = TCL_ERROR;
	    goto error;
	}
	if (n > 2) {
	    Tcl_SetObjResult(interp, Tcl_NewListObj(n - 1, &elem[1]));
................................................................................
	attr += 1;
	idx = 4;
    } else {
	/* By default, write the "value" attribute. */
	attr = UA_ATTRIBUTEID_VALUE;
	idx = 3;
    }
    type = FindType(uai, uah, Tcl_GetString(objv[idx]));
    if (type == NULL) {
	Tcl_SetResult(interp, "unsupported type", TCL_STATIC);
	goto error;
    }
    idx++;
    value = EncodeFromObj(interp, uai, uah, type, NULL, objv[idx], 0);
    if (value == NULL) {
................................................................................
    }
    nInArgs = (objc - 4) / 2;
    inArgs = (UA_Variant *) UA_Array_new(nInArgs, &UA_TYPES[UA_TYPES_VARIANT]);
    for (i = 0; i < nInArgs; i++) {
	int idx = i * 2 + 4;
	void *value;

	type = FindType(uai, uah, Tcl_GetString(objv[idx]));
	if (type == NULL) {
	    Tcl_SetResult(interp, "unsupported type", TCL_STATIC);
	    goto argsError;
	}
	idx++;
	if (objv[idx]->typePtr == uai->listObjType)  {
	    int j, n;
................................................................................
{
    UAI *uai = (UAI *) clientData;
    UAH *uah;
    Tcl_HashEntry *hPtr;
    UA_StatusCode uaret;
    int flag;

    if (objc > 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "handle");
	return TCL_ERROR;
    }
    hPtr = Tcl_FindHashEntry(&uai->handles, Tcl_GetString(objv[1]));
    if (hPtr == NULL) {
	Tcl_SetResult(interp, "handle not found", TCL_STATIC);
	ReportError(interp, uai, NULL, 0);
................................................................................
{
    UAI *uai = (UAI *) clientData;
    UAH *uah;
    Tcl_HashEntry *hPtr;
    UA_NodeId nodeid, encid;
    int cmd, i, j;
    Tcl_Obj *obj;
    UA_DataType *type;
    size_t max;
    static const char *cmdNames[] = {
	"begin", "commit", "enum", "struct", NULL
    };
    enum cmdTag {
	CMD_begin, CMD_commit, CMD_enum, CMD_struct
    };
................................................................................
	}
	if ((nodeid.identifierType != UA_NODEIDTYPE_NUMERIC) ||
	    (encid.identifierType != UA_NODEIDTYPE_NUMERIC)) {
	    Tcl_SetResult(interp, "need numeric node identifier", TCL_STATIC);
	    ReportError(interp, uai, NULL, 0);
	    return TCL_ERROR;
	}
	hPtr = Tcl_FindHashEntry(&uai->types, Tcl_GetString(objv[6]));
	if (hPtr == NULL) {
	    Tcl_SetResult(interp, "unsupported type for enum", TCL_STATIC);
	    ReportError(interp, uai, NULL, 0);
	    return TCL_ERROR;
	}
	type = (UA_DataType *) Tcl_GetHashValue(hPtr);

	if ((type->membersSize != 1) || !type->pointerFree) {




	    Tcl_SetResult(interp, "unsupported type for enum", TCL_STATIC);
	    ReportError(interp, uai, NULL, 0);
	    return TCL_ERROR;
	}
	max = nodeid.identifier.numeric;
	if (max < encid.identifier.numeric) {
	    max = encid.identifier.numeric;
................................................................................
	}
	obj = GetSymObj(uai, Tcl_GetString(objv[3]),
			(char **) &uah->types[i].typeName);
	Tcl_IncrRefCount(obj);
	uah->types[i].typeId = nodeid;
	uah->types[i].memSize = type->memSize;
	uah->types[i].typeIndex = i;
	uah->types[i].membersSize = 1;
#if (UA_OPEN62541_VER_MAJOR < 1)
	uah->types[i].builtin = 0;
#else
	uah->types[i].typeKind = UA_DATATYPEKIND_ENUM;
#endif
	uah->types[i].pointerFree = 1;
	uah->types[i].overlayable = UA_BINARY_OVERLAYABLE_INTEGER;
	uah->types[i].binaryEncodingId = 0;



	uah->types[i].members = ckalloc(sizeof(UA_DataTypeMember));
	uah->types[i].members[0] = type->members[0];
	obj = GetSymObj(uai, "",
			(char **) &uah->types[i].members[0].memberName);
	Tcl_IncrRefCount(obj);





	break;

    case CMD_struct:
	if ((objc < 8) || (objc % 2 != 0)) {
	    Tcl_WrongNumArgs(interp, 1, objv,
		"handle struct name nodeid encid type member ...");
	    return TCL_ERROR;
................................................................................
	    return TCL_ERROR;
	}
	if (ParseNodeId(interp, &encid, Tcl_GetString(objv[5])) == NULL) {
	    return TCL_ERROR;
	}
	/* Validate member types. */
	for (i = 6; i < objc; i += 2) {
	    char *name = Tcl_GetString(objv[i]);

	    hPtr = Tcl_FindHashEntry(&uai->types, name);
	    if (hPtr != NULL) {
		continue;
	    }
	    for (j = 0; j < UA_TYPES_COUNT; j++) {
		if ((UA_TYPES[j].typeName != NULL) &&
		    (strcmp(name, UA_TYPES[j].typeName) == 0)) {
		    break;
		}
	    }
	    if (j < UA_TYPES_COUNT) {
		continue;
	    }
	    for (j = 0; j < uah->typesSize; j++) {
		if ((uah->types[j].typeName != NULL) &&
		    (strcmp(name, uah->types[j].typeName) == 0)) {
		    break;
		}
	    }
	    if (j < uah->typesSize) {
		continue;
	    }
	    Tcl_SetResult(interp, "unknown type", TCL_STATIC);
	    ReportError(interp, uai, NULL, 0);
	    return TCL_ERROR;

	}
	max = nodeid.identifier.numeric;
	if (max < encid.identifier.numeric) {
	    max = encid.identifier.numeric;
	}
	max += 64;
	if (uah->types == NULL) {
................................................................................
	uah->types[i].pointerFree = 1;
	uah->types[i].overlayable = 0;
	uah->types[i].binaryEncodingId = encid.identifier.numeric;
	j = sizeof(UA_DataTypeMember) * uah->types[i].membersSize;
	uah->types[i].members = ckalloc(j);
	memset(uah->types[i].members, 0, j);
	for (j = 6; j < objc; j += 2) {
	    int k, isZero = 1;
	    char *name = Tcl_GetString(objv[j]);


	    type = NULL;
	    hPtr = Tcl_FindHashEntry(&uai->types, name);
	    if (hPtr != NULL) {
		type = (UA_DataType *) Tcl_GetHashValue(hPtr);
	    } else {
		for (k = 0; k < UA_TYPES_COUNT; k++) {
		    if ((UA_TYPES[k].typeName != NULL) &&
			(strcmp(name, UA_TYPES[k].typeName) == 0)) {
			type = (UA_DataType *) &UA_TYPES[k];
			break;
		    }
		}
	    }
	    if (type == NULL) {
		for (k = 0; k < uah->typesSize; k++) {
		    if ((uah->types[k].typeName != NULL) &&
			(strcmp(name, uah->types[k].typeName) == 0)) {
			isZero = 0;
			type = &uah->types[k];
			break;
		    }
		}
	    }
	    k = (j - 6) / 2;
	    obj = GetSymObj(uai, Tcl_GetString(objv[j + 1]),
			    (char **) &uah->types[i].members[k].memberName);
	    Tcl_IncrRefCount(obj);
	    uah->types[i].members[k].memberTypeIndex = type->typeIndex;
	    uah->types[i].members[k].padding =







|
>







 







|







 







>
>
>
>

|
|
|
|
|
|
|

|
|

>







 







|







 







|







 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







|







 







|







 







|







 







|







 







|
|




|
>
|
>
>
>
>







 







<
<
<
<
<
<



>
>
>





>
>
>
>
>







 







|
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|
|
>







 







|
<

>
|
|
<
<

<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<







164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
....
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
....
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
....
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
....
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
....
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
....
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
....
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
....
5572
5573
5574
5575
5576
5577
5578
5579
5580
5581
5582
5583
5584
5585
5586
....
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
....
6591
6592
6593
6594
6595
6596
6597
6598
6599
6600
6601
6602
6603
6604
6605
....
7688
7689
7690
7691
7692
7693
7694
7695
7696
7697
7698
7699
7700
7701
7702
....
7817
7818
7819
7820
7821
7822
7823
7824
7825
7826
7827
7828
7829
7830
7831
7832
7833
7834
7835
7836
7837
7838
7839
7840
7841
7842
7843
....
7861
7862
7863
7864
7865
7866
7867






7868
7869
7870
7871
7872
7873
7874
7875
7876
7877
7878
7879
7880
7881
7882
7883
7884
7885
7886
7887
7888
7889
7890
....
7893
7894
7895
7896
7897
7898
7899
7900


7901




















7902
7903
7904
7905
7906
7907
7908
7909
7910
7911
7912
....
7940
7941
7942
7943
7944
7945
7946
7947

7948
7949
7950
7951


7952












7953




7954
7955
7956
7957
7958
7959
7960
				    void *ctx,
				    UA_StatusChangeNotification *ntf);
static void		ReleaseSubs(UAH *uah);
static void		ReleaseMethodsEtc(UAH *uah, int killProcs);
static void		ReleaseTypes(UAH *uah, int all);
static void		DisconnectClient(UAH *uah);
static void		FreeHandle(char *clientData);
static const UA_DataType *FindType(UAI *uai, UAH *uah, int inDef,
				   const char *name);
static UA_NodeId *	ParseNodeId(Tcl_Interp *interp, UA_NodeId *nodein,
				    const char *string);
static UA_NodeId *	ParseTypeOrNodeId(Tcl_Interp *interp,
					  UAH *uah, UA_NodeId *nodein,
					  const char *string);
static UA_NodeId *	ParseRefTypeOrNodeId(Tcl_Interp *interp,
					     UAI *uai, UA_NodeId *nodein,
................................................................................
 * Side effects:
 *	None.
 *
 *-------------------------------------------------------------------------
 */

static const UA_DataType *
FindType(UAI *uai, UAH *uah, int inDef, const char *name)
{
    size_t j;
    Tcl_HashEntry *hPtr;
    const UA_DataType *type = NULL;

    hPtr = Tcl_FindHashEntry(&uai->types, name);
    if (hPtr != NULL) {
................................................................................
		break;
	    }
	}
	if ((type == NULL) && (uah != NULL)) {
	    size_t typesSize;
	    UA_DataType *types;

	    if (inDef) {
		types = uah->types;
		typesSize = uah->typesSize;
	    } else {
#if (UA_OPEN62541_VER_MAJOR < 1)
		if (uah->client != NULL) {
		    UA_Client_getCustomDataTypes(uah->client,
						 &types, &typesSize);
		} else {
		    UA_Server_getCustomDataTypes(uah->server,
						 &types, &typesSize);
		}
#else
		types = (UA_DataType *) uah->typesArray.types;
		typesSize = uah->typesArray.typesSize;
#endif
	    }
	    for (j = 0; j < typesSize; j++) {
		if ((types[j].typeName != NULL) &&
		    (strcmp(types[j].typeName, name) == 0)) {
		    type = &types[j];
		    break;
		}
	    }
................................................................................

static UA_NodeId *
ParseTypeOrNodeId(Tcl_Interp *interp, UAH *uah, UA_NodeId *nodein,
		  const char *string)
{
    if ((strchr(string, ';') == NULL) &&
	(strchr(string, '=') == NULL)) {
	const UA_DataType *type = FindType(uah->uai, uah, 0, string);

	if (type != NULL) {
	    *nodein = type->typeId;
	    return nodein;
	}
    }
    return ParseNodeId(interp, nodein, string);
................................................................................
	errstr = "no result buffer";
	goto error;
    }
    namein->name = UA_STRING_NULL;
    if (len > 2) {
	char *p = NULL, *q = strchr(string, ':');

	if ((q != NULL) && (q != string)) {
	    nsindex = strtol(string, &p, 10);
	    if ((p == q) && (p - string < len)) {
		string = p + 1;
	    } else {
		nsindex = 0;
	    }
	}
................................................................................
	}
	if (n == 1) {
	    Tcl_SetResult(interp, "zero, two, or more elements required",
			  TCL_STATIC);
	    goto error;
	}
	if (n) {
	    vtype = FindType(uai, uah, 0, Tcl_GetString(elem[0]));
	    if ((vtype == NULL) || (vtype == &UA_TYPES[UA_TYPES_VARIANT])) {
		Tcl_SetResult(interp, "unsupported type", TCL_STATIC);
		goto error;
	    }
	    /* Now skip type element. */
	    elem += 1;
	    n -= 1;
................................................................................
		    value = NULL;
		    goto error;
		}
		UA_delete(subval, subtype);
		data = (char *) data + subtype->memSize;
	    }
	}
#if (UA_OPEN62541_VER_MAJOR >= 1)
    } else if ((type->membersSize == 0) &&
	       (type->typeKind == UA_DATATYPEKIND_ENUM) &&
	       (type->typeIndex == UA_TYPES_INT32)) {
	/* Map scalar enum types to wide integer. */
	Tcl_WideInt v;

	if (Tcl_GetWideIntFromObj(interp, obj, &v) != TCL_OK) {
	    goto error;
	}
	value = UA_new(type);
	((UA_Int64 *) value)[0] = v;
#endif
    } else {
	Tcl_SetResult(interp, "unsupported type", TCL_STATIC);
    }
error:
    return value;
}
 
................................................................................
	    goto error;
	} else if (n < 2) {
	    Tcl_SetResult(interp, "need two or more elements, type and data",
			  TCL_STATIC);
	    ret = TCL_ERROR;
	    goto error;
	}
	type = FindType(uai, uah, 0, Tcl_GetString(elem[0]));
	if (type == NULL) {
	    Tcl_SetResult(interp, "unsupported type", TCL_STATIC);
	    ret = TCL_ERROR;
	    goto error;
	}
	if (n > 2) {
	    Tcl_SetObjResult(interp, Tcl_NewListObj(n - 1, &elem[1]));
................................................................................
	attr += 1;
	idx = 4;
    } else {
	/* By default, write the "value" attribute. */
	attr = UA_ATTRIBUTEID_VALUE;
	idx = 3;
    }
    type = FindType(uai, uah, 0, Tcl_GetString(objv[idx]));
    if (type == NULL) {
	Tcl_SetResult(interp, "unsupported type", TCL_STATIC);
	goto error;
    }
    idx++;
    value = EncodeFromObj(interp, uai, uah, type, NULL, objv[idx], 0);
    if (value == NULL) {
................................................................................
    }
    nInArgs = (objc - 4) / 2;
    inArgs = (UA_Variant *) UA_Array_new(nInArgs, &UA_TYPES[UA_TYPES_VARIANT]);
    for (i = 0; i < nInArgs; i++) {
	int idx = i * 2 + 4;
	void *value;

	type = FindType(uai, uah, 0, Tcl_GetString(objv[idx]));
	if (type == NULL) {
	    Tcl_SetResult(interp, "unsupported type", TCL_STATIC);
	    goto argsError;
	}
	idx++;
	if (objv[idx]->typePtr == uai->listObjType)  {
	    int j, n;
................................................................................
{
    UAI *uai = (UAI *) clientData;
    UAH *uah;
    Tcl_HashEntry *hPtr;
    UA_StatusCode uaret;
    int flag;

    if (objc != 2) {
	Tcl_WrongNumArgs(interp, 1, objv, "handle");
	return TCL_ERROR;
    }
    hPtr = Tcl_FindHashEntry(&uai->handles, Tcl_GetString(objv[1]));
    if (hPtr == NULL) {
	Tcl_SetResult(interp, "handle not found", TCL_STATIC);
	ReportError(interp, uai, NULL, 0);
................................................................................
{
    UAI *uai = (UAI *) clientData;
    UAH *uah;
    Tcl_HashEntry *hPtr;
    UA_NodeId nodeid, encid;
    int cmd, i, j;
    Tcl_Obj *obj;
    const UA_DataType *type;
    size_t max;
    static const char *cmdNames[] = {
	"begin", "commit", "enum", "struct", NULL
    };
    enum cmdTag {
	CMD_begin, CMD_commit, CMD_enum, CMD_struct
    };
................................................................................
	}
	if ((nodeid.identifierType != UA_NODEIDTYPE_NUMERIC) ||
	    (encid.identifierType != UA_NODEIDTYPE_NUMERIC)) {
	    Tcl_SetResult(interp, "need numeric node identifier", TCL_STATIC);
	    ReportError(interp, uai, NULL, 0);
	    return TCL_ERROR;
	}
	type = FindType(uai, uah, 1, Tcl_GetString(objv[6]));
	if (type == NULL) {
	    Tcl_SetResult(interp, "unsupported type for enum", TCL_STATIC);
	    ReportError(interp, uai, NULL, 0);
	    return TCL_ERROR;
	}
	if (
#if (UA_OPEN62541_VER_MAJOR < 1)
	    (type->membersSize != 1) || !type->pointerFree
#else
	    (type->membersSize != 0) || !type->pointerFree
#endif
	) {
	    Tcl_SetResult(interp, "unsupported type for enum", TCL_STATIC);
	    ReportError(interp, uai, NULL, 0);
	    return TCL_ERROR;
	}
	max = nodeid.identifier.numeric;
	if (max < encid.identifier.numeric) {
	    max = encid.identifier.numeric;
................................................................................
	}
	obj = GetSymObj(uai, Tcl_GetString(objv[3]),
			(char **) &uah->types[i].typeName);
	Tcl_IncrRefCount(obj);
	uah->types[i].typeId = nodeid;
	uah->types[i].memSize = type->memSize;
	uah->types[i].typeIndex = i;






	uah->types[i].pointerFree = 1;
	uah->types[i].overlayable = UA_BINARY_OVERLAYABLE_INTEGER;
	uah->types[i].binaryEncodingId = 0;
#if (UA_OPEN62541_VER_MAJOR < 1)
	uah->types[i].builtin = 0;
	uah->types[i].membersSize = 1;
	uah->types[i].members = ckalloc(sizeof(UA_DataTypeMember));
	uah->types[i].members[0] = type->members[0];
	obj = GetSymObj(uai, "",
			(char **) &uah->types[i].members[0].memberName);
	Tcl_IncrRefCount(obj);
#else
	uah->types[i].typeKind = UA_DATATYPEKIND_ENUM;
	uah->types[i].membersSize = 0;
	uah->types[i].members = NULL;
#endif
	break;

    case CMD_struct:
	if ((objc < 8) || (objc % 2 != 0)) {
	    Tcl_WrongNumArgs(interp, 1, objv,
		"handle struct name nodeid encid type member ...");
	    return TCL_ERROR;
................................................................................
	    return TCL_ERROR;
	}
	if (ParseNodeId(interp, &encid, Tcl_GetString(objv[5])) == NULL) {
	    return TCL_ERROR;
	}
	/* Validate member types. */
	for (i = 6; i < objc; i += 2) {
	    type = FindType(uai, uah, 1, Tcl_GetString(objv[i]));


	    if (type == NULL) {




















		Tcl_SetResult(interp, "unknown type in struct", TCL_STATIC);
		ReportError(interp, uai, NULL, 0);
		return TCL_ERROR;
	    }		
	}
	max = nodeid.identifier.numeric;
	if (max < encid.identifier.numeric) {
	    max = encid.identifier.numeric;
	}
	max += 64;
	if (uah->types == NULL) {
................................................................................
	uah->types[i].pointerFree = 1;
	uah->types[i].overlayable = 0;
	uah->types[i].binaryEncodingId = encid.identifier.numeric;
	j = sizeof(UA_DataTypeMember) * uah->types[i].membersSize;
	uah->types[i].members = ckalloc(j);
	memset(uah->types[i].members, 0, j);
	for (j = 6; j < objc; j += 2) {
	    int k, isZero;


	    type = FindType(uai, uah, 1, Tcl_GetString(objv[j]));
	    if ((type >= &UA_TYPES[0]) && (type < &UA_TYPES[UA_TYPES_COUNT])) {
		isZero = 1;


	    } else {












		isZero = 0;




	    }
	    k = (j - 6) / 2;
	    obj = GetSymObj(uai, Tcl_GetString(objv[j + 1]),
			    (char **) &uah->types[i].members[k].memberName);
	    Tcl_IncrRefCount(obj);
	    uah->types[i].members[k].memberTypeIndex = type->typeIndex;
	    uah->types[i].members[k].padding =