Skip to content

Commit

Permalink
feat(util): Enable RelativePath parsing to use non-standard Reference…
Browse files Browse the repository at this point in the history
…Types by their BrowseName
  • Loading branch information
jpfr committed Dec 19, 2023
1 parent 1e3c584 commit d900f56
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 16 deletions.
7 changes: 7 additions & 0 deletions include/open62541/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,13 @@ UA_readNumberWithBase(const UA_Byte *buf, size_t buflen,
#ifdef UA_ENABLE_PARSING
UA_EXPORT UA_StatusCode
UA_RelativePath_parse(UA_RelativePath *rp, const UA_String str);

/* Supports the lookup of non-standard ReferenceTypes by their browse name in
* the information model of a server. The first matching result in the
* ReferenceType hierarchy is used. */
UA_EXPORT UA_StatusCode
UA_RelativePath_parseWithServer(UA_Server *server, UA_RelativePath *rp,
const UA_String str);
#endif

/**
Expand Down
19 changes: 15 additions & 4 deletions src/util/ua_types_lex.c
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,10 @@ parse_refpath_qn(UA_QualifiedName *qn, const char *pos, const char *end) {
}

static UA_StatusCode
parse_relativepath(UA_RelativePath *rp, const char *pos, const char *end) {
parse_relativepath(UA_Server *server, UA_RelativePath *rp, const UA_String str) {
const char *pos = (const char*)str.data;
const char *end = (const char*)(str.data + str.length);

LexContext context;
memset(&context, 0, sizeof(LexContext));
const char *begin = NULL, *finish = NULL;
Expand Down Expand Up @@ -802,7 +805,7 @@ parse_relativepath(UA_RelativePath *rp, const char *pos, const char *end) {
}
UA_QualifiedName refqn;
res |= parse_refpath_qn(&refqn, begin, finish);
res |= lookupRefType(&refqn, &current.referenceTypeId);
res |= lookupRefType(server, &refqn, &current.referenceTypeId);
UA_QualifiedName_clear(&refqn);
goto reftype_target;
}
Expand Down Expand Up @@ -884,8 +887,16 @@ parse_relativepath(UA_RelativePath *rp, const char *pos, const char *end) {

UA_StatusCode
UA_RelativePath_parse(UA_RelativePath *rp, const UA_String str) {
UA_StatusCode res =
parse_relativepath(rp, (const char*)str.data, (const char*)str.data+str.length);
UA_StatusCode res = parse_relativepath(NULL, rp, str);
if(res != UA_STATUSCODE_GOOD)
UA_RelativePath_clear(rp);
return res;
}

UA_StatusCode
UA_RelativePath_parseWithServer(UA_Server *server, UA_RelativePath *rp,
const UA_String str) {
UA_StatusCode res = parse_relativepath(server, rp, str);
if(res != UA_STATUSCODE_GOOD)
UA_RelativePath_clear(rp);
return res;
Expand Down
19 changes: 15 additions & 4 deletions src/util/ua_types_lex.re
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,10 @@ parse_refpath_qn(UA_QualifiedName *qn, const char *pos, const char *end) {
}

static UA_StatusCode
parse_relativepath(UA_RelativePath *rp, const char *pos, const char *end) {
parse_relativepath(UA_Server *server, UA_RelativePath *rp, const UA_String str) {
const char *pos = (const char*)str.data;
const char *end = (const char*)(str.data + str.length);

LexContext context;
memset(&context, 0, sizeof(LexContext));
const char *begin = NULL, *finish = NULL;
Expand Down Expand Up @@ -344,7 +347,7 @@ parse_relativepath(UA_RelativePath *rp, const char *pos, const char *end) {
}
UA_QualifiedName refqn;
res |= parse_refpath_qn(&refqn, begin, finish);
res |= lookupRefType(&refqn, &current.referenceTypeId);
res |= lookupRefType(server, &refqn, &current.referenceTypeId);
UA_QualifiedName_clear(&refqn);
goto reftype_target;
}
Expand Down Expand Up @@ -381,8 +384,16 @@ parse_relativepath(UA_RelativePath *rp, const char *pos, const char *end) {

UA_StatusCode
UA_RelativePath_parse(UA_RelativePath *rp, const UA_String str) {
UA_StatusCode res =
parse_relativepath(rp, (const char*)str.data, (const char*)str.data+str.length);
UA_StatusCode res = parse_relativepath(NULL, rp, str);
if(res != UA_STATUSCODE_GOOD)
UA_RelativePath_clear(rp);
return res;
}

UA_StatusCode
UA_RelativePath_parseWithServer(UA_Server *server, UA_RelativePath *rp,
const UA_String str) {
UA_StatusCode res = parse_relativepath(server, rp, str);
if(res != UA_STATUSCODE_GOOD)
UA_RelativePath_clear(rp);
return res;
Expand Down
49 changes: 42 additions & 7 deletions src/util/ua_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#define UA_INLINABLE_IMPL 1

#include <open62541/types_generated_handling.h>
#include <open62541/server.h>
#include <open62541/util.h>

#include "ua_util_internal.h"
Expand Down Expand Up @@ -538,15 +539,49 @@ static const RefTypeName knownRefTypes[KNOWNREFTYPES] = {
};

UA_StatusCode
lookupRefType(UA_QualifiedName *qn, UA_NodeId *outRefTypeId) {
if(qn->namespaceIndex != 0)
return UA_STATUSCODE_BADNOTFOUND;
for(size_t i = 0; i < KNOWNREFTYPES; i++) {
if(UA_String_equal(&qn->name, &knownRefTypes[i].browseName)) {
*outRefTypeId = UA_NODEID_NUMERIC(0, knownRefTypes[i].identifier);
return UA_STATUSCODE_GOOD;
lookupRefType(UA_Server *server, UA_QualifiedName *qn, UA_NodeId *outRefTypeId) {
/* Check well-known ReferenceTypes first */
if(qn->namespaceIndex == 0) {
for(size_t i = 0; i < KNOWNREFTYPES; i++) {
if(UA_String_equal(&qn->name, &knownRefTypes[i].browseName)) {
*outRefTypeId = UA_NODEID_NUMERIC(0, knownRefTypes[i].identifier);
return UA_STATUSCODE_GOOD;
}
}
}

/* Browse the information model. Return the first results if the browse name
* in the hierarchy is ambiguous. */
if(server) {
UA_BrowseDescription bd;
UA_BrowseDescription_init(&bd);
bd.nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_REFERENCES);
bd.browseDirection = UA_BROWSEDIRECTION_FORWARD;
bd.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
bd.nodeClassMask = UA_NODECLASS_REFERENCETYPE;

size_t resultsSize = 0;
UA_ExpandedNodeId *results = NULL;
UA_StatusCode res =
UA_Server_browseRecursive(server, &bd, &resultsSize, &results);
if(res != UA_STATUSCODE_GOOD)
return res;
for(size_t i = 0; i < resultsSize; i++) {
UA_QualifiedName bn;
UA_Server_readBrowseName(server, results[i].nodeId, &bn);
if(UA_QualifiedName_equal(qn, &bn)) {
UA_QualifiedName_clear(&bn);
*outRefTypeId = results[i].nodeId;
UA_NodeId_clear(&results[i].nodeId);
UA_Array_delete(results, resultsSize, &UA_TYPES[UA_TYPES_NODEID]);
return UA_STATUSCODE_GOOD;
}
UA_QualifiedName_clear(&bn);
}

UA_Array_delete(results, resultsSize, &UA_TYPES[UA_TYPES_NODEID]);
}

return UA_STATUSCODE_BADNOTFOUND;
}

Expand Down
2 changes: 1 addition & 1 deletion src/util/ua_util_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ typedef UA_StatusCode status;

/* Well-known ReferenceTypes */
UA_StatusCode
lookupRefType(UA_QualifiedName *qn, UA_NodeId *outRefTypeId);
lookupRefType(UA_Server *server, UA_QualifiedName *qn, UA_NodeId *outRefTypeId);

/**
* Error checking macros
Expand Down
30 changes: 30 additions & 0 deletions tests/check_types_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <open62541/types.h>
#include <open62541/types_generated_handling.h>
#include "test_helpers.h"
#include "open62541/util.h"

#include <stdlib.h>
Expand Down Expand Up @@ -209,6 +210,34 @@ START_TEST(parseRelativePath) {
UA_RelativePath_clear(&rp);
} END_TEST

START_TEST(parseRelativePathWithServer) {
UA_Server *server = UA_Server_newForUnitTest();

/* Add a custom non-hierarchical reference type */
UA_NodeId refTypeId;
UA_ReferenceTypeAttributes refattr = UA_ReferenceTypeAttributes_default;
refattr.displayName = UA_LOCALIZEDTEXT(NULL, "MyRef");
refattr.inverseName = UA_LOCALIZEDTEXT(NULL, "RefMy");
UA_QualifiedName browseName = UA_QUALIFIEDNAME(1, "MyRef");
UA_StatusCode res =
UA_Server_addReferenceTypeNode(server, UA_NODEID_NULL,
UA_NODEID_NUMERIC(0, UA_NS0ID_NONHIERARCHICALREFERENCES),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
browseName, refattr, NULL, &refTypeId);
ck_assert_int_eq(res, UA_STATUSCODE_GOOD);

/* Use the browse name in the path string. Expect the NodeId of the reference type */
UA_RelativePath rp;
res = UA_RelativePath_parseWithServer(server, &rp,
UA_STRING("/1:Boiler<1:MyRef>1:HeatSensor"));
ck_assert_int_eq(res, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(rp.elementsSize, 2);
ck_assert(UA_NodeId_equal(&rp.elements[1].referenceTypeId, &refTypeId));
UA_RelativePath_clear(&rp);

UA_Server_delete(server);
} END_TEST

int main(void) {
Suite *s = suite_create("Test Builtin Type Parsing");
TCase *tc = tcase_create("test cases");
Expand All @@ -226,6 +255,7 @@ int main(void) {
tcase_add_test(tc, parseExpandedNodeIdIntegerFailNSU);
tcase_add_test(tc, parseExpandedNodeIdIntegerFailNSU2);
tcase_add_test(tc, parseRelativePath);
tcase_add_test(tc, parseRelativePathWithServer);
suite_add_tcase(s, tc);

SRunner *sr = srunner_create(s);
Expand Down

0 comments on commit d900f56

Please sign in to comment.