add tools from distri, assign data sources

This commit is contained in:
Wolfgang Hottgenroth 2016-06-08 15:03:13 +02:00
parent 7586087ecc
commit 607f7af7f8
34 changed files with 32174 additions and 12 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
*~ *~
*.o

View File

@ -1,21 +1,29 @@
CC = gcc CC = gcc
CFLAGS = -std=c99 -I ./ -I ./include -DUA_NO_AMALGAMATION # put pre-processor settings (-I, -D, etc) here CFLAGS = -std=c99 -I ./ -I ./include -DUA_NO_AMALGAMATION # put pre-processor settings (-I, -D, etc) here
LDFLAGS = # put linker settings here LDFLAGS = # put linker settings here
LIBS = ./lib/libopen62541-static.a
.PHONY: all .PHONY: all
all: myServer server_method server_folders myTestModelServer all: myServer server_method server_folders myTestModelServer
myServer: myServer.o ./lib/libopen62541-static.a myServer: myServer.o
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LIBS)
server_method: server_method.o ./lib/libopen62541-static.a server_method: server_method.o
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LIBS)
server_folders: server_folders.o ./lib/libopen62541-static.a server_folders: server_folders.o
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LIBS)
myTestModelServer: myTestModelServer.o TestModel.o ./lib/libopen62541-static.a TestModel.c: testmodel.xml
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ ./tools/pyUANamespace/generate_open62541CCode.py tools/schema/namespace0/Opc.Ua.NodeSet2.xml $^ TestModel
TestModel.h: TestModel.c
myTestModelServer.o: myTestModelServer.c TestModel.h
myTestModelServer: myTestModelServer.o TestModel.o
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) $^ $(LIBS)
.c.o: .c.o:
$(CC) $(CFLAGS) -c $< $(CC) $(CFLAGS) -c $<

View File

