Merge pull request #4959 from open62541/1.3

Merge 1.3 to master
This commit is contained in:
Julius Pfrommer 2022-03-06 22:04:00 +01:00 committed by GitHub
commit be9fbf4f61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1471 additions and 1540 deletions

View File

@ -165,6 +165,7 @@ option(UA_ENABLE_SUBSCRIPTIONS_EVENTS "Enable event monitoring" ON)
option(UA_ENABLE_DA "Enable OPC UA DataAccess (Part 8) definitions" ON)
option(UA_ENABLE_HISTORIZING "Enable basic support for historical access (client and server)" OFF)
option(UA_ENABLE_DISCOVERY "Enable Discovery Service (LDS)" OFF)
option(UA_ENABLE_JSON_ENCODING "Enable JSON encoding" OFF)
option(UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS "Enable the use of A&C. (EXPERIMENTAL)" OFF)
mark_as_advanced(UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS)
@ -422,9 +423,6 @@ if(UA_ENABLE_PUBSUB_BUFMALLOC)
endif()
endif()
option(UA_ENABLE_JSON_ENCODING "Enable Json encoding (EXPERIMENTAL)" OFF)
mark_as_advanced(UA_ENABLE_JSON_ENCODING)
option(UA_ENABLE_PUBSUB_MQTT "Enable publish/subscribe with mqtt (experimental)" OFF)
mark_as_advanced(UA_ENABLE_PUBSUB_MQTT)
if(UA_ENABLE_PUBSUB_MQTT)

95
deps/jsmn/jsmn.c vendored
View File

@ -2,6 +2,14 @@
#define JSMN_STRICT
/* what is the next expected element? */
enum JSMN_NEXT {
JSMN_KEY = 0,
JSMN_COLON,
JSMN_VALUE,
JSMN_COMMA
};
/**
* Allocates a fresh unused token from the token pull.
*/
@ -151,11 +159,12 @@ static int jsmn_parse_string(jsmn_parser *parser, const char *js,
* Parse JSON string and fill tokens.
*/
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
jsmntok_t *tokens, unsigned int num_tokens) {
jsmntok_t *tokens, unsigned int num_tokens) {
int r;
int i;
jsmntok_t *token;
unsigned int count = parser->toknext;
enum JSMN_NEXT next = JSMN_VALUE;
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
char c;
@ -168,6 +177,8 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
if (tokens == NULL) {
break;
}
if(next != JSMN_VALUE)
return JSMN_ERROR_INVAL;
token = jsmn_alloc_token(parser, tokens, num_tokens);
if (token == NULL)
return JSMN_ERROR_NOMEM;
@ -180,11 +191,19 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
token->start = (int)parser->pos;
parser->toksuper = (int)parser->toknext - 1;
if(c == '{')
next = JSMN_KEY;
else
next = JSMN_VALUE;
break;
case '}': case ']':
if (tokens == NULL)
break;
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
if(type == JSMN_OBJECT && next != JSMN_KEY && next != JSMN_COMMA)
return JSMN_ERROR_INVAL;
else if (type == JSMN_ARRAY && next != JSMN_VALUE && next != JSMN_COMMA)
return JSMN_ERROR_INVAL;
#ifdef JSMN_PARENT_LINKS
if (parser->toknext < 1) {
return JSMN_ERROR_INVAL;
@ -194,8 +213,8 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
return JSMN_ERROR_INVAL;
}
token->end = parser->pos + 1;
}
token->end = (int)parser->pos + 1;
parser->toksuper = token->parent;
break;
}
@ -208,19 +227,21 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
token = &tokens[token->parent];
}
#else
/* Find the surrounding token and close it */
for (i = (int)parser->toknext - 1; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
if (token->type != type) {
if (token->type != type)
return JSMN_ERROR_INVAL;
}
parser->toksuper = -1;
token->end = (int)parser->pos + 1;
break;
}
}
/* Error if unmatched closing bracket */
if (i == -1) return JSMN_ERROR_INVAL;
if (i == -1)
return JSMN_ERROR_INVAL;
/* Find the new super token (first not closed) */
for (; i >= 0; i--) {
token = &tokens[i];
if (token->start != -1 && token->end == -1) {
@ -229,59 +250,67 @@ int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
}
}
#endif
next = JSMN_COMMA;
break;
case '\"':
if(next == JSMN_KEY) {
next = JSMN_COLON;
} else if(next == JSMN_VALUE) {
next = JSMN_COMMA;
if (parser->toksuper != -1 && tokens != NULL)
tokens[parser->toksuper].size++;
} else {
return JSMN_ERROR_INVAL;
}
r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
if (r < 0) return r;
count++;
if (parser->toksuper != -1 && tokens != NULL)
tokens[parser->toksuper].size++;
break;
case '\t' : case '\r' : case '\n' : case ' ':
break;
case ':':
parser->toksuper = (int)parser->toknext - 1;
if(next != JSMN_COLON) /* Must expect a colon */
return JSMN_ERROR_INVAL;
/* The current toksuper must be an object */
if(parser->toknext == 0 ||
tokens[parser->toksuper].type != JSMN_OBJECT)
return JSMN_ERROR_INVAL;
/* Previous token must be a string */
if(tokens[parser->toknext - 1].type != JSMN_STRING)
return JSMN_ERROR_INVAL;
next = JSMN_VALUE;
break;
case ',':
if (tokens != NULL && parser->toksuper != -1 &&
tokens[parser->toksuper].type != JSMN_ARRAY &&
tokens[parser->toksuper].type != JSMN_OBJECT) {
#ifdef JSMN_PARENT_LINKS
parser->toksuper = tokens[parser->toksuper].parent;
#else
for (i = (int)parser->toknext - 1; i >= 0; i--) {
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
if (tokens[i].start != -1 && tokens[i].end == -1) {
parser->toksuper = i;
break;
}
}
}
#endif
}
if(next != JSMN_COMMA) /* Must expect a comma */
return JSMN_ERROR_INVAL;
if(parser->toksuper == -1)
return JSMN_ERROR_INVAL;
if(tokens &&
tokens[parser->toksuper].type != JSMN_ARRAY &&
tokens[parser->toksuper].type != JSMN_OBJECT)
return JSMN_ERROR_INVAL;
if(tokens && tokens[parser->toksuper].type == JSMN_OBJECT)
next = JSMN_KEY;
else
next = JSMN_VALUE;
break;
#ifdef JSMN_STRICT
/* In strict mode primitives are: numbers and booleans */
case '-': case '0': case '1' : case '2': case '3' : case '4':
case '5': case '6': case '7' : case '8': case '9':
case 't': case 'f': case 'n' :
/* And they must not be keys of the object */
if (tokens != NULL && parser->toksuper != -1) {
jsmntok_t *t = &tokens[parser->toksuper];
if (t->type == JSMN_OBJECT ||
(t->type == JSMN_STRING && t->size != 0)) {
return JSMN_ERROR_INVAL;
}
}
#else
/* In non-strict mode every unquoted value is a primitive */
default:
#endif
if(next != JSMN_VALUE)
return JSMN_ERROR_INVAL;
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
if (r < 0) return r;
count++;
if (parser->toksuper != -1 && tokens != NULL)
tokens[parser->toksuper].size++;
next = JSMN_COMMA;
break;
#ifdef JSMN_STRICT

View File

@ -2,12 +2,7 @@
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information. */
/**
*
* Attention! WIP: The event-filter mechanism is currently "Work in progress" and will be completed
* soon. Some of the below defined filters are currently not working and will be completed
* in on of the next event-filter-batches.
*
* When using events, the client is mostly only interested in a small selection of events.
* When using events, the client is most often only interested in a small selection of events.
* To avoid unnecessary event transmission, the client can configure filters. These filters
* are part of the event-subscription configuration.
*
@ -28,8 +23,7 @@
#define USE_FILTER_OR_TYPEOF
static UA_Boolean running = true;
const size_t nSelectClauses = 2;
const size_t nWhereClauses = 1;
#define SELECT_CLAUSE_FIELD_COUNT 3
/**
* Setting up SelectClauses
@ -40,37 +34,30 @@ const size_t nWhereClauses = 1;
* In this Example we are selecting two SimpleAttributeOperands to be returned, one as a Message and one as Severity.
*/
static UA_SimpleAttributeOperand *
setupSelectClauses(void) {
setupSelectClauses(size_t selectedFieldsSize, UA_QualifiedName *qName) {
UA_SimpleAttributeOperand *selectClauses = (UA_SimpleAttributeOperand*)
UA_Array_new(nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
UA_Array_new(selectedFieldsSize, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
if(!selectClauses)
return NULL;
for(size_t i =0; i<nSelectClauses; ++i) {
for(size_t i =0; i<selectedFieldsSize; ++i) {
UA_SimpleAttributeOperand_init(&selectClauses[i]);
}
//configure Message field
selectClauses[0].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
selectClauses[0].browsePathSize = 1;
selectClauses[0].browsePath = (UA_QualifiedName*)
UA_Array_new(selectClauses[0].browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
if(!selectClauses[0].browsePath) {
UA_SimpleAttributeOperand_delete(selectClauses);
return NULL;
}
selectClauses[0].attributeId = UA_ATTRIBUTEID_VALUE;
selectClauses[0].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "Message");
//configure Severity field
selectClauses[1].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
selectClauses[1].browsePathSize = 1;
selectClauses[1].browsePath = (UA_QualifiedName*)
UA_Array_new(selectClauses[1].browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
if(!selectClauses[1].browsePath) {
UA_SimpleAttributeOperand_delete(selectClauses);
return NULL;
}
selectClauses[1].attributeId = UA_ATTRIBUTEID_VALUE;
selectClauses[1].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "Severity");
for (size_t i = 0; i < selectedFieldsSize; ++i) {
selectClauses[i].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
selectClauses[i].browsePathSize = 1;
selectClauses[i].browsePath = (UA_QualifiedName*)
UA_Array_new(selectClauses[i].browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
if(!selectClauses[i].browsePath) {
UA_SimpleAttributeOperand_delete(selectClauses);
return NULL;
}
selectClauses[i].attributeId = UA_ATTRIBUTEID_VALUE;
char fieldName[64];
memcpy(fieldName, qName[i].name.data, qName[i].name.length);
fieldName[qName[i].name.length] = '\0';
selectClauses[i].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, fieldName);
}
return selectClauses;
}
@ -123,15 +110,22 @@ setupOfTypeFilter(UA_ContentFilterElement *element, UA_UInt16 nsIndex, UA_UInt32
}
static void
setupLiteralOperand(UA_ContentFilterElement *element, size_t count, UA_Variant *literals){
for(size_t i = 0; i < count; ++i) {
element->filterOperands[i].content.decoded.type = &UA_TYPES[UA_TYPES_LITERALOPERAND];
element->filterOperands[i].encoding = UA_EXTENSIONOBJECT_DECODED;
UA_LiteralOperand *literalOperand = UA_LiteralOperand_new();
UA_LiteralOperand_init(literalOperand);
literalOperand->value = literals[i];
element->filterOperands[i].content.decoded.data = literalOperand;
}
setupLiteralOperand(UA_ContentFilterElement *element, size_t operandIndex, UA_Variant literal){
element->filterOperands[operandIndex].content.decoded.type = &UA_TYPES[UA_TYPES_LITERALOPERAND];
element->filterOperands[operandIndex].encoding = UA_EXTENSIONOBJECT_DECODED;
UA_LiteralOperand *literalOperand = UA_LiteralOperand_new();
UA_LiteralOperand_init(literalOperand);
literalOperand->value = literal;
element->filterOperands[operandIndex].content.decoded.data = literalOperand;
}
static void
setupSimpleAttributeOperand(UA_ContentFilterElement *element, size_t operandIndex, UA_SimpleAttributeOperand attributeOperand){
element->filterOperands[operandIndex].content.decoded.type = &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND];
element->filterOperands[operandIndex].encoding = UA_EXTENSIONOBJECT_DECODED;
UA_SimpleAttributeOperand *simpleAttributeOperand = UA_SimpleAttributeOperand_new();
*simpleAttributeOperand = attributeOperand;
element->filterOperands[operandIndex].content.decoded.data = simpleAttributeOperand;
}
/**
@ -152,6 +146,8 @@ setupLiteralOperand(UA_ContentFilterElement *element, size_t count, UA_Variant *
* (EventTypeId == NodeID("CancelScanEvent") ||
* (EventTypeId == NodeID("CancelScanFinishedEvent") ||
* (EventTypeId == NodeID("ShutdownEvent"))
* filterSelection 3:
* ((OfType 5003 ) (and) ((Equal 99 == 99) (and) (Equal Event-Field "servity" > 99))
*
*/
static UA_StatusCode
@ -253,7 +249,7 @@ setupWhereClauses(UA_ContentFilter *contentFilter, UA_UInt16 whereClauseSize, UA
contentFilter->elements[1].filterOperator = UA_FILTEROPERATOR_OFTYPE;
contentFilter->elements[2].filterOperator = UA_FILTEROPERATOR_AND;
contentFilter->elements[3].filterOperator = UA_FILTEROPERATOR_EQUALS;
contentFilter->elements[4].filterOperator = UA_FILTEROPERATOR_EQUALS;
contentFilter->elements[4].filterOperator = UA_FILTEROPERATOR_GREATERTHAN;
contentFilter->elements[0].filterOperandsSize = 2;
contentFilter->elements[1].filterOperandsSize = 1;
@ -273,13 +269,61 @@ setupWhereClauses(UA_ContentFilter *contentFilter, UA_UInt16 whereClauseSize, UA
setupTwoOperandsFilter(&contentFilter->elements[2], 3, 4);
setupOfTypeFilter(&contentFilter->elements[1], 1, 5000);
UA_UInt32 literal_value;
UA_Variant literalContent[2];
memset(literalContent, 0, sizeof(UA_Variant) * 2);
UA_Variant_setScalar(&literalContent[0], &literal_value, &UA_TYPES[UA_TYPES_UINT32]);
UA_Variant_setScalar(&literalContent[1], &literal_value, &UA_TYPES[UA_TYPES_UINT32]);
setupLiteralOperand(&contentFilter->elements[3], 2, literalContent);
setupLiteralOperand(&contentFilter->elements[4], 2, literalContent);
UA_Variant literalContent;
UA_Variant_init(&literalContent);
UA_UInt32 literal_value = 99;
UA_Variant_setScalarCopy(&literalContent, &literal_value, &UA_TYPES[UA_TYPES_UINT32]);
setupLiteralOperand(&contentFilter->elements[3], 0, literalContent);
setupLiteralOperand(&contentFilter->elements[3], 1, literalContent);
UA_SimpleAttributeOperand sao;
UA_SimpleAttributeOperand_init(&sao);
sao.attributeId = UA_ATTRIBUTEID_VALUE;
sao.typeDefinitionId = UA_NODEID_NUMERIC(0, 5000);
sao.browsePathSize = 1;
UA_QualifiedName *qn = UA_QualifiedName_new();
*qn = UA_QUALIFIEDNAME_ALLOC(0, "Severity");
sao.browsePath = qn;
setupSimpleAttributeOperand(&contentFilter->elements[4], 0, sao);
setupLiteralOperand(&contentFilter->elements[4], 1, literalContent);
break;
}
case 4: {
contentFilter->elements[0].filterOperator = UA_FILTEROPERATOR_AND;
contentFilter->elements[1].filterOperator = UA_FILTEROPERATOR_OFTYPE;
contentFilter->elements[2].filterOperator = UA_FILTEROPERATOR_GREATERTHAN;
contentFilter->elements[0].filterOperandsSize = 2;
contentFilter->elements[1].filterOperandsSize = 1;
contentFilter->elements[2].filterOperandsSize = 2;
/* Setup Operand Arrays */
result = setupOperandArrays(contentFilter);
if(result != UA_STATUSCODE_GOOD) {
UA_ContentFilter_clear(contentFilter);
return UA_STATUSCODE_BADCONFIGURATIONERROR;
}
// init clauses
setupTwoOperandsFilter(&contentFilter->elements[0], 1, 2);
setupOfTypeFilter(&contentFilter->elements[1], 1, 5000);
UA_Variant literalContent;
UA_Variant_init(&literalContent);
UA_UInt32 literal_value = 99;
UA_Variant_setScalarCopy(&literalContent, &literal_value, &UA_TYPES[UA_TYPES_UINT32]);
UA_SimpleAttributeOperand sao;
UA_SimpleAttributeOperand_init(&sao);
sao.attributeId = UA_ATTRIBUTEID_VALUE;
sao.typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
sao.browsePathSize = 1;
UA_QualifiedName *qn = UA_QualifiedName_new();
*qn = UA_QUALIFIEDNAME_ALLOC(0, "Severity");
sao.browsePath = qn;
setupSimpleAttributeOperand(&contentFilter->elements[2], 0, sao);
setupLiteralOperand(&contentFilter->elements[2], 1, literalContent);
break;
}
default:
@ -302,6 +346,12 @@ handler_events_filter(UA_Client *client, UA_UInt32 subId, void *subContext,
UA_LocalizedText *lt = (UA_LocalizedText *)eventFields[i].data;
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Message: '%.*s'", (int)lt->text.length, lt->text.data);
} else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_NODEID])) {
UA_String nodeIdName = UA_STRING_ALLOC("");
UA_NodeId_print((UA_NodeId *)eventFields[i].data, &nodeIdName);
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"TypeNodeId: '%.*s'", (int)nodeIdName.length, nodeIdName.data);
UA_String_clear(&nodeIdName);
} else {
#ifdef UA_ENABLE_TYPEDESCRIPTION
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
@ -325,7 +375,7 @@ int main(int argc, char *argv[]) {
signal(SIGTERM, stopHandler);
if(argc < 2) {
printf("Usage: tutorial_client_events <opc.tcp://server-url>\n");
printf("Usage: tutorial_client_event_filter <opc.tcp://server-url>\n");
return EXIT_FAILURE;
}
@ -359,9 +409,16 @@ int main(int argc, char *argv[]) {
UA_EventFilter filter;
UA_EventFilter_init(&filter);
filter.selectClauses = setupSelectClauses();
filter.selectClausesSize = nSelectClauses;
retval = setupWhereClauses(&filter.whereClause, 5, 3);
UA_QualifiedName qm[SELECT_CLAUSE_FIELD_COUNT];
qm[0].namespaceIndex = 0;
qm[0].name = UA_STRING("Message");
qm[1].namespaceIndex = 0;
qm[1].name = UA_STRING("Severity");
qm[2].namespaceIndex = 0;
qm[2].name = UA_STRING("EventType");
filter.selectClauses = setupSelectClauses(SELECT_CLAUSE_FIELD_COUNT, qm);
filter.selectClausesSize = SELECT_CLAUSE_FIELD_COUNT;
retval = setupWhereClauses(&filter.whereClause, 3, 4);
if(retval != UA_STATUSCODE_GOOD) {
UA_Client_delete(client);
return EXIT_FAILURE;
@ -395,7 +452,7 @@ int main(int argc, char *argv[]) {
cleanup:
UA_MonitoredItemCreateResult_clear(&result);
UA_Client_Subscriptions_deleteSingle(client, response.subscriptionId);
UA_Array_delete(filter.selectClauses, nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
UA_Array_delete(filter.selectClauses, SELECT_CLAUSE_FIELD_COUNT, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
UA_Array_delete(filter.whereClause.elements, filter.whereClause.elementsSize, &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT]);
UA_Client_disconnect(client);

View File

@ -198,7 +198,7 @@ generateRandomEventMethodCallback(UA_Server *server,
UA_UInt32 random = (UA_UInt32) UA_UInt32_random() % SAMPLE_EVENT_TYPES_COUNT;
/* set up event */
UA_NodeId eventNodeId;
UA_StatusCode retval = setUpEvent(server, &eventNodeId, eventTypes[random], true,random);
UA_StatusCode retval = setUpEvent(server, &eventNodeId, eventTypes[random], true, random);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Creating event failed. StatusCode %s", UA_StatusCode_name(retval));
@ -213,6 +213,37 @@ generateRandomEventMethodCallback(UA_Server *server,
return retval;
}
static UA_StatusCode
generateCustomizedEventMethodCallback(UA_Server *server,
const UA_NodeId *sessionId, void *sessionHandle,
const UA_NodeId *methodId, void *methodContext,
const UA_NodeId *objectId, void *objectContext,
size_t inputSize, const UA_Variant *input,
size_t outputSize, UA_Variant *output) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Creating customized event");
UA_UInt16 *severity = (UA_UInt16*)input->data;
UA_UInt32 random = (UA_UInt32) UA_UInt32_random() % SAMPLE_EVENT_TYPES_COUNT;
UA_NodeId eventNodeId;
UA_StatusCode retval = setUpEvent(server, &eventNodeId, eventTypes[random], true, random);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Creating event failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
//Overwrite severity
UA_Server_writeObjectProperty_scalar(server, eventNodeId,
UA_QUALIFIEDNAME(0, "Severity"),
severity, &UA_TYPES[UA_TYPES_UINT16]);
retval = UA_Server_triggerEvent(server, eventNodeId,
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER),
NULL, UA_TRUE);
if(retval != UA_STATUSCODE_GOOD)
UA_LOG_WARNING(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND,
"Triggering event failed. StatusCode %s", UA_StatusCode_name(retval));
return UA_STATUSCODE_GOOD;
}
static void
addGenerateSampleEventsMethod(UA_Server *server) {
UA_MethodAttributes generateAttr = UA_MethodAttributes_default;
@ -243,6 +274,28 @@ addGenerateSingleRandomEventMethod(UA_Server *server) {
0, NULL, 0, NULL, NULL, NULL);
}
static void
addGenerateSingleCustomizedEventMethod(UA_Server *server) {
UA_Argument inputArgument;
UA_Argument_init(&inputArgument);
inputArgument.description = UA_LOCALIZEDTEXT("en-US", "Severity");
inputArgument.name = UA_STRING("Severity");
inputArgument.dataType = UA_TYPES[UA_TYPES_UINT16].typeId;
inputArgument.valueRank = UA_VALUERANK_SCALAR;
UA_MethodAttributes generateAttr = UA_MethodAttributes_default;
generateAttr.description = UA_LOCALIZEDTEXT("en-US","Generate a customized event.");
generateAttr.displayName = UA_LOCALIZEDTEXT("en-US","Generate customized event");
generateAttr.executable = true;
generateAttr.userExecutable = true;
UA_Server_addMethodNode(server, UA_NODEID_NUMERIC(1, 65002),
UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASCOMPONENT),
UA_QUALIFIEDNAME(1, "Generate customized Event"),
generateAttr, &generateCustomizedEventMethodCallback,
1, &inputArgument, 0, NULL, NULL, NULL);
}
static void stopHandler(int sig) {
running = false;
}
@ -259,6 +312,7 @@ int main(int argc, char *argv[]) {
addSampleEventTypes(server);
addGenerateSampleEventsMethod(server);
addGenerateSingleRandomEventMethod(server);
addGenerateSingleCustomizedEventMethod(server);
UA_StatusCode retval = UA_Server_run(server, &running);

View File

@ -33,6 +33,7 @@ int main(int argc, char **argv)
config->customDataTypes = &customTypesArray;
UA_StatusCode retval;
/* create nodes from nodeset */
if(namespace_testnodeset_generated(server) != UA_STATUSCODE_GOOD) {

View File

@ -787,6 +787,10 @@ setInformationModel(UA_Server *server) {
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
/* Create the reusable event instance */
UA_Server_createEvent(server, UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE), &eventId);
UA_UInt16 eventSeverity = 500;
UA_Server_writeObjectProperty_scalar(server, eventId,
UA_QUALIFIEDNAME(0, "Severity"),
&eventSeverity, &UA_TYPES[UA_TYPES_UINT16]);
/* Trigger the event from two variables */
UA_ValueCallback eventTriggerValueBackend;
@ -1278,6 +1282,10 @@ int main(int argc, char **argv) {
if(!enableAnon)
disableAnonymous(&config);
/* Limit the number of SecureChannels and Sessions */
config.maxSecureChannels = 10;
config.maxSessions = 20;
/* Revolve the SecureChannel token every 300 seconds */
config.maxSecurityTokenLifetime = 300000;
@ -1293,7 +1301,7 @@ int main(int argc, char **argv) {
/* Set Subscription limits */
#ifdef UA_ENABLE_SUBSCRIPTIONS
config.maxSubscriptions = 100;
config.maxSubscriptions = 20;
#endif
/* If RequestTimestamp is '0', log the warning and proceed */

View File

@ -1227,6 +1227,10 @@ UA_encodeJson(const void *src, const UA_DataType *type, UA_ByteString *outBuf,
* Zero-out the entire structure initially to ensure code-compatibility when
* more fields are added in a later release. */
typedef struct {
const UA_String *namespaces;
size_t namespacesSize;
const UA_String *serverUris;
size_t serverUrisSize;
const UA_DataTypeArray *customTypes; /* Begin of a linked list with custom
* datatype definitions */
} UA_DecodeJsonOptions;

View File

@ -12,23 +12,23 @@
#include "ua_types_encoding_json.h"
/* Json keys for dsm */
const char * UA_DECODEKEY_MESSAGES = ("Messages");
const char * UA_DECODEKEY_MESSAGETYPE = ("MessageType");
const char * UA_DECODEKEY_MESSAGEID = ("MessageId");
const char * UA_DECODEKEY_PUBLISHERID = ("PublisherId");
const char * UA_DECODEKEY_DATASETCLASSID = ("DataSetClassId");
const char * UA_DECODEKEY_MESSAGES = "Messages";
const char * UA_DECODEKEY_MESSAGETYPE = "MessageType";
const char * UA_DECODEKEY_MESSAGEID = "MessageId";
const char * UA_DECODEKEY_PUBLISHERID = "PublisherId";
const char * UA_DECODEKEY_DATASETCLASSID = "DataSetClassId";
/* Json keys for dsm */
const char * UA_DECODEKEY_DATASETWRITERID = ("DataSetWriterId");
const char * UA_DECODEKEY_SEQUENCENUMBER = ("SequenceNumber");
const char * UA_DECODEKEY_METADATAVERSION = ("MetaDataVersion");
const char * UA_DECODEKEY_TIMESTAMP = ("Timestamp");
const char * UA_DECODEKEY_DSM_STATUS = ("Status");
const char * UA_DECODEKEY_PAYLOAD = ("Payload");
const char * UA_DECODEKEY_DS_TYPE = ("Type");
const char * UA_DECODEKEY_DATASETWRITERID = "DataSetWriterId";
const char * UA_DECODEKEY_SEQUENCENUMBER = "SequenceNumber";
const char * UA_DECODEKEY_METADATAVERSION = "MetaDataVersion";
const char * UA_DECODEKEY_TIMESTAMP = "Timestamp";
const char * UA_DECODEKEY_DSM_STATUS = "Status";
const char * UA_DECODEKEY_PAYLOAD = "Payload";
const char * UA_DECODEKEY_DS_TYPE = "Type";
/* -- json encoding/decoding -- */
static UA_StatusCode writeJsonKey_UA_String(CtxJson *ctx, UA_String *in){
static UA_StatusCode writeJsonKey_UA_String(CtxJson *ctx, UA_String *in) {
UA_STACKARRAY(char, out, in->length + 1);
memcpy(out, in->data, in->length);
out[in->length] = 0;
@ -36,8 +36,9 @@ static UA_StatusCode writeJsonKey_UA_String(CtxJson *ctx, UA_String *in){
}
static UA_StatusCode
UA_DataSetMessage_encodeJson_internal(const UA_DataSetMessage* src, UA_UInt16 dataSetWriterId,
CtxJson *ctx){
UA_DataSetMessage_encodeJson_internal(const UA_DataSetMessage* src,
UA_UInt16 dataSetWriterId,
CtxJson *ctx) {
status rv = writeJsonObjStart(ctx);
/* DataSetWriterId */
@ -56,7 +57,8 @@ UA_DataSetMessage_encodeJson_internal(const UA_DataSetMessage* src, UA_UInt16 da
}
/* MetaDataVersion */
if(src->header.configVersionMajorVersionEnabled || src->header.configVersionMinorVersionEnabled) {
if(src->header.configVersionMajorVersionEnabled ||
src->header.configVersionMinorVersionEnabled) {
UA_ConfigurationVersionDataType cvd;
cvd.majorVersion = src->header.configVersionMajorVersion;
cvd.minorVersion = src->header.configVersionMinorVersion;
@ -77,7 +79,7 @@ UA_DataSetMessage_encodeJson_internal(const UA_DataSetMessage* src, UA_UInt16 da
/* Status */
if(src->header.statusEnabled) {
rv |= writeJsonObjElm(ctx, UA_DECODEKEY_DSM_STATUS,
&src->header.status, &UA_TYPES[UA_TYPES_STATUSCODE]);
&src->header.status, &UA_TYPES[UA_TYPES_UINT16]);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
@ -88,39 +90,36 @@ UA_DataSetMessage_encodeJson_internal(const UA_DataSetMessage* src, UA_UInt16 da
/* TODO: currently no difference between delta and key frames. Own
* dataSetMessageType for json?. If the field names are not defined, write
* out empty field names. */
if(src->header.dataSetMessageType == UA_DATASETMESSAGE_DATAKEYFRAME) {
if(src->header.dataSetMessageType != UA_DATASETMESSAGE_DATAKEYFRAME)
return UA_STATUSCODE_BADNOTSUPPORTED; /* Delta frames not supported */
if(src->header.fieldEncoding == UA_FIELDENCODING_VARIANT) {
/* KEYFRAME VARIANT */
for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) {
if(src->data.keyFrameData.fieldNames)
rv |= writeJsonKey_UA_String(ctx, &src->data.keyFrameData.fieldNames[i]);
else
rv |= writeJsonKey(ctx, "");
rv |= encodeJsonInternal(&(src->data.keyFrameData.dataSetFields[i].value),
&UA_TYPES[UA_TYPES_VARIANT], ctx);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
} else if(src->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) {
/* KEYFRAME DATAVALUE */
for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) {
if(src->data.keyFrameData.fieldNames)
rv |= writeJsonKey_UA_String(ctx, &src->data.keyFrameData.fieldNames[i]);
else
rv |= writeJsonKey(ctx, "");
rv |= encodeJsonInternal(&src->data.keyFrameData.dataSetFields[i],
&UA_TYPES[UA_TYPES_DATAVALUE], ctx);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
} else {
/* RawData */
return UA_STATUSCODE_BADNOTIMPLEMENTED;
if(src->header.fieldEncoding == UA_FIELDENCODING_VARIANT) {
/* KEYFRAME VARIANT */
for(UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) {
if(src->data.keyFrameData.fieldNames)
rv |= writeJsonKey_UA_String(ctx, &src->data.keyFrameData.fieldNames[i]);
else
rv |= writeJsonKey(ctx, "");
rv |= encodeJsonInternal(&(src->data.keyFrameData.dataSetFields[i].value),
&UA_TYPES[UA_TYPES_VARIANT], ctx);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
} else if(src->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) {
/* KEYFRAME DATAVALUE */
for(UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) {
if(src->data.keyFrameData.fieldNames)
rv |= writeJsonKey_UA_String(ctx, &src->data.keyFrameData.fieldNames[i]);
else
rv |= writeJsonKey(ctx, "");
rv |= encodeJsonInternal(&src->data.keyFrameData.dataSetFields[i],
&UA_TYPES[UA_TYPES_DATAVALUE], ctx);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
} else {
/* DeltaFrame */
return UA_STATUSCODE_BADNOTSUPPORTED;
/* RawData */
return UA_STATUSCODE_BADNOTIMPLEMENTED;
}
rv |= writeJsonObjEnd(ctx); /* Payload */
rv |= writeJsonObjEnd(ctx); /* DataSetMessage */
@ -191,18 +190,20 @@ UA_NetworkMessage_encodeJson_internal(const UA_NetworkMessage* src, CtxJson *ctx
/* Payload: DataSetMessages */
UA_Byte count = src->payloadHeader.dataSetPayloadHeader.count;
if(count > 0){
UA_UInt16 *dataSetWriterIds = src->payloadHeader.dataSetPayloadHeader.dataSetWriterIds;
if(!dataSetWriterIds){
if(count > 0) {
UA_UInt16 *dataSetWriterIds =
src->payloadHeader.dataSetPayloadHeader.dataSetWriterIds;
if(!dataSetWriterIds)
return UA_STATUSCODE_BADENCODINGERROR;
}
rv |= writeJsonKey(ctx, UA_DECODEKEY_MESSAGES);
rv |= writeJsonArrStart(ctx); /* start array */
for (UA_UInt16 i = 0; i < count; i++) {
const UA_DataSetMessage *dataSetMessages =
src->payload.dataSetPayload.dataSetMessages;
for(UA_UInt16 i = 0; i < count; i++) {
writeJsonCommaIfNeeded(ctx);
rv |= UA_DataSetMessage_encodeJson_internal(&src->payload.dataSetPayload.dataSetMessages[i],
rv |= UA_DataSetMessage_encodeJson_internal(&dataSetMessages[i],
dataSetWriterIds[i], ctx);
if(rv != UA_STATUSCODE_GOOD)
return rv;
@ -218,9 +219,10 @@ UA_NetworkMessage_encodeJson_internal(const UA_NetworkMessage* src, CtxJson *ctx
UA_StatusCode
UA_NetworkMessage_encodeJson(const UA_NetworkMessage *src,
UA_Byte **bufPos, const UA_Byte **bufEnd, UA_String *namespaces,
size_t namespaceSize, UA_String *serverUris,
size_t serverUriSize, UA_Boolean useReversible) {
UA_Byte **bufPos, const UA_Byte **bufEnd,
UA_String *namespaces, size_t namespaceSize,
UA_String *serverUris, size_t serverUriSize,
UA_Boolean useReversible) {
/* Set up the context */
CtxJson ctx;
memset(&ctx, 0, sizeof(ctx));
@ -246,7 +248,6 @@ UA_NetworkMessage_calcSizeJson(const UA_NetworkMessage *src,
UA_String *namespaces, size_t namespaceSize,
UA_String *serverUris, size_t serverUriSize,
UA_Boolean useReversible){
/* Set up the context */
CtxJson ctx;
memset(&ctx, 0, sizeof(ctx));
@ -266,19 +267,19 @@ UA_NetworkMessage_calcSizeJson(const UA_NetworkMessage *src,
return (size_t)ctx.pos;
}
/* decode json */
/* decode json */
static status
MetaDataVersion_decodeJsonInternal(void* cvd, const UA_DataType *type, CtxJson *ctx,
ParseCtx *parseCtx, UA_Boolean moveToken){
return decodeJsonInternal(cvd, &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE],
ctx, parseCtx, UA_TRUE);
MetaDataVersion_decodeJsonInternal(void* cvd, const UA_DataType *type,
CtxJson *ctx, ParseCtx *parseCtx) {
return decodeJsonJumpTable[UA_DATATYPEKIND_STRUCTURE]
(cvd, &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE], ctx, parseCtx);
}
static status
DataSetPayload_decodeJsonInternal(void* dsmP, const UA_DataType *type, CtxJson *ctx,
ParseCtx *parseCtx, UA_Boolean moveToken) {
DataSetPayload_decodeJsonInternal(void* dsmP, const UA_DataType *type,
CtxJson *ctx, ParseCtx *parseCtx) {
UA_DataSetMessage* dsm = (UA_DataSetMessage*)dsmP;
dsm->header.dataSetMessageValid = UA_TRUE;
dsm->header.dataSetMessageValid = true;
if(isJsonNull(ctx, parseCtx)) {
parseCtx->index++;
return UA_STATUSCODE_GOOD;
@ -297,7 +298,7 @@ DataSetPayload_decodeJsonInternal(void* dsmP, const UA_DataType *type, CtxJson *
/* iterate over the key/value pairs in the object. Keys are stored in fieldnames. */
for(size_t i = 0; i < length; ++i) {
ret = getDecodeSignature(UA_TYPES_STRING)(&fieldNames[i], type, ctx, parseCtx, UA_TRUE);
ret = decodeJsonJumpTable[UA_DATATYPEKIND_STRING](&fieldNames[i], type, ctx, parseCtx);
if(ret != UA_STATUSCODE_GOOD)
return ret;
@ -307,19 +308,18 @@ DataSetPayload_decodeJsonInternal(void* dsmP, const UA_DataType *type, CtxJson *
status foundBody = lookAheadForKey("Body", ctx, parseCtx, &searchResult);
if(foundType == UA_STATUSCODE_GOOD && foundBody == UA_STATUSCODE_GOOD){
dsm->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
ret = getDecodeSignature(UA_TYPES_VARIANT)
(&dsm->data.keyFrameData.dataSetFields[i].value, type, ctx, parseCtx, UA_TRUE);
dsm->data.keyFrameData.dataSetFields[i].hasValue = UA_TRUE;
ret = decodeJsonJumpTable[UA_DATATYPEKIND_VARIANT]
(&dsm->data.keyFrameData.dataSetFields[i].value, type, ctx, parseCtx);
dsm->data.keyFrameData.dataSetFields[i].hasValue = true;
} else {
dsm->header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
ret = getDecodeSignature(UA_TYPES_DATAVALUE)
(&dsm->data.keyFrameData.dataSetFields[i], type, ctx, parseCtx, UA_TRUE);
dsm->data.keyFrameData.dataSetFields[i].hasValue = UA_TRUE;
ret = decodeJsonJumpTable[UA_DATATYPEKIND_DATAVALUE]
(&dsm->data.keyFrameData.dataSetFields[i], type, ctx, parseCtx);
dsm->data.keyFrameData.dataSetFields[i].hasValue = true;
}
if(ret != UA_STATUSCODE_GOOD)
return ret;
}
return ret;
@ -327,45 +327,35 @@ DataSetPayload_decodeJsonInternal(void* dsmP, const UA_DataType *type, CtxJson *
static status
DatasetMessage_Payload_decodeJsonInternal(UA_DataSetMessage* dsm, const UA_DataType *type,
CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken) {
CtxJson *ctx, ParseCtx *parseCtx) {
UA_ConfigurationVersionDataType cvd;
UA_UInt16 dataSetWriterId; /* the id is currently not processed */
UA_UInt16 dataSetWriterId;
dsm->header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
DecodeEntry entries[6] = {
{UA_DECODEKEY_DATASETWRITERID, &dataSetWriterId,
getDecodeSignature(UA_TYPES_UINT16), false, NULL},
{UA_DECODEKEY_SEQUENCENUMBER, &dsm->header.dataSetMessageSequenceNr,
getDecodeSignature(UA_TYPES_UINT16), false, NULL},
{UA_DECODEKEY_METADATAVERSION, &cvd,
&MetaDataVersion_decodeJsonInternal, false, NULL},
{UA_DECODEKEY_TIMESTAMP, &dsm->header.timestamp,
getDecodeSignature(UA_TYPES_DATETIME), false, NULL},
{UA_DECODEKEY_DSM_STATUS, &dsm->header.status,
getDecodeSignature(UA_TYPES_UINT16), false, NULL},
{UA_DECODEKEY_PAYLOAD, dsm,
&DataSetPayload_decodeJsonInternal, false, NULL}
{UA_DECODEKEY_DATASETWRITERID, &dataSetWriterId, NULL, false, &UA_TYPES[UA_TYPES_UINT16]},
{UA_DECODEKEY_SEQUENCENUMBER, &dsm->header.dataSetMessageSequenceNr, NULL, false, &UA_TYPES[UA_TYPES_UINT16]},
{UA_DECODEKEY_METADATAVERSION, &cvd, &MetaDataVersion_decodeJsonInternal, false, NULL},
{UA_DECODEKEY_TIMESTAMP, &dsm->header.timestamp, NULL, false, &UA_TYPES[UA_TYPES_DATETIME]},
{UA_DECODEKEY_DSM_STATUS, &dsm->header.status, NULL, false, &UA_TYPES[UA_TYPES_UINT16]},
{UA_DECODEKEY_PAYLOAD, dsm, &DataSetPayload_decodeJsonInternal, false, NULL}
};
status ret = decodeFields(ctx, parseCtx, entries, 6);
status ret = decodeFields(ctx, parseCtx, entries, 6, NULL);
if(ret != UA_STATUSCODE_GOOD || !entries[0].found){
/* no dataSetwriterid. Is mandatory. Abort. */
/* Error or no DatasetWriterId found or no payload found */
if(ret != UA_STATUSCODE_GOOD || !entries[0].found || !entries[5].found)
return UA_STATUSCODE_BADDECODINGERROR;
}else{
if(parseCtx->custom != NULL){
UA_UInt16* dataSetWriterIdsArray = (UA_UInt16*)parseCtx->custom;
if(parseCtx->currentCustomIndex < parseCtx->numCustom){
dataSetWriterIdsArray[parseCtx->currentCustomIndex] = dataSetWriterId;
parseCtx->currentCustomIndex++;
}else{
return UA_STATUSCODE_BADDECODINGERROR;
}
}else{
return UA_STATUSCODE_BADDECODINGERROR;
}
}
/* Set the DatasetWriterId in the context */
if(!parseCtx->custom)
return UA_STATUSCODE_BADDECODINGERROR;
if(parseCtx->currentCustomIndex >= parseCtx->numCustom)
return UA_STATUSCODE_BADDECODINGERROR;
UA_UInt16* dataSetWriterIdsArray = (UA_UInt16*)parseCtx->custom;
dataSetWriterIdsArray[parseCtx->currentCustomIndex] = dataSetWriterId;
parseCtx->currentCustomIndex++;
dsm->header.dataSetMessageSequenceNrEnabled = entries[1].found;
dsm->header.configVersionMajorVersion = cvd.majorVersion;
dsm->header.configVersionMinorVersion = cvd.minorVersion;
@ -373,21 +363,17 @@ DatasetMessage_Payload_decodeJsonInternal(UA_DataSetMessage* dsm, const UA_DataT
dsm->header.configVersionMinorVersionEnabled = entries[2].found;
dsm->header.timestampEnabled = entries[3].found;
dsm->header.statusEnabled = entries[4].found;
if(!entries[5].found){
/* No payload found */
return UA_STATUSCODE_BADDECODINGERROR;
}
dsm->header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME;
dsm->header.picoSecondsIncluded = UA_FALSE;
dsm->header.dataSetMessageValid = UA_TRUE;
dsm->header.picoSecondsIncluded = false;
dsm->header.dataSetMessageValid = true;
dsm->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
return ret;
return UA_STATUSCODE_GOOD;
}
static status
DatasetMessage_Array_decodeJsonInternal(void *UA_RESTRICT dst, const UA_DataType *type,
CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken) {
CtxJson *ctx, ParseCtx *parseCtx) {
/* Array! */
if(getJsmnType(parseCtx) != JSMN_ARRAY)
return UA_STATUSCODE_BADDECODINGERROR;
@ -398,8 +384,9 @@ DatasetMessage_Array_decodeJsonInternal(void *UA_RESTRICT dst, const UA_DataType
return UA_STATUSCODE_GOOD;
/* Allocate memory */
UA_DataSetMessage *dsm = (UA_DataSetMessage*)UA_calloc(length, sizeof(UA_DataSetMessage));
if(dsm == NULL)
UA_DataSetMessage *dsm = (UA_DataSetMessage*)
UA_calloc(length, sizeof(UA_DataSetMessage));
if(!dsm)
return UA_STATUSCODE_BADOUTOFMEMORY;
/* Copy new Pointer do dest */
@ -411,7 +398,7 @@ DatasetMessage_Array_decodeJsonInternal(void *UA_RESTRICT dst, const UA_DataType
status ret = UA_STATUSCODE_BADDECODINGERROR;
/* Decode array members */
for(size_t i = 0; i < length; ++i) {
ret = DatasetMessage_Payload_decodeJsonInternal(&dsm[i], NULL, ctx, parseCtx, UA_TRUE);
ret = DatasetMessage_Payload_decodeJsonInternal(&dsm[i], NULL, ctx, parseCtx);
if(ret != UA_STATUSCODE_GOOD)
return ret;
}
@ -419,27 +406,27 @@ DatasetMessage_Array_decodeJsonInternal(void *UA_RESTRICT dst, const UA_DataType
return ret;
}
static status NetworkMessage_decodeJsonInternal(UA_NetworkMessage *dst, CtxJson *ctx,
ParseCtx *parseCtx) {
static status
NetworkMessage_decodeJsonInternal(UA_NetworkMessage *dst, CtxJson *ctx,
ParseCtx *parseCtx) {
memset(dst, 0, sizeof(UA_NetworkMessage));
dst->chunkMessage = UA_FALSE;
dst->groupHeaderEnabled = UA_FALSE;
dst->payloadHeaderEnabled = UA_FALSE;
dst->picosecondsEnabled = UA_FALSE;
dst->promotedFieldsEnabled = UA_FALSE;
dst->chunkMessage = false;
dst->groupHeaderEnabled = false;
dst->payloadHeaderEnabled = false;
dst->picosecondsEnabled = false;
dst->promotedFieldsEnabled = false;
/* Look forward for publisheId, if present check if type if primitve (Number) or String. */
u8 publishIdTypeIndex = UA_TYPES_STRING;
const UA_DataType *pubIdType = &UA_TYPES[UA_TYPES_STRING];
size_t searchResultPublishIdType = 0;
status found = lookAheadForKey(UA_DECODEKEY_PUBLISHERID, ctx,
parseCtx, &searchResultPublishIdType);
if(found == UA_STATUSCODE_GOOD) {
jsmntok_t publishIdToken = parseCtx->tokenArray[searchResultPublishIdType];
if(publishIdToken.type == JSMN_PRIMITIVE) {
publishIdTypeIndex = UA_TYPES_UINT64;
pubIdType = &UA_TYPES[UA_TYPES_UINT64];
dst->publisherIdType = UA_PUBLISHERDATATYPE_UINT64; //store in biggest possible
} else if(publishIdToken.type == JSMN_STRING) {
publishIdTypeIndex = UA_TYPES_STRING;
dst->publisherIdType = UA_PUBLISHERDATATYPE_STRING;
} else {
return UA_STATUSCODE_BADDECODINGERROR;
@ -462,21 +449,22 @@ static status NetworkMessage_decodeJsonInternal(UA_NetworkMessage *dst, CtxJson
parseCtx->numCustom = messageCount;
/* MessageType */
UA_Boolean isUaData = UA_TRUE;
UA_Boolean isUaData = true;
size_t searchResultMessageType = 0;
found = lookAheadForKey(UA_DECODEKEY_MESSAGETYPE, ctx, parseCtx, &searchResultMessageType);
if(found != UA_STATUSCODE_GOOD)
return UA_STATUSCODE_BADDECODINGERROR;
size_t size = (size_t)(parseCtx->tokenArray[searchResultMessageType].end - parseCtx->tokenArray[searchResultMessageType].start);
size_t size = (size_t)(parseCtx->tokenArray[searchResultMessageType].end -
parseCtx->tokenArray[searchResultMessageType].start);
char* msgType = (char*)(ctx->pos + parseCtx->tokenArray[searchResultMessageType].start);
if(size == 7) { //ua-data
if(strncmp(msgType, "ua-data", size) != 0)
return UA_STATUSCODE_BADDECODINGERROR;
isUaData = UA_TRUE;
isUaData = true;
} else if(size == 11) { //ua-metadata
if(strncmp(msgType, "ua-metadata", size) != 0)
return UA_STATUSCODE_BADDECODINGERROR;
isUaData = UA_FALSE;
isUaData = false;
} else {
return UA_STATUSCODE_BADDECODINGERROR;
}
@ -488,18 +476,18 @@ static status NetworkMessage_decodeJsonInternal(UA_NetworkMessage *dst, CtxJson
/* Network Message */
UA_String messageType;
DecodeEntry entries[5] = {
{UA_DECODEKEY_MESSAGEID, &dst->messageId, getDecodeSignature(UA_TYPES_STRING), false, NULL},
{UA_DECODEKEY_MESSAGEID, &dst->messageId, NULL, false, &UA_TYPES[UA_TYPES_STRING]},
{UA_DECODEKEY_MESSAGETYPE, &messageType, NULL, false, NULL},
{UA_DECODEKEY_PUBLISHERID, &dst->publisherId.publisherIdString, getDecodeSignature(publishIdTypeIndex), false, NULL},
{UA_DECODEKEY_DATASETCLASSID, &dst->dataSetClassId, getDecodeSignature(UA_TYPES_GUID), false, NULL},
{UA_DECODEKEY_PUBLISHERID, &dst->publisherId.publisherIdString, NULL, false, pubIdType},
{UA_DECODEKEY_DATASETCLASSID, &dst->dataSetClassId, NULL, false, &UA_TYPES[UA_TYPES_GUID]},
{UA_DECODEKEY_MESSAGES, &dst->payload.dataSetPayload.dataSetMessages, &DatasetMessage_Array_decodeJsonInternal, false, NULL}
};
//Store publisherId in correct union
if(publishIdTypeIndex == UA_TYPES_UINT64)
if(pubIdType == &UA_TYPES[UA_TYPES_UINT64])
entries[2].fieldPointer = &dst->publisherId.publisherIdUInt64;
status ret = decodeFields(ctx, parseCtx, entries, 5, NULL);
status ret = decodeFields(ctx, parseCtx, entries, 5);
if(ret != UA_STATUSCODE_GOOD)
return ret;
@ -508,26 +496,29 @@ static status NetworkMessage_decodeJsonInternal(UA_NetworkMessage *dst, CtxJson
if(dst->publisherIdEnabled)
dst->publisherIdType = UA_PUBLISHERDATATYPE_STRING;
dst->dataSetClassIdEnabled = entries[3].found;
dst->payloadHeaderEnabled = UA_TRUE;
dst->payloadHeaderEnabled = true;
dst->payloadHeader.dataSetPayloadHeader.count = (UA_Byte)messageCount;
//Set the dataSetWriterIds. They are filled in the dataSet decoding.
/* Set the dataSetWriterIds. They are filled in the dataSet decoding. */
dst->payloadHeader.dataSetPayloadHeader.dataSetWriterIds = (UA_UInt16*)parseCtx->custom;
return ret;
}
status UA_NetworkMessage_decodeJson(UA_NetworkMessage *dst, const UA_ByteString *src){
status
UA_NetworkMessage_decodeJson(UA_NetworkMessage *dst, const UA_ByteString *src) {
/* Set up the context */
CtxJson ctx;
memset(&ctx, 0, sizeof(CtxJson));
ParseCtx parseCtx;
memset(&parseCtx, 0, sizeof(ParseCtx));
parseCtx.tokenArray = (jsmntok_t*)UA_malloc(sizeof(jsmntok_t) * UA_JSON_MAXTOKENCOUNT);
parseCtx.tokenArray = (jsmntok_t*)
UA_malloc(sizeof(jsmntok_t) * UA_JSON_MAXTOKENCOUNT);
if(!parseCtx.tokenArray)
return UA_STATUSCODE_BADOUTOFMEMORY;
memset(parseCtx.tokenArray, 0, sizeof(jsmntok_t) * UA_JSON_MAXTOKENCOUNT);
status ret = tokenize(&parseCtx, &ctx, src);
if(ret != UA_STATUSCODE_GOOD){
if(ret != UA_STATUSCODE_GOOD)
return ret;
}
ret = NetworkMessage_decodeJsonInternal(dst, &ctx, &parseCtx);
UA_free(parseCtx.tokenArray);
return ret;

View File

@ -16,6 +16,7 @@
* Copyright 2018 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Martin Lang)
* Copyright 2019 (c) Kalycito Infotech Private Limited
* Copyright 2021 (c) Fraunhofer IOSB (Author: Jan Hermes)
* Copyright 2022 (c) Fraunhofer IOSB (Author: Andreas Ebner)
*/
#include "ua_server_internal.h"
@ -534,8 +535,7 @@ UA_Server_getStatistics(UA_Server *server) {
stat.ns = server->networkStatistics;
stat.scs = server->secureChannelStatistics;
stat.ss.currentSessionCount =
server->serverDiagnosticsSummary.currentSessionCount;
stat.ss.currentSessionCount = server->activeSessionCount;
stat.ss.cumulatedSessionCount =
server->serverDiagnosticsSummary.cumulatedSessionCount;
stat.ss.securityRejectedSessionCount =
@ -679,6 +679,13 @@ UA_Server_run_startup(UA_Server *server) {
startMulticastDiscoveryServer(server);
#endif
/* Update Endpoint description */
for(size_t i = 0; i < server->config.endpointsSize; ++i){
UA_ApplicationDescription_clear(&server->config.endpoints[i].server);
UA_ApplicationDescription_copy(&server->config.applicationDescription,
&server->config.endpoints[i].server);
}
server->state = UA_SERVERLIFECYCLE_FRESH;
return result;

View File

@ -40,10 +40,10 @@ void UA_debug_dumpCompleteChunk(UA_Server *const server, UA_Connection *const co
/********************/
UA_StatusCode
sendServiceFault(UA_SecureChannel *channel, UA_UInt32 requestId, UA_UInt32 requestHandle,
const UA_DataType *responseType, UA_StatusCode statusCode) {
UA_Response response;
UA_init(&response, responseType);
sendServiceFault(UA_SecureChannel *channel, UA_UInt32 requestId,
UA_UInt32 requestHandle, UA_StatusCode statusCode) {
UA_ServiceFault response;
UA_ServiceFault_init(&response);
UA_ResponseHeader *responseHeader = &response.responseHeader;
responseHeader->requestHandle = requestHandle;
responseHeader->timestamp = UA_DateTime_now();
@ -55,11 +55,12 @@ sendServiceFault(UA_SecureChannel *channel, UA_UInt32 requestId, UA_UInt32 reque
/* Send error message. Message type is MSG and not ERR, since we are on a
* SecureChannel! */
return UA_SecureChannel_sendSymmetricMessage(channel, requestId, UA_MESSAGETYPE_MSG,
&response, responseType);
return UA_SecureChannel_sendSymmetricMessage(channel, requestId,
UA_MESSAGETYPE_MSG, &response,
&UA_TYPES[UA_TYPES_SERVICEFAULT]);
}
/* This is not an ERR message, the connection is not closed afterwards */
/* This is not an ERR message, the connection is not closed afterwards */
static UA_StatusCode
decodeHeaderSendServiceFault(UA_SecureChannel *channel, const UA_ByteString *msg,
size_t offset, const UA_DataType *responseType,
@ -70,8 +71,7 @@ decodeHeaderSendServiceFault(UA_SecureChannel *channel, const UA_ByteString *msg
&UA_TYPES[UA_TYPES_REQUESTHEADER], NULL);
if(retval != UA_STATUSCODE_GOOD)
return retval;
retval = sendServiceFault(channel, requestId, requestHeader.requestHandle,
responseType, error);
retval = sendServiceFault(channel, requestId, requestHeader.requestHandle, error);
UA_RequestHeader_clear(&requestHeader);
return retval;
}
@ -454,6 +454,11 @@ sendResponse(UA_Server *server, UA_Session *session, UA_SecureChannel *channel,
if(!channel)
return UA_STATUSCODE_BADINTERNALERROR;
/* If the overall service call failed, answer with a ServiceFault */
if(response->responseHeader.serviceResult != UA_STATUSCODE_GOOD)
return sendServiceFault(channel, requestId, response->responseHeader.requestHandle,
response->responseHeader.serviceResult);
/* Prepare the ResponseHeader */
response->responseHeader.timestamp = UA_DateTime_now();
@ -569,7 +574,7 @@ processMSGDecoded(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 reques
) {
serviceRes = UA_STATUSCODE_BADSECURITYPOLICYREJECTED;
channelRes = sendServiceFault(channel, requestId, requestHeader->requestHandle,
responseType, UA_STATUSCODE_BADSECURITYPOLICYREJECTED);
UA_STATUSCODE_BADSECURITYPOLICYREJECTED);
goto update_statistics;
}
@ -596,13 +601,14 @@ processMSGDecoded(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 reques
/* Get the Session bound to the SecureChannel (not necessarily activated) */
if(!UA_NodeId_isNull(&requestHeader->authenticationToken)) {
UA_LOCK(&server->serviceMutex);
UA_StatusCode retval = getBoundSession(
server, channel, &requestHeader->authenticationToken, &session);
UA_StatusCode retval =
getBoundSession(server, channel,
&requestHeader->authenticationToken, &session);
UA_UNLOCK(&server->serviceMutex);
if(retval != UA_STATUSCODE_GOOD) {
serviceRes = response->responseHeader.serviceResult;
channelRes = sendServiceFault(channel, requestId, requestHeader->requestHandle,
responseType, retval);
channelRes = sendServiceFault(channel, requestId,
requestHeader->requestHandle, retval);
goto update_statistics;
}
}
@ -622,7 +628,7 @@ processMSGDecoded(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 reques
#endif
serviceRes = UA_STATUSCODE_BADSESSIONIDINVALID;
channelRes = sendServiceFault(channel, requestId, requestHeader->requestHandle,
responseType, UA_STATUSCODE_BADSESSIONIDINVALID);
UA_STATUSCODE_BADSESSIONIDINVALID);
goto update_statistics;
}
@ -653,7 +659,7 @@ processMSGDecoded(UA_Server *server, UA_SecureChannel *channel, UA_UInt32 reques
}
serviceRes = UA_STATUSCODE_BADSESSIONNOTACTIVATED;
channelRes = sendServiceFault(channel, requestId, requestHeader->requestHandle,
responseType, UA_STATUSCODE_BADSESSIONNOTACTIVATED);
UA_STATUSCODE_BADSESSIONNOTACTIVATED);
goto update_statistics;
}
@ -786,7 +792,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
"See the 'verifyRequestTimestamp' setting.");
if(server->config.verifyRequestTimestamp <= UA_RULEHANDLING_ABORT) {
retval = sendServiceFault(channel, requestId, requestHeader->requestHandle,
responseType, UA_STATUSCODE_BADINVALIDTIMESTAMP);
UA_STATUSCODE_BADINVALIDTIMESTAMP);
UA_clear(&request, requestType);
return retval;
}

View File

@ -104,6 +104,7 @@ struct UA_Server {
/* Session Management */
LIST_HEAD(session_list, session_list_entry) sessions;
UA_UInt32 sessionCount;
UA_UInt32 activeSessionCount;
UA_Session adminSession; /* Local access to the services (for startup and
* maintenance) uses this Session with all possible
* access rights (Session Id: 1) */
@ -183,8 +184,8 @@ UA_Server_configSecureChannel(void *application, UA_SecureChannel *channel,
const UA_AsymmetricAlgorithmSecurityHeader *asymHeader);
UA_StatusCode
sendServiceFault(UA_SecureChannel *channel, UA_UInt32 requestId, UA_UInt32 requestHandle,
const UA_DataType *responseType, UA_StatusCode statusCode);
sendServiceFault(UA_SecureChannel *channel, UA_UInt32 requestId,
UA_UInt32 requestHandle, UA_StatusCode statusCode);
void
UA_Server_closeSecureChannel(UA_Server *server, UA_SecureChannel *channel,

View File

@ -516,6 +516,8 @@ readDiagnostics(UA_Server *server, const UA_NodeId *sessionId, void *sessionCont
switch(nodeId->identifier.numeric) {
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY:
server->serverDiagnosticsSummary.currentSessionCount =
server->activeSessionCount;
data = &server->serverDiagnosticsSummary;
type = &UA_TYPES[UA_TYPES_SERVERDIAGNOSTICSSUMMARYDATATYPE];
break;
@ -523,7 +525,7 @@ readDiagnostics(UA_Server *server, const UA_NodeId *sessionId, void *sessionCont
data = &server->serverDiagnosticsSummary.serverViewCount;
break;
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CURRENTSESSIONCOUNT:
data = &server->serverDiagnosticsSummary.currentSessionCount;
data = &server->activeSessionCount;
break;
case UA_NS0ID_SERVER_SERVERDIAGNOSTICS_SERVERDIAGNOSTICSSUMMARY_CUMULATEDSESSIONCOUNT:
data = &server->serverDiagnosticsSummary.cumulatedSessionCount;

View File

@ -252,6 +252,12 @@ Service_GetEndpoints(UA_Server *server, UA_Session *session,
for(size_t i = 0; i < clone_times; ++i) {
retval |= UA_EndpointDescription_copy(&server->config.endpoints[j],
&response->endpoints[pos]);
UA_String_clear(&response->endpoints[pos].endpointUrl);
UA_Array_delete(response->endpoints[pos].server.discoveryUrls,
response->endpoints[pos].server.discoveryUrlsSize,
&UA_TYPES[UA_TYPES_STRING]);
response->endpoints[pos].server.discoveryUrls = NULL;
response->endpoints[pos].server.discoveryUrlsSize = 0;
if(nl_endpointurl)
endpointUrl = &server->config.networkLayers[i].discoveryUrl;
retval |= UA_String_copy(endpointUrl, &response->endpoints[pos].endpointUrl);

View File

@ -42,7 +42,7 @@ setAbsoluteFromPercentageDeadband(UA_Server *server, UA_Session *session,
browseSimplifiedBrowsePath(server, mon->itemToMonitor.nodeId, 1, &qn);
if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
UA_BrowsePathResult_clear(&bpr);
return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED;
return UA_STATUSCODE_BADFILTERNOTALLOWED;
}
/* Read the range */
@ -56,7 +56,7 @@ setAbsoluteFromPercentageDeadband(UA_Server *server, UA_Session *session,
if(!UA_Variant_isScalar(&rangeVal.value) ||
rangeVal.value.type != &UA_TYPES[UA_TYPES_RANGE]) {
UA_DataValue_clear(&rangeVal);
return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED;
return UA_STATUSCODE_BADFILTERNOTALLOWED;
}
/* Compute the abs deadband */
@ -68,7 +68,7 @@ setAbsoluteFromPercentageDeadband(UA_Server *server, UA_Session *session,
/* EURange invalid or NaN? */
if(absDeadband < 0.0 || absDeadband != absDeadband) {
UA_DataValue_clear(&rangeVal);
return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED;
return UA_STATUSCODE_BADFILTERNOTALLOWED;
}
/* Adjust the original filter */

View File

@ -57,13 +57,15 @@ UA_Server_removeSession(UA_Server *server, session_list_entry *sentry,
UA_Session_detachFromSecureChannel(session);
/* Deactivate the session */
sentry->session.activated = false;
if(sentry->session.activated) {
sentry->session.activated = false;
server->activeSessionCount--;
}
/* Detach the session from the session manager and make the capacity
* available */
LIST_REMOVE(sentry, pointers);
server->sessionCount--;
server->serverDiagnosticsSummary.currentSessionCount--;
switch(event) {
case UA_DIAGNOSTICEVENT_CLOSE:
@ -217,31 +219,36 @@ UA_Server_createSession(UA_Server *server, UA_SecureChannel *channel,
const UA_CreateSessionRequest *request, UA_Session **session) {
UA_LOCK_ASSERT(&server->serviceMutex, 1);
if(server->sessionCount >= server->config.maxSessions)
if(server->sessionCount >= server->config.maxSessions) {
UA_LOG_WARNING_CHANNEL(&server->config.logger, channel,
"Could not create a Session - Server limits reached");
return UA_STATUSCODE_BADTOOMANYSESSIONS;
}
session_list_entry *newentry = (session_list_entry*)
UA_malloc(sizeof(session_list_entry));
if(!newentry)
return UA_STATUSCODE_BADOUTOFMEMORY;
UA_atomic_addUInt32(&server->sessionCount, 1);
/* Initialize the Session */
UA_Session_init(&newentry->session);
newentry->session.sessionId = UA_NODEID_GUID(1, UA_Guid_random());
newentry->session.header.authenticationToken = UA_NODEID_GUID(1, UA_Guid_random());
newentry->session.timeout = server->config.maxSessionTimeout;
if(request->requestedSessionTimeout <= server->config.maxSessionTimeout &&
request->requestedSessionTimeout > 0)
newentry->session.timeout = request->requestedSessionTimeout;
else
newentry->session.timeout = server->config.maxSessionTimeout;
/* Attach the session to the channel. But don't activate for now. */
if(channel)
UA_Session_attachToSecureChannel(&newentry->session, channel);
UA_Session_updateLifetime(&newentry->session);
/* Add to the server */
LIST_INSERT_HEAD(&server->sessions, newentry, pointers);
server->sessionCount++;
*session = &newentry->session;
return UA_STATUSCODE_GOOD;
}
@ -843,7 +850,7 @@ Service_ActivateSession(UA_Server *server, UA_SecureChannel *channel,
/* Activate the session */
if(!session->activated) {
session->activated = true;
server->serverDiagnosticsSummary.currentSessionCount++;
server->activeSessionCount++;
server->serverDiagnosticsSummary.cumulatedSessionCount++;
}

View File

@ -237,7 +237,6 @@ Service_Publish(UA_Server *server, UA_Session *session,
if(TAILQ_EMPTY(&session->subscriptions)) {
sendServiceFault(session->header.channel, requestId,
request->requestHeader.requestHandle,
&UA_TYPES[UA_TYPES_PUBLISHRESPONSE],
UA_STATUSCODE_BADNOSUBSCRIPTION);
return UA_STATUSCODE_BADNOSUBSCRIPTION;
}
@ -250,7 +249,6 @@ Service_Publish(UA_Server *server, UA_Session *session,
if(!UA_Session_reachedPublishReqLimit(server, session)) {
sendServiceFault(session->header.channel, requestId,
request->requestHeader.requestHandle,
&UA_TYPES[UA_TYPES_PUBLISHRESPONSE],
UA_STATUSCODE_BADINTERNALERROR);
return UA_STATUSCODE_BADINTERNALERROR;
}
@ -262,7 +260,6 @@ Service_Publish(UA_Server *server, UA_Session *session,
if(!entry) {
sendServiceFault(session->header.channel, requestId,
request->requestHeader.requestHandle,
&UA_TYPES[UA_TYPES_PUBLISHRESPONSE],
UA_STATUSCODE_BADOUTOFMEMORY);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
@ -282,7 +279,6 @@ Service_Publish(UA_Server *server, UA_Session *session,
UA_free(entry);
sendServiceFault(session->header.channel, requestId,
request->requestHeader.requestHandle,
&UA_TYPES[UA_TYPES_PUBLISHRESPONSE],
UA_STATUSCODE_BADOUTOFMEMORY);
return UA_STATUSCODE_BADOUTOFMEMORY;
}

View File

@ -1322,10 +1322,7 @@ browseSimplifiedBrowsePath(UA_Server *server, const UA_NodeId origin,
bp.relativePath.elementsSize = browsePathSize;
/* Browse */
UA_UInt32 nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE;
#ifdef UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS
nodeClassMask |= UA_NODECLASS_OBJECTTYPE;
#endif /* UA_ENABLE_SUBSCRIPTIONS_ALARMS_CONDITIONS */
UA_UInt32 nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE | UA_NODECLASS_OBJECTTYPE;
Operation_TranslateBrowsePathToNodeIds(server, &server->adminSession, &nodeClassMask, &bp, &bpr);
return bpr;

View File

@ -316,6 +316,50 @@ implicitNumericVariantTransformation(UA_Variant *variant, void *data){
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
implicitNumericVariantTransformationUnsingedToSigned(UA_Variant *variant, void *data){
if(variant->type == &UA_TYPES[UA_TYPES_UINT64]){
if(*(UA_UInt64 *)variant->data > UA_INT64_MAX)
return UA_STATUSCODE_BADTYPEMISMATCH;
*(UA_Int64 *)data = *(UA_Int64 *)variant->data;
UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]);
} else if(variant->type == &UA_TYPES[UA_TYPES_UINT32]){
*(UA_Int64 *)data = *(UA_Int32 *)variant->data;
UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]);
} else if(variant->type == &UA_TYPES[UA_TYPES_UINT16]){
*(UA_Int64 *)data = *(UA_Int16 *)variant->data;
UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]);
} else if(variant->type == &UA_TYPES[UA_TYPES_BYTE]){
*(UA_Int64 *)data = *(UA_Byte *)variant->data;
UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_INT64]);
} else {
return UA_STATUSCODE_BADTYPEMISMATCH;
}
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
implicitNumericVariantTransformationSignedToUnSigned(UA_Variant *variant, void *data){
if(*(UA_Int64 *)variant->data < 0)
return UA_STATUSCODE_BADTYPEMISMATCH;
if(variant->type == &UA_TYPES[UA_TYPES_INT64]){
*(UA_UInt64 *)data = *(UA_UInt64 *)variant->data;
UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]);
} else if(variant->type == &UA_TYPES[UA_TYPES_INT32]){
*(UA_UInt64 *)data = *(UA_UInt32 *)variant->data;
UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]);
} else if(variant->type == &UA_TYPES[UA_TYPES_INT16]){
*(UA_UInt64 *)data = *(UA_UInt16 *)variant->data;
UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]);
} else if(variant->type == &UA_TYPES[UA_TYPES_SBYTE]){
*(UA_UInt64 *)data = *(UA_Byte *)variant->data;
UA_Variant_setScalar(variant, data, &UA_TYPES[UA_TYPES_UINT64]);
} else {
return UA_STATUSCODE_BADTYPEMISMATCH;
}
return UA_STATUSCODE_GOOD;
}
/* 0 -> Same Type, 1 -> Implicit Cast, 2 -> Only explicit Cast, -1 -> cast invalid */
static UA_SByte convertLookup[21][21] = {
{ 0, 1,-1,-1, 1,-1, 1,-1, 1, 1, 1,-1, 1,-1, 2,-1,-1, 1, 1, 1,-1},
@ -416,7 +460,9 @@ compareOperation(UA_Variant *firstOperand, UA_Variant *secondOperand, UA_FilterO
UA_TYPES_DIFFERENT_NUMERIC_FLOATING_POINT,
UA_TYPES_DIFFERENT_TEXT,
UA_TYPES_DIFFERENT_COMPARE_FORBIDDEN,
UA_TYPES_DIFFERENT_COMPARE_EXPLIC
UA_TYPES_DIFFERENT_COMPARE_EXPLIC,
UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_SIGNED,
UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_UNSIGNED
} compareHandlingRuleEnum;
if(castRule == 0 &&
@ -445,6 +491,14 @@ compareOperation(UA_Variant *firstOperand, UA_Variant *secondOperand, UA_FilterO
isStringType(firstOperand->type->typeKind)&&
isStringType(secondOperand->type->typeKind)){
compareHandlingRuleEnum = UA_TYPES_DIFFERENT_TEXT;
} else if(castRule == 1 &&
isNumericSigned(firstOperand->type->typeKind) &&
isNumericUnsigned(secondOperand->type->typeKind)){
compareHandlingRuleEnum = UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_SIGNED;
} else if(castRule == 1 &&
isNumericSigned(secondOperand->type->typeKind) &&
isNumericUnsigned(firstOperand->type->typeKind)){
compareHandlingRuleEnum = UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_UNSIGNED;
} else if(castRule == -1 || castRule == 2){
compareHandlingRuleEnum = UA_TYPES_DIFFERENT_COMPARE_EXPLIC;
} else {
@ -467,6 +521,12 @@ compareOperation(UA_Variant *firstOperand, UA_Variant *secondOperand, UA_FilterO
compareHandlingRuleEnum == UA_TYPES_DIFFERENT_NUMERIC_FLOATING_POINT) {
implicitNumericVariantTransformation(firstCompareOperand, variantContent);
implicitNumericVariantTransformation(secondCompareOperand, &variantContent[8]);
} else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_SIGNED) {
implicitNumericVariantTransformation(firstCompareOperand, variantContent);
implicitNumericVariantTransformationUnsingedToSigned(secondCompareOperand, &variantContent[8]);
} else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_UNSIGNED) {
implicitNumericVariantTransformation(firstCompareOperand, variantContent);
implicitNumericVariantTransformationSignedToUnSigned(secondCompareOperand, &variantContent[8]);
} else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_TEXT) {
firstCompareOperand->type = &UA_TYPES[UA_TYPES_STRING];
secondCompareOperand->type = &UA_TYPES[UA_TYPES_STRING];
@ -479,12 +539,18 @@ compareOperation(UA_Variant *firstOperand, UA_Variant *secondOperand, UA_FilterO
}
} else {
UA_Byte variantContent[16];
memset(&variantContent, 0, sizeof(UA_Byte) * 16);
if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_NUMERIC_SIGNED ||
compareHandlingRuleEnum == UA_TYPES_DIFFERENT_NUMERIC_UNSIGNED ||
compareHandlingRuleEnum == UA_TYPES_DIFFERENT_NUMERIC_FLOATING_POINT) {
memset(&variantContent, 0, sizeof(UA_Byte) * 16);
implicitNumericVariantTransformation(firstCompareOperand, variantContent);
implicitNumericVariantTransformation(secondCompareOperand, &variantContent[8]);
} else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_SIGNED) {
implicitNumericVariantTransformation(firstCompareOperand, variantContent);
implicitNumericVariantTransformationUnsingedToSigned(secondCompareOperand, &variantContent[8]);
} else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_SIGNEDNESS_CAST_TO_UNSIGNED) {
implicitNumericVariantTransformation(firstCompareOperand, variantContent);
implicitNumericVariantTransformationSignedToUnSigned(secondCompareOperand, &variantContent[8]);
} else if(compareHandlingRuleEnum == UA_TYPES_DIFFERENT_TEXT) {
firstCompareOperand->type = &UA_TYPES[UA_TYPES_STRING];
secondCompareOperand->type = &UA_TYPES[UA_TYPES_STRING];
@ -1033,6 +1099,41 @@ UA_Event_staticSelectClauseValidation(UA_Server *server,
break;
}
}
/* Get the list of Subtypes from current node */
UA_ReferenceTypeSet reftypes_interface =
UA_REFTYPESET(UA_REFERENCETYPEINDEX_HASSUBTYPE);
UA_ExpandedNodeId *chilTypeNodes = NULL;
size_t chilTypeNodesSize = 0;
UA_StatusCode res;
res = browseRecursive(server, 1, &eventFilter->selectClauses[i].typeDefinitionId,
UA_BROWSEDIRECTION_FORWARD, &reftypes_interface, UA_NODECLASS_OBJECTTYPE,
true, &chilTypeNodesSize, &chilTypeNodes);
if(res!=UA_STATUSCODE_GOOD){
result[i] = UA_STATUSCODE_BADATTRIBUTEIDINVALID;
continue;
}
UA_Boolean subTypeContainField = false;
for (size_t j = 0; j < chilTypeNodesSize; ++j) {
/* browsPath element is defined in path */
UA_BrowsePathResult bpr =
browseSimplifiedBrowsePath(server, chilTypeNodes[j].nodeId,
eventFilter->selectClauses[i].browsePathSize,
eventFilter->selectClauses[i].browsePath);
if(bpr.statusCode != UA_STATUSCODE_GOOD){
UA_BrowsePathResult_clear(&bpr);
continue;
}
subTypeContainField = true;
UA_BrowsePathResult_clear(&bpr);
}
if(!subTypeContainField)
result[i] = UA_STATUSCODE_BADNODEIDUNKNOWN;
UA_Array_delete(chilTypeNodes, chilTypeNodesSize, &UA_TYPES[UA_TYPES_EXPANDEDNODEID]);
if(result[i] != UA_STATUSCODE_GOOD)
continue;
/*indexRange is defined ? */

File diff suppressed because it is too large Load Diff

View File

@ -50,19 +50,6 @@ UA_encodeJsonInternal(const void *src, const UA_DataType *type, uint8_t **bufPos
size_t serverUriSize,
UA_Boolean useReversible) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
/* Decodes a scalar value described by type from json encoding.
*
* @param src The buffer with the json encoded value. Must not be NULL.
* @param dst The target value. Must not be NULL. The target is assumed to have
* size type->memSize. The value is reset to zero before decoding. If
* decoding fails, members are deleted and the value is reset (zeroed)
* again.
* @param type The value type. Must not be NULL.
* @return Returns a statuscode whether decoding succeeded. */
UA_StatusCode
UA_decodeJsonInternal(const UA_ByteString *src, void *dst,
const UA_DataType *type) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
/* Interal Definitions
*
* For future by the PubSub encoding */
@ -150,43 +137,49 @@ typedef struct {
size_t numCustom;
void * custom;
size_t currentCustomIndex;
const UA_DataTypeArray *customTypes;
} ParseCtx;
typedef UA_StatusCode
(*encodeJsonSignature)(const void *src, const UA_DataType *type, CtxJson *ctx);
typedef UA_StatusCode
(*decodeJsonSignature)(void *dst, const UA_DataType *type, CtxJson *ctx,
ParseCtx *parseCtx, UA_Boolean moveToken);
(*decodeJsonSignature)(void *dst, const UA_DataType *type,
CtxJson *ctx, ParseCtx *parseCtx);
/* Map for decoding a Json Object. An array of this is passed to the
* decodeFields function. If the key "fieldName" is found in the json object
* (mark as found and) decode the value with the "function" and write result
* into "fieldPointer" (destination). */
typedef struct {
const char * fieldName;
void * fieldPointer;
const char *fieldName;
void *fieldPointer;
decodeJsonSignature function;
UA_Boolean found;
const UA_DataType *type;
const UA_DataType *type; /* Must be set for values that can be "null". If
* the function is not set, decode via the
* type->typeKind. */
} DecodeEntry;
UA_StatusCode
decodeFields(CtxJson *ctx, ParseCtx *parseCtx,
DecodeEntry *entries, size_t entryCount,
const UA_DataType *type);
DecodeEntry *entries, size_t entryCount);
UA_StatusCode
decodeJsonInternal(void *dst, const UA_DataType *type,
CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken);
/* Expose the jump tables and some methods for PubSub JSON decoding */
extern const encodeJsonSignature encodeJsonJumpTable[UA_DATATYPEKINDS];
extern const decodeJsonSignature decodeJsonJumpTable[UA_DATATYPEKINDS];
/* workaround: TODO generate functions for UA_xxx_decodeJson */
decodeJsonSignature getDecodeSignature(u8 index);
UA_StatusCode lookAheadForKey(const char* search, CtxJson *ctx, ParseCtx *parseCtx, size_t *resultIndex);
jsmntype_t getJsmnType(const ParseCtx *parseCtx);
UA_StatusCode lookAheadForKey(const char* search, CtxJson *ctx,
ParseCtx *parseCtx, size_t *resultIndex);
UA_StatusCode tokenize(ParseCtx *parseCtx, CtxJson *ctx, const UA_ByteString *src);
UA_Boolean isJsonNull(const CtxJson *ctx, const ParseCtx *parseCtx);
static UA_INLINE
jsmntype_t getJsmnType(const ParseCtx *parseCtx) {
return parseCtx->tokenArray[parseCtx->index].type;
}
_UA_END_DECLS
#endif /* UA_TYPES_ENCODING_JSON_H_ */

View File

@ -20,6 +20,11 @@
# pragma warning(disable: 4146)
#endif
static UA_INLINE UA_StatusCode
UA_decodeJsonInternal(const UA_ByteString *src, void *dst, const UA_DataType *type) {
return UA_decodeJson(src, dst, type, NULL);
}
/* Test Boolean */
START_TEST(UA_Boolean_true_json_encode) {
@ -1417,7 +1422,7 @@ START_TEST(UA_StatusCode_nonReversible_good_json_encode) {
// then
ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
char* result = "null";
char* result = "{\"Code\":0,\"Symbol\":\"Good\"}";
ck_assert_str_eq(result, (char*)buf.data);
UA_ByteString_clear(&buf);
UA_StatusCode_delete(src);
@ -1863,7 +1868,7 @@ START_TEST(UA_DiagInfo_noFields_json_encode) {
*bufPos = 0;
// then
ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
char* result = "null";
char* result = "{}";
ck_assert_str_eq(result, (char*)buf.data);
UA_ByteString_clear(&buf);
UA_DiagnosticInfo_delete(src);
@ -1900,7 +1905,6 @@ START_TEST(UA_DiagInfo_smallBuffer_json_encode) {
status s = UA_encodeJsonInternal((void *) src, type, &bufPos, &bufEnd, NULL, 0, NULL, 0, true);
*bufPos = 0;
// then
ck_assert_int_eq(s, UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED);
UA_ByteString_clear(&buf);
@ -2511,6 +2515,34 @@ START_TEST(UA_Variant_Array_UInt16_json_encode) {
}
END_TEST
START_TEST(UA_Variant_Array_UInt16_Null_json_encode) {
UA_Variant *src = UA_Variant_new();
UA_Variant_init(src);
UA_Variant_setArray(src, NULL, 0, &UA_TYPES[UA_TYPES_UINT16]);
const UA_DataType *type = &UA_TYPES[UA_TYPES_VARIANT];
size_t size = UA_calcSizeJsonInternal((void *) src, type, NULL, 0, NULL, 0, true);
UA_ByteString buf;
UA_ByteString_allocBuffer(&buf, size+1);
UA_Byte *bufPos = &buf.data[0];
const UA_Byte *bufEnd = &buf.data[size+1];
status s = UA_encodeJsonInternal((void *) src, type, &bufPos, &bufEnd, NULL, 0, NULL, 0, true);
ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
*bufPos = 0;
// then
ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
char* result = "{\"Type\":5,\"Body\":[]}";
ck_assert_str_eq(result, (char*)buf.data);
UA_ByteString_clear(&buf);
UA_Variant_delete(src);
}
END_TEST
START_TEST(UA_Variant_Array_Byte_json_encode) {
UA_Variant *src = UA_Variant_new();
UA_Variant_init(src);
@ -3212,7 +3244,7 @@ START_TEST(UA_DataValue_null_json_encode) {
*bufPos = 0;
// then
ck_assert_int_eq(s, UA_STATUSCODE_GOOD);
char* result = "null";
char* result = "{}";
ck_assert_str_eq(result, (char*)buf.data);
UA_ByteString_clear(&buf);
UA_DataValue_delete(src);
@ -4347,7 +4379,7 @@ END_TEST
START_TEST(UA_ByteString_bad_json_decode) {
UA_ByteString out;
UA_ByteString_init(&out);
UA_ByteString buf = UA_STRING("\"\x90!\xc5 c{\",");
UA_ByteString buf = UA_STRING("\"\x90!\xc5 c{\"");
// when
UA_StatusCode retval = UA_decodeJsonInternal(&buf, &out, &UA_TYPES[UA_TYPES_BYTESTRING]);
@ -4364,9 +4396,6 @@ START_TEST(UA_ByteString_null_json_decode) {
UA_StatusCode retval = UA_decodeJsonInternal(&buf, &out, &UA_TYPES[UA_TYPES_VARIANT]);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_int_eq(out.type->typeKind, UA_DATATYPEKIND_BYTESTRING);
UA_ByteString *outData = (UA_ByteString*)out.data;
ck_assert_ptr_ne(outData, NULL);
ck_assert_ptr_eq(outData->data, NULL);
UA_Variant_clear(&out);
}
END_TEST
@ -4620,7 +4649,6 @@ START_TEST(UA_QualifiedName_null_json_decode) {
UA_StatusCode retval = UA_decodeJsonInternal(&buf, &out, &UA_TYPES[UA_TYPES_VARIANT]);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_int_eq(out.type->typeKind, UA_DATATYPEKIND_QUALIFIEDNAME);
ck_assert_ptr_ne(out.data, NULL);
UA_Variant_clear(&out);
}
END_TEST
@ -4671,7 +4699,6 @@ START_TEST(UA_LocalizedText_null_json_decode) {
UA_StatusCode retval = UA_decodeJsonInternal(&buf, &out, &UA_TYPES[UA_TYPES_VARIANT]);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_int_eq(out.type->typeKind, UA_DATATYPEKIND_LOCALIZEDTEXT);
ck_assert_ptr_ne(out.data, NULL);
UA_Variant_clear(&out);
}
END_TEST
@ -5016,13 +5043,6 @@ START_TEST(UA_DiagnosticInfo_null_json_decode) {
UA_StatusCode retval = UA_decodeJsonInternal(&buf, &out, &UA_TYPES[UA_TYPES_VARIANT]);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_int_eq(out.type->typeKind, UA_DATATYPEKIND_DIAGNOSTICINFO);
ck_assert_uint_eq(((UA_DiagnosticInfo*)out.data)->hasAdditionalInfo, 0);
ck_assert_uint_eq(((UA_DiagnosticInfo*)out.data)->hasInnerDiagnosticInfo, 0);
ck_assert_uint_eq(((UA_DiagnosticInfo*)out.data)->hasInnerStatusCode, 0);
ck_assert_uint_eq(((UA_DiagnosticInfo*)out.data)->hasLocale, 0);
ck_assert_uint_eq(((UA_DiagnosticInfo*)out.data)->hasLocalizedText, 0);
ck_assert_uint_eq(((UA_DiagnosticInfo*)out.data)->hasNamespaceUri, 0);
ck_assert_uint_eq(((UA_DiagnosticInfo*)out.data)->hasSymbolicId, 0);
UA_Variant_clear(&out);
}
END_TEST
@ -5086,7 +5106,6 @@ START_TEST(UA_DataValue_null_json_decode) {
UA_ByteString buf = UA_STRING("{\"Type\":23,\"Body\":null}");
UA_StatusCode retval = UA_decodeJsonInternal(&buf, &out, &UA_TYPES[UA_TYPES_VARIANT]);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_ptr_ne(out.data, NULL);
UA_Variant_clear(&out);
}
END_TEST
@ -5217,7 +5236,7 @@ START_TEST(UA_VariantBoolNull_json_decode) {
UA_StatusCode retval = UA_decodeJsonInternal(&buf, &out, &UA_TYPES[UA_TYPES_VARIANT]);
// then
ck_assert_int_eq(retval, UA_STATUSCODE_BADDECODINGERROR);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
UA_Variant_clear(&out);
}
END_TEST
@ -5844,6 +5863,7 @@ static Suite *testSuite_builtin_json(void) {
//Array
tcase_add_test(tc_json_encode, UA_Variant_Array_UInt16_json_encode);
tcase_add_test(tc_json_encode, UA_Variant_Array_UInt16_Null_json_encode);
tcase_add_test(tc_json_encode, UA_Variant_Array_Byte_json_encode);
tcase_add_test(tc_json_encode, UA_Variant_Array_String_json_encode);

View File

@ -24,6 +24,7 @@ LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
return 0;
size_t jsonSize = UA_calcSizeJson(&value, &UA_TYPES[UA_TYPES_VARIANT], NULL);
UA_assert(jsonSize > 0); /* 0 => fail */
UA_ByteString buf2 = UA_BYTESTRING_NULL;
retval = UA_ByteString_allocBuffer(&buf2, jsonSize);
@ -38,18 +39,15 @@ LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
UA_Variant value2;
UA_Variant_init(&value2);
retval = UA_decodeJson(&buf2, &value2, &UA_TYPES[UA_TYPES_VARIANT], NULL);
if(retval != UA_STATUSCODE_GOOD) {
UA_Variant_clear(&value);
UA_ByteString_clear(&buf2);
return 0;
}
UA_assert(UA_order(&value, &value2, &UA_TYPES[UA_TYPES_VARIANT]) == UA_ORDER_EQ);
UA_Variant_clear(&value);
UA_assert(retval == UA_STATUSCODE_GOOD);
/* TODO: Enable this assertion when the binary-JSON-binary roundtrip is complete.
* Waiting for Mantis issue #7750.
* UA_assert(UA_order(&value, &value2, &UA_TYPES[UA_TYPES_VARIANT]) == UA_ORDER_EQ); */
UA_ByteString buf3 = UA_BYTESTRING_NULL;
retval = UA_ByteString_allocBuffer(&buf3, jsonSize);
if(retval != UA_STATUSCODE_GOOD) {
UA_Variant_clear(&value);
UA_Variant_clear(&value2);
UA_ByteString_clear(&buf2);
return 0;
@ -61,6 +59,7 @@ LLVMFuzzerTestOneInput(uint8_t *data, size_t size) {
UA_assert(buf2.length == buf3.length);
UA_assert(memcmp(buf2.data, buf3.data, buf2.length) == 0);
UA_Variant_clear(&value);
UA_Variant_clear(&value2);
UA_ByteString_clear(&buf2);
UA_ByteString_clear(&buf3);

View File

@ -81,7 +81,7 @@ START_TEST(UA_PubSub_EncodeAllOptionalFields) {
m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue = true;
size_t size = UA_NetworkMessage_calcSizeJson(&m, NULL, 0, NULL, 0, true);
ck_assert_int_eq(size, 342);
ck_assert_int_eq(size, 340);
UA_ByteString buffer;
UA_StatusCode rv = UA_ByteString_allocBuffer(&buffer, size+1);
@ -97,7 +97,7 @@ START_TEST(UA_PubSub_EncodeAllOptionalFields) {
// then
ck_assert_int_eq(rv, UA_STATUSCODE_GOOD);
char* result = "{\"MessageId\":\"ABCDEFGH\",\"MessageType\":\"ua-data\",\"PublisherId\":65535,\"DataSetClassId\":\"00000001-0002-0003-0000-000000000000\",\"Messages\":[{\"DataSetWriterId\":12345,\"SequenceNumber\":4711,\"MetaDataVersion\":{\"MajorVersion\":42,\"MinorVersion\":7},\"Timestamp\":\"1601-01-13T20:38:31.1111111Z\",\"Status\":2764857,\"Payload\":{\"Field1\":{\"Type\":7,\"Body\":27}}}]}";
char* result = "{\"MessageId\":\"ABCDEFGH\",\"MessageType\":\"ua-data\",\"PublisherId\":65535,\"DataSetClassId\":\"00000001-0002-0003-0000-000000000000\",\"Messages\":[{\"DataSetWriterId\":12345,\"SequenceNumber\":4711,\"MetaDataVersion\":{\"MajorVersion\":42,\"MinorVersion\":7},\"Timestamp\":\"1601-01-13T20:38:31.1111111Z\",\"Status\":12345,\"Payload\":{\"Field1\":{\"Type\":7,\"Body\":27}}}]}";
ck_assert_str_eq(result, (char*)buffer.data);
UA_ByteString_clear(&buffer);

View File

@ -602,7 +602,7 @@ START_TEST(Server_MonitoredItemsPercentFilterSetLaterMissingEURange) {
ck_assert_uint_eq(modifyResponse.resultsSize, 1);
/* missing EURange. See https://reference.opcfoundation.org/v104/Core/docs/Part8/6.2/ */
ck_assert_uint_eq(modifyResponse.results[0].statusCode,
UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED);
UA_STATUSCODE_BADFILTERNOTALLOWED);
UA_ModifyMonitoredItemsResponse_clear(&modifyResponse);
@ -948,7 +948,7 @@ START_TEST(Server_MonitoredItemsPercentFilterSetOnCreateMissingEURange) {
ck_assert_uint_eq(createResponse.resultsSize, 1);
/* missing EURange. See https://reference.opcfoundation.org/v104/Core/docs/Part8/6.2/ */
ck_assert_uint_eq(createResponse.results[0].statusCode,
UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED);
UA_STATUSCODE_BADFILTERNOTALLOWED);
newMonitoredItemIds[0] = createResponse.results[0].monitoredItemId;
UA_CreateMonitoredItemsResponse_clear(&createResponse);

View File

@ -460,6 +460,39 @@ setupLiteralOperand(UA_ContentFilterElement *element, size_t count, UA_Variant *
}
}
START_TEST(selectFilterValidation) {
/* setup event filter */
UA_EventFilter filter;
UA_EventFilter_init(&filter);
filter.whereClause.elementsSize = 0;
filter.whereClause.elements = NULL;
filter.selectClauses = UA_SimpleAttributeOperand_new();
filter.selectClausesSize = 1;
UA_SimpleAttributeOperand_init(filter.selectClauses);
filter.selectClauses->typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
filter.selectClauses->browsePathSize = 1;
filter.selectClauses->browsePath = (UA_QualifiedName*)
UA_Array_new(filter.selectClauses->browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
filter.selectClauses->attributeId = UA_ATTRIBUTEID_VALUE;
filter.selectClauses->browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "FOOBAR");
UA_MonitoredItemCreateResult createResult;
createResult = addMonitoredItem(handler_events_simple, &filter, true);
ck_assert_uint_eq(createResult.statusCode, UA_STATUSCODE_BADNODEIDUNKNOWN);
UA_QualifiedName_clear(&filter.selectClauses->browsePath[0]);
filter.selectClauses->browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "");
createResult = addMonitoredItem(handler_events_simple, &filter, true);
ck_assert_uint_eq(createResult.statusCode, UA_STATUSCODE_BADNODEIDUNKNOWN);
UA_QualifiedName_delete(&filter.selectClauses->browsePath[0]);
filter.selectClauses->browsePath = NULL;
filter.selectClauses->browsePathSize = 0;
createResult = addMonitoredItem(handler_events_simple, &filter, true);
ck_assert_uint_eq(createResult.statusCode, UA_STATUSCODE_BADBROWSENAMEINVALID);
UA_EventFilter_clear(&filter);
} END_TEST
/* Test Case "not-Operator" Description:
Phase 1:
Action -> Fire default "EventType_A_Layer_1" Event
@ -775,6 +808,7 @@ static Suite *testSuite_Client(void) {
Suite *s = suite_create("Server Subscription Event Filters");
TCase *tc_server = tcase_create("Basic Event Filters");
tcase_add_unchecked_fixture(tc_server, setup, teardown);
tcase_add_test(tc_server, selectFilterValidation);
tcase_add_test(tc_server, notOperatorValidation);
tcase_add_test(tc_server, ofTypeOperatorValidation);
tcase_add_test(tc_server, orTypeOperatorValidation);

View File

@ -109,7 +109,7 @@ os.system("""openssl req \
-x509 -sha256 \
-newkey rsa:{} \
-keyout localhost.key -days 365 \
-subj "/C=DE/O=open62541/CN=open62541Server@localhost"\
-subj "/C=DE/L=Here/O=open62541/CN=open62541Server@localhost"\
-out localhost.crt""".format(openssl_conf, keysize))
os.system("openssl x509 -in localhost.crt -outform der -out %s_cert.der" % (certificatename))
os.system("openssl rsa -inform PEM -in localhost.key -outform DER -out %s_key.der"% (certificatename))

View File

@ -2,7 +2,7 @@ from datatypes import Boolean, Byte, SByte, \
Int16, UInt16, Int32, UInt32, Int64, UInt64, Float, Double, \
String, XmlElement, ByteString, Structure, ExtensionObject, LocalizedText, \
NodeId, ExpandedNodeId, DateTime, QualifiedName, StatusCode, \
DiagnosticInfo, Guid, BuiltinType
DiagnosticInfo, Guid, BuiltinType, EnumerationType
import datetime
import re
@ -121,7 +121,7 @@ def generateNodeValueCode(prepend , node, instanceName, valueName, global_var_co
node.value = 0.0
else:
node.value = 0
if encRule is None:
if encRule is None or isinstance(encRule.member_type, EnumerationType):
return prepend + " = (UA_" + node.__class__.__name__ + ") " + str(node.value) + ";"
else:
return prepend + " = (UA_" + encRule.member_type.name + ") " + str(node.value) + ";"
@ -130,6 +130,10 @@ def generateNodeValueCode(prepend , node, instanceName, valueName, global_var_co
elif isinstance(node, XmlElement):
return prepend + " = " + generateXmlElementCode(node.value, alloc=asIndirect) + ";"
elif isinstance(node, ByteString):
# Basically the prepend must be passed to the generateByteStrongCode function so that the nested structures are
# generated correctly. In case of a pointer the valueName is used. This is for example the case with NS0
# (ns=0;i=8252)
valueName = valueName if prepend[0] == '*' else prepend
# replace whitespaces between tags and remove newlines
return prepend + " = UA_BYTESTRING_NULL;" if not node.value else generateByteStringCode(
node.value, valueName, global_var_code, isPointer=asIndirect)

View File

@ -241,12 +241,26 @@ class Value(object):
return extobj
extobj.value = []
members = enc.members
# The EncodingMask must be skipped.
if ebodypart.localName == "EncodingMask":
ebodypart = getNextElementNode(ebodypart)
for e in enc.members:
# The SwitchField must be checked.
if ebodypart.localName == "SwitchField":
# The switch field is the index of the available union fields starting with 1
data = int(ebodypart.firstChild.data)
if data == 0:
# If the switch field is 0 then no field is present. A Union with no fields present has the same meaning as a NULL value.
members = []
else:
members = []
members.append(enc.members[data-1])
ebodypart = getNextElementNode(ebodypart)
for e in members:
# ebodypart can be None if the field is not set, although the field is not optional.
if ebodypart is None:
if not e.is_optional:
@ -326,7 +340,7 @@ class Value(object):
else:
members = []
members.append(enc.members[data-1])
ebodypart = getNextElementNode(body)
ebodypart = getNextElementNode(body)
else:
logger.error(str(parent.id) + ": Could not parse <SwitchFiled> for Union.")
return self