diff --git a/src/ua_types_encoding_json.c b/src/ua_types_encoding_json.c
index 1999d4c13..eb77026c1 100644
--- a/src/ua_types_encoding_json.c
+++ b/src/ua_types_encoding_json.c
@@ -1955,8 +1955,8 @@ DECODE_JSON(String) {
/* No escaping */
if(*p != '\\') {
/* In the ASCII range, but not a printable character */
- if(*p < 32 || *p == 127)
- goto cleanup;
+ /* if(*p < 32 || *p == 127) */
+ /* goto cleanup; */
*(pos++) = *(p++);
continue;
@@ -2084,21 +2084,27 @@ lookAheadForKey(const char *key, CtxJson *ctx,
parseCtx->index++; /* Move to the first key */
while(parseCtx->index < parseCtx->tokenCount &&
parseCtx->tokenArray[parseCtx->index].start < end) {
- UA_assert(getJsmnType(parseCtx) == JSMN_STRING); /* Key must be a string */
+ /* Key must be a string (TODO: Make this an assert after replacing jsmn) */
+ if(getJsmnType(parseCtx) != JSMN_STRING)
+ return UA_STATUSCODE_BADDECODINGERROR;
- parseCtx->index++; /* Move to the value already */
- UA_assert(parseCtx->index < parseCtx->tokenCount); /* Key followed by a value */
+ /* Move index to the value */
+ parseCtx->index++;
- /* Compare the key */
+ /* Value for the key must exist (TODO: Make this an assert after replacing jsmn) */
+ if(parseCtx->index >= parseCtx->tokenCount)
+ return UA_STATUSCODE_BADDECODINGERROR;
+
+ /* Compare the key (previous index) */
if(jsoneq((char*)ctx->pos, &parseCtx->tokenArray[parseCtx->index-1], key) == 0) {
- *resultIndex = parseCtx->index; /* Point to the value */
+ *resultIndex = parseCtx->index; /* Point result to the current index */
ret = UA_STATUSCODE_GOOD;
break;
}
- skipObject(parseCtx);
+ skipObject(parseCtx); /* Jump over the value (can also be an array or object) */
}
- parseCtx->index = oldIndex; /* Restore index */
+ parseCtx->index = oldIndex; /* Restore the old index */
return ret;
}
@@ -2360,6 +2366,7 @@ DECODE_JSON(DateTime) {
denom *= 0.1;
pos++;
}
+ frac += 0.00000005; /* Correct rounding when converting to integer */
dt += (UA_DateTime)(frac * UA_DATETIME_SEC);
}
@@ -2771,7 +2778,9 @@ decodeFields(CtxJson *ctx, ParseCtx *parseCtx,
for(size_t currObj = 0; currObj < objectCount &&
parseCtx->index < parseCtx->tokenCount; currObj++) {
- UA_assert(getJsmnType(parseCtx) == JSMN_STRING); /* Key must be a string */
+ /* Key must be a string (TODO: Convert to assert when jsmn is replaced) */
+ if(getJsmnType(parseCtx) != JSMN_STRING)
+ return UA_STATUSCODE_BADDECODINGERROR;
/* Start searching at the index of currObj */
for(size_t i = currObj; i < entryCount + currObj; i++) {
diff --git a/tests/nodeset-compiler/check_nodeset_compiler_testnodeset.c b/tests/nodeset-compiler/check_nodeset_compiler_testnodeset.c
index d0d77d684..aa5033ccb 100644
--- a/tests/nodeset-compiler/check_nodeset_compiler_testnodeset.c
+++ b/tests/nodeset-compiler/check_nodeset_compiler_testnodeset.c
@@ -175,6 +175,50 @@ START_TEST(checkInputArguments) {
}
END_TEST
+START_TEST(checkGuid) {
+ UA_Variant out;
+ UA_Variant_init(&out);
+ UA_StatusCode status = UA_Server_readValue(server, UA_NODEID_NUMERIC(testNamespaceIndex, 7051), &out);
+ ck_assert(status == UA_STATUSCODE_GOOD);
+ ck_assert(out.type == &UA_TYPES[UA_TYPES_GUID]);
+ UA_Guid *scalarData = (UA_Guid *)out.data;
+ UA_Guid scalarGuidVal = UA_GUID("7822a391-de79-4a59-b08d-b70bc63fecec");
+ ck_assert(UA_Guid_equal(scalarData, &scalarGuidVal));
+ UA_Variant_clear(&out);
+ status = UA_Server_readValue(server, UA_NODEID_NUMERIC(testNamespaceIndex, 7052), &out);
+ ck_assert(status == UA_STATUSCODE_GOOD);
+ ck_assert(out.type == &UA_TYPES[UA_TYPES_GUID]);
+ ck_assert(out.arrayLength == 3);
+ UA_Guid *ArrayData = (UA_Guid *)out.data;
+ UA_Guid ArrayGuidVal[3] = {UA_GUID("7822a391-1111-4a59-b08d-b70bc63fecec"),
+ UA_GUID("7822a391-2222-4a59-b08d-b70bc63fecec"),
+ UA_GUID("7822a391-3333-4a59-b08d-b70bc63fecec")};
+ ck_assert(UA_Guid_equal(&ArrayData[0], &ArrayGuidVal[0]));
+ ck_assert(UA_Guid_equal(&ArrayData[1], &ArrayGuidVal[1]));
+ ck_assert(UA_Guid_equal(&ArrayData[2], &ArrayGuidVal[2]));
+ UA_Variant_clear(&out);
+}
+END_TEST
+
+START_TEST(checkDataSetMetaData) {
+ UA_Variant out;
+ UA_Variant_init(&out);
+ UA_StatusCode status = UA_Server_readValue(server, UA_NODEID_NUMERIC(testNamespaceIndex, 6021), &out);
+ ck_assert(status == UA_STATUSCODE_GOOD);
+ ck_assert(out.type == &UA_TYPES[UA_TYPES_DATASETMETADATATYPE]);
+ UA_DataSetMetaDataType *p = (UA_DataSetMetaDataType *)out.data;
+ UA_String dataSetName = UA_STRING("DataSetName");
+ ck_assert(UA_String_equal(&p->name, &dataSetName) == UA_TRUE);
+ ck_assert(p->fieldsSize == 1);
+ UA_String fieldName = UA_STRING("FieldName");
+ ck_assert(UA_String_equal(&p->fields[0].name, &fieldName) == UA_TRUE);
+ UA_Guid guid = UA_GUID("10000000-2000-3000-4000-500000000000");
+ ck_assert(UA_Guid_equal(&p->dataSetClassId, &guid) == UA_TRUE);
+
+ UA_Variant_clear(&out);
+}
+END_TEST
+
static Suite *testSuite_Client(void) {
Suite *s = suite_create("Server Nodeset Compiler");
TCase *tc_server = tcase_create("Server Testnodeset");
@@ -186,6 +230,8 @@ static Suite *testSuite_Client(void) {
tcase_add_test(tc_server, readValueRank);
tcase_add_test(tc_server, checkFrameValues);
tcase_add_test(tc_server, checkInputArguments);
+ tcase_add_test(tc_server, checkGuid);
+ tcase_add_test(tc_server, checkDataSetMetaData);
suite_add_tcase(s, tc_server);
return s;
}
diff --git a/tests/nodeset-compiler/testnodeset.xml b/tests/nodeset-compiler/testnodeset.xml
index d9625f7b7..f0d384f46 100644
--- a/tests/nodeset-compiler/testnodeset.xml
+++ b/tests/nodeset-compiler/testnodeset.xml
@@ -8,6 +8,7 @@
i=6
i=7
i=11
+ i=14
i=35
i=37
i=38
@@ -21,6 +22,7 @@
ns=1;i=4002
ns=1;i=3006
ns=1;i=1003
+ i=14523
@@ -600,4 +602,87 @@
+
+ TestGuidScalar
+
+ i=63
+ ns=1;i=5100
+
+
+
+ 7822a391-de79-4a59-b08d-b70bc63fecec
+
+
+
+
+ TestGuidArray
+
+ i=63
+ ns=1;i=5100
+
+
+
+
+ 7822a391-1111-4a59-b08d-b70bc63fecec
+
+
+ 7822a391-2222-4a59-b08d-b70bc63fecec
+
+
+ 7822a391-3333-4a59-b08d-b70bc63fecec
+
+
+
+
+
+ TestDataSetMetaData
+
+ ns=1;i=5001
+ i=68
+
+
+
+
+ i=14794
+
+
+
+
+
+
+
+
+
+ DataSetName
+
+
+
+ FieldName
+
+ 0
+ 10
+
+ i=10
+
+ -1
+
+ 0
+
+ 10000000-2000-3000-4000-500000000000
+
+
+
+
+
+ 10000000-2000-3000-4000-500000000000
+
+
+ 1
+ 1
+
+
+
+
+
+
diff --git a/tools/nodeset_compiler/backend_open62541.py b/tools/nodeset_compiler/backend_open62541.py
index 1ee4c78f9..e295ab50a 100644
--- a/tools/nodeset_compiler/backend_open62541.py
+++ b/tools/nodeset_compiler/backend_open62541.py
@@ -238,7 +238,7 @@ _UA_END_DECLS
writec("\n".join(code_global))
writec("\n")
writec("\nstatic UA_StatusCode function_" + outfilebase + "_" + str(functionNumber) + "_begin(UA_Server *server, UA_UInt16* ns) {")
- if isinstance(node, MethodNode):
+ if isinstance(node, MethodNode) or isinstance(node.parent, MethodNode):
writec("#ifdef UA_ENABLE_METHODCALLS")
writec(code)
@@ -259,7 +259,7 @@ _UA_END_DECLS
writec("return retVal;")
- if isinstance(node, MethodNode):
+ if isinstance(node, MethodNode) or isinstance(node.parent, MethodNode):
writec("#else")
writec("return UA_STATUSCODE_GOOD;")
writec("#endif /* UA_ENABLE_METHODCALLS */")
@@ -267,10 +267,10 @@ _UA_END_DECLS
writec("\nstatic UA_StatusCode function_" + outfilebase + "_" + str(functionNumber) + "_finish(UA_Server *server, UA_UInt16* ns) {")
- if isinstance(node, MethodNode):
+ if isinstance(node, MethodNode) or isinstance(node.parent, MethodNode):
writec("#ifdef UA_ENABLE_METHODCALLS")
writec("return " + generateNodeCode_finish(node))
- if isinstance(node, MethodNode):
+ if isinstance(node, MethodNode) or isinstance(node.parent, MethodNode):
writec("#else")
writec("return UA_STATUSCODE_GOOD;")
writec("#endif /* UA_ENABLE_METHODCALLS */")
diff --git a/tools/nodeset_compiler/backend_open62541_datatypes.py b/tools/nodeset_compiler/backend_open62541_datatypes.py
index 15cc63e86..884c5f89c 100644
--- a/tools/nodeset_compiler/backend_open62541_datatypes.py
+++ b/tools/nodeset_compiler/backend_open62541_datatypes.py
@@ -85,6 +85,12 @@ def generateQualifiedNameCode(value, alloc=False,):
return u"UA_QUALIFIEDNAME{}(ns[{}], {})".format("_ALLOC" if alloc else "",
str(value.ns), splitStringLiterals(vn))
+def generateGuidCode(value):
+ if not value or len(value) != 5:
+ return "UA_GUID_NULL"
+ else:
+ return "UA_GUID(\"{}\")".format('-'.join(value))
+
def generateNodeIdCode(value):
if not value:
return "UA_NODEID_NUMERIC(0, 0)"
@@ -154,7 +160,7 @@ def generateNodeValueCode(prepend , node, instanceName, valueName, global_var_co
elif isinstance(node, DiagnosticInfo):
raise Exception("generateNodeValueCode for type " + node.__class__.name + " not implemented")
elif isinstance(node, Guid):
- raise Exception("generateNodeValueCode for type " + node.__class__.name + " not implemented")
+ return prepend + " = " + generateGuidCode(node.value) + ";"
elif isinstance(node, ExtensionObject):
if asIndirect == False:
return prepend + " = *" + str(instanceName) + ";"
@@ -163,6 +169,8 @@ def generateNodeValueCode(prepend , node, instanceName, valueName, global_var_co
code = []
if idxList is None:
raise Exception("No index was passed and the code generation cannot generate the array element")
+ if len(node) == 0:
+ return "\n".join(code)
# Code generation for structure arrays with fields of type Buildin.
# Example:
# Structure []
@@ -174,6 +182,10 @@ def generateNodeValueCode(prepend , node, instanceName, valueName, global_var_co
typeOfArray = encRule.member_type.name
arrayName = encRule.name
code.append("UA_STACKARRAY(UA_" + typeOfArray + ", " + arrayName+", {0});".format(len(node)))
+ # memset is used here instead of UA_Init. Finding the dataType nodeID (to get the type array)
+ # would require searching whole nodeset to match the type name
+ code.append("memset({arrayName}, 0, sizeof(UA_{typeOfArray}) * {arrayLength});".format(arrayName=arrayName, typeOfArray=typeOfArray,
+ arrayLength=len(node)))
for idx,subv in enumerate(node):
code.append(generateNodeValueCode(arrayName + "[" + str(idx) + "]", subv, instanceName, valueName, global_var_code, asIndirect, encRule=encRule, idxList=idx))
code.append(prepend + "Size = {0};".format(len(node)))
@@ -201,6 +213,10 @@ def generateNodeValueCode(prepend , node, instanceName, valueName, global_var_co
typeOfArray = encRule.member_type.name
arrayName = encRule.name
code.append("UA_STACKARRAY(UA_" + typeOfArray + ", " + arrayName+", {0});".format(len(node.value)))
+ # memset is used here instead of UA_Init. Finding the dataType nodeID (to get the type array)
+ # would require searching whole nodeset to match the type name
+ code.append("memset({arrayName}, 0, sizeof(UA_{typeOfArray}) * {arrayLength});".format(arrayName=arrayName, typeOfArray=typeOfArray,
+ arrayLength=len(node.value)))
# Values is a list of lists
# The current index must be passed so that the code path for evaluating lists has the current index value and can generate the code correctly.
for idx,subv in enumerate(node.value):
diff --git a/tools/nodeset_compiler/backend_open62541_nodes.py b/tools/nodeset_compiler/backend_open62541_nodes.py
index b7af3666d..7042261d0 100644
--- a/tools/nodeset_compiler/backend_open62541_nodes.py
+++ b/tools/nodeset_compiler/backend_open62541_nodes.py
@@ -12,7 +12,7 @@
### Copyright 2018 (c) Jannis Volker
### Copyright 2018 (c) Ralph Lange
-from datatypes import ExtensionObject, NodeId, StatusCode, DiagnosticInfo, Guid, Value
+from datatypes import ExtensionObject, NodeId, StatusCode, DiagnosticInfo, Value
from nodes import ReferenceTypeNode, ObjectNode, VariableNode, VariableTypeNode, MethodNode, ObjectTypeNode, DataTypeNode, ViewNode
from backend_open62541_datatypes import makeCIdentifier, generateLocalizedTextCode, generateQualifiedNameCode, generateNodeIdCode, \
generateExpandedNodeIdCode, generateNodeValueCode
@@ -394,9 +394,7 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True):
if isArrayVariableNode(node, parentNode):
# User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
- if isinstance(node.value[0], Guid):
- logger.warn("Don't know how to print array of GUID in node " + str(parentNode.id))
- elif isinstance(node.value[0], DiagnosticInfo):
+ if isinstance(node.value[0], DiagnosticInfo):
logger.warn("Don't know how to print array of DiagnosticInfo in node " + str(parentNode.id))
elif isinstance(node.value[0], StatusCode):
logger.warn("Don't know how to print array of StatusCode in node " + str(parentNode.id))
@@ -423,9 +421,7 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True):
#scalar value
else:
# User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
- if isinstance(node.value[0], Guid):
- logger.warn("Don't know how to print scalar GUID in node " + str(parentNode.id))
- elif isinstance(node.value[0], DiagnosticInfo):
+ if isinstance(node.value[0], DiagnosticInfo):
logger.warn("Don't know how to print scalar DiagnosticInfo in node " + str(parentNode.id))
elif isinstance(node.value[0], StatusCode):
logger.warn("Don't know how to print scalar StatusCode in node " + str(parentNode.id))
diff --git a/tools/nodeset_compiler/datatypes.py b/tools/nodeset_compiler/datatypes.py
index 0c0e8e72c..d60508cfa 100644
--- a/tools/nodeset_compiler/datatypes.py
+++ b/tools/nodeset_compiler/datatypes.py
@@ -347,7 +347,7 @@ class Value(object):
childValue = ebodypart.firstChild
- if not childValue == ebodypart.ELEMENT_NODE:
+ if not childValue.nodeType == ebodypart.ELEMENT_NODE:
childValue = getNextElementNode(childValue)
for e in members:
if isinstance(e, StructMember):
@@ -355,7 +355,7 @@ class Value(object):
if isinstance(e.member_type, BuiltinType):
if e.is_array:
values = []
- for el in ebodypart.childNodes:
+ for el in childValue.childNodes:
if not el.nodeType == el.ELEMENT_NODE:
continue
t = self.getTypeByString(e.member_type.name, None)
@@ -375,12 +375,13 @@ class Value(object):
structure = Structure()
structure.alias = e.name
structure.value = []
- structure.__parseXMLSingleValue(childValue, parentDataTypeNode, parent, parser, alias=None, encodingPart=e.member_type)
+ if not len(childValue.childNodes) == 0:
+ structure.__parseXMLSingleValue(childValue, parentDataTypeNode, parent, parser, alias=None, encodingPart=e.member_type)
self.value.append(structure)
return structure
elif isinstance(e.member_type, EnumerationType):
t = self.getTypeByString("Int32", None)
- t.parseXML(ebodypart)
+ t.parseXML(childValue)
t.alias = e.name
self.value.append(t)
else:
@@ -846,10 +847,15 @@ class Guid(Value):
def parseXML(self, xmlvalue):
self.checkXML(xmlvalue)
- val = getXmlTextTrimmed(xmlvalue.firstChild)
+ # Support GUID in format:
+ #
+ # 01234567-89AB-CDEF-ABCD-0123456789AB
+ #
+ if len(xmlvalue.getElementsByTagName("String")) != 0:
+ val = getXmlTextTrimmed(xmlvalue.getElementsByTagName("String")[0].firstChild)
if val is None:
- self.value = [0, 0, 0, 0] # Catch XML by setting the value to a default
+ self.value = ['00000000', '0000', '0000', '0000', '000000000000'] # Catch XML by setting the value to a default
else:
self.value = val
self.value = self.value.replace("{", "")
@@ -862,9 +868,8 @@ class Guid(Value):
except Exception:
logger.error("Invalid formatting of Guid. Expected {01234567-89AB-CDEF-ABCD-0123456789AB}, got " + \
unicode(xmlvalue.firstChild.data))
- tmp = [0, 0, 0, 0, 0]
+ self.value = ['00000000', '0000', '0000', '0000', '000000000000']
if len(tmp) != 5:
logger.error("Invalid formatting of Guid. Expected {01234567-89AB-CDEF-ABCD-0123456789AB}, got " + \
unicode(xmlvalue.firstChild.data))
- tmp = [0, 0, 0, 0]
- self.value = tmp
+ self.value = ['00000000', '0000', '0000', '0000', '000000000000']