@ -30456,6 +30456,47 @@ UA_Server_addObjectNode(server, nodeId, parentNodeId, parentReferenceNodeId, nod
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5001), UA_NODEID_NUMERIC(0, 40), UA_EXPANDEDNODEID_NUMERIC(0, 58), true); UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5001), UA_NODEID_NUMERIC(0, 40), UA_EXPANDEDNODEID_NUMERIC(0, 58), true);
} while(0); } while(0);
do {
// Referencing node found and declared as parent: ns=2;i=1002/1:TemperatureSensorType using i=47/HasComponent
// Node: opcua_node_object_t(ns=2;i=5006), 1:DeviceData
UA_ObjectAttributes attr;
UA_ObjectAttributes_init(&attr);
attr.displayName = UA_LOCALIZEDTEXT("", "DeviceData");
attr.description = UA_LOCALIZEDTEXT("", "");
UA_NodeId nodeId = UA_NODEID_NUMERIC(2, 5006);
UA_NodeId typeDefinition = UA_NODEID_NULL;
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(2, 1002);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, 47);
UA_QualifiedName nodeName = UA_QUALIFIEDNAME(1, "DeviceData");
UA_Server_addObjectNode(server, nodeId, parentNodeId, parentReferenceNodeId, nodeName
, typeDefinition
, attr, NULL, NULL);
// This node has the following references that can be created:
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5006), UA_NODEID_NUMERIC(0, 40), UA_EXPANDEDNODEID_NUMERIC(0, 58), true);
} while(0);
do {
// Referencing node found and declared as parent: ns=2;i=5006/1:DeviceData using i=47/HasComponent
// Node: opcua_node_variable_t(ns=2;i=6005), 1:UpTime
UA_VariableAttributes attr;
UA_VariableAttributes_init(&attr);
attr.displayName = UA_LOCALIZEDTEXT("", "UpTime");
attr.description = UA_LOCALIZEDTEXT("", "");
UA_UInt32 opcua_node_variable_t_ns_2_i_6005_variant_DataContents = (UA_UInt32) 0;
UA_Variant_setScalar( &attr.value, &opcua_node_variable_t_ns_2_i_6005_variant_DataContents, &UA_TYPES[UA_TYPES_UINT32]);
UA_UInt32_deleteMembers(&opcua_node_variable_t_ns_2_i_6005_variant_DataContents);
UA_NodeId nodeId = UA_NODEID_NUMERIC(2, 6005);
UA_NodeId typeDefinition = UA_NODEID_NULL;
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(2, 5006);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, 47);
UA_QualifiedName nodeName = UA_QUALIFIEDNAME(1, "UpTime");
UA_Server_addVariableNode(server, nodeId, parentNodeId, parentReferenceNodeId, nodeName
, typeDefinition
, attr, NULL, NULL);
// This node has the following references that can be created:
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 6005), UA_NODEID_NUMERIC(0, 40), UA_EXPANDEDNODEID_NUMERIC(0, 63), true);
} while(0);
do { do {
// Referencing node found and declared as parent: ns=2;i=1002/1:TemperatureSensorType using i=47/HasComponent // Referencing node found and declared as parent: ns=2;i=1002/1:TemperatureSensorType using i=47/HasComponent
// Node: opcua_node_object_t(ns=2;i=5002), 1:Measurement // Node: opcua_node_object_t(ns=2;i=5002), 1:Measurement
@ -31222,6 +31263,8 @@ UA_Server_addReference(server, UA_NODEID_NUMERIC(0, 7612), UA_NODEID_NUMERIC(0,
UA_Server_addReference(server, UA_NODEID_NUMERIC(0, 12078), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true); UA_Server_addReference(server, UA_NODEID_NUMERIC(0, 12078), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true);
UA_Server_addReference(server, UA_NODEID_NUMERIC(0, 7614), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true); UA_Server_addReference(server, UA_NODEID_NUMERIC(0, 7614), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true);
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5001), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true); UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5001), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true);
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5006), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true);
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 6005), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true);
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5002), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true); UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5002), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true);
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 6002), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true); UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 6002), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true);
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 6001), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true); UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 6001), UA_NODEID_NUMERIC(0, 37), UA_EXPANDEDNODEID_NUMERIC(0, 78), true);
@ -31287,6 +31330,47 @@ UA_Server_addObjectNode(server, nodeId, parentNodeId, parentReferenceNodeId, nod
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5003), UA_NODEID_NUMERIC(0, 40), UA_EXPANDEDNODEID_NUMERIC(0, 58), true); UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5003), UA_NODEID_NUMERIC(0, 40), UA_EXPANDEDNODEID_NUMERIC(0, 58), true);
} while(0); } while(0);
do {
// Referencing node found and declared as parent: ns=2;i=5004/1:TemperatureSensor using i=47/HasComponent
// Node: opcua_node_object_t(ns=2;i=5007), 1:DeviceData
UA_ObjectAttributes attr;
UA_ObjectAttributes_init(&attr);
attr.displayName = UA_LOCALIZEDTEXT("", "DeviceData");
attr.description = UA_LOCALIZEDTEXT("", "");
UA_NodeId nodeId = UA_NODEID_NUMERIC(2, 5007);
UA_NodeId typeDefinition = UA_NODEID_NULL;
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(2, 5004);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, 47);
UA_QualifiedName nodeName = UA_QUALIFIEDNAME(1, "DeviceData");
UA_Server_addObjectNode(server, nodeId, parentNodeId, parentReferenceNodeId, nodeName
, typeDefinition
, attr, NULL, NULL);
// This node has the following references that can be created:
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 5007), UA_NODEID_NUMERIC(0, 40), UA_EXPANDEDNODEID_NUMERIC(0, 58), true);
} while(0);
do {
// Referencing node found and declared as parent: ns=2;i=5007/1:DeviceData using i=47/HasComponent
// Node: opcua_node_variable_t(ns=2;i=6006), 1:UpTime
UA_VariableAttributes attr;
UA_VariableAttributes_init(&attr);
attr.displayName = UA_LOCALIZEDTEXT("", "UpTime");
attr.description = UA_LOCALIZEDTEXT("", "");
UA_UInt32 opcua_node_variable_t_ns_2_i_6006_variant_DataContents = (UA_UInt32) 0;
UA_Variant_setScalar( &attr.value, &opcua_node_variable_t_ns_2_i_6006_variant_DataContents, &UA_TYPES[UA_TYPES_UINT32]);
UA_UInt32_deleteMembers(&opcua_node_variable_t_ns_2_i_6006_variant_DataContents);
UA_NodeId nodeId = UA_NODEID_NUMERIC(2, 6006);
UA_NodeId typeDefinition = UA_NODEID_NULL;
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(2, 5007);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, 47);
UA_QualifiedName nodeName = UA_QUALIFIEDNAME(1, "UpTime");
UA_Server_addVariableNode(server, nodeId, parentNodeId, parentReferenceNodeId, nodeName
, typeDefinition
, attr, NULL, NULL);
// This node has the following references that can be created:
UA_Server_addReference(server, UA_NODEID_NUMERIC(2, 6006), UA_NODEID_NUMERIC(0, 40), UA_EXPANDEDNODEID_NUMERIC(0, 63), true);
} while(0);
do { do {
// Referencing node found and declared as parent: ns=2;i=5004/1:TemperatureSensor using i=47/HasComponent // Referencing node found and declared as parent: ns=2;i=5004/1:TemperatureSensor using i=47/HasComponent
// Node: opcua_node_object_t(ns=2;i=5005), 1:Measurement // Node: opcua_node_object_t(ns=2;i=5005), 1:Measurement

View File

@ -5,6 +5,8 @@
#include <signal.h> #include <signal.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h>
#include <sys/select.h>
#include <ua_types.h> #include <ua_types.h>
#include <ua_server.h> #include <ua_server.h>
@ -18,11 +20,36 @@
UA_Logger logger = Logger_Stdout; UA_Logger logger = Logger_Stdout;
UA_Boolean running = true; UA_Boolean running = true;
/*
* shared data
*/
uint32_t upTime = 0;
double measuredTemperature = 0;
static void stopHandler(int sign) { static void stopHandler(int sign) {
UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c"); UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "received ctrl-c");
running = false; running = false;
} }
static UA_StatusCode
readInteger(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
const UA_NumericRange *range, UA_DataValue *dataValue) {
dataValue->hasValue = true;
UA_Variant_setScalarCopy(&dataValue->value, handle, &UA_TYPES[UA_TYPES_INT32]);
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
readDouble(void *handle, const UA_NodeId nodeid, UA_Boolean sourceTimeStamp,
const UA_NumericRange *range, UA_DataValue *dataValue) {
dataValue->hasValue = true;
UA_Variant_setScalarCopy(&dataValue->value, handle, &UA_TYPES[UA_TYPES_DOUBLE]);
return UA_STATUSCODE_GOOD;
}
int main(int argc, char** argv) { int main(int argc, char** argv) {
signal(SIGINT, stopHandler); /* catches ctrl-c */ signal(SIGINT, stopHandler); /* catches ctrl-c */
@ -36,11 +63,42 @@ int main(int argc, char** argv) {
/* create nodes from nodeset */ /* create nodes from nodeset */
TestModel(server); TestModel(server);
/* start server */ UA_DataSource uptimeDataSource = (UA_DataSource) {
UA_StatusCode retval = UA_Server_run(server, &running); //UA_blocks until running=false .handle = &upTime, .read = readInteger, .write = 0};
UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(2, 6006),
uptimeDataSource);
UA_DataSource measuredTemperatureDataSource = (UA_DataSource) {
.handle = &measuredTemperature, .read = readDouble, .write = 0};
UA_Server_setVariableNode_dataSource(server, UA_NODEID_NUMERIC(2, 6004),
measuredTemperatureDataSource);
/*
UA_StatusCode retval = UA_Server_run(server, &running); //UA_blocks until running=false
*/
UA_StatusCode retval = UA_Server_run_startup(server);
if (retval == UA_STATUSCODE_GOOD) {
while (running == true) {
upTime = time(0);
measuredTemperature += 0.25;
uint16_t canWait = 0;
canWait = UA_Server_run_iterate(server, 0);
// UA_LOG_INFO(logger, UA_LOGCATEGORY_USERLAND, "canWait: %i", canWait);
struct timeval timeout = { .tv_sec = 0, .tv_usec = canWait * 1000 };
select(0, 0, 0, 0, &timeout);
}
}
/* ctrl-c received -> clean up */ /* ctrl-c received -> clean up */
UA_Server_delete(server); UA_Server_delete(server);
nl.deleteMembers(&nl); nl.deleteMembers(&nl);
return (int)retval; // return (int)retval;
} }

0
testmodel.tt2pro Normal file → Executable file
View File

BIN
testmodel.ua Normal file → Executable file

Binary file not shown.

43
testmodel.xml Normal file → Executable file
View File

@ -3,6 +3,7 @@
<Uri>http://krohnegroup.com/TestModel/</Uri> <Uri>http://krohnegroup.com/TestModel/</Uri>
</NamespaceUris> </NamespaceUris>
<Aliases> <Aliases>
<Alias Alias="UInt32">i=7</Alias>
<Alias Alias="Double">i=11</Alias> <Alias Alias="Double">i=11</Alias>
<Alias Alias="String">i=12</Alias> <Alias Alias="String">i=12</Alias>
<Alias Alias="Organizes">i=35</Alias> <Alias Alias="Organizes">i=35</Alias>
@ -14,13 +15,14 @@
</Aliases> </Aliases>
<Extensions> <Extensions>
<Extension> <Extension>
<ModelInfo Tool="UaModeler" Hash="ngSlXA/wXFdk+KET7IyQOQ==" Version="1.4.3"/> <ModelInfo Tool="UaModeler" Hash="2VjgZydlSX7P08Hasg03fQ==" Version="1.4.3"/>
</Extension> </Extension>
</Extensions> </Extensions>
<UAObjectType NodeId="ns=1;i=1002" BrowseName="1:TemperatureSensorType"> <UAObjectType NodeId="ns=1;i=1002" BrowseName="1:TemperatureSensorType">
<DisplayName>TemperatureSensorType</DisplayName> <DisplayName>TemperatureSensorType</DisplayName>
<References> <References>
<Reference ReferenceType="HasComponent">ns=1;i=5001</Reference> <Reference ReferenceType="HasComponent">ns=1;i=5001</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=5006</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=5002</Reference> <Reference ReferenceType="HasComponent">ns=1;i=5002</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference> <Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References> </References>
@ -43,6 +45,26 @@
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=6002</Reference> <Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=6002</Reference>
</References> </References>
</UAVariable> </UAVariable>
<UAObject ParentNodeId="ns=1;i=1002" NodeId="ns=1;i=5006" BrowseName="1:DeviceData">
<DisplayName>DeviceData</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=58</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1002</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=6005</Reference>
</References>
</UAObject>
<UAVariable DataType="UInt32" ParentNodeId="ns=1;i=5006" NodeId="ns=1;i=6005" BrowseName="1:UpTime" UserAccessLevel="3" AccessLevel="3">
<DisplayName>UpTime</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=5006</Reference>
</References>
<Value>
<uax:UInt32>0</uax:UInt32>
</Value>
</UAVariable>
<UAObject ParentNodeId="ns=1;i=1002" NodeId="ns=1;i=5002" BrowseName="1:Measurement"> <UAObject ParentNodeId="ns=1;i=1002" NodeId="ns=1;i=5002" BrowseName="1:Measurement">
<DisplayName>Measurement</DisplayName> <DisplayName>Measurement</DisplayName>
<References> <References>
@ -68,6 +90,7 @@
<DisplayName>TemperatureSensor</DisplayName> <DisplayName>TemperatureSensor</DisplayName>
<References> <References>
<Reference ReferenceType="HasComponent">ns=1;i=5003</Reference> <Reference ReferenceType="HasComponent">ns=1;i=5003</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=5007</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=5005</Reference> <Reference ReferenceType="HasComponent">ns=1;i=5005</Reference>
<Reference ReferenceType="Organizes" IsForward="false">i=85</Reference> <Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
<Reference ReferenceType="HasTypeDefinition">ns=1;i=1002</Reference> <Reference ReferenceType="HasTypeDefinition">ns=1;i=1002</Reference>
@ -89,6 +112,24 @@
<Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=6004</Reference> <Reference ReferenceType="HasProperty" IsForward="false">ns=1;i=6004</Reference>
</References> </References>
</UAVariable> </UAVariable>
<UAObject ParentNodeId="ns=1;i=5004" NodeId="ns=1;i=5007" BrowseName="1:DeviceData">
<DisplayName>DeviceData</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=58</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=5004</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=6006</Reference>
</References>
</UAObject>
<UAVariable DataType="UInt32" ParentNodeId="ns=1;i=5007" NodeId="ns=1;i=6006" BrowseName="1:UpTime" UserAccessLevel="3" AccessLevel="3">
<DisplayName>UpTime</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=5007</Reference>
</References>
<Value>
<uax:UInt32>0</uax:UInt32>
</Value>
</UAVariable>
<UAObject ParentNodeId="ns=1;i=5004" NodeId="ns=1;i=5005" BrowseName="1:Measurement"> <UAObject ParentNodeId="ns=1;i=5004" NodeId="ns=1;i=5005" BrowseName="1:Measurement">
<DisplayName>Measurement</DisplayName> <DisplayName>Measurement</DisplayName>
<References> <References>

View File

@ -0,0 +1,29 @@
i=2253
i=2254
i=2255
i=2256
i=2257
i=2258
i=2259
i=2260
i=2261
i=2262
i=2263
i=2264
i=2265
i=2266
i=2267
i=2271
i=2274
i=2292
i=2294
i=2735
i=2992
i=2993
i=2994
i=2268
i=274
i=295
i=296
i=11715
i=11492

View File

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,150 @@
pyUANamespace
=============
pyUANamespace is a collection of python 2 scripts that can parse OPC UA XML Namespace definition files and transform them into a class representation. This facilitates both reprinting the namespace in a different non-XML format (such as C-Code or DOT) and analysis of the namespace structure.
### Documentation
The pyUANamespace implementation has been contributed by a research project of the chair for Process Control Systems Engineering of the TU Dresden. It was not strictly speaking created as a C generator, but could be easily modified to fullfill this role for open62541.
## Functionality in open62541
In open62541, the linked python namespace generated by the pyUANamespace classes are used to print C-Code that will automatically initialize a server. Apart from parsing XML, most classes also have a printOpen62541Header() or printOpen62541Header_Subtype() function that can reprint the node as C Code compatible with the project.
This function has been wrapped into the generate_open62541CCode.py program, which implements the compiler and option checking relevant to this task. The program is called as follows:
```bash
$ python generate_open62541CCode.py /path/to/NodeSet.xml /path/to/outputfile.c
```
Several options have been made available to further facilitate header generation. Calling the script without arguments will produce a usage helper listing all available options.
# Overwriting NodeSet definitions
Most notably, any number of XML files can be passed. The latest XML definition of a node found is used.
```bash
$ python generate_open62541CCode.py /path/to/NodeSet0.xml /path/to/OverwriteNodeSet0.xml /path/to/outputfile.c
```
# Blacklisting Nodes
If a set of nodes is not to be contained in the namespace, they can be specified as a blacklist file containing one nodeId per line in the following format:
```
ns=1;id=2323
id=11122;
```
This file can be passed to the compiler, which will remove these nodes prior to linking the namespace.
```bash
$ python generate_open62541CCode.py -b blacklist.txt /path/to/NodeSet.xml /path/to/outputfile.c
```
In this particular example, nodes `ns=1;id=2323` and `ns=0;id=11122` will be removed from the namespace (which may invalidate other nodes referring to them).
# Ignoring Nodes
Blacklisting removes nodes, which means that any other nodes referring to these nodes will contain invalid references. If a namespace should be generated that can use all datatypes, objectstypes, etc., but should only print specific nodes into the C Header, a set of nodes can be ignored. This will cause the compiler to use them in the linked namespace where other nodes can use them (e.g. in buildEncodingRules()), but they will not appear as part of the header file.
Ignorelists have the same format as blacklists and are passed to the compiler like so:.
```bash
$ python generate_open62541CCode.py -i ignorelist.txt /path/to/NodeSet.xml /path/to/outputfile.c
```
Given the blacklist example, the nodes `ns=1;id=2323` and `ns=0;id=11122` will not be part of the header, but other nodes may attempt to create references to them or use them as dataTypes.
# Supressing attributes
Most of OPC UA Namespaces depend heavily on strings. These can bloat up memory usage in applications where memory is a critical resource. The compiler can be instructed to suppress allocation for certain attributes, which will be initialized to sensible defaults or NULL pointers where applicable.
```bash
$ python generate_open62541CCode.py -s browsename -s displayname -s nodeid /path/to/NodeSet.xml /path/to/outputfile.c
```
Currently, the following base attributes of nodes can be suppressed:
- browseName
- displayName
- description
- nodeId
- writeMask
- userWriteMask
Further attributes may be added at a later point depending on demand.
## Core functionality
OPC UA node types, base data types and references are described in ua_node_types.py and ua_builtin_types.py. These classes are primarily intended to act as part of an AST to parse OPC UA Namespace description files. They implement a hierarchic/rekursive parsing of XML DOM objects, supplementing their respective properties from the XML description.
A manager class called ua_namespace is included in the respective source file. This class does _not_ correspond to a OPC UA Namespace. It is an aggregator and manager for nodes and references which may belong to any number of namespaces. This class includes extensive parsing/validation to ensure that a complete and consistent namespace is generated.
## Namespace compilation internals
Compiling a namespace consists of the following steps:
- Read one or more XML definitions
- Link references to actual nodes (also includes references to dataTypes, etc.)
- Sanitize/Remove any nodes and references that could not be properly parsed
- Derive encoding rules for datatypes
- Parse/Allocate variable values according to their dataType definitions
Reading and parsing XML files is handled by ua_namespace.parseXML(/path/to/file.xml). It is the first part of a multipass compiler that will create all nodes contained in the XML description.
During the reading of XML files, nodes will attempt to parse any attributes they own, but not refer to any other nodes. References will be kept as XML descriptions. Any number of XML files can be read in this phase. __NOTE__: In the open62541 (this) implementation, duplicate nodes (same NodeId) are allowed. The last definition encountered will be kept. This allows overwriting specific nodes of a much larger XML file to with implementation specific nodes.
The next phase of the compilation is to link all references. The phase is called by ua_namespace.linkOpenPointers(). All references will attempt to locate their target() node in the namespace and point to it.
During the sanitation phase called by ua_namespace.sanitize(), nodes check themselves for invalid attributes. Most notably any references that could not be resolved to a node will be removed from the nodes.
When calling ua_namespace.buildEncodingRules(), dataType nodes are examined to determine if and how the can be encoded as a serialization of OPC UA builtin types as well as their aliases.
The following fragment of a variable value can illustrate the necessity for determining encoding rules:
```xml
<Argument>
<Name>ServerHandles</Name>
<DataType>
<Identifier>i=7</Identifier>
</DataType>
<ValueRank>1</ValueRank>
<ArrayDimensions />
<Description p5:nil="true" xmlns:p5="http://www.w3.org/2001/XMLSchema-instance" />
</Argument>
```
The tags body, TypeId, Identifier, and Argument are aliases for builtin encodings or structures thereof. Without knowing which type they represent, an appropriate value class (and with that a parser) cannot be created. pyUANamespace will resolve this specific case by determining the encoding as follows:
```
LastMethodOutputArguments : Argument -> i=296
+ [['Name', ['String']], ['DataType', ['NodeId']], ['ValueRank', ['Int32']], ['ArrayDimensions', ['UInt32']], ['Description', ['LocalizedText']]] (already analyzed)
```
DataTypes that cannot be encoded as a definite serial object (e.g. by having a member of NumericType, but not a specific one), will have their isEncodable() attribute disabled. All dataTypes that complete this node can be used to effectively determine the size and serialization properties of any variables.
Having encoding rules means that data can now be parsed when a <Value> tag is encountered in a description. Calling ua_namespace.allocateVariables() will do just that for any variable node that holds XML Values.
The result of this compilation is a completely linked and instantiated OPC UA namespace.
An example compiler can be built as follows:
```python
class testing:
def __init__(self):
self.namespace = opcua_namespace("testing")
log(self, "Phase 1: Reading XML file nodessets")
self.namespace.parseXML("Opc.Ua.NodeSet2.xml")
self.namespace.parseXML("DeviceNodesSet.xml")
self.namespace.parseXML("MyNodesSet.xml")
log(self, "Phase 2: Linking address space references and datatypes")
self.namespace.linkOpenPointers()
self.namespace.sanitize()
log(self, "Phase 3: Comprehending DataType encoding rules")
self.namespace.buildEncodingRules()
log(self, "Phase 4: Allocating variable value data")
self.namespace.allocateVariables()
```

View File

@ -0,0 +1,203 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
###
### Author: Chris Iatrou (ichrispa@core-vector.net)
### Version: rev 13
###
### This program was created for educational purposes and has been
### contributed to the open62541 project by the author. All licensing
### terms for this source is inherited by the terms and conditions
### specified for by the open62541 project (see the projects readme
### file for more information on the LGPL terms and restrictions).
###
### This program is not meant to be used in a production environment. The
### author is not liable for any complications arising due to the use of
### this program.
###
from __future__ import print_function
from sys import argv, exit
from os import path
from ua_namespace import *
from logger import *
from open62541_XMLPreprocessor import open62541_XMLPreprocessor
def usage():
print("Script usage:")
print("generate_open62541CCode [-i <ignorefile> | -b <blacklistfile>] <namespace XML> [namespace.xml[ namespace.xml[...]]] <output file>\n")
print("generate_open62541CCode will first read all XML files passed on the command line, then ")
print("link and check the namespace. All nodes that fullfill the basic requirements will then be")
print("printed as C-Code intended to be included in the open62541 OPC-UA Server that will")
print("initialize the corresponding name space.\n")
print("Manditory Arguments:")
print("<namespace XML> At least one Namespace XML file must be passed.")
print("<output file> The basename for the <output file>.c and <output file>.h files to be generated.")
print(" This will also be the function name used in the header and c-file.\n\n")
print("Additional Arguments:")
print(""" -i <ignoreFile> Loads a list of NodeIDs stored in ignoreFile (one NodeID per line)
The compiler will assume that these Nodes have been created externally
and not generate any code for them. They will however be linked to
from other nodes.""")
print(""" -b <blacklistFile> Loads a list of NodeIDs stored in blacklistFile (one NodeID per line)
Any of the nodeIds encountered in this file will be removed from the namespace
prior to compilation. Any references to these nodes will also be removed""")
print(""" -s <attribute> Suppresses the generation of some node attributes. Currently supported
options are 'description', 'browseName', 'displayName', 'writeMask', 'userWriteMask'
and 'nodeid'.""")
print(""" namespaceXML Any number of namespace descriptions in XML format. Note that the
last description of a node encountered will be used and all prior definitions
are discarded.""")
if __name__ == '__main__':
# Check if the parameters given correspond to actual files
infiles = []
ouffile = ""
ignoreFiles = []
blacklistFiles = []
supressGenerationOfAttribute=[]
GLOBAL_LOG_LEVEL = LOG_LEVEL_DEBUG
arg_isIgnore = False
arg_isBlacklist = False
arg_isSupress = False
if len(argv) < 2:
usage()
exit(1)
for filename in argv[1:-1]:
if arg_isIgnore:
arg_isIgnore = False
if path.exists(filename):
ignoreFiles.append(filename)
else:
log(None, "File " + str(filename) + " does not exist.", LOG_LEVEL_ERROR)
usage()
exit(1)
elif arg_isBlacklist:
arg_isBlacklist = False
if path.exists(filename):
blacklistFiles.append(filename)
else:
log(None, "File " + str(filename) + " does not exist.", LOG_LEVEL_ERROR)
usage()
exit(1)
elif arg_isSupress:
arg_isSupress = False
supressGenerationOfAttribute.append(filename.lower())
else:
if path.exists(filename):
infiles.append(filename)
elif filename.lower() == "-i" or filename.lower() == "--ignore" :
arg_isIgnore = True
elif filename.lower() == "-b" or filename.lower() == "--blacklist" :
arg_isBlacklist = True
elif filename.lower() == "-s" or filename.lower() == "--suppress" :
arg_isSupress = True
else:
log(None, "File " + str(filename) + " does not exist.", LOG_LEVEL_ERROR)
usage()
exit(1)
# Creating the header is tendious. We can skip the entire process if
# the header exists.
#if path.exists(argv[-1]+".c") or path.exists(argv[-1]+".h"):
# log(None, "File " + str(argv[-1]) + " does already exists.", LOG_LEVEL_INFO)
# log(None, "Header generation will be skipped. Delete the header and rerun this script if necessary.", LOG_LEVEL_INFO)
# exit(0)
# Open the output file
outfileh = open(argv[-1]+".h", r"w+")
outfilec = open(argv[-1]+".c", r"w+")
# Create a new namespace
# Note that the name is actually completely symbolic, it has no other
# function but to distinguish this specific class.
# A namespace class acts as a container for nodes. The nodes may belong
# to any number of different OPC-UA namespaces.
ns = opcua_namespace("open62541")
# Clean up the XML files by removing duplicate namespaces and unwanted prefixes
preProc = open62541_XMLPreprocessor()
for xmlfile in infiles:
log(None, "Preprocessing " + str(xmlfile), LOG_LEVEL_INFO)
preProc.addDocument(xmlfile)
preProc.preprocessAll()
for xmlfile in preProc.getPreProcessedFiles():
log(None, "Parsing " + str(xmlfile), LOG_LEVEL_INFO)
ns.parseXML(xmlfile)
# We need to notify the open62541 server of the namespaces used to be able to use i.e. ns=3
namespaceArrayNames = preProc.getUsedNamespaceArrayNames()
for key in namespaceArrayNames:
ns.addNamespace(key, namespaceArrayNames[key])
# Remove any temp files - they are not needed after the AST is created
# Removed for debugging
preProc.removePreprocessedFiles()
# Remove blacklisted nodes from the namespace
# Doing this now ensures that unlinkable pointers will be cleanly removed
# during sanitation.
for blacklist in blacklistFiles:
bl = open(blacklist, "r")
for line in bl.readlines():
line = line.replace(" ","")
id = line.replace("\n","")
if ns.getNodeByIDString(id) == None:
log(None, "Can't blacklist node, namespace does currently not contain a node with id " + str(id), LOG_LEVEL_WARN)
else:
ns.removeNodeById(line)
bl.close()
# Link the references in the namespace
log(None, "Linking namespace nodes and references", LOG_LEVEL_INFO)
ns.linkOpenPointers()
# Remove nodes that are not printable or contain parsing errors, such as
# unresolvable or no references or invalid NodeIDs
ns.sanitize()
# Parse Datatypes in order to find out what the XML keyed values actually
# represent.
# Ex. <rpm>123</rpm> is not encodable
# only after parsing the datatypes, it is known that
# rpm is encoded as a double
log(None, "Building datatype encoding rules", LOG_LEVEL_INFO)
ns.buildEncodingRules()
# Allocate/Parse the data values. In order to do this, we must have run
# buidEncodingRules.
log(None, "Allocating variables", LOG_LEVEL_INFO)
ns.allocateVariables()
# Users may have manually defined some nodes in their code already (such as serverStatus).
# To prevent those nodes from being reprinted, we will simply mark them as already
# converted to C-Code. That way, they will still be reffered to by other nodes, but
# they will not be created themselves.
ignoreNodes = []
for ignore in ignoreFiles:
ig = open(ignore, "r")
for line in ig.readlines():
line = line.replace(" ","")
id = line.replace("\n","")
if ns.getNodeByIDString(id) == None:
log(None, "Can't ignore node, Namespace does currently not contain a node with id " + str(id), LOG_LEVEL_WARN)
else:
ignoreNodes.append(ns.getNodeByIDString(id))
ig.close()
# Create the C Code
log(None, "Generating Header", LOG_LEVEL_INFO)
# Returns a tuple of (["Header","lines"],["Code","lines","generated"])
generatedCode=ns.printOpen62541Header(ignoreNodes, supressGenerationOfAttribute, outfilename=path.basename(argv[-1]))
for line in generatedCode[0]:
outfileh.write(line+"\n")
for line in generatedCode[1]:
outfilec.write(line+"\n")
outfilec.close()
outfileh.close()
exit(0)

View File

@ -0,0 +1,46 @@
#!/usr/bin/env/python
# -*- coding: utf-8 -*-
###
### Author: Chris Iatrou (ichrispa@core-vector.net)
### Version: rev 13
###
### This program was created for educational purposes and has been
### contributed to the open62541 project by the author. All licensing
### terms for this source is inherited by the terms and conditions
### specified for by the open62541 project (see the projects readme
### file for more information on the LGPL terms and restrictions).
###
### This program is not meant to be used in a production environment. The
### author is not liable for any complications arising due to the use of
### this program.
###
from __future__ import print_function
import inspect
###
### Tidy logging helpers
###
LOG_LEVEL_DEBUG = 4
LOG_LEVEL_INFO = 2
LOG_LEVEL_WARN = 1
LOG_LEVEL_ERROR = 0
LOG_LEVEL_SILENT = -1
# Change the following to filter logging output
GLOBAL_LOG_LEVEL = LOG_LEVEL_SILENT
def log(callee, logstr, level=LOG_LEVEL_DEBUG):
prefixes = { LOG_LEVEL_DEBUG : "DBG: ",
LOG_LEVEL_INFO : "INF: ",
LOG_LEVEL_WARN : "WRN: ",
LOG_LEVEL_ERROR : "ERR: ",
LOG_LEVEL_SILENT: ""
}
if level <= GLOBAL_LOG_LEVEL:
if prefixes.has_key(level):
print(str(prefixes[level]) + callee.__class__.__name__ + "." + inspect.stack()[1][3] + "(): " + logstr)
else:
print(callee.__class__.__name__ + "." + inspect.stack()[1][3] + "(): " + logstr)

Binary file not shown.

View File

@ -0,0 +1,303 @@
#!/usr/bin/env/python
# -*- coding: utf-8 -*-
###
### Author: Chris Iatrou (ichrispa@core-vector.net)
### Version: rev 13
###
### This program was created for educational purposes and has been
### contributed to the open62541 project by the author. All licensing
### terms for this source is inherited by the terms and conditions
### specified for by the open62541 project (see the projects readme
### file for more information on the LGPL terms and restrictions).
###
### This program is not meant to be used in a production environment. The
### author is not liable for any complications arising due to the use of
### this program.
###
from logger import *
from ua_constants import *
import string
__unique_item_id = 0
defined_typealiases = []
class open62541_MacroHelper():
def __init__(self, supressGenerationOfAttribute=[]):
self.supressGenerationOfAttribute = supressGenerationOfAttribute
def getCreateExpandedNodeIDMacro(self, node):
if node.id().i != None:
return "UA_EXPANDEDNODEID_NUMERIC(" + str(node.id().ns) + ", " + str(node.id().i) + ")"
elif node.id().s != None:
return "UA_EXPANDEDNODEID_STRING(" + str(node.id().ns) + ", " + node.id().s + ")"
elif node.id().b != None:
log(self, "NodeID Generation macro for bytestrings has not been implemented.")
return ""
elif node.id().g != None:
log(self, "NodeID Generation macro for guids has not been implemented.")
return ""
else:
return ""
def substitutePunctuationCharacters(self, input):
''' substitutePunctuationCharacters
Replace punctuation characters in input. Part of this class because it is used by
ua_namespace on occasion.
returns: C-printable string representation of input
'''
# No punctuation characters <>!$
illegal_chars = list(string.punctuation)
# underscore is allowed
illegal_chars.remove('_')
illegal = "".join(illegal_chars)
substitution = ""
# Map all punctuation characters to underscore
for illegal_char in illegal_chars:
substitution = substitution + '_'
return input.translate(string.maketrans(illegal, substitution), illegal)
def getNodeIdDefineString(self, node):
code = []
extrNs = node.browseName().split(":")
symbolic_name = ""
# strip all characters that would be illegal in C-Code
if len(extrNs) > 1:
nodename = extrNs[1]
else:
nodename = extrNs[0]
symbolic_name = self.substitutePunctuationCharacters(nodename)
if symbolic_name != nodename :
log(self, "Subsituted characters in browsename for nodeid " + str(node.id().i) + " while generating C-Code ", LOG_LEVEL_WARN)
if symbolic_name in defined_typealiases:
log(self, "Typealias definition of " + str(node.id().i) + " is non unique!", LOG_LEVEL_WARN)
extendedN = 1
while (symbolic_name+"_"+str(extendedN) in defined_typealiases):
log(self, "Typealias definition of " + str(node.id().i) + " is non unique!", LOG_LEVEL_WARN)
extendedN+=1
symbolic_name = symbolic_name+"_"+str(extendedN)
defined_typealiases.append(symbolic_name)
code.append("#define UA_NS" + str(node.id().ns) + "ID_" + symbolic_name.upper() + " " + str(node.id().i))
return code
def getCreateNodeIDMacro(self, node):
if node.id().i != None:
return "UA_NODEID_NUMERIC(" + str(node.id().ns) + ", " + str(node.id().i) + ")"
elif node.id().s != None:
return "UA_NODEID_STRING(" + str(node.id().ns) + ", " + node.id().s + ")"
elif node.id().b != None:
log(self, "NodeID Generation macro for bytestrings has not been implemented.")
return ""
elif node.id().g != None:
log(self, "NodeID Generation macro for guids has not been implemented.")
return ""
else:
return ""
def getCreateStandaloneReference(self, sourcenode, reference):
code = []
if reference.isForward():
code.append("UA_Server_addReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", true);")
else:
code.append("UA_Server_addReference(server, " + self.getCreateNodeIDMacro(sourcenode) + ", " + self.getCreateNodeIDMacro(reference.referenceType()) + ", " + self.getCreateExpandedNodeIDMacro(reference.target()) + ", false);")
return code
def getCreateNodeNoBootstrap(self, node, parentNode, parentReference, unprintedNodes):
code = []
code.append("// Node: " + str(node) + ", " + str(node.browseName()))
if node.nodeClass() == NODE_CLASS_OBJECT:
nodetype = "Object"
elif node.nodeClass() == NODE_CLASS_VARIABLE:
nodetype = "Variable"
elif node.nodeClass() == NODE_CLASS_METHOD:
nodetype = "Method"
elif node.nodeClass() == NODE_CLASS_OBJECTTYPE:
nodetype = "ObjectType"
elif node.nodeClass() == NODE_CLASS_REFERENCETYPE:
nodetype = "ReferenceType"
elif node.nodeClass() == NODE_CLASS_VARIABLETYPE:
nodetype = "VariableType"
elif node.nodeClass() == NODE_CLASS_DATATYPE:
nodetype = "DataType"
elif node.nodeClass() == NODE_CLASS_VIEW:
nodetype = "View"
else:
code.append("/* undefined nodeclass */")
return code;
# If this is a method, construct in/outargs for addMethod
#inputArguments.arrayDimensionsSize = 0;
#inputArguments.arrayDimensions = NULL;
#inputArguments.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
# Node ordering should have made sure that arguments, if they exist, have not been printed yet
if node.nodeClass() == NODE_CLASS_METHOD:
inArgVal = []
outArgVal = []
code.append("UA_Argument *inputArguments = NULL;")
code.append("UA_Argument *outputArguments = NULL;")
for r in node.getReferences():
if r.target() != None and r.target().nodeClass() == NODE_CLASS_VARIABLE and r.target().browseName() == 'InputArguments':
while r.target() in unprintedNodes:
unprintedNodes.remove(r.target())
if r.target().value() != None:
inArgVal = r.target().value().value
elif r.target() != None and r.target().nodeClass() == NODE_CLASS_VARIABLE and r.target().browseName() == 'OutputArguments':
while r.target() in unprintedNodes:
unprintedNodes.remove(r.target())
if r.target().value() != None:
outArgVal = r.target().value().value
if len(inArgVal)>0:
code.append("")
code.append("inputArguments = (UA_Argument *) malloc(sizeof(UA_Argument) * " + str(len(inArgVal)) + ");")
code.append("int inputArgumentCnt;")
code.append("for (inputArgumentCnt=0; inputArgumentCnt<" + str(len(inArgVal)) + "; inputArgumentCnt++) UA_Argument_init(&inputArguments[inputArgumentCnt]); ")
argumentCnt = 0
for inArg in inArgVal:
if inArg.getValueFieldByAlias("Description") != None:
code.append("inputArguments[" + str(argumentCnt) + "].description = UA_LOCALIZEDTEXT(\"" + str(inArg.getValueFieldByAlias("Description")[0]) + "\",\"" + str(inArg.getValueFieldByAlias("Description")[1]) + "\");")
if inArg.getValueFieldByAlias("Name") != None:
code.append("inputArguments[" + str(argumentCnt) + "].name = UA_STRING(\"" + str(inArg.getValueFieldByAlias("Name")) + "\");")
if inArg.getValueFieldByAlias("ValueRank") != None:
code.append("inputArguments[" + str(argumentCnt) + "].valueRank = " + str(inArg.getValueFieldByAlias("ValueRank")) + ";")
if inArg.getValueFieldByAlias("DataType") != None:
code.append("inputArguments[" + str(argumentCnt) + "].dataType = " + str(self.getCreateNodeIDMacro(inArg.getValueFieldByAlias("DataType"))) + ";")
#if inArg.getValueFieldByAlias("ArrayDimensions") != None:
# code.append("inputArguments[" + str(argumentCnt) + "].arrayDimensions = " + str(inArg.getValueFieldByAlias("ArrayDimensions")) + ";")
argumentCnt += 1
if len(outArgVal)>0:
code.append("")
code.append("outputArguments = (UA_Argument *) malloc(sizeof(UA_Argument) * " + str(len(outArgVal)) + ");")
code.append("int outputArgumentCnt;")
code.append("for (outputArgumentCnt=0; outputArgumentCnt<" + str(len(outArgVal)) + "; outputArgumentCnt++) UA_Argument_init(&outputArguments[outputArgumentCnt]); ")
argumentCnt = 0
for outArg in outArgVal:
if outArg.getValueFieldByAlias("Description") != None:
code.append("outputArguments[" + str(argumentCnt) + "].description = UA_LOCALIZEDTEXT(\"" + str(outArg.getValueFieldByAlias("Description")[0]) + "\",\"" + str(outArg.getValueFieldByAlias("Description")[1]) + "\");")
if outArg.getValueFieldByAlias("Name") != None:
code.append("outputArguments[" + str(argumentCnt) + "].name = UA_STRING(\"" + str(outArg.getValueFieldByAlias("Name")) + "\");")
if outArg.getValueFieldByAlias("ValueRank") != None:
code.append("outputArguments[" + str(argumentCnt) + "].valueRank = " + str(outArg.getValueFieldByAlias("ValueRank")) + ";")
if outArg.getValueFieldByAlias("DataType") != None:
code.append("outputArguments[" + str(argumentCnt) + "].dataType = " + str(self.getCreateNodeIDMacro(outArg.getValueFieldByAlias("DataType"))) + ";")
#if outArg.getValueFieldByAlias("ArrayDimensions") != None:
# code.append("outputArguments[" + str(argumentCnt) + "].arrayDimensions = " + str(outArg.getValueFieldByAlias("ArrayDimensions")) + ";")
argumentCnt += 1
# print the attributes struct
code.append("UA_%sAttributes attr;" % nodetype)
code.append("UA_%sAttributes_init(&attr);" % nodetype);
code.append("attr.displayName = UA_LOCALIZEDTEXT(\"\", \"" + str(node.displayName()) + "\");")
code.append("attr.description = UA_LOCALIZEDTEXT(\"\", \"" + str(node.description()) + "\");")
if nodetype in ["Variable", "VariableType"]:
code = code + node.printOpen62541CCode_SubtypeEarly(bootstrapping = False)
elif nodetype == "Method":
if node.executable():
code.append("attr.executable = true;")
if node.userExecutable():
code.append("attr.userExecutable = true;")
code.append("UA_NodeId nodeId = " + str(self.getCreateNodeIDMacro(node)) + ";")
if nodetype in ["Object", "Variable"]:
code.append("UA_NodeId typeDefinition = UA_NODEID_NULL;") # todo instantiation of object and variable types
code.append("UA_NodeId parentNodeId = " + str(self.getCreateNodeIDMacro(parentNode)) + ";")
code.append("UA_NodeId parentReferenceNodeId = " + str(self.getCreateNodeIDMacro(parentReference.referenceType())) + ";")
extrNs = node.browseName().split(":")
if len(extrNs) > 1:
code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(" + str(extrNs[0]) + ", \"" + extrNs[1] + "\");")
else:
code.append("UA_QualifiedName nodeName = UA_QUALIFIEDNAME(0, \"" + str(node.browseName()) + "\");")
# In case of a MethodNode: Add in|outArg struct generation here. Mandates that namespace reordering was done using
# Djikstra (check that arguments have not been printed). (@ichrispa)
code.append("UA_Server_add%sNode(server, nodeId, parentNodeId, parentReferenceNodeId, nodeName" % nodetype)
if nodetype in ["Object", "Variable"]:
code.append(" , typeDefinition")
if nodetype != "Method":
code.append(" , attr, NULL, NULL);")
else:
code.append(" , attr, (UA_MethodCallback) NULL, NULL, " + str(len(inArgVal)) + ", inputArguments, " + str(len(outArgVal)) + ", outputArguments, NULL);")
return code
def getCreateNodeBootstrap(self, node):
nodetype = ""
code = []
code.append("// Node: " + str(node) + ", " + str(node.browseName()))
if node.nodeClass() == NODE_CLASS_OBJECT:
nodetype = "Object"
elif node.nodeClass() == NODE_CLASS_VARIABLE:
nodetype = "Variable"
elif node.nodeClass() == NODE_CLASS_METHOD:
nodetype = "Method"
elif node.nodeClass() == NODE_CLASS_OBJECTTYPE:
nodetype = "ObjectType"
elif node.nodeClass() == NODE_CLASS_REFERENCETYPE:
nodetype = "ReferenceType"
elif node.nodeClass() == NODE_CLASS_VARIABLETYPE:
nodetype = "VariableType"
elif node.nodeClass() == NODE_CLASS_DATATYPE:
nodetype = "DataType"
elif node.nodeClass() == NODE_CLASS_VIEW:
nodetype = "View"
else:
code.append("/* undefined nodeclass */")
return;
code.append("UA_" + nodetype + "Node *" + node.getCodePrintableID() + " = UA_NodeStore_new" + nodetype + "Node();")
if not "browsename" in self.supressGenerationOfAttribute:
extrNs = node.browseName().split(":")
if len(extrNs) > 1:
code.append(node.getCodePrintableID() + "->browseName = UA_QUALIFIEDNAME_ALLOC(" + str(extrNs[0]) + ", \"" + extrNs[1] + "\");")
else:
code.append(node.getCodePrintableID() + "->browseName = UA_QUALIFIEDNAME_ALLOC(0, \"" + node.browseName() + "\");")
if not "displayname" in self.supressGenerationOfAttribute:
code.append(node.getCodePrintableID() + "->displayName = UA_LOCALIZEDTEXT_ALLOC(\"en_US\", \"" + node.displayName() + "\");")
if not "description" in self.supressGenerationOfAttribute:
code.append(node.getCodePrintableID() + "->description = UA_LOCALIZEDTEXT_ALLOC(\"en_US\", \"" + node.description() + "\");")
if not "writemask" in self.supressGenerationOfAttribute:
if node.__node_writeMask__ != 0:
code.append(node.getCodePrintableID() + "->writeMask = (UA_Int32) " + str(node.__node_writeMask__) + ";")
if not "userwritemask" in self.supressGenerationOfAttribute:
if node.__node_userWriteMask__ != 0:
code.append(node.getCodePrintableID() + "->userWriteMask = (UA_Int32) " + str(node.__node_userWriteMask__) + ";")
if not "nodeid" in self.supressGenerationOfAttribute:
if node.id().ns != 0:
code.append(node.getCodePrintableID() + "->nodeId.namespaceIndex = " + str(node.id().ns) + ";")
if node.id().i != None:
code.append(node.getCodePrintableID() + "->nodeId.identifier.numeric = " + str(node.id().i) + ";")
elif node.id().b != None:
code.append(node.getCodePrintableID() + "->nodeId.identifierType = UA_NODEIDTYPE_BYTESTRING;")
log(self, "ByteString IDs for nodes has not been implemented yet.", LOG_LEVEL_ERROR)
return []
elif node.id().g != None:
#<jpfr> the string is sth like { .length = 111, .data = <ptr> }
#<jpfr> there you _may_ alloc the <ptr> on the heap
#<jpfr> for the guid, just set it to {.data1 = 111, .data2 = 2222, ....
code.append(node.getCodePrintableID() + "->nodeId.identifierType = UA_NODEIDTYPE_GUID;")
log(self, "GUIDs for nodes has not been implemented yet.", LOG_LEVEL_ERROR)
return []
elif node.id().s != None:
code.append(node.getCodePrintableID() + "->nodeId.identifierType = UA_NODEIDTYPE_STRING;")
code.append(node.getCodePrintableID() + "->nodeId.identifier.numeric = UA_STRING_ALLOC(\"" + str(node.id().i) + "\");")
else:
log(self, "Node ID is not numeric, bytestring, guid or string. I do not know how to create c code for that...", LOG_LEVEL_ERROR)
return []
return code

Binary file not shown.

View File

@ -0,0 +1,390 @@
#!/usr/bin/env/python
# -*- coding: utf-8 -*-
###
### Author: Chris Iatrou (ichrispa@core-vector.net)
###
### This program was created for educational purposes and has been
### contributed to the open62541 project by the author. All licensing
### terms for this source is inherited by the terms and conditions
### specified for by the open62541 project (see the projects readme
### file for more information on the LGPL terms and restrictions).
###
### This program is not meant to be used in a production environment. The
### author is not liable for any complications arising due to the use of
### this program.
###
from logger import *
from ua_constants import *
import tempfile
import xml.dom.minidom as dom
import os
import string
from collections import Counter
from ua_namespace import opcua_node_id_t
class preProcessDocument:
originXML = '' # Original XML passed to the preprocessor
targetXML = () # tuple of (fileHandle, fileName)
nodeset = '' # Parsed DOM XML object
parseOK = False;
containedNodes = [] # contains tuples of (opcua_node_id_t, xmlelement)
referencedNodes = [] # contains tuples of (opcua_node_id_t, xmlelement)
namespaceOrder = [] # contains xmlns:sX attributed as tuples (int ns, string name)
namespaceQualifiers = [] # contains all xmlns:XYZ qualifiers that might prefix value aliases (like "<uax:Int32>")
referencedNamesSpaceUris = [] # contains <NamespaceUris> URI elements
def __init__(self, originXML):
self.originXML = originXML
self.targetXML = tempfile.mkstemp(prefix=os.path.basename(originXML)+"_preProcessed-" ,suffix=".xml")
self.parseOK = True
self.containedNodes = []
self.referencedNodes = []
self.namespaceOrder = []
self.referencedNamesSpaceUris = []
self.namespaceQualifiers = []
try:
self.nodeset = dom.parse(originXML)
if len(self.nodeset.getElementsByTagName("UANodeSet")) == 0 or len(self.nodeset.getElementsByTagName("UANodeSet")) > 1:
log(self, "Document " + self.targetXML[1] + " contains no or more then 1 nodeset", LOG_LEVEL_ERROR)
self.parseOK = False
except:
self.parseOK = False
log(self, "Adding new document to be preprocessed " + os.path.basename(originXML) + " as " + self.targetXML[1], LOG_LEVEL_DEBUG)
def clean(self):
#os.close(self.targetXML[0]) Don't -> done to flush() after finalize()
os.remove(self.targetXML[1])
def getTargetXMLName(self):
if (self.parseOK):
return self.targetXML[1]
return None
def extractNamespaceURIs(self):
""" extractNamespaceURIs
minidom gobbles up <NamespaceUris></NamespaceUris> elements, without a decent
way to reliably access this dom2 <uri></uri> elements (only attribute xmlns= are
accessible using minidom). We need them for dereferencing though... This
function attempts to do just that.
returns: Nothing
"""
infile = open(self.originXML)
foundURIs = False
nsline = ""
line = infile.readline()
for line in infile:
if "<namespaceuris>" in line.lower():
foundURIs = True
elif "</namespaceuris>" in line.lower():
foundURIs = False
nsline = nsline + line
break
if foundURIs:
nsline = nsline + line
if len(nsline) > 0:
ns = dom.parseString(nsline).getElementsByTagName("NamespaceUris")
for uri in ns[0].childNodes:
if uri.nodeType != uri.ELEMENT_NODE:
continue
self.referencedNamesSpaceUris.append(uri.firstChild.data)
infile.close()
def analyze(self):
""" analyze()
analyze will gather information about the nodes and references contained in a XML File
to facilitate later preprocessing stages that adresss XML dependency issues
returns: No return value
"""
nodeIds = []
ns = self.nodeset.getElementsByTagName("UANodeSet")
# We need to find out what the namespace calls itself and other referenced, as numeric id's are pretty
# useless sans linked nodes. There is two information sources...
self.extractNamespaceURIs() # From <URI>...</URI> definitions
for key in ns[0].attributes.keys(): # from xmlns:sX attributes
if "xmlns:" in key: # Any key: we will be removing these qualifiers from Values later
self.namespaceQualifiers.append(key.replace("xmlns:",""))
if "xmlns:s" in key: # get a numeric nsId and modelname/uri
self.namespaceOrder.append((int(key.replace("xmlns:s","")), ns[0].getAttribute(key)))
# Get all nodeIds contained in this XML
for nd in ns[0].childNodes:
if nd.nodeType != nd.ELEMENT_NODE:
continue
if nd.hasAttribute(u'NodeId'):
self.containedNodes.append( (opcua_node_id_t(nd.getAttribute(u'NodeId')), nd) )
refs = nd.getElementsByTagName(u'References')[0]
for ref in refs.childNodes:
if ref.nodeType == ref.ELEMENT_NODE:
self.referencedNodes.append( (opcua_node_id_t(ref.firstChild.data), ref) )
log(self, "Nodes: " + str(len(self.containedNodes)) + " References: " + str(len(self.referencedNodes)), LOG_LEVEL_DEBUG)
def getNamespaceId(self):
""" namespaceId()
Counts the namespace IDs in all nodes of this XML and picks the most used
namespace as the numeric identifier of this data model.
returns: Integer ID of the most propable/most used namespace in this XML
"""
max = 0;
namespaceIdGuessed = 0;
idDict = {}
for ndid in self.containedNodes:
if not idDict.has_key(ndid[0].ns):
idDict[ndid[0].ns] = 1
else:
idDict[ndid[0].ns] = idDict[ndid[0].ns] + 1
for entry in idDict:
if idDict[entry] > max:
max = idDict[entry]
namespaceIdGuessed = entry
log(self, "XML Contents are propably in namespace " + str(entry) + " (used by " + str(idDict[entry]) + " Nodes)", LOG_LEVEL_DEBUG)
return namespaceIdGuessed
def getReferencedNamespaceUri(self, nsId):
""" getReferencedNamespaceUri
returns an URL that hopefully corresponds to the nsId that was used to reference this model
return: URI string corresponding to nsId
"""
# Might be the more reliable method: Get the URI from the xmlns attributes (they have numers)
if len(self.namespaceOrder) > 0:
for el in self.namespaceOrder:
if el[0] == nsId:
return el[1]
# Fallback:
# Some models do not have xmlns:sX attributes, but still <URI>s (usually when they only reference NS0)
if len(self.referencedNamesSpaceUris) > 0 and len(self.referencedNamesSpaceUris) >= nsId-1:
return self.referencedNamesSpaceUris[nsId-1]
#Nope, not found.
return ""
def getNamespaceDependencies(self):
deps = []
for ndid in self.referencedNodes:
if not ndid[0].ns in deps:
deps.append(ndid[0].ns)
return deps
def finalize(self):
outfile = self.targetXML[0]
outline = self.nodeset.toxml()
for qualifier in self.namespaceQualifiers:
rq = qualifier+":"
outline = outline.replace(rq.decode('UTF-8'), "")
os.write(outfile, outline.encode('UTF-8'))
os.close(outfile)
def reassignReferencedNamespaceId(self, currentNsId, newNsId):
""" reassignReferencedNamespaceId
Iterates over all references in this document, find references to currentNsId and changes them to newNsId.
NodeIds themselves are not altered.
returns: nothing
"""
for refNd in self.referencedNodes:
if refNd[0].ns == currentNsId:
refNd[1].firstChild.data = refNd[1].firstChild.data.replace("ns="+str(currentNsId), "ns="+str(newNsId))
refNd[0].ns = newNsId
refNd[0].toString()
def reassignNamespaceId(self, currentNsId, newNsId):
""" reassignNamespaceId
Iterates over all nodes in this document, find those in namespace currentNsId and changes them to newNsId.
returns: nothing
"""
log(self, "Migrating nodes /w ns index " + str(currentNsId) + " to " + str(newNsId), LOG_LEVEL_DEBUG)
for nd in self.containedNodes:
if nd[0].ns == currentNsId:
# In our own document, update any references to this node
for refNd in self.referencedNodes:
if refNd[0].ns == currentNsId and refNd[0] == nd[0]:
refNd[1].firstChild.data = refNd[1].firstChild.data.replace("ns="+str(currentNsId), "ns="+str(newNsId))
refNd[0].ns = newNsId
refNd[0].toString()
nd[1].setAttribute(u'NodeId', nd[1].getAttribute(u'NodeId').replace("ns="+str(currentNsId), "ns="+str(newNsId)))
nd[0].ns = newNsId
nd[0].toString()
class open62541_XMLPreprocessor:
preProcDocuments = []
def __init__(self):
self.preProcDocuments = []
def addDocument(self, documentPath):
self.preProcDocuments.append(preProcessDocument(documentPath))
def removePreprocessedFiles(self):
for doc in self.preProcDocuments:
doc.clean()
def getPreProcessedFiles(self):
files = []
for doc in self.preProcDocuments:
if (doc.parseOK):
files.append(doc.getTargetXMLName())
return files
def testModelCongruencyAgainstReferences(self, doc, refs):
""" testModelCongruencyAgainstReferences
Counts how many of the nodes referencef in refs can be found in the model
doc.
returns: double corresponding to the percentage of hits
"""
sspace = len(refs)
if sspace == 0:
return float(0)
found = 0
for ref in refs:
for n in doc.containedNodes:
if str(ref) == str(n[0]):
print ref, n[0]
found = found + 1
break
return float(found)/float(sspace)
def preprocess_assignUniqueNsIds(self):
nsdep = []
docLst = []
# Search for namespace 0('s) - plural possible if user is overwriting NS0 defaults
# Remove them from the list of namespaces, zero does not get demangled
for doc in self.preProcDocuments:
if doc.getNamespaceId() == 0:
docLst.append(doc)
for doc in docLst:
self.preProcDocuments.remove(doc)
# Reassign namespace id's to be in ascending order
nsidx = 1 # next namespace id to assign on collision (first one will be "2")
for doc in self.preProcDocuments:
nsidx = nsidx + 1
nsid = doc.getNamespaceId()
doc.reassignNamespaceId(nsid, nsidx)
docLst.append(doc)
log(self, "Document " + doc.originXML + " is now namespace " + str(nsidx), LOG_LEVEL_INFO)
self.preProcDocuments = docLst
def getUsedNamespaceArrayNames(self):
""" getUsedNamespaceArrayNames
Returns the XML xmlns:s1 or <URI>[0] of each XML document (if contained/possible)
returns: dict of int:nsId -> string:url
"""
nsName = {}
for doc in self.preProcDocuments:
uri = doc.getReferencedNamespaceUri(1)
if uri == None:
uri = "http://modeluri.not/retrievable/from/xml"
nsName[doc.getNamespaceId()] = doc.getReferencedNamespaceUri(1)
return nsName
def preprocess_linkDependantModels(self):
revertToStochastic = [] # (doc, int id), where id was not resolvable using model URIs
# Attemp to identify the model relations by using model URIs in xmlns:sX or <URI> contents
for doc in self.preProcDocuments:
nsid = doc.getNamespaceId()
dependencies = doc.getNamespaceDependencies()
for d in dependencies:
if d != nsid and d != 0:
# Attempt to identify the namespace URI this d referes to...
nsUri = doc.getReferencedNamespaceUri(d) # FIXME: This could actually fail and return ""!
log(self, "Need a namespace referenced as " + str(d) + ". Which hopefully is " + nsUri, LOG_LEVEL_INFO)
targetDoc = None
for tgt in self.preProcDocuments:
# That model, whose URI is known but its current id is not, will
# refer have referred to itself as "1"
if tgt.getReferencedNamespaceUri(1) == nsUri:
targetDoc = tgt
break
if not targetDoc == None:
# Found the model... relink the references
doc.reassignReferencedNamespaceId(d, targetDoc.getNamespaceId())
continue
else:
revertToStochastic.append((doc, d))
log(self, "Failed to reliably identify which XML/Model " + os.path.basename(doc.originXML) + " calls ns=" +str(d), LOG_LEVEL_WARN)
for (doc, d) in revertToStochastic:
log(self, "Attempting to find stochastic match for target namespace ns=" + str(d) + " of " + os.path.basename(doc.originXML), LOG_LEVEL_WARN)
# Copy all references to the given namespace
refs = []
matches = [] # list of (match%, targetDoc) to pick from later
for ref in doc.referencedNodes:
if ref[0].ns == d:
refs.append(opcua_node_id_t(str(ref[0])))
for tDoc in self.preProcDocuments:
tDocId = tDoc.getNamespaceId()
# Scenario: If these references did target this documents namespace...
for r in refs:
r.ns = tDocId
r.toString()
# ... how many of them would be found!?
c = self.testModelCongruencyAgainstReferences(tDoc, refs)
print c
if c>0:
matches.append(c, tDoc)
best = (0, None)
for m in matches:
print m[0]
if m[0] > best[0]:
best = m
if best[1] != None:
log(self, "Best match (" + str(best[1]*100) + "%) for what " + os.path.basename(doc.originXML) + " refers to as ns="+str(d)+" was " + os.path.basename(best[1].originXML), LOG_LEVEL_WARN)
doc.reassignReferencedNamespaceId(d, best[1].getNamespaceId())
else:
log(self, "Failed to find a match for what " + os.path.basename(doc.originXML) + " refers to as ns=" + str(d) ,LOG_LEVEL_ERROR )
def preprocessAll(self):
##
## First: Gather statistics about the namespaces:
for doc in self.preProcDocuments:
doc.analyze()
# Preprocess step: Remove XML specific Naming scheme ("uax:")
# FIXME: Not implemented
##
## Preprocess step: Check namespace ID multiplicity and reassign IDs if necessary
##
self.preprocess_assignUniqueNsIds()
self.preprocess_linkDependantModels()
##
## Prep step: prevent any XML from using namespace 1 (reserved for instances)
## FIXME: Not implemented
##
## Final: Write modified XML tmp files
for doc in self.preProcDocuments:
doc.finalize()
return True

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,57 @@
#!/usr/bin/env/python
# -*- coding: utf-8 -*-
###
### Author: Chris Iatrou (ichrispa@core-vector.net)
### Version: rev 13
###
### This program was created for educational purposes and has been
### contributed to the open62541 project by the author. All licensing
### terms for this source is inherited by the terms and conditions
### specified for by the open62541 project (see the projects readme
### file for more information on the LGPL terms and restrictions).
###
### This program is not meant to be used in a production environment. The
### author is not liable for any complications arising due to the use of
### this program.
###
NODE_CLASS_GENERERIC = 0
NODE_CLASS_OBJECT = 1
NODE_CLASS_VARIABLE = 2
NODE_CLASS_METHOD = 4
NODE_CLASS_OBJECTTYPE = 8
NODE_CLASS_VARIABLETYPE = 16
NODE_CLASS_REFERENCETYPE = 32
NODE_CLASS_DATATYPE = 64
NODE_CLASS_VIEW = 128
# Not in OPC-UA, but exists in XML
NODE_CLASS_METHODTYPE = 256
##
## Numeric codes used to encode binary type fields:
##
BUILTINTYPE_TYPEID_EXTENSIONOBJECT = 1
BUILTINTYPE_TYPEID_LOCALIZEDTEXT = 2
BUILTINTYPE_TYPEID_EXPANDEDNODEID = 3
BUILTINTYPE_TYPEID_NODEID = 4
BUILTINTYPE_TYPEID_DATETIME = 5
BUILTINTYPE_TYPEID_QUALIFIEDNAME = 6
BUILTINTYPE_TYPEID_STATUSCODE = 7
BUILTINTYPE_TYPEID_GUID = 8
BUILTINTYPE_TYPEID_BOOLEAN = 9
BUILTINTYPE_TYPEID_BYTE = 10
BUILTINTYPE_TYPEID_SBYTE = 11
BUILTINTYPE_TYPEID_INT16 = 12
BUILTINTYPE_TYPEID_UINT16 = 13
BUILTINTYPE_TYPEID_INT32 = 14
BUILTINTYPE_TYPEID_UINT32 = 15
BUILTINTYPE_TYPEID_INT64 = 16
BUILTINTYPE_TYPEID_UINT64 = 17
BUILTINTYPE_TYPEID_FLOAT = 18
BUILTINTYPE_TYPEID_DOUBLE = 19
BUILTINTYPE_TYPEID_STRING = 20
BUILTINTYPE_TYPEID_XMLELEMENT = 21
BUILTINTYPE_TYPEID_BYTESTRING = 22
BUILTINTYPE_TYPEID_DIAGNOSTICINFO = 23

Binary file not shown.

View File

@ -0,0 +1,811 @@
#!/usr/bin/env/python
# -*- coding: utf-8 -*-
###
### Author: Chris Iatrou (ichrispa@core-vector.net)
### Version: rev 13
###
### This program was created for educational purposes and has been
### contributed to the open62541 project by the author. All licensing
### terms for this source is inherited by the terms and conditions
### specified for by the open62541 project (see the projects readme
### file for more information on the LGPL terms and restrictions).
###
### This program is not meant to be used in a production environment. The
### author is not liable for any complications arising due to the use of
### this program.
###
from __future__ import print_function
import sys
from time import struct_time, strftime, strptime, mktime
from struct import pack as structpack
from logger import *;
from ua_builtin_types import *;
from ua_node_types import *;
from ua_constants import *;
from open62541_MacroHelper import open62541_MacroHelper
def getNextElementNode(xmlvalue):
if xmlvalue == None:
return None
xmlvalue = xmlvalue.nextSibling
while not xmlvalue == None and not xmlvalue.nodeType == xmlvalue.ELEMENT_NODE:
xmlvalue = xmlvalue.nextSibling
return xmlvalue
###
### Namespace Organizer
###
class opcua_namespace():
""" Class holding and managing a set of OPCUA nodes.
This class handles parsing XML description of namespaces, instantiating
nodes, linking references, graphing the namespace and compiling a binary
representation.
Note that nodes assigned to this class are not restricted to having a
single namespace ID. This class represents the entire physical address
space of the binary representation and all nodes that are to be included
in that segment of memory.
"""
nodes = []
nodeids = {}
aliases = {}
__linkLater__ = []
__binaryIndirectPointers__ = []
name = ""
knownNodeTypes = ""
namespaceIdentifiers = {} # list of 'int':'string' giving different namespace an array-mapable name
def __init__(self, name):
self.nodes = []
self.knownNodeTypes = ['variable', 'object', 'method', 'referencetype', \
'objecttype', 'variabletype', 'methodtype', \
'datatype', 'referencetype', 'aliases']
self.name = name
self.nodeids = {}
self.aliases = {}
self.namespaceIdentifiers = {}
self.__binaryIndirectPointers__ = []
def addNamespace(self, numericId, stringURL):
self.namespaceIdentifiers[numericId] = stringURL
def linkLater(self, pointer):
""" Called by nodes or references who have parsed an XML reference to a
node represented by a string.
No return value
XML String representations of references have the form 'i=xy' or
'ns=1;s="This unique Node"'. Since during the parsing of this attribute
only a subset of nodes are known/parsed, this reference string cannot be
linked when encountered.
References register themselves with the namespace to have their target
attribute (string) parsed by linkOpenPointers() when all nodes are
created, so that target can be dereferenced an point to an actual node.
"""
self.__linkLater__.append(pointer)
def getUnlinkedPointers(self):
""" Return the list of references registered for linking during the next call
of linkOpenPointers()
"""
return self.__linkLater__
def unlinkedItemCount(self):
""" Returns the number of unlinked references that will be processed during
the next call of linkOpenPointers()
"""
return len(self.__linkLater__)
def buildAliasList(self, xmlelement):
""" Parses the <Alias> XML Element present in must XML NodeSet definitions.
No return value
Contents the Alias element are stored in a dictionary for further
dereferencing during pointer linkage (see linkOpenPointer()).
"""
if not xmlelement.tagName == "Aliases":
log(self, "XMLElement passed is not an Aliaslist", LOG_LEVEL_ERROR)
return
for al in xmlelement.childNodes:
if al.nodeType == al.ELEMENT_NODE:
if al.hasAttribute("Alias"):
aliasst = al.getAttribute("Alias")
if sys.version_info[0] < 3:
aliasnd = unicode(al.firstChild.data)
else:
aliasnd = al.firstChild.data
if not aliasst in self.aliases:
self.aliases[aliasst] = aliasnd
log(self, "Added new alias \"" + str(aliasst) + "\" == \"" + str(aliasnd) + "\"")
else:
if self.aliases[aliasst] != aliasnd:
log(self, "Alias definitions for " + aliasst + " differ. Have " + self.aliases[aliasst] + " but XML defines " + aliasnd + ". Keeping current definition.", LOG_LEVEL_ERROR)
def getNodeByBrowseName(self, idstring):
""" Returns the first node in the nodelist whose browseName matches idstring.
"""
matches = []
for n in self.nodes:
if idstring==str(n.browseName()):
matches.append(n)
if len(matches) > 1:
log(self, "Found multiple nodes with same ID!?", LOG_LEVEL_ERROR)
if len(matches) == 0:
return None
else:
return matches[0]
def getNodeByIDString(self, idstring):
""" Returns the first node in the nodelist whose id string representation
matches idstring.
"""
matches = []
for n in self.nodes:
if idstring==str(n.id()):
matches.append(n)
if len(matches) > 1:
log(self, "Found multiple nodes with same ID!?", LOG_LEVEL_ERROR)
if len(matches) == 0:
return None
else:
return matches[0]
def createNode(self, ndtype, xmlelement):
""" createNode is instantiates a node described by xmlelement, its type being
defined by the string ndtype.
No return value
If the xmlelement is an <Alias>, the contents will be parsed and stored
for later dereferencing during pointer linking (see linkOpenPointers).
Recognized types are:
* UAVariable
* UAObject
* UAMethod
* UAView
* UAVariableType
* UAObjectType
* UAMethodType
* UAReferenceType
* UADataType
For every recognized type, an appropriate node class is added to the node
list of the namespace. The NodeId of the given node is created and parsing
of the node attributes and elements is delegated to the parseXML() and
parseXMLSubType() functions of the instantiated class.
If the NodeID attribute is non-unique in the node list, the creation is
deferred and an error is logged.
"""
if not isinstance(xmlelement, dom.Element):
log(self, "Error: Can not create node from invalid XMLElement", LOG_LEVEL_ERROR)
return
# An ID is manditory for everything but aliases!
id = None
for idname in ['NodeId', 'NodeID', 'nodeid']:
if xmlelement.hasAttribute(idname):
id = xmlelement.getAttribute(idname)
if ndtype == 'aliases':
self.buildAliasList(xmlelement)
return
elif id == None:
log(self, "Error: XMLElement has no id, node will not be created!", LOG_LEVEL_INFO)
return
else:
id = opcua_node_id_t(id)
if str(id) in self.nodeids:
# Normal behavior: Do not allow duplicates, first one wins
#log(self, "XMLElement with duplicate ID " + str(id) + " found, node will not be created!", LOG_LEVEL_ERROR)
#return
# Open62541 behavior for header generation: Replace the duplicate with the new node
log(self, "XMLElement with duplicate ID " + str(id) + " found, node will be replaced!", LOG_LEVEL_INFO)
nd = self.getNodeByIDString(str(id))
self.nodes.remove(nd)
self.nodeids.pop(str(nd.id()))
node = None
if (ndtype == 'variable'):
node = opcua_node_variable_t(id, self)
elif (ndtype == 'object'):
node = opcua_node_object_t(id, self)
elif (ndtype == 'method'):
node = opcua_node_method_t(id, self)
elif (ndtype == 'objecttype'):
node = opcua_node_objectType_t(id, self)
elif (ndtype == 'variabletype'):
node = opcua_node_variableType_t(id, self)
elif (ndtype == 'methodtype'):
node = opcua_node_methodType_t(id, self)
elif (ndtype == 'datatype'):
node = opcua_node_dataType_t(id, self)
elif (ndtype == 'referencetype'):
node = opcua_node_referenceType_t(id, self)
else:
log(self, "No node constructor for type " + ndtype, LOG_LEVEL_ERROR)
if node != None:
node.parseXML(xmlelement)
self.nodes.append(node)
self.nodeids[str(node.id())] = node
def removeNodeById(self, nodeId):
nd = self.getNodeByIDString(nodeId)
if nd == None:
return False
log(self, "Removing nodeId " + str(nodeId), LOG_LEVEL_DEBUG)
self.nodes.remove(nd)
if nd.getInverseReferences() != None:
for ref in nd.getInverseReferences():
src = ref.target();
src.removeReferenceToNode(nd)
return True
def registerBinaryIndirectPointer(self, node):
""" Appends a node to the list of nodes that should be contained in the
first 765 bytes (255 pointer slots a 3 bytes) in the binary
representation (indirect referencing space).
This function is reserved for references and dataType pointers.
"""
if not node in self.__binaryIndirectPointers__:
self.__binaryIndirectPointers__.append(node)
return self.__binaryIndirectPointers__.index(node)
def getBinaryIndirectPointerIndex(self, node):
""" Returns the slot/index of a pointer in the indirect referencing space
(first 765 Bytes) of the binary representation.
"""
if not node in self.__binaryIndirectPointers__:
return -1
return self.__binaryIndirectPointers__.index(node)
def parseXML(self, xmldoc):
""" Reads an XML Namespace definition and instantiates node.
No return value
parseXML open the file xmldoc using xml.dom.minidom and searches for
the first UANodeSet Element. For every Element encountered, createNode
is called to instantiate a node of the appropriate type.
"""
typedict = {}
UANodeSet = dom.parse(xmldoc).getElementsByTagName("UANodeSet")
if len(UANodeSet) == 0:
log(self, "Error: No NodeSets found", LOG_LEVEL_ERROR)
return
if len(UANodeSet) != 1:
log(self, "Error: Found more than 1 Nodeset in XML File", LOG_LEVEL_ERROR)
UANodeSet = UANodeSet[0]
for nd in UANodeSet.childNodes:
if nd.nodeType != nd.ELEMENT_NODE:
continue
ndType = nd.tagName.lower()
if ndType[:2] == "ua":
ndType = ndType[2:]
elif not ndType in self.knownNodeTypes:
log(self, "XML Element or NodeType " + ndType + " is unknown and will be ignored", LOG_LEVEL_WARN)
continue
if not ndType in typedict:
typedict[ndType] = 1
else:
typedict[ndType] = typedict[ndType] + 1
self.createNode(ndType, nd)
log(self, "Currently " + str(len(self.nodes)) + " nodes in address space. Type distribution for this run was: " + str(typedict))
def linkOpenPointers(self):
""" Substitutes symbolic NodeIds in references for actual node instances.
No return value
References that have registered themselves with linkLater() to have
their symbolic NodeId targets ("ns=2; i=32") substited for an actual
node will be iterated by this function. For each reference encountered
in the list of unlinked/open references, the target string will be
evaluated and searched for in the node list of this namespace. If found,
the target attribute of the reference will be substituted for the
found node.
If a reference fails to get linked, it will remain in the list of
unlinked references. The individual items in this list can be
retrieved using getUnlinkedPointers().
"""
linked = []
log(self, str(self.unlinkedItemCount()) + " pointers need to get linked.")
for l in self.__linkLater__:
targetLinked = False
if not l.target() == None and not isinstance(l.target(), opcua_node_t):
if isinstance(l.target(),str) or isinstance(l.target(),unicode):
# If is not a node ID, it should be an alias. Try replacing it
# with a proper node ID
if l.target() in self.aliases:
l.target(self.aliases[l.target()])
# If the link is a node ID, try to find it hopening that no ass has
# defined more than one kind of id for that sucker
if l.target()[:2] == "i=" or l.target()[:2] == "g=" or \
l.target()[:2] == "b=" or l.target()[:2] == "s=" or \
l.target()[:3] == "ns=" :
tgt = self.getNodeByIDString(str(l.target()))
if tgt == None:
log(self, "Failed to link pointer to target (node not found) " + l.target(), LOG_LEVEL_ERROR)
else:
l.target(tgt)
targetLinked = True
else:
log(self, "Failed to link pointer to target (target not Alias or Node) " + l.target(), LOG_LEVEL_ERROR)
else:
log(self, "Failed to link pointer to target (don't know dummy type + " + str(type(l.target())) + " +) " + str(l.target()), LOG_LEVEL_ERROR)
else:
log(self, "Pointer has null target: " + str(l), LOG_LEVEL_ERROR)
referenceLinked = False
if not l.referenceType() == None:
if l.referenceType() in self.aliases:
l.referenceType(self.aliases[l.referenceType()])
if l.referenceType()[:2] == "i=" or l.referenceType()[:2] == "g=" or \
l.referenceType()[:2] == "b=" or l.referenceType()[:2] == "s=":
tgt = self.getNodeByIDString(str(l.referenceType()))
if tgt == None:
log(self, "Failed to link reference type to target (node not found) " + l.referenceType(), LOG_LEVEL_ERROR)
else:
l.referenceType(tgt)
referenceLinked = True
else:
referenceLinked = True
if referenceLinked == True and targetLinked == True:
linked.append(l)
# References marked as "not forward" must be inverted (removed from source node, assigned to target node and relinked)
log(self, "Inverting reference direction for all references with isForward==False attribute (is this correct!?)", LOG_LEVEL_WARN)
for n in self.nodes:
for r in n.getReferences():
if r.isForward() == False:
tgt = r.target()
if isinstance(tgt, opcua_node_t):
nref = opcua_referencePointer_t(n, parentNode=tgt)
nref.referenceType(r.referenceType())
tgt.addReference(nref)
# Create inverse references for all nodes
log(self, "Updating all referencedBy fields in nodes for inverse lookups.")
for n in self.nodes:
n.updateInverseReferences()
for l in linked:
self.__linkLater__.remove(l)
if len(self.__linkLater__) != 0:
log(self, str(len(self.__linkLater__)) + " could not be linked.", LOG_LEVEL_WARN)
def sanitize(self):
remove = []
log(self, "Sanitizing nodes and references...")
for n in self.nodes:
if n.sanitize() == False:
remove.append(n)
if not len(remove) == 0:
log(self, str(len(remove)) + " nodes will be removed because they failed sanitation.", LOG_LEVEL_WARN)
# FIXME: Some variable ns=0 nodes fail because they don't have DataType fields...
# How should this be handles!?
log(self, "Not actually removing nodes... it's unclear if this is valid or not", LOG_LEVEL_WARN)
def getRoot(self):
""" Returns the first node instance with the browseName "Root".
"""
return self.getNodeByBrowseName("Root")
def buildEncodingRules(self):
""" Calls buildEncoding() for all DataType nodes (opcua_node_dataType_t).
No return value
"""
stat = {True: 0, False: 0}
for n in self.nodes:
if isinstance(n, opcua_node_dataType_t):
n.buildEncoding()
stat[n.isEncodable()] = stat[n.isEncodable()] + 1
log(self, "Type definitions built/passed: " + str(stat), LOG_LEVEL_DEBUG)
def allocateVariables(self):
for n in self.nodes:
if isinstance(n, opcua_node_variable_t):
n.allocateValue()
def printDot(self, filename="namespace.dot"):
""" Outputs a graphiz/dot description of all nodes in the namespace.
Output will written into filename to be parsed by dot/neato...
Note that for namespaces with more then 20 nodes the reference structure
will lead to a mostly illegible and huge graph. Use printDotGraphWalk()
for plotting specific portions of a large namespace.
"""
file=open(filename, 'w+')
file.write("digraph ns {\n")
for n in self.nodes:
file.write(n.printDot())
file.write("}\n")
file.close()
def getSubTypesOf(self, tdNodes = None, currentNode = None, hasSubtypeRefNode = None):
# If this is a toplevel call, collect the following information as defaults
if tdNodes == None:
tdNodes = []
if currentNode == None:
currentNode = self.getNodeByBrowseName("HasTypeDefinition")
tdNodes.append(currentNode)
if len(tdNodes) < 1:
return []
if hasSubtypeRefNode == None:
hasSubtypeRefNode = self.getNodeByBrowseName("HasSubtype")
if hasSubtypeRefNode == None:
return tdNodes
# collect all subtypes of this node
for ref in currentNode.getReferences():
if ref.isForward() and ref.referenceType().id() == hasSubtypeRefNode.id():
tdNodes.append(ref.target())
self.getTypeDefinitionNodes(tdNodes=tdNodes, currentNode = ref.target(), hasSubtypeRefNode=hasSubtypeRefNode)
return tdNodes
def printDotGraphWalk(self, depth=1, filename="out.dot", rootNode=None, followInverse = False, excludeNodeIds=[]):
""" Outputs a graphiz/dot description the nodes centered around rootNode.
References beginning from rootNode will be followed for depth steps. If
"followInverse = True" is passed, then inverse (not Forward) references
will also be followed.
Nodes can be excluded from the graph by passing a list of NodeIds as
string representation using excludeNodeIds (ex ["i=53", "ns=2;i=453"]).
Output is written into filename to be parsed by dot/neato/srfp...
"""
iter = depth
processed = []
if rootNode == None or \
not isinstance(rootNode, opcua_node_t) or \
not rootNode in self.nodes:
root = self.getRoot()
else:
root = rootNode
file=open(filename, 'w+')
if root == None:
return
file.write("digraph ns {\n")
file.write(root.printDot())
refs=[]
if followInverse == True:
refs = root.getReferences(); # + root.getInverseReferences()
else:
for ref in root.getReferences():
if ref.isForward():
refs.append(ref)
while iter > 0:
tmp = []
for ref in refs:
if isinstance(ref.target(), opcua_node_t):
tgt = ref.target()
if not str(tgt.id()) in excludeNodeIds:
if not tgt in processed:
file.write(tgt.printDot())
processed.append(tgt)
if ref.isForward() == False and followInverse == True:
tmp = tmp + tgt.getReferences(); # + tgt.getInverseReferences()
elif ref.isForward() == True :
tmp = tmp + tgt.getReferences();
refs = tmp
iter = iter - 1
file.write("}\n")
file.close()
def __reorder_getMinWeightNode__(self, nmatrix):
rcind = -1
rind = -1
minweight = -1
minweightnd = None
for row in nmatrix:
rcind += 1
if row[0] == None:
continue
w = sum(row[1:])
if minweight < 0:
rind = rcind
minweight = w
minweightnd = row[0]
elif w < minweight:
rind = rcind
minweight = w
minweightnd = row[0]
return (rind, minweightnd, minweight)
def reorderNodesMinDependencies(self):
# create a matrix represtantion of all node
#
nmatrix = []
for n in range(0,len(self.nodes)):
nmatrix.append([None] + [0]*len(self.nodes))
typeRefs = []
tn = self.getNodeByBrowseName("HasTypeDefinition")
if tn != None:
typeRefs.append(tn)
typeRefs = typeRefs + self.getSubTypesOf(currentNode=tn)
subTypeRefs = []
tn = self.getNodeByBrowseName("HasSubtype")
if tn != None:
subTypeRefs.append(tn)
subTypeRefs = subTypeRefs + self.getSubTypesOf(currentNode=tn)
log(self, "Building connectivity matrix for node order optimization.")
# Set column 0 to contain the node
for node in self.nodes:
nind = self.nodes.index(node)
nmatrix[nind][0] = node
# Determine the dependencies of all nodes
for node in self.nodes:
nind = self.nodes.index(node)
#print "Examining node " + str(nind) + " " + str(node)
for ref in node.getReferences():
if isinstance(ref.target(), opcua_node_t):
tind = self.nodes.index(ref.target())
# Typedefinition of this node has precedence over this node
if ref.referenceType() in typeRefs and ref.isForward():
nmatrix[nind][tind+1] += 1
# isSubTypeOf/typeDefinition of this node has precedence over this node
elif ref.referenceType() in subTypeRefs and not ref.isForward():
nmatrix[nind][tind+1] += 1
# Else the target depends on us
elif ref.isForward():
nmatrix[tind][nind+1] += 1
log(self, "Using Djikstra topological sorting to determine printing order.")
reorder = []
while len(reorder) < len(self.nodes):
(nind, node, w) = self.__reorder_getMinWeightNode__(nmatrix)
#print str(100*float(len(reorder))/len(self.nodes)) + "% " + str(w) + " " + str(node) + " " + str(node.browseName())
reorder.append(node)
for ref in node.getReferences():
if isinstance(ref.target(), opcua_node_t):
tind = self.nodes.index(ref.target())
if ref.referenceType() in typeRefs and ref.isForward():
nmatrix[nind][tind+1] -= 1
elif ref.referenceType() in subTypeRefs and not ref.isForward():
nmatrix[nind][tind+1] -= 1
elif ref.isForward():
nmatrix[tind][nind+1] -= 1
nmatrix[nind][0] = None
self.nodes = reorder
log(self, "Nodes reordered.")
return
def printOpen62541Header(self, printedExternally=[], supressGenerationOfAttribute=[], outfilename=""):
unPrintedNodes = []
unPrintedRefs = []
code = []
header = []
# Reorder our nodes to produce a bare minimum of bootstrapping dependencies
log(self, "Reordering nodes for minimal dependencies during printing.")
self.reorderNodesMinDependencies()
# Some macros (UA_EXPANDEDNODEID_MACRO()...) are easily created, but
# bulky. This class will help to offload some code.
codegen = open62541_MacroHelper(supressGenerationOfAttribute=supressGenerationOfAttribute)
# Populate the unPrinted-Lists with everything we have.
# Every Time a nodes printfunction is called, it will pop itself and
# all printed references from these lists.
for n in self.nodes:
if not n in printedExternally:
unPrintedNodes.append(n)
else:
log(self, "Node " + str(n.id()) + " is being ignored.", LOG_LEVEL_DEBUG)
for n in unPrintedNodes:
for r in n.getReferences():
if (r.target() != None) and (r.target().id() != None) and (r.parent() != None):
unPrintedRefs.append(r)
log(self, str(len(unPrintedNodes)) + " Nodes, " + str(len(unPrintedRefs)) + "References need to get printed.", LOG_LEVEL_DEBUG)
header.append("/* WARNING: This is a generated file.\n * Any manual changes will be overwritten.\n\n */")
code.append("/* WARNING: This is a generated file.\n * Any manual changes will be overwritten.\n\n */")
header.append('#ifndef '+outfilename.upper()+'_H_')
header.append('#define '+outfilename.upper()+'_H_')
header.append('#ifdef UA_NO_AMALGAMATION')
header.append('#include "server/ua_server_internal.h"')
header.append('#include "server/ua_nodes.h"')
header.append('#include "ua_util.h"')
header.append('#include "ua_types.h"')
header.append('#include "ua_types_encoding_binary.h"')
header.append('#include "ua_types_generated_encoding_binary.h"')
header.append('#include "ua_transport_generated_encoding_binary.h"')
header.append('#else')
header.append('#include "open62541.h"')
header.append('#define NULL ((void *)0)')
header.append('#endif')
code.append('#include "'+outfilename+'.h"')
code.append("UA_INLINE void "+outfilename+"(UA_Server *server) {")
# Before printing nodes, we need to request additional namespace arrays from the server
for nsid in self.namespaceIdentifiers:
if nsid == 0 or nsid==1:
continue
else:
name = self.namespaceIdentifiers[nsid]
name = name.replace("\"","\\\"")
code.append("UA_Server_addNamespace(server, \"" + name.encode('UTF-8') + "\");")
# Find all references necessary to create the namespace and
# "Bootstrap" them so all other nodes can safely use these referencetypes whenever
# they can locate both source and target of the reference.
log(self, "Collecting all references used in the namespace.", LOG_LEVEL_DEBUG)
refsUsed = []
for n in self.nodes:
# Since we are already looping over all nodes, use this chance to print NodeId defines
if n.id().ns != 0:
nc = n.nodeClass()
if nc != NODE_CLASS_OBJECT and nc != NODE_CLASS_VARIABLE and nc != NODE_CLASS_VIEW:
header = header + codegen.getNodeIdDefineString(n)
# Now for the actual references...
for r in n.getReferences():
# Only print valid refernces in namespace 0 (users will not want their refs bootstrapped)
if not r.referenceType() in refsUsed and r.referenceType() != None and r.referenceType().id().ns == 0:
refsUsed.append(r.referenceType())
log(self, str(len(refsUsed)) + " reference types are used in the namespace, which will now get bootstrapped.", LOG_LEVEL_DEBUG)
for r in refsUsed:
code = code + r.printOpen62541CCode(unPrintedNodes, unPrintedRefs);
header.append("extern void "+outfilename+"(UA_Server *server);\n")
header.append("#endif /* "+outfilename.upper()+"_H_ */")
# Note to self: do NOT - NOT! - try to iterate over unPrintedNodes!
# Nodes remove themselves from this list when printed.
log(self, "Printing all other nodes.", LOG_LEVEL_DEBUG)
for n in self.nodes:
code = code + n.printOpen62541CCode(unPrintedNodes, unPrintedRefs, supressGenerationOfAttribute=supressGenerationOfAttribute)
if len(unPrintedNodes) != 0:
log(self, "" + str(len(unPrintedNodes)) + " nodes could not be translated to code.", LOG_LEVEL_WARN)
else:
log(self, "Printing suceeded for all nodes", LOG_LEVEL_DEBUG)
if len(unPrintedRefs) != 0:
log(self, "Attempting to print " + str(len(unPrintedRefs)) + " unprinted references.", LOG_LEVEL_DEBUG)
tmprefs = []
for r in unPrintedRefs:
if not (r.target() not in unPrintedNodes) and not (r.parent() in unPrintedNodes):
if not isinstance(r.parent(), opcua_node_t):
log(self, "Reference has no parent!", LOG_LEVEL_DEBUG)
elif not isinstance(r.parent().id(), opcua_node_id_t):
log(self, "Parents nodeid is not a nodeID!", LOG_LEVEL_DEBUG)
else:
if (len(tmprefs) == 0):
code.append("// Creating leftover references:")
code = code + codegen.getCreateStandaloneReference(r.parent(), r)
code.append("")
tmprefs.append(r)
# Remove printed refs from list
for r in tmprefs:
unPrintedRefs.remove(r)
if len(unPrintedRefs) != 0:
log(self, "" + str(len(unPrintedRefs)) + " references could not be translated to code.", LOG_LEVEL_WARN)
else:
log(self, "Printing succeeded for all references", LOG_LEVEL_DEBUG)
code.append("}")
return (header,code)
###
### Testing
###
class testing:
def __init__(self):
self.namespace = opcua_namespace("testing")
log(self, "Phase 1: Reading XML file nodessets")
self.namespace.parseXML("Opc.Ua.NodeSet2.xml")
#self.namespace.parseXML("Opc.Ua.NodeSet2.Part4.xml")
#self.namespace.parseXML("Opc.Ua.NodeSet2.Part5.xml")
#self.namespace.parseXML("Opc.Ua.SimulationNodeSet2.xml")
log(self, "Phase 2: Linking address space references and datatypes")
self.namespace.linkOpenPointers()
self.namespace.sanitize()
log(self, "Phase 3: Comprehending DataType encoding rules")
self.namespace.buildEncodingRules()
log(self, "Phase 4: Allocating variable value data")
self.namespace.allocateVariables()
bin = self.namespace.buildBinary()
f = open("binary.base64","w+")
f.write(bin.encode("base64"))
f.close()
allnodes = self.namespace.nodes;
ns = [self.namespace.getRoot()]
i = 0
#print "Starting depth search on " + str(len(allnodes)) + " nodes starting with from " + str(ns)
while (len(ns) < len(allnodes)):
i = i + 1;
tmp = [];
print("Iteration: " + str(i))
for n in ns:
tmp.append(n)
for r in n.getReferences():
if (not r.target() in tmp):
tmp.append(r.target())
print("...tmp, " + str(len(tmp)) + " nodes discovered")
ns = []
for n in tmp:
ns.append(n)
print("...done, " + str(len(ns)) + " nodes discovered")
log(self, "Phase 5: Printing pretty graph")
self.namespace.printDotGraphWalk(depth=1, rootNode=self.namespace.getNodeByIDString("i=84"), followInverse=False, excludeNodeIds=["i=29", "i=22", "i=25"])
#self.namespace.printDot()
class testing_open62541_header:
def __init__(self):
self.namespace = opcua_namespace("testing")
log(self, "Phase 1: Reading XML file nodessets")
self.namespace.parseXML("Opc.Ua.NodeSet2.xml")
#self.namespace.parseXML("Opc.Ua.NodeSet2.Part4.xml")
#self.namespace.parseXML("Opc.Ua.NodeSet2.Part5.xml")
#self.namespace.parseXML("Opc.Ua.SimulationNodeSet2.xml")
log(self, "Phase 2: Linking address space references and datatypes")
self.namespace.linkOpenPointers()
self.namespace.sanitize()
log(self, "Phase 3: Calling C Printers")
code = self.namespace.printOpen62541Header()
codeout = open("./open62541_namespace.c", "w+")
for line in code:
codeout.write(line + "\n")
codeout.close()
return
# Call testing routine if invoked standalone.
# For better debugging, it is advised to import this file using an interactive
# python shell and instantiating a namespace.
#
# import ua_types.py as ua; ns=ua.testing().namespace
if __name__ == '__main__':
tst = testing_open62541_header()

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,95 @@
<opc:TypeDictionary
xmlns:opc="http://opcfoundation.org/BinarySchema/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ua="http://opcfoundation.org/UA/"
xmlns:tns="http://opcfoundation.org/UA/"
DefaultByteOrder="LittleEndian"
TargetNamespace="http://opcfoundation.org/UA/"
>
<opc:Import Namespace="http://opcfoundation.org/BinarySchema/" />
<!-- Transport types begin -->
<opc:EnumeratedType Name="MessageType" LengthInBits="32">
<opc:Documentation>Message Type and whether the message contains an intermediate chunk</opc:Documentation>
<opc:EnumeratedValue Name="ACK" Value="0x4B4341" />
<opc:EnumeratedValue Name="HEL" Value="0x4C4548" />
<opc:EnumeratedValue Name="MSG" Value="0x47534D" />
<opc:EnumeratedValue Name="OPN" Value="0x4E504F" />
<opc:EnumeratedValue Name="CLO" Value="0x4F4C43" />
</opc:EnumeratedType>
<opc:EnumeratedType Name="ChunkType" LengthInBits="32">
<opc:Documentation>Type of the chunk</opc:Documentation>
<opc:EnumeratedValue Name="FINAL" Value="0x46000000" />
<opc:EnumeratedValue Name="INTERMEDIATE" Value="0x43000000" />
<opc:EnumeratedValue Name="ABORT" Value="0x41000000" />
</opc:EnumeratedType>
<opc:StructuredType Name="TcpMessageHeader">
<opc:Documentation>TCP Header</opc:Documentation>
<opc:Field Name="MessageTypeAndChunkType" TypeName="opc:UInt32" />
<opc:Field Name="MessageSize" TypeName="opc:UInt32" />
</opc:StructuredType>
<opc:StructuredType Name="TcpHelloMessage">
<opc:Documentation>Hello Message</opc:Documentation>
<opc:Field Name="ProtocolVersion" TypeName="opc:UInt32" />
<opc:Field Name="ReceiveBufferSize" TypeName="opc:UInt32" />
<opc:Field Name="SendBufferSize" TypeName="opc:UInt32" />
<opc:Field Name="MaxMessageSize" TypeName="opc:UInt32" />
<opc:Field Name="MaxChunkCount" TypeName="opc:UInt32" />
<opc:Field Name="EndpointUrl" TypeName="opc:String" />
</opc:StructuredType>
<opc:StructuredType Name="TcpAcknowledgeMessage">
<opc:Documentation>Acknowledge Message</opc:Documentation>
<opc:Field Name="ProtocolVersion" TypeName="opc:UInt32" />
<opc:Field Name="ReceiveBufferSize" TypeName="opc:UInt32" />
<opc:Field Name="SendBufferSize" TypeName="opc:UInt32" />
<opc:Field Name="MaxMessageSize" TypeName="opc:UInt32" />
<opc:Field Name="MaxChunkCount" TypeName="opc:UInt32" />
</opc:StructuredType>
<opc:StructuredType Name="SecureConversationMessageHeader">
<opc:Documentation>Secure Layer Sequence Header</opc:Documentation>
<opc:Field Name="MessageHeader" TypeName="opc:TcpMessageHeader" />
<opc:Field Name="SecureChannelId" TypeName="opc:UInt32" />
</opc:StructuredType>
<opc:StructuredType Name="AsymmetricAlgorithmSecurityHeader">
<opc:Documentation>Security Header</opc:Documentation>
<opc:Field Name="SecurityPolicyUri" TypeName="opc:ByteString" />
<opc:Field Name="SenderCertificate" TypeName="opc:ByteString" />
<opc:Field Name="ReceiverCertificateThumbprint" TypeName="opc:ByteString" />
</opc:StructuredType>
<opc:StructuredType Name="SymmetricAlgorithmSecurityHeader">
<opc:Documentation>Secure Layer Symmetric Algorithm Header</opc:Documentation>
<opc:Field Name="TokenId" TypeName="opc:UInt32" />
</opc:StructuredType>
<opc:StructuredType Name="SequenceHeader">
<opc:Documentation>Secure Layer Sequence Header</opc:Documentation>
<opc:Field Name="SequenceNumber" TypeName="opc:UInt32" />
<opc:Field Name="RequestId" TypeName="opc:UInt32"/>
</opc:StructuredType>
<opc:StructuredType Name="SecureConversationMessageFooter">
<opc:Documentation>Secure Conversation Message Footer</opc:Documentation>
<opc:Field Name="PaddingSize" TypeName="opc:Byte" />
<opc:Field Name="Padding" TypeName="opc:Byte" LengthField="PaddingSize" />
<opc:Field Name="Signature" TypeName="opc:Byte"/>
</opc:StructuredType>
<opc:StructuredType Name="SecureConversationMessageAbortBody">
<opc:Documentation>Secure Conversation Message Abort Body</opc:Documentation>
<opc:Field Name="Error" TypeName="opc:UInt32" />
<opc:Field Name="Reason" TypeName="opc:String" />
</opc:StructuredType>
<!-- Transport types end -->
</opc:TypeDictionary>

4879
tools/schema/NodeIds.csv Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,158 @@
Boolean
SByte
Byte
Int16
UInt16
Int32
UInt32
Int64
UInt64
Float
Double
String
DateTime
Guid
ByteString
XmlElement
NodeId
ExpandedNodeId
StatusCode
QualifiedName
LocalizedText
ExtensionObject
DataValue
Variant
DiagnosticInfo
NodeClass
ReferenceNode
ApplicationDescription
ApplicationType
ChannelSecurityToken
OpenSecureChannelRequest
OpenSecureChannelResponse
CloseSecureChannelRequest
CloseSecureChannelResponse
RequestHeader
ResponseHeader
SecurityTokenRequestType
MessageSecurityMode
CloseSessionResponse
CloseSessionRequest
ActivateSessionRequest
ActivateSessionResponse
SignatureData
SignedSoftwareCertificate
CreateSessionResponse
CreateSessionRequest
EndpointDescription
UserTokenPolicy
UserTokenType
GetEndpointsRequest
GetEndpointsResponse
PublishRequest
PublishResponse
FindServersRequest
FindServersResponse
SubscriptionAcknowledgement
ReadRequest
ReadResponse
ReadValueId
TimestampsToReturn
WriteRequest
WriteResponse
WriteValue
CreateMonitoredItemsResponse
MonitoredItemCreateResult
CreateMonitoredItemsRequest
MonitoredItemCreateRequest
MonitoringMode
MonitoringParameters
TranslateBrowsePathsToNodeIdsRequest
TranslateBrowsePathsToNodeIdsResponse
BrowsePath
BrowsePathResult
RelativePath
BrowsePathTarget
RelativePathElement
BrowseResponse
BrowseResult
ReferenceDescription
BrowseRequest
ViewDescription
BrowseNextRequest
BrowseNextResponse
DeleteSubscriptionsRequest
DeleteSubscriptionsResponse
BrowseDescription
BrowseDirection
AddNodesRequest
AddNodesResponse
AddNodesItem
AddNodesResult
AddReferencesRequest
AddReferencesResponse
AddReferencesItem
BrowseResultMask
ServerState
ServerStatusDataType
BuildInfo
IdType
NodeAttributes
VariableAttributes
ObjectAttributes
ReferenceTypeAttributes
ViewAttributes
MethodAttributes
ObjectTypeAttributes
VariableTypeAttributes
DataTypeAttributes
NodeAttributesMask
DeleteNodesItem
DeleteNodesRequest
DeleteNodesResponse
DeleteReferencesItem
DeleteReferencesRequest
DeleteReferencesResponse
RegisterNodesRequest
RegisterNodesResponse
UnregisterNodesRequest
UnregisterNodesResponse
UserIdentityToken
UserNameIdentityToken
AnonymousIdentityToken
ServiceFault
CallMethodRequest
CallMethodResult
CallResponse
CallRequest
Argument
FilterOperator
ContentFilterElement
ContentFilter
QueryDataDescription
NodeTypeDescription
QueryFirstRequest
QueryDataSet
ParsingResult
ContentFilterElementResult
ContentFilterResult
QueryFirstResponse
QueryNextRequest
QueryNextResponse
CreateSubscriptionRequest
CreateSubscriptionResponse
SetPublishingModeRequest
SetPublishingModeResponse
DeleteMonitoredItemsRequest
DeleteMonitoredItemsResponse
NotificationMessage
MonitoredItemNotification
DataChangeNotification
ModifySubscriptionRequest
ModifySubscriptionResponse
RepublishRequest
RepublishResponse
MonitoredItemModifyRequest
ModifyMonitoredItemsRequest
MonitoredItemModifyResult
ModifyMonitoredItemsResponse

View File

@ -0,0 +1,11 @@
MessageType
ChunkType
TcpMessageHeader
TcpHelloMessage
TcpAcknowledgeMessage
SecureConversationMessageHeader
AsymmetricAlgorithmSecurityHeader
SymmetricAlgorithmSecurityHeader
SequenceHeader
SecureConversationMessageFooter
SecureConversationMessageAbortBody

View File

@ -0,0 +1,823 @@
<?xml version="1.0" encoding="utf-8"?>
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Version="1.02" LastModified="2013-03-06T05:36:44.0862658Z" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd">
<Aliases>
<Alias Alias="Boolean">i=1</Alias>
<Alias Alias="SByte">i=2</Alias>
<Alias Alias="Byte">i=3</Alias>
<Alias Alias="Int16">i=4</Alias>
<Alias Alias="UInt16">i=5</Alias>
<Alias Alias="Int32">i=6</Alias>
<Alias Alias="UInt32">i=7</Alias>
<Alias Alias="Int64">i=8</Alias>
<Alias Alias="UInt64">i=9</Alias>
<Alias Alias="Float">i=10</Alias>
<Alias Alias="Double">i=11</Alias>
<Alias Alias="DateTime">i=13</Alias>
<Alias Alias="String">i=12</Alias>
<Alias Alias="ByteString">i=15</Alias>
<Alias Alias="Guid">i=14</Alias>
<Alias Alias="XmlElement">i=16</Alias>
<Alias Alias="NodeId">i=17</Alias>
<Alias Alias="ExpandedNodeId">i=18</Alias>
<Alias Alias="QualifiedName">i=20</Alias>
<Alias Alias="LocalizedText">i=21</Alias>
<Alias Alias="StatusCode">i=19</Alias>
<Alias Alias="Structure">i=22</Alias>
<Alias Alias="Number">i=26</Alias>
<Alias Alias="Integer">i=27</Alias>
<Alias Alias="UInteger">i=28</Alias>
<Alias Alias="HasComponent">i=47</Alias>
<Alias Alias="HasProperty">i=46</Alias>
<Alias Alias="Organizes">i=35</Alias>
<Alias Alias="HasEventSource">i=36</Alias>
<Alias Alias="HasNotifier">i=48</Alias>
<Alias Alias="HasSubtype">i=45</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="HasEncoding">i=38</Alias>
<Alias Alias="HasDescription">i=39</Alias>
</Aliases>
<UAReferenceType NodeId="i=31" BrowseName="References" IsAbstract="true" Symmetric="true">
<DisplayName>References</DisplayName>
<Description>The abstract base type for all references.</Description>
<References />
<InverseName>References</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=45" BrowseName="HasSubtype">
<DisplayName>HasSubtype</DisplayName>
<Description>The type for non-looping hierarchical references that are used to define sub types.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=34</Reference>
</References>
<InverseName>HasSupertype</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=33" BrowseName="HierarchicalReferences" IsAbstract="true">
<DisplayName>HierarchicalReferences</DisplayName>
<Description>The abstract base type for all hierarchical references.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=31</Reference>
</References>
<InverseName>HierarchicalReferences</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=32" BrowseName="NonHierarchicalReferences" IsAbstract="true">
<DisplayName>NonHierarchicalReferences</DisplayName>
<Description>The abstract base type for all non-hierarchical references.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=31</Reference>
</References>
<InverseName>NonHierarchicalReferences</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=34" BrowseName="HasChild">
<DisplayName>HasChild</DisplayName>
<Description>The abstract base type for all non-looping hierarchical references.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=33</Reference>
</References>
<InverseName>ChildOf</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=35" BrowseName="Organizes">
<DisplayName>Organizes</DisplayName>
<Description>The type for hierarchical references that are used to organize nodes.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=33</Reference>
</References>
<InverseName>OrganizedBy</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=36" BrowseName="HasEventSource">
<DisplayName>HasEventSource</DisplayName>
<Description>The type for non-looping hierarchical references that are used to organize event sources.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=33</Reference>
</References>
<InverseName>EventSourceOf</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=37" BrowseName="HasModellingRule">
<DisplayName>HasModellingRule</DisplayName>
<Description>The type for references from instance declarations to modelling rule nodes.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
<InverseName>ModellingRuleOf</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=38" BrowseName="HasEncoding">
<DisplayName>HasEncoding</DisplayName>
<Description>The type for references from data type nodes to to data type encoding nodes.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
<InverseName>EncodingOf</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=39" BrowseName="HasDescription">
<DisplayName>HasDescription</DisplayName>
<Description>The type for references from data type encoding nodes to data type description nodes.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
<InverseName>DescriptionOf</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=40" BrowseName="HasTypeDefinition">
<DisplayName>HasTypeDefinition</DisplayName>
<Description>The type for references from a instance node its type defintion node.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
<InverseName>TypeDefinitionOf</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=41" BrowseName="GeneratesEvent">
<DisplayName>GeneratesEvent</DisplayName>
<Description>The type for references from a node to an event type that is raised by node.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
<InverseName>GeneratesEvent</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=44" BrowseName="Aggregates">
<DisplayName>Aggregates</DisplayName>
<Description>The type for non-looping hierarchical references that are used to aggregate nodes into complex types.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=34</Reference>
</References>
<InverseName>AggregatedBy</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=46" BrowseName="HasProperty">
<DisplayName>HasProperty</DisplayName>
<Description>The type for non-looping hierarchical reference from a node to its property.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=44</Reference>
</References>
<InverseName>PropertyOf</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=47" BrowseName="HasComponent">
<DisplayName>HasComponent</DisplayName>
<Description>The type for non-looping hierarchical reference from a node to its component.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=44</Reference>
</References>
<InverseName>ComponentOf</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=48" BrowseName="HasNotifier">
<DisplayName>HasNotifier</DisplayName>
<Description>The type for non-looping hierarchical references that are used to indicate how events propagate from node to node.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=36</Reference>
</References>
<InverseName>NotifierOf</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=49" BrowseName="HasOrderedComponent">
<DisplayName>HasOrderedComponent</DisplayName>
<Description>The type for non-looping hierarchical reference from a node to its component when the order of references matters.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=47</Reference>
</References>
<InverseName>OrderedComponentOf</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=51" BrowseName="FromState">
<DisplayName>FromState</DisplayName>
<Description>The type for a reference to the state before a transition.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
<InverseName>ToTransition</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=52" BrowseName="ToState">
<DisplayName>ToState</DisplayName>
<Description>The type for a reference to the state after a transition.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
<InverseName>FromTransition</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=53" BrowseName="HasCause">
<DisplayName>HasCause</DisplayName>
<Description>The type for a reference to a method that can cause a transition to occur.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
<InverseName>MayBeCausedBy</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=54" BrowseName="HasEffect">
<DisplayName>HasEffect</DisplayName>
<Description>The type for a reference to an event that may be raised when a transition occurs.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=32</Reference>
</References>
<InverseName>MayBeEffectedBy</InverseName>
</UAReferenceType>
<UAReferenceType NodeId="i=56" BrowseName="HasHistoricalConfiguration">
<DisplayName>HasHistoricalConfiguration</DisplayName>
<Description>The type for a reference to the historical configuration for a data variable.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=44</Reference>
</References>
<InverseName>HistoricalConfigurationOf</InverseName>
</UAReferenceType>
<UAObject NodeId="i=84" BrowseName="Root" SymbolicName="RootFolder">
<DisplayName>Root</DisplayName>
<Description>The root of the server address space.</Description>
<References>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
</References>
</UAObject>
<UAObject NodeId="i=85" BrowseName="Objects" SymbolicName="ObjectsFolder">
<DisplayName>Objects</DisplayName>
<Description>The browse entry point when looking for objects in the server address space.</Description>
<References>
<Reference ReferenceType="Organizes" IsForward="false">i=84</Reference>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
</References>
</UAObject>
<UAObject NodeId="i=86" BrowseName="Types" SymbolicName="TypesFolder">
<DisplayName>Types</DisplayName>
<Description>The browse entry point when looking for types in the server address space.</Description>
<References>
<Reference ReferenceType="Organizes" IsForward="false">i=84</Reference>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
</References>
</UAObject>
<UAObject NodeId="i=87" BrowseName="Views" SymbolicName="ViewsFolder">
<DisplayName>Views</DisplayName>
<Description>The browse entry point when looking for views in the server address space.</Description>
<References>
<Reference ReferenceType="Organizes" IsForward="false">i=84</Reference>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
</References>
</UAObject>
<UAObject NodeId="i=91" BrowseName="ReferenceTypes" SymbolicName="ReferenceTypesFolder">
<DisplayName>ReferenceTypes</DisplayName>
<Description>The browse entry point when looking for reference types in the server address space.</Description>
<References>
<Reference ReferenceType="Organizes" IsForward="false">i=86</Reference>
<Reference ReferenceType="Organizes">i=31</Reference>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
</References>
</UAObject>
<UAObjectType NodeId="i=58" BrowseName="BaseObjectType">
<DisplayName>BaseObjectType</DisplayName>
<Description>The base type for all object nodes.</Description>
<References />
</UAObjectType>
<UAObjectType NodeId="i=61" BrowseName="FolderType">
<DisplayName>FolderType</DisplayName>
<Description>The type for objects that organize other nodes.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<UAObjectType NodeId="i=2004" BrowseName="ServerType">
<DisplayName>ServerType</DisplayName>
<Description>Specifies the current status and capabilities of the server.</Description>
<References>
<Reference ReferenceType="HasProperty">i=2005</Reference>
<Reference ReferenceType="HasProperty">i=2006</Reference>
<Reference ReferenceType="HasComponent">i=2007</Reference>
<Reference ReferenceType="HasProperty">i=2008</Reference>
<Reference ReferenceType="HasProperty">i=2742</Reference>
<Reference ReferenceType="HasComponent">i=2009</Reference>
<Reference ReferenceType="HasComponent">i=2010</Reference>
<Reference ReferenceType="HasComponent">i=2011</Reference>
<Reference ReferenceType="HasComponent">i=2012</Reference>
<Reference ReferenceType="HasComponent">i=11527</Reference>
<Reference ReferenceType="HasComponent">i=11489</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<UAObjectType NodeId="i=2020" BrowseName="ServerDiagnosticsType">
<DisplayName>ServerDiagnosticsType</DisplayName>
<Description>The diagnostics information for a server.</Description>
<References>
<Reference ReferenceType="HasComponent">i=2021</Reference>
<Reference ReferenceType="HasComponent">i=2022</Reference>
<Reference ReferenceType="HasComponent">i=2023</Reference>
<Reference ReferenceType="HasComponent">i=2744</Reference>
<Reference ReferenceType="HasProperty">i=2025</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<UAObjectType NodeId="i=2013" BrowseName="ServerCapabilitiesType">
<DisplayName>ServerCapabilitiesType</DisplayName>
<Description>Describes the capabilities supported by the server.</Description>
<References>
<Reference ReferenceType="HasProperty">i=2014</Reference>
<Reference ReferenceType="HasProperty">i=2016</Reference>
<Reference ReferenceType="HasProperty">i=2017</Reference>
<Reference ReferenceType="HasProperty">i=2732</Reference>
<Reference ReferenceType="HasProperty">i=2733</Reference>
<Reference ReferenceType="HasProperty">i=2734</Reference>
<Reference ReferenceType="HasProperty">i=3049</Reference>
<Reference ReferenceType="HasProperty">i=11549</Reference>
<Reference ReferenceType="HasProperty">i=11550</Reference>
<Reference ReferenceType="HasComponent">i=11551</Reference>
<Reference ReferenceType="HasComponent">i=2019</Reference>
<Reference ReferenceType="HasComponent">i=2754</Reference>
<Reference ReferenceType="HasComponent">i=11562</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<UAVariableType NodeId="i=2138" BrowseName="ServerStatusType" DataType="i=862">
<DisplayName>ServerStatusType</DisplayName>
<References>
<Reference ReferenceType="HasComponent">i=2139</Reference>
<Reference ReferenceType="HasComponent">i=2140</Reference>
<Reference ReferenceType="HasComponent">i=2141</Reference>
<Reference ReferenceType="HasComponent">i=2142</Reference>
<Reference ReferenceType="HasComponent">i=2752</Reference>
<Reference ReferenceType="HasComponent">i=2753</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=63</Reference>
</References>
</UAVariableType>
<UAVariableType NodeId="i=3051" BrowseName="BuildInfoType" DataType="i=338">
<DisplayName>BuildInfoType</DisplayName>
<References>
<Reference ReferenceType="HasComponent">i=3052</Reference>
<Reference ReferenceType="HasComponent">i=3053</Reference>
<Reference ReferenceType="HasComponent">i=3054</Reference>
<Reference ReferenceType="HasComponent">i=3055</Reference>
<Reference ReferenceType="HasComponent">i=3056</Reference>
<Reference ReferenceType="HasComponent">i=3057</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=63</Reference>
</References>
</UAVariableType>
<UAObject NodeId="i=90" BrowseName="DataTypes" SymbolicName="DataTypesFolder">
<DisplayName>DataTypes</DisplayName>
<Description>The browse entry point when looking for data types in the server address space.</Description>
<References>
<Reference ReferenceType="Organizes" IsForward="false">i=86</Reference>
<Reference ReferenceType="Organizes">i=24</Reference>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
</References>
</UAObject>
<UADataType NodeId="i=24" BrowseName="BaseDataType" IsAbstract="true">
<DisplayName>BaseDataType</DisplayName>
<Description>Describes a value that can have any valid DataType.</Description>
<References />
</UADataType>
<UADataType NodeId="i=1" BrowseName="Boolean">
<DisplayName>Boolean</DisplayName>
<Description>Describes a value that is either TRUE or FALSE.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=26" BrowseName="Number" IsAbstract="true">
<DisplayName>Number</DisplayName>
<Description>Describes a value that can have any numeric DataType.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=10" BrowseName="Float">
<DisplayName>Float</DisplayName>
<Description>Describes a value that is an IEEE 754-1985 single precision floating point number.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=26</Reference>
</References>
</UADataType>
<UADataType NodeId="i=11" BrowseName="Double">
<DisplayName>Double</DisplayName>
<Description>Describes a value that is an IEEE 754-1985 double precision floating point number.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=26</Reference>
</References>
</UADataType>
<UADataType NodeId="i=27" BrowseName="Integer" IsAbstract="true">
<DisplayName>Integer</DisplayName>
<Description>Describes a value that can have any integer DataType.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=26</Reference>
</References>
</UADataType>
<UADataType NodeId="i=2" BrowseName="SByte">
<DisplayName>SByte</DisplayName>
<Description>Describes a value that is an integer between -128 and 127.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=27</Reference>
</References>
</UADataType>
<UADataType NodeId="i=4" BrowseName="Int16">
<DisplayName>Int16</DisplayName>
<Description>Describes a value that is an integer between 32,768 and 32,767.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=27</Reference>
</References>
</UADataType>
<UADataType NodeId="i=6" BrowseName="Int32">
<DisplayName>Int32</DisplayName>
<Description>Describes a value that is an integer between 2,147,483,648 and 2,147,483,647.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=27</Reference>
</References>
</UADataType>
<UADataType NodeId="i=8" BrowseName="Int64">
<DisplayName>Int64</DisplayName>
<Description>Describes a value that is an integer between 9,223,372,036,854,775,808 and 9,223,372,036,854,775,807.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=27</Reference>
</References>
</UADataType>
<UADataType NodeId="i=28" BrowseName="UInteger" IsAbstract="true">
<DisplayName>UInteger</DisplayName>
<Description>Describes a value that can have any unsigned integer DataType.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=27</Reference>
</References>
</UADataType>
<UADataType NodeId="i=3" BrowseName="Byte">
<DisplayName>Byte</DisplayName>
<Description>Describes a value that is an integer between 0 and 255.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=28</Reference>
</References>
</UADataType>
<UADataType NodeId="i=5" BrowseName="UInt16">
<DisplayName>UInt16</DisplayName>
<Description>Describes a value that is an integer between 0 and 65535.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=28</Reference>
</References>
</UADataType>
<UADataType NodeId="i=7" BrowseName="UInt32">
<DisplayName>UInt32</DisplayName>
<Description>Describes a value that is an integer between 0 and 4,294,967,295.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=28</Reference>
</References>
</UADataType>
<UADataType NodeId="i=9" BrowseName="UInt64">
<DisplayName>UInt64</DisplayName>
<Description>Describes a value that is an integer between 0 and 18,446,744,073,709,551,615.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=28</Reference>
</References>
</UADataType>
<UADataType NodeId="i=12" BrowseName="String">
<DisplayName>String</DisplayName>
<Description>Describes a value that is a sequence of printable Unicode characters.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=13" BrowseName="DateTime">
<DisplayName>DateTime</DisplayName>
<Description>Describes a value that is a Gregorian calender date and time.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=14" BrowseName="Guid">
<DisplayName>Guid</DisplayName>
<Description>Describes a value that is a 128-bit globally unique identifier.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=15" BrowseName="ByteString">
<DisplayName>ByteString</DisplayName>
<Description>Describes a value that is a sequence of bytes.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=16" BrowseName="XmlElement">
<DisplayName>XmlElement</DisplayName>
<Description>Describes a value that is an XML element.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=17" BrowseName="NodeId">
<DisplayName>NodeId</DisplayName>
<Description>Describes a value that is an identifier for a node within a Server address space.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=18" BrowseName="ExpandedNodeId">
<DisplayName>ExpandedNodeId</DisplayName>
<Description>Describes a value that is an absolute identifier for a node.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=19" BrowseName="StatusCode">
<DisplayName>StatusCode</DisplayName>
<Description>Describes a value that is a code representing the outcome of an operation by a Server.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=20" BrowseName="QualifiedName">
<DisplayName>QualifiedName</DisplayName>
<Description>Describes a value that is a name qualified by a namespace.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=21" BrowseName="LocalizedText">
<DisplayName>LocalizedText</DisplayName>
<Description>Describes a value that is human readable Unicode text with a locale identifier.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=22" BrowseName="Structure" IsAbstract="true">
<DisplayName>Structure</DisplayName>
<Description>Describes a value that is any type of structure that can be described with a data encoding.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=862" BrowseName="ServerStatusDataType">
<DisplayName>ServerStatusDataType</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=22</Reference>
</References>
<Definition Name="ServerStatusDataType">
<Field Name="StartTime" DataType="i=294" />
<Field Name="CurrentTime" DataType="i=294" />
<Field Name="State" DataType="i=852" />
<Field Name="BuildInfo" DataType="i=338" />
<Field Name="SecondsTillShutdown" DataType="i=7" />
<Field Name="ShutdownReason" DataType="i=21" />
</Definition>
</UADataType>
<UADataType NodeId="i=338" BrowseName="BuildInfo">
<DisplayName>BuildInfo</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=22</Reference>
</References>
<Definition Name="BuildInfo">
<Field Name="ProductUri" DataType="i=12" />
<Field Name="ManufacturerName" DataType="i=12" />
<Field Name="ProductName" DataType="i=12" />
<Field Name="SoftwareVersion" DataType="i=12" />
<Field Name="BuildNumber" DataType="i=12" />
<Field Name="BuildDate" DataType="i=294" />
</Definition>
</UADataType>
<UADataType NodeId="i=23" BrowseName="DataValue">
<DisplayName>DataValue</DisplayName>
<Description>Describes a value that is a structure containing a value, a status code and timestamps.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=25" BrowseName="DiagnosticInfo">
<DisplayName>DiagnosticInfo</DisplayName>
<Description>Describes a value that is a structure containing diagnostics associated with a StatusCode.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=29" BrowseName="Enumeration" IsAbstract="true">
<DisplayName>Enumeration</DisplayName>
<Description>Describes a value that is an enumerated DataType.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=24</Reference>
</References>
</UADataType>
<UADataType NodeId="i=852" BrowseName="ServerState">
<DisplayName>ServerState</DisplayName>
<References>
<Reference ReferenceType="HasProperty">i=7612</Reference>
<Reference ReferenceType="HasSubtype" IsForward="false">i=29</Reference>
</References>
<Definition Name="ServerState">
<Field Name="Running" Value="0" />
<Field Name="Failed" Value="1" />
<Field Name="NoConfiguration" Value="2" />
<Field Name="Suspended" Value="3" />
<Field Name="Shutdown" Value="4" />
<Field Name="Test" Value="5" />
<Field Name="CommunicationFault" Value="6" />
<Field Name="Unknown" Value="7" />
</Definition>
</UADataType>
<UAObject NodeId="i=89" BrowseName="VariableTypes" SymbolicName="VariableTypesFolder">
<DisplayName>VariableTypes</DisplayName>
<Description>The browse entry point when looking for variable types in the server address space.</Description>
<References>
<Reference ReferenceType="Organizes" IsForward="false">i=86</Reference>
<Reference ReferenceType="Organizes">i=62</Reference>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
</References>
</UAObject>
<UAVariableType NodeId="i=62" BrowseName="BaseVariableType" IsAbstract="true" ValueRank="-2">
<DisplayName>BaseVariableType</DisplayName>
<Description>The abstract base type for all variable nodes.</Description>
<References />
</UAVariableType>
<UAVariableType NodeId="i=63" BrowseName="BaseDataVariableType" ValueRank="-2">
<DisplayName>BaseDataVariableType</DisplayName>
<Description>The type for variable that represents a process value.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=62</Reference>
</References>
</UAVariableType>
<UAVariableType NodeId="i=68" BrowseName="PropertyType" ValueRank="-2">
<DisplayName>PropertyType</DisplayName>
<Description>The type for variable that represents a property of another node.</Description>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=62</Reference>
</References>
</UAVariableType>
<UAObject NodeId="i=2253" BrowseName="Server" EventNotifier="1">
<DisplayName>Server</DisplayName>
<References>
<Reference ReferenceType="HasProperty">i=2254</Reference>
<Reference ReferenceType="HasProperty">i=2255</Reference>
<Reference ReferenceType="HasComponent">i=2256</Reference>
<Reference ReferenceType="HasProperty">i=2267</Reference>
<Reference ReferenceType="HasProperty">i=2994</Reference>
<Reference ReferenceType="HasComponent">i=2268</Reference>
<Reference ReferenceType="HasComponent">i=2274</Reference>
<Reference ReferenceType="HasComponent">i=2295</Reference>
<Reference ReferenceType="HasComponent">i=2296</Reference>
<Reference ReferenceType="HasComponent">i=11715</Reference>
<Reference ReferenceType="HasComponent">i=11492</Reference>
<Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
<Reference ReferenceType="HasTypeDefinition">i=2004</Reference>
</References>
</UAObject>
<UAVariable NodeId="i=2255" BrowseName="NamespaceArray" ParentNodeId="i=2253" DataType="String" ValueRank="1" MinimumSamplingInterval="1000">
<DisplayName>NamespaceArray</DisplayName>
<Description>The list of namespace URIs used by the server.</Description>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=2253</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2254" BrowseName="ServerArray" ParentNodeId="i=2253" DataType="String" ValueRank="1" MinimumSamplingInterval="1000">
<DisplayName>ServerArray</DisplayName>
<Description>The list of server URIs used by the server.</Description>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=2253</Reference>
</References>
</UAVariable>
<UAObject NodeId="i=2268" BrowseName="ServerCapabilities" ParentNodeId="i=2253">
<DisplayName>ServerCapabilities</DisplayName>
<Description>Describes capabilities supported by the server.</Description>
<References>
<Reference ReferenceType="HasProperty">i=2269</Reference>
<Reference ReferenceType="HasProperty">i=2271</Reference>
<Reference ReferenceType="HasProperty">i=2272</Reference>
<Reference ReferenceType="HasProperty">i=2735</Reference>
<Reference ReferenceType="HasProperty">i=2736</Reference>
<Reference ReferenceType="HasProperty">i=2737</Reference>
<Reference ReferenceType="HasProperty">i=3704</Reference>
<Reference ReferenceType="HasProperty">i=11702</Reference>
<Reference ReferenceType="HasProperty">i=11703</Reference>
<Reference ReferenceType="HasComponent">i=11704</Reference>
<Reference ReferenceType="HasComponent">i=2996</Reference>
<Reference ReferenceType="HasComponent">i=2997</Reference>
<Reference ReferenceType="HasTypeDefinition">i=2013</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2253</Reference>
</References>
</UAObject>
<UAVariable NodeId="i=2271" BrowseName="LocaleIdArray" ParentNodeId="i=2268" DataType="i=295" ValueRank="1">
<DisplayName>LocaleIdArray</DisplayName>
<Description>A list of locales supported by the server.</Description>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=2268</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2735" BrowseName="MaxBrowseContinuationPoints" ParentNodeId="i=2268" DataType="UInt16">
<DisplayName>MaxBrowseContinuationPoints</DisplayName>
<Description>The maximum number of continuation points for Browse operations per session.</Description>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=2268</Reference>
</References>
</UAVariable>
<UAObject NodeId="i=2274" BrowseName="ServerDiagnostics" ParentNodeId="i=2253">
<DisplayName>ServerDiagnostics</DisplayName>
<Description>Reports diagnostics about the server.</Description>
<References>
<Reference ReferenceType="HasComponent">i=2275</Reference>
<Reference ReferenceType="HasComponent">i=2289</Reference>
<Reference ReferenceType="HasComponent">i=2290</Reference>
<Reference ReferenceType="HasComponent">i=3706</Reference>
<Reference ReferenceType="HasProperty">i=2294</Reference>
<Reference ReferenceType="HasTypeDefinition">i=2020</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2253</Reference>
</References>
</UAObject>
<UAVariable NodeId="i=2294" BrowseName="EnabledFlag" ParentNodeId="i=2274" DataType="Boolean" AccessLevel="3" UserAccessLevel="3">
<DisplayName>EnabledFlag</DisplayName>
<Description>If TRUE the diagnostics collection is enabled.</Description>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=2274</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2256" BrowseName="ServerStatus" ParentNodeId="i=2253" DataType="i=862" MinimumSamplingInterval="1000">
<DisplayName>ServerStatus</DisplayName>
<Description>The current status of the server.</Description>
<References>
<Reference ReferenceType="HasComponent">i=2257</Reference>
<Reference ReferenceType="HasComponent">i=2258</Reference>
<Reference ReferenceType="HasComponent">i=2259</Reference>
<Reference ReferenceType="HasComponent">i=2260</Reference>
<Reference ReferenceType="HasComponent">i=2992</Reference>
<Reference ReferenceType="HasComponent">i=2993</Reference>
<Reference ReferenceType="HasTypeDefinition">i=2138</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2253</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2257" BrowseName="StartTime" ParentNodeId="i=2256" DataType="i=294">
<DisplayName>StartTime</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2258" BrowseName="CurrentTime" ParentNodeId="i=2256" DataType="i=294">
<DisplayName>CurrentTime</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2259" BrowseName="State" ParentNodeId="i=2256" DataType="i=852">
<DisplayName>State</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2260" BrowseName="BuildInfo" ParentNodeId="i=2256" DataType="i=338">
<DisplayName>BuildInfo</DisplayName>
<References>
<Reference ReferenceType="HasComponent">i=2262</Reference>
<Reference ReferenceType="HasComponent">i=2263</Reference>
<Reference ReferenceType="HasComponent">i=2261</Reference>
<Reference ReferenceType="HasComponent">i=2264</Reference>
<Reference ReferenceType="HasComponent">i=2265</Reference>
<Reference ReferenceType="HasComponent">i=2266</Reference>
<Reference ReferenceType="HasTypeDefinition">i=3051</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2262" BrowseName="ProductUri" ParentNodeId="i=2260" DataType="String" MinimumSamplingInterval="1000">
<DisplayName>ProductUri</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2263" BrowseName="ManufacturerName" ParentNodeId="i=2260" DataType="String" MinimumSamplingInterval="1000">
<DisplayName>ManufacturerName</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2261" BrowseName="ProductName" ParentNodeId="i=2260" DataType="String" MinimumSamplingInterval="1000">
<DisplayName>ProductName</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2264" BrowseName="SoftwareVersion" ParentNodeId="i=2260" DataType="String" MinimumSamplingInterval="1000">
<DisplayName>SoftwareVersion</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2265" BrowseName="BuildNumber" ParentNodeId="i=2260" DataType="String" MinimumSamplingInterval="1000">
<DisplayName>BuildNumber</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2266" BrowseName="BuildDate" ParentNodeId="i=2260" DataType="i=294" MinimumSamplingInterval="1000">
<DisplayName>BuildDate</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2260</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2992" BrowseName="SecondsTillShutdown" ParentNodeId="i=2256" DataType="UInt32">
<DisplayName>SecondsTillShutdown</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=2993" BrowseName="ShutdownReason" ParentNodeId="i=2256" DataType="LocalizedText">
<DisplayName>ShutdownReason</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2256</Reference>
</References>
</UAVariable>
</UANodeSet>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
This directory contains XML nodesets of namespace 0 for automatic generation.
The generation option can be activated via CMake option ENABLE_GENERATE_NAMESPACE0.
The nodesets can be selected via CMake variable GENERATE_NAMESPACE0_FILE.