Check-in [3461a2287e]
Not logged in

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

Overview
Comment:minor update in topcua subtree
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3461a2287ee6bbc198035f41678880ffaf7338ef
User & Date: chw 2018-09-13 08:45:04.636
Context
2018-09-13
16:44
some fixes in tkpath for MacOSX (observed on high sierra) check-in: 745a6479d3 user: chw tags: trunk
08:45
minor update in topcua subtree check-in: 3461a2287e user: chw tags: trunk
2018-09-12
08:43
add selected tcllib upstream changes check-in: 898ffc44fd user: chw tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to jni/topcua/doc/opcua.n.
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
\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
the address space derived from the client or server object \fIhandle\fR.
This feature requires the tDOM package for parsing XML and is highly
experimental. It can create encoders/decoders for simple structure data types
defined in the address space which perform a mapping from/to tcl dictionaries.
For further information, see the server_types.tcl and client_types.tcl scripts
in the examples directory. If this command is used, it should be invoked prior
to creating method stubs, since methods may require custom data types in their
arguments.
.TP
\fBopcua info\fR ?\fIhandle\fR?
.







|
|
|







479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
\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
the address space derived from the client or server object \fIhandle\fR.
This feature is highly experimental and requires the tDOM package for parsing
XML. It can create encoders/decoders for simple structure data types defined
in the address space which perform a mapping from/to tcl dictionaries.
For further information, see the server_types.tcl and client_types.tcl scripts
in the examples directory. If this command is used, it should be invoked prior
to creating method stubs, since methods may require custom data types in their
arguments.
.TP
\fBopcua info\fR ?\fIhandle\fR?
.
553
554
555
556
557
558
559

















560
561
562
563
564
565
566
method stub procedures and other information. That namespace is
tied to the life time of the client or server object.
.TP
\fBopcua parent\fR \fIhandle nodeid\fR
.
Returns the parent node identifier of the given node identifier \fInodeid\fR
on the client or server object \fIhandle\fR.

















.TP
\fBopcua read\fR \fIhandle nodeid\fR ?\fIattr\fR?
.
Performs a read operation on the client or server object \fIhandle\fR
and returns the value of attribute \fIattr\fR of the node identifier
\fInodeid\fR. If \fIattr\fR is omitted, it defaults to the \fIValue\fR
attribute.







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







553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
method stub procedures and other information. That namespace is
tied to the life time of the client or server object.
.TP
\fBopcua parent\fR \fIhandle nodeid\fR
.
Returns the parent node identifier of the given node identifier \fInodeid\fR
on the client or server object \fIhandle\fR.
.TP
\fBopcua ptree\fR \fIhandle\fR ?\fInodeid\fR?
.
Returns information similar to \fBopcua tree\fR using the client
or server object \fIhandle\fR. The address space is traversed starting
at the node identifier \fInodeid\fR (the root node if omitted). The
result list is made up of browse path name, node identifier,
node class path, reference node identier, and type node identifier.
The browse path name is a path name like notation made up of the
browse names pointing to the final node as seen from the starting node.
Browse names are written as qualified names, i.e. including the
numeric namespace index if not in root namespace. Similarly, the
node class path is a path name like notation made up of the node
classes of all nodes along the path. The \fBopcua ptree\fR command
is used internally by the \fBopcua genstubs\fR command in order to
filter out objects and methods when creating stub Tcl commands to
invoke methods on objects.
.TP
\fBopcua read\fR \fIhandle nodeid\fR ?\fIattr\fR?
.
Performs a read operation on the client or server object \fIhandle\fR
and returns the value of attribute \fIattr\fR of the node identifier
\fInodeid\fR. If \fIattr\fR is omitted, it defaults to the \fIValue\fR
attribute.
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
their direction. A reference may be abbreviated as slash for
\fIHierarchicalReferences\fR or as dot for \fIAggregates\fR.
.TP
\fBopcua tree\fR \fIhandle\fR ?\fInodeid\fR?
.
Returns information similar to \fBopcua browse\fR using the client
or server object \fIhandle\fR. The address space is traversed starting
on the node identifier \fInodeid\fR (the root node if omitted). The
result list is made up of tree level (0-based), node identifier,
browse name (qualified name), display name (locale and text), node class,
reference node identifier, and type node identifier.
.TP
\fBopcua type\fR \fIhandle nodeid\fR ?\fIattr\fR?
.
Performs a read operation on the client or server object \fIhandle\fR







|







662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
their direction. A reference may be abbreviated as slash for
\fIHierarchicalReferences\fR or as dot for \fIAggregates\fR.
.TP
\fBopcua tree\fR \fIhandle\fR ?\fInodeid\fR?
.
Returns information similar to \fBopcua browse\fR using the client
or server object \fIhandle\fR. The address space is traversed starting
at the node identifier \fInodeid\fR (the root node if omitted). The
result list is made up of tree level (0-based), node identifier,
browse name (qualified name), display name (locale and text), node class,
reference node identifier, and type node identifier.
.TP
\fBopcua type\fR \fIhandle nodeid\fR ?\fIattr\fR?
.
Performs a read operation on the client or server object \fIhandle\fR
Changes to jni/topcua/topcua.c.
1
2
3
4
5
6
7
8
9
10
11
12
/*
 * topcua.c --
 *
 *      This file contains a proof of concept Tcl binding to the
 *	open62541 OPC UA library (client and server).
 *
 * Copyright (c) 2018 Christian Werner <chw at ch minus werner dot de>
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */





|







1
2
3
4
5
6
7
8
9
10
11
12
/*
 * topcua.c --
 *
 *      This file contains a proof of concept Tcl binding to the
 *	open62541 OPC/UA library (client and server).
 *
 * Copyright (c) 2018 Christian Werner <chw at ch minus werner dot de>
 *
 * See the file "license.terms" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 */

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
    int nCmdObjs;			/* For the Tcl callback. */
    Tcl_Obj **cmdObjs;			/* Ditto. */
    int nSavedCmdObjs;			/* Ditto. */
    Tcl_Obj **savedCmdObjs;		/* Ditto. */
} UAQ;

/*
 * Structure representing an OPC UA client or server.
 */

typedef struct UAH {
    struct UAI *uai;			/* Pointer to per-interp data. */
    Tcl_Interp *interp;			/* Interpreter for the client. */
    UA_Client *client;			/* Client connection handle. */
    UA_Server *server;			/* Server handle. */







|







71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
    int nCmdObjs;			/* For the Tcl callback. */
    Tcl_Obj **cmdObjs;			/* Ditto. */
    int nSavedCmdObjs;			/* Ditto. */
    Tcl_Obj **savedCmdObjs;		/* Ditto. */
} UAQ;

/*
 * Structure representing an OPC/UA client or server.
 */

typedef struct UAH {
    struct UAI *uai;			/* Pointer to per-interp data. */
    Tcl_Interp *interp;			/* Interpreter for the client. */
    UA_Client *client;			/* Client connection handle. */
    UA_Server *server;			/* Server handle. */
1069
1070
1071
1072
1073
1074
1075





1076
















































1077
1078
1079
1080
1081
1082
1083
	g->data4[4] = d[7];
	g->data4[5] = d[8];
	g->data4[6] = d[9];
	g->data4[7] = d[10];
	break;
    }
    case UA_NODEIDTYPE_BYTESTRING:





	errstr = "bytestring identifier unsupported";
















































	goto error;
    }
    return nodein;
error:
    if (interp != NULL) {
	Tcl_Obj *list[4];
	UA_StatusCode uaret = UA_STATUSCODE_BADINTERNALERROR;







>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
	g->data4[4] = d[7];
	g->data4[5] = d[8];
	g->data4[6] = d[9];
	g->data4[7] = d[10];
	break;
    }
    case UA_NODEIDTYPE_BYTESTRING:
	if ((string[0] == '0') && (string[1] == 'x')) {
	    UA_ByteString *bs = &nodein->identifier.byteString;
	    char buffer[3];
	    int v;
	    Tcl_DString ds;

	    Tcl_DStringInit(&ds);
	    string += 2;
	    while (1) {
		buffer[0] = string[0];
		if (string[0] == '\0') {
		    break;
		}
		buffer[1] = string[1];
		if (string[1] == '\0') {
		    break;
		}
		buffer[2] = '\0';
		if (sscanf(buffer, "%x", &v) != 1) {
		    break;
		}
		buffer[0] = v;
		Tcl_DStringAppend(&ds, buffer, 1);
		string += 2;
	    }
	    bs->length = Tcl_DStringLength(&ds);
	    if (bs->length > 0) {
		bs->data = (UA_Byte *) UA_malloc(bs->length);
		if (bs->data == NULL) {
		    bs->length = 0;
		    bs->data = (UA_Byte *) UA_EMPTY_ARRAY_SENTINEL;
		} else {
		    memcpy(bs->data, Tcl_DStringValue(&ds), bs->length);
		}
	    } else {
		bs->data = (UA_Byte *) UA_EMPTY_ARRAY_SENTINEL;
	    }
	    Tcl_DStringFree(&ds);
	} else {
	    UA_ByteString *bs = &nodein->identifier.byteString;

	    bs->length = strlen(string);
	    if (bs->length > 0) {
		bs->data = (UA_Byte *) UA_malloc(bs->length);
		if (bs->data == NULL) {
		    bs->length = 0;
		    bs->data = (UA_Byte *) UA_EMPTY_ARRAY_SENTINEL;
		} else {
		    memcpy(bs->data, string, bs->length);
		}
	    } else {
		bs->data = (UA_Byte *) UA_EMPTY_ARRAY_SENTINEL;
	    }
	}
	goto error;
    }
    return nodein;
error:
    if (interp != NULL) {
	Tcl_Obj *list[4];
	UA_StatusCode uaret = UA_STATUSCODE_BADINTERNALERROR;
1280
1281
1282
1283
1284
1285
1286

1287
1288
1289
1290
1291
1292
1293
 *
 *-------------------------------------------------------------------------
 */

static char *
PrintNodeId(const UA_NodeId *nodein, Tcl_DString *dsPtr)
{

    char buffer[64];

    Tcl_DStringInit(dsPtr);
    if (nodein->namespaceIndex) {
	sprintf(buffer, "ns=%d;", nodein->namespaceIndex);
	Tcl_DStringAppend(dsPtr, buffer, -1);
    }







>







1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
 *
 *-------------------------------------------------------------------------
 */

static char *
PrintNodeId(const UA_NodeId *nodein, Tcl_DString *dsPtr)
{
    size_t i;
    char buffer[64];

    Tcl_DStringInit(dsPtr);
    if (nodein->namespaceIndex) {
	sprintf(buffer, "ns=%d;", nodein->namespaceIndex);
	Tcl_DStringAppend(dsPtr, buffer, -1);
    }
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323


1324
1325
1326
1327
1328
1329
1330
		nodein->identifier.guid.data4[4],
		nodein->identifier.guid.data4[5],
		nodein->identifier.guid.data4[6],
		nodein->identifier.guid.data4[7]);
	Tcl_DStringAppend(dsPtr, buffer, -1);
	break;
    case UA_NODEIDTYPE_BYTESTRING:
	/* TBD: base64 */
	Tcl_DStringAppend(dsPtr, "b=", 2);
	Tcl_DStringAppend(dsPtr, (char *) nodein->identifier.byteString.data,
			  nodein->identifier.byteString.length);


	break;
    }
    return Tcl_DStringValue(dsPtr);
}

/*
 *-------------------------------------------------------------------------







<
|
|
|
>
>







1367
1368
1369
1370
1371
1372
1373

1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
		nodein->identifier.guid.data4[4],
		nodein->identifier.guid.data4[5],
		nodein->identifier.guid.data4[6],
		nodein->identifier.guid.data4[7]);
	Tcl_DStringAppend(dsPtr, buffer, -1);
	break;
    case UA_NODEIDTYPE_BYTESTRING:

	Tcl_DStringAppend(dsPtr, "b=0x", 2);
	for (i = 0; i < nodein->identifier.byteString.length; i++) {
	    sprintf(buffer, "%02x", nodein->identifier.byteString.data[i]);
	    Tcl_DStringAppend(dsPtr, buffer, 2);
	}
	break;
    }
    return Tcl_DStringValue(dsPtr);
}

/*
 *-------------------------------------------------------------------------
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
}

/*
 *-------------------------------------------------------------------------
 *
 * DecodeToObj --
 *
 *	Decode a OPCUA type to a Tcl_Obj.
 *
 * Results:
 *	A pointer to a Tcl_Obj (which is empty when decoding is not
 *	possible due to data type).
 *
 * Side effects:
 *	None.







|







2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
}

/*
 *-------------------------------------------------------------------------
 *
 * DecodeToObj --
 *
 *	Decode a OPC/UA type to a Tcl_Obj.
 *
 * Results:
 *	A pointer to a Tcl_Obj (which is empty when decoding is not
 *	possible due to data type).
 *
 * Side effects:
 *	None.
2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139

	switch (ext->encoding) {
	case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING:
	    p = PrintNodeId(&ext->content.encoded.typeId, &ds);
	    list[0] = Tcl_NewStringObj(p, -1);
	    Tcl_DStringFree(&ds);
	    if (ext->content.encoded.body.data != NULL) {
		/* TBD: is base64 better ? */
		list[1] =
		    Tcl_NewByteArrayObj(ext->content.encoded.body.data,
					ext->content.encoded.body.length);
	    } else {
		list[1] = Tcl_NewObj();
	    }
	    return Tcl_NewListObj(2, list);







<







2180
2181
2182
2183
2184
2185
2186

2187
2188
2189
2190
2191
2192
2193

	switch (ext->encoding) {
	case UA_EXTENSIONOBJECT_ENCODED_BYTESTRING:
	    p = PrintNodeId(&ext->content.encoded.typeId, &ds);
	    list[0] = Tcl_NewStringObj(p, -1);
	    Tcl_DStringFree(&ds);
	    if (ext->content.encoded.body.data != NULL) {

		list[1] =
		    Tcl_NewByteArrayObj(ext->content.encoded.body.data,
					ext->content.encoded.body.length);
	    } else {
		list[1] = Tcl_NewObj();
	    }
	    return Tcl_NewListObj(2, list);
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
		v->data4[6], v->data4[7]);
	return Tcl_NewStringObj(buffer, -1);
    }
    if (type == &UA_TYPES[UA_TYPES_BYTESTRING]) {
	UA_ByteString *v = (UA_ByteString *) data;

	if (v->data != NULL) {
	    /* TBD: is base64 better ? */
	    return Tcl_NewByteArrayObj(v->data, v->length);
	}
	return Tcl_NewObj();
    }
    if (type == &UA_TYPES[UA_TYPES_XMLELEMENT]) {
	UA_String *v = (UA_String *) data;








<







2303
2304
2305
2306
2307
2308
2309

2310
2311
2312
2313
2314
2315
2316
		v->data4[6], v->data4[7]);
	return Tcl_NewStringObj(buffer, -1);
    }
    if (type == &UA_TYPES[UA_TYPES_BYTESTRING]) {
	UA_ByteString *v = (UA_ByteString *) data;

	if (v->data != NULL) {

	    return Tcl_NewByteArrayObj(v->data, v->length);
	}
	return Tcl_NewObj();
    }
    if (type == &UA_TYPES[UA_TYPES_XMLELEMENT]) {
	UA_String *v = (UA_String *) data;

2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
}

/*
 *-------------------------------------------------------------------------
 *
 * EncodeFromObj --
 *
 *	Encode a Tcl_Obj to an OPCUA type.
 *
 * Results:
 *	A pointer to an allocated OPCUA value.
 *
 * Side effects:
 *	None.
 *
 *-------------------------------------------------------------------------
 */








|


|







2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
}

/*
 *-------------------------------------------------------------------------
 *
 * EncodeFromObj --
 *
 *	Encode a Tcl_Obj to an OPC/UA type.
 *
 * Results:
 *	A pointer to an allocated OPC/UA value.
 *
 * Side effects:
 *	None.
 *
 *-------------------------------------------------------------------------
 */

3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
}

/*
 *-------------------------------------------------------------------------
 *
 * MethodInvoke --
 *
 *	Method invocation callback from OPC UA server.
 *
 * Results:
 *	UA_StatusCode.
 *
 * Side effects:
 *	Many, since Tcl code gets evaluated.
 *







|







3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
}

/*
 *-------------------------------------------------------------------------
 *
 * MethodInvoke --
 *
 *	Method invocation callback from OPC/UA server.
 *
 * Results:
 *	UA_StatusCode.
 *
 * Side effects:
 *	Many, since Tcl code gets evaluated.
 *