Json_pubsub: add encoding/decoding for networkmessage and datasetmessage

This commit is contained in:
Lukas M 2018-08-07 12:16:46 +02:00 committed by Julius Pfrommer
parent 1dbd3d9dac
commit 067d4c357f
8 changed files with 1337 additions and 48 deletions

View File

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) 2017-2018 Fraunhofer IOSB (Author: Andreas Ebner)
* Copyright (c) 2018 Fraunhofer IOSB (Author: Lukas Meling)
*/
#include "ua_types_encoding_binary.h"
@ -748,10 +749,21 @@ UA_PubSubDataSetWriter_generateKeyFrameMessage(UA_Server *server, UA_DataSetMess
if(!dataSetMessage->data.keyFrameData.dataSetFields)
return UA_STATUSCODE_BADOUTOFMEMORY;
/* json keys */
dataSetMessage->data.keyFrameData.fieldNames = (UA_String *)
UA_Array_new(currentDataSet->fieldSize, &UA_TYPES[UA_TYPES_STRING]);
if(!dataSetMessage->data.keyFrameData.fieldNames)
return UA_STATUSCODE_BADOUTOFMEMORY;
/* Loop over the fields */
size_t counter = 0;
UA_DataSetField *dsf;
LIST_FOREACH(dsf, &currentDataSet->fields, listEntry) {
/* for json store the fieldNameAlias in */
UA_String_copy(&dsf->config.field.variable.fieldNameAlias,
&dataSetMessage->data.keyFrameData.fieldNames[counter]);
/* Sample the value */
UA_DataValue *dfv = &dataSetMessage->data.keyFrameData.dataSetFields[counter];
UA_PubSubDataSetField_sampleValue(server, dsf, dfv);
@ -796,9 +808,20 @@ UA_PubSubDataSetWriter_generateDeltaFrameMessage(UA_Server *server,
dataSetMessage->header.dataSetMessageValid = true;
dataSetMessage->header.dataSetMessageType = UA_DATASETMESSAGE_DATADELTAFRAME;
/* json keys, TODO: add to delete Members*/
dataSetMessage->data.deltaFrameData.fieldNames = (UA_String *)
UA_Array_new(currentDataSet->fieldSize, &UA_TYPES[UA_TYPES_STRING]);
if(!dataSetMessage->data.deltaFrameData.fieldNames)
return UA_STATUSCODE_BADOUTOFMEMORY;
UA_DataSetField *dsf;
size_t counter = 0;
LIST_FOREACH(dsf, &currentDataSet->fields, listEntry) {
/* for json store the fieldNameAlias in */
UA_String_copy(&dsf->config.field.variable.fieldNameAlias,
&dataSetMessage->data.deltaFrameData.fieldNames[counter]);
/* Sample the value */
UA_DataValue value;
UA_DataValue_init(&value);
@ -876,6 +899,14 @@ UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *da
/* Reset the message */
memset(dataSetMessage, 0, sizeof(UA_DataSetMessage));
//TODO:
//dataSetMessage->header.dataSetWriterId = dataSetWriter->config.dataSetWriterId;
UA_UInt16 messageType = 0;
UA_JsonDataSetWriterMessageDataType *jsonDataSetWriterMessageDataType = NULL;
//UA_JsonDataSetWriterMessageDataType defaultJsonConfiguration;
/* Currently is only UADP supported. The configuration Flags are included
* inside the std. defined UA_UadpDataSetWriterMessageDataType */
UA_UadpDataSetWriterMessageDataType defaultUadpConfiguration;
@ -885,6 +916,13 @@ UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *da
(dataSetWriter->config.messageSettings.content.decoded.type == &UA_TYPES[UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE])) {
dataSetWriterMessageDataType = (UA_UadpDataSetWriterMessageDataType *)
dataSetWriter->config.messageSettings.content.decoded.data;
messageType = UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE;
} else if((dataSetWriter->config.messageSettings.encoding == UA_EXTENSIONOBJECT_DECODED ||
dataSetWriter->config.messageSettings.encoding == UA_EXTENSIONOBJECT_DECODED_NODELETE) &&
(dataSetWriter->config.messageSettings.content.decoded.type == &UA_TYPES[UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE])) {
jsonDataSetWriterMessageDataType = (UA_JsonDataSetWriterMessageDataType *)
dataSetWriter->config.messageSettings.content.decoded.data;
messageType = UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE;
} else {
/* create default flag configuration if no
* UadpDataSetWriterMessageDataType was passed in */
@ -893,8 +931,12 @@ UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *da
(UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP | UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION |
UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION);
dataSetWriterMessageDataType = &defaultUadpConfiguration;
messageType = UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE;
}
if(messageType == UA_TYPES_UADPDATASETWRITERMESSAGEDATATYPE){
/* Sanity-test the configuration */
if(dataSetWriterMessageDataType->networkMessageNumber != 0 ||
dataSetWriterMessageDataType->dataSetOffset != 0 ||
@ -918,41 +960,79 @@ UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *da
dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
}
/* Std: 'The DataSetMessageContentMask defines the flags for the content of the DataSetMessage header.' */
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION){
dataSetMessage->header.configVersionMajorVersionEnabled = UA_TRUE;
dataSetMessage->header.configVersionMajorVersion =
currentDataSet->dataSetMetaData.configurationVersion.majorVersion;
}
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION){
dataSetMessage->header.configVersionMinorVersionEnabled = UA_TRUE;
dataSetMessage->header.configVersionMinorVersion =
currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
}
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
dataSetMessage->header.dataSetMessageSequenceNrEnabled = UA_TRUE;
dataSetMessage->header.dataSetMessageSequenceNr =
dataSetWriter->actualDataSetMessageSequenceCount;
}
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) {
dataSetMessage->header.timestampEnabled = UA_TRUE;
dataSetMessage->header.timestamp = UA_DateTime_now();
}
/* TODO: Picoseconds resolution not supported atm */
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS) {
dataSetMessage->header.picoSecondsIncluded = UA_FALSE;
}
/* Std: 'The DataSetMessageContentMask defines the flags for the content of the DataSetMessage header.' */
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION){
dataSetMessage->header.configVersionMajorVersionEnabled = UA_TRUE;
dataSetMessage->header.configVersionMajorVersion =
currentDataSet->dataSetMetaData.configurationVersion.majorVersion;
}
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION){
dataSetMessage->header.configVersionMinorVersionEnabled = UA_TRUE;
dataSetMessage->header.configVersionMinorVersion =
currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
}
/* TODO: Statuscode not supported yet */
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_STATUS){
dataSetMessage->header.statusEnabled = UA_FALSE;
}
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
dataSetMessage->header.dataSetMessageSequenceNrEnabled = UA_TRUE;
dataSetMessage->header.dataSetMessageSequenceNr =
dataSetWriter->actualDataSetMessageSequenceCount;
}
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) {
dataSetMessage->header.timestampEnabled = UA_TRUE;
dataSetMessage->header.timestamp = UA_DateTime_now();
}
/* TODO: Picoseconds resolution not supported atm */
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS) {
dataSetMessage->header.picoSecondsIncluded = UA_FALSE;
}
/* TODO: Statuscode not supported yet */
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_STATUS){
dataSetMessage->header.statusEnabled = UA_FALSE;
}
}else if(messageType == UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE){
/*UA_JSONDATASETMESSAGECONTENTMASK_DATASETWRITERID:
* "This value is mandatory", Part 14: 7.2.3.3 Table 92 */
/* Std: 'The DataSetMessageContentMask defines the flags for the content of the DataSetMessage header.' */
if(jsonDataSetWriterMessageDataType->dataSetMessageContentMask & UA_JSONDATASETMESSAGECONTENTMASK_METADATAVERSION){
dataSetMessage->header.configVersionMajorVersionEnabled = UA_TRUE;
dataSetMessage->header.configVersionMajorVersion =
currentDataSet->dataSetMetaData.configurationVersion.majorVersion;
}
if(jsonDataSetWriterMessageDataType->dataSetMessageContentMask & UA_JSONDATASETMESSAGECONTENTMASK_METADATAVERSION){
dataSetMessage->header.configVersionMinorVersionEnabled = UA_TRUE;
dataSetMessage->header.configVersionMinorVersion =
currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
}
if(jsonDataSetWriterMessageDataType->dataSetMessageContentMask & UA_JSONDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
dataSetMessage->header.dataSetMessageSequenceNrEnabled = UA_TRUE;
dataSetMessage->header.dataSetMessageSequenceNr =
dataSetWriter->actualDataSetMessageSequenceCount;
}
if(jsonDataSetWriterMessageDataType->dataSetMessageContentMask & UA_JSONDATASETMESSAGECONTENTMASK_TIMESTAMP) {
dataSetMessage->header.timestampEnabled = UA_TRUE;
dataSetMessage->header.timestamp = UA_DateTime_now();
}
/* TODO: Statuscode not supported yet */
if(jsonDataSetWriterMessageDataType->dataSetMessageContentMask & UA_JSONDATASETMESSAGECONTENTMASK_STATUS){
dataSetMessage->header.statusEnabled = UA_FALSE;
}
}
/* Set the sequence count. Automatically rolls over to zero */
dataSetWriter->actualDataSetMessageSequenceCount++;
/* TODO: remove. Hack for not generating deltaframes for json
* Spec does not define deltaframes for json encoding
*/
if(messageType != UA_TYPES_JSONDATASETWRITERMESSAGEDATATYPE){
#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
/* Check if the PublishedDataSet version has changed -> if yes flush the lastValue store and send a KeyFrame */
if(dataSetWriter->connectedDataSetVersion.majorVersion != currentDataSet->dataSetMetaData.configurationVersion.majorVersion ||
@ -988,6 +1068,7 @@ UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *da
dataSetWriter->deltaFrameCounter = 1;
#endif
}
UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter);
return UA_STATUSCODE_GOOD;
@ -1005,7 +1086,8 @@ UA_WriterGroup_publishCallback(UA_Server *server, UA_WriterGroup *writerGroup) {
if(writerGroup->writersCount <= 0)
return;
if(writerGroup->config.encodingMimeType != UA_PUBSUB_ENCODING_UADP) {
if(writerGroup->config.encodingMimeType != UA_PUBSUB_ENCODING_UADP &&
writerGroup->config.encodingMimeType != UA_PUBSUB_ENCODING_JSON) {
UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER, "Unknown encoding type.");
return;
}
@ -1125,23 +1207,54 @@ UA_WriterGroup_publishCallback(UA_Server *server, UA_WriterGroup *writerGroup) {
nmStore->payload.dataSetPayload.sizes = &dsmSizes[currentDSMPosition];
nmStore->payloadHeader.dataSetPayloadHeader.dataSetWriterIds = &dsWriterIds[currentDSMPosition];
}
//send the prepared messages
UA_ByteString buf;
size_t msgSize = UA_NetworkMessage_calcSizeBinary(&nmStore[i]);
if(UA_ByteString_allocBuffer(&buf, msgSize) == UA_STATUSCODE_GOOD) {
UA_Byte *bufPos = buf.data;
memset(bufPos, 0, msgSize);
const UA_Byte *bufEnd = &(buf.data[buf.length]);
if(UA_NetworkMessage_encodeBinary(&nmStore[i], &bufPos, bufEnd) != UA_STATUSCODE_GOOD){
UA_ByteString_deleteMembers(&buf);
return;
};
connection->channel->send(connection->channel, &writerGroup->config.transportSettings, &buf);
UA_PubSubConnection *connection = UA_PubSubConnection_findConnectionbyId(server, writerGroup->linkedConnection);
if(!connection){
UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_SERVER, "Publish failed. PubSubConnection invalid.");
return;
}
//DataSet Array mit MetaData übergeben?
//fieldNamesPerWriter
if(writerGroup->config.encodingMimeType == UA_PUBSUB_ENCODING_JSON){
UA_ByteString buf;
size_t msgSize = 2000; //WIP, TODO: get Json buffer size!
if(UA_ByteString_allocBuffer(&buf, msgSize) == UA_STATUSCODE_GOOD) {
UA_Byte *bufPos = buf.data;
memset(bufPos, 0, msgSize);
const UA_Byte *bufEnd = &(buf.data[buf.length]);
if(UA_NetworkMessage_encodeJson(&nmStore[i], &bufPos, bufEnd, UA_TRUE) != UA_STATUSCODE_GOOD){
UA_ByteString_deleteMembers(&buf);
return;
};
size_t len = strlen((char*)buf.data);
buf.length = len;
connection->channel->send(connection->channel, &writerGroup->config.transportSettings, &buf);
}
UA_ByteString_deleteMembers(&buf);
}else if(writerGroup->config.encodingMimeType == UA_PUBSUB_ENCODING_UADP) {
//send the prepared messages
UA_ByteString buf;
size_t msgSize = UA_NetworkMessage_calcSizeBinary(&nmStore[i]);
if(UA_ByteString_allocBuffer(&buf, msgSize) == UA_STATUSCODE_GOOD) {
UA_Byte *bufPos = buf.data;
memset(bufPos, 0, msgSize);
const UA_Byte *bufEnd = &(buf.data[buf.length]);
if(UA_NetworkMessage_encodeBinary(&nmStore[i], &bufPos, bufEnd) != UA_STATUSCODE_GOOD){
UA_ByteString_deleteMembers(&buf);
return;
};
connection->channel->send(connection->channel, &writerGroup->config.transportSettings, &buf);
}
UA_ByteString_deleteMembers(&buf);
}
//The stack allocated sizes and dataSetWriterIds field must be set to NULL to prevent invalid free.
nmStore[i].payload.dataSetPayload.sizes = NULL;
nmStore->payloadHeader.dataSetPayloadHeader.dataSetWriterIds = NULL;
UA_ByteString_deleteMembers(&buf);
UA_NetworkMessage_deleteMembers(&nmStore[i]);
}
}

View File

@ -10,6 +10,8 @@
#include "ua_types_generated.h"
#include "ua_types_generated_encoding_binary.h"
#include "ua_types_generated_handling.h"
#include "ua_log_stdout.h"
#include "ua_types_encoding_json.h"
#ifdef UA_ENABLE_PUBSUB /* conditional compilation */
@ -810,6 +812,14 @@ UA_NetworkMessage_deleteMembers(UA_NetworkMessage* p) {
if(p->securityHeader.securityFooterEnabled && (p->securityHeader.securityFooterSize > 0))
UA_ByteString_deleteMembers(&p->securityFooter);
if(p->messageIdEnabled){
UA_String_deleteMembers(&p->messageId);
}
if(p->publisherIdEnabled && p->publisherIdType == UA_PUBLISHERDATATYPE_STRING){
UA_String_deleteMembers(&p->publisherId.publisherIdString);
}
memset(p, 0, sizeof(UA_NetworkMessage));
}
@ -1264,6 +1274,12 @@ void UA_DataSetMessage_free(const UA_DataSetMessage* p) {
if(p->data.keyFrameData.dataSetFields != NULL)
UA_Array_delete(p->data.keyFrameData.dataSetFields, p->data.keyFrameData.fieldCount,
&UA_TYPES[UA_TYPES_DATAVALUE]);
/* Json keys */
if(p->data.keyFrameData.fieldNames != NULL){
UA_Array_delete(p->data.keyFrameData.fieldNames, p->data.keyFrameData.fieldCount,
&UA_TYPES[UA_TYPES_STRING]);
}
} else if(p->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME) {
if(p->data.deltaFrameData.deltaFrameFields != NULL) {
for(UA_UInt16 i = 0; i < p->data.deltaFrameData.fieldCount; i++) {
@ -1274,8 +1290,825 @@ void UA_DataSetMessage_free(const UA_DataSetMessage* p) {
}
}
UA_free(p->data.deltaFrameData.deltaFrameFields);
UA_Array_delete(p->data.deltaFrameData.fieldNames, p->data.deltaFrameData.fieldCount,
&UA_TYPES[UA_TYPES_STRING]);
}
}
}
/* ----Json------ */
static UA_StatusCode
UA_DataSetMessage_encodeJson(const UA_DataSetMessage* src, UA_UInt16 dataSetWriterId, UA_Byte **bufPos,
const UA_Byte *bufEnd, UA_Boolean useReversible) {
CtxJson ctx;
ctx.pos = *bufPos;
ctx.end = bufEnd;
ctx.depth = 0;
status rv = UA_STATUSCODE_GOOD;
encodingJsonStartObject(&ctx);
/* DataSetWriterId */
rv = writeKey(&ctx, "DataSetWriterId", UA_FALSE);
rv = UA_encodeJson(&dataSetWriterId, &UA_TYPES[UA_TYPES_UINT16], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
/* DataSetMessageSequenceNr */
if(src->header.dataSetMessageSequenceNrEnabled) {
rv = writeKey(&ctx, "SequenceNumber", UA_TRUE);
rv = UA_encodeJson(&(src->header.dataSetMessageSequenceNr), &UA_TYPES[UA_TYPES_UINT16], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
/* MetaDataVersion */
if(src->header.configVersionMajorVersionEnabled || src->header.configVersionMinorVersionEnabled) {
rv = writeKey(&ctx, "MetaDataVersion", UA_TRUE);
UA_ConfigurationVersionDataType cvd;
cvd.majorVersion = src->header.configVersionMajorVersion;
cvd.minorVersion = src->header.configVersionMinorVersion;
rv = UA_encodeJson(&cvd, &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
/* Timestamp */
if(src->header.timestampEnabled) {
rv = writeKey(&ctx, "Timestamp", UA_TRUE);
rv = UA_encodeJson(&(src->header.timestamp), &UA_TYPES[UA_TYPES_DATETIME], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
/* Status */
if(src->header.statusEnabled) {
rv = writeKey(&ctx, "Status", UA_TRUE);
rv = UA_encodeJson(&(src->header.status), &UA_TYPES[UA_TYPES_STATUSCODE], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
rv = writeKey(&ctx, "Payload", UA_TRUE);
encodingJsonStartObject(&ctx); //Payload
/* TODO: currently no difference between delta and key frames. Own dataSetMessageType for json?*/
if(src->header.dataSetMessageType == UA_DATASETMESSAGE_DATAKEYFRAME) {
if(src->data.keyFrameData.fieldNames == NULL){
return UA_STATUSCODE_BADENCODINGERROR;
}
if(src->header.fieldEncoding == UA_FIELDENCODING_VARIANT) {
for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) {
writeKey_UA_String(&ctx, &src->data.keyFrameData.fieldNames[i], i==0?UA_FALSE: UA_TRUE);
rv = UA_encodeJson(&(src->data.keyFrameData.dataSetFields[i].value),
&UA_TYPES[UA_TYPES_VARIANT], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, ctx.useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
} else if(src->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) {
return UA_STATUSCODE_BADNOTIMPLEMENTED;
} else if(src->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) {
for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) {
writeKey_UA_String(&ctx, &src->data.keyFrameData.fieldNames[i], i==0?UA_FALSE: UA_TRUE);
rv = UA_encodeJson(&(src->data.keyFrameData.dataSetFields[i]), &UA_TYPES[UA_TYPES_DATAVALUE], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
}
}else if(src->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME){
if(src->data.deltaFrameData.fieldNames == NULL){
return UA_STATUSCODE_BADENCODINGERROR;
}
if(src->header.fieldEncoding == UA_FIELDENCODING_VARIANT) {
for (UA_UInt16 i = 0; i < src->data.deltaFrameData.fieldCount; i++) {
writeKey_UA_String(&ctx, &src->data.deltaFrameData.fieldNames[i], i==0?UA_FALSE: UA_TRUE);
rv = UA_encodeJson(&(src->data.deltaFrameData.deltaFrameFields[i].fieldValue.value), &UA_TYPES[UA_TYPES_VARIANT], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
} else if(src->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) {
return UA_STATUSCODE_BADNOTIMPLEMENTED;
} else if(src->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) {
for (UA_UInt16 i = 0; i < src->data.deltaFrameData.fieldCount; i++) {
writeKey_UA_String(&ctx, &src->data.deltaFrameData.fieldNames[i], i==0?UA_FALSE: UA_TRUE);
rv = UA_encodeJson(&(src->data.deltaFrameData.deltaFrameFields[i].fieldValue), &UA_TYPES[UA_TYPES_DATAVALUE], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
}
}
encodingJsonEndObject(&ctx); /* Payload */
encodingJsonEndObject(&ctx); /* DataSetMessage */
*bufPos = ctx.pos;
bufEnd = ctx.end;
return rv;
}
UA_StatusCode
UA_NetworkMessage_encodeJson(const UA_NetworkMessage* src, UA_Byte **bufPos,
const UA_Byte *bufEnd, UA_Boolean useReversible) {
status rv = UA_STATUSCODE_GOOD;
if(src->networkMessageType == UA_NETWORKMESSAGE_DATASET) {
CtxJson ctx;
ctx.pos = *bufPos;
ctx.end = bufEnd;
ctx.depth = 0;
encodingJsonStartObject(&ctx);
/* MessageId */
rv = writeKey(&ctx, "MessageId", UA_FALSE);
UA_Guid guid = UA_Guid_random();
rv = UA_encodeJson(&guid, &UA_TYPES[UA_TYPES_GUID], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
/* MessageType */
rv = writeKey(&ctx, "MessageType", UA_TRUE);
UA_String s = UA_STRING("ua-data");
rv = UA_encodeJson(&s, &UA_TYPES[UA_TYPES_STRING], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
/* PublisherId */
if(src->publisherIdEnabled) {
rv = writeKey(&ctx, "PublisherId", UA_TRUE);
switch (src->publisherIdType) {
case UA_PUBLISHERDATATYPE_BYTE:
rv = UA_encodeJson(&src->publisherId.publisherIdByte, &UA_TYPES[UA_TYPES_BYTE], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
break;
case UA_PUBLISHERDATATYPE_UINT16:
rv = UA_encodeJson(&src->publisherId.publisherIdUInt16, &UA_TYPES[UA_TYPES_UINT16], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
break;
case UA_PUBLISHERDATATYPE_UINT32:
rv = UA_encodeJson(&src->publisherId.publisherIdUInt32, &UA_TYPES[UA_TYPES_UINT32], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
break;
case UA_PUBLISHERDATATYPE_UINT64:
rv = UA_encodeJson(&src->publisherId.publisherIdUInt64, &UA_TYPES[UA_TYPES_UINT64], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
break;
case UA_PUBLISHERDATATYPE_STRING:
rv = UA_encodeJson(&src->publisherId.publisherIdString, &UA_TYPES[UA_TYPES_STRING], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
break;
default:
rv = UA_STATUSCODE_BADINTERNALERROR;
break;
}
}
if(rv != UA_STATUSCODE_GOOD)
return rv;
/* DataSetClassId */
if(src->dataSetClassIdEnabled) {
rv = writeKey(&ctx, "DataSetClassId", UA_TRUE);
rv = UA_encodeJson(&src->dataSetClassId, &UA_TYPES[UA_TYPES_GUID], &ctx.pos, &ctx.end, NULL, 0, NULL, 0, useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
/* Payload: DataSetMessages */
UA_Byte count = src->payloadHeader.dataSetPayloadHeader.count;
if(count > 0){
UA_UInt16 *dataSetWriterIds = src->payloadHeader.dataSetPayloadHeader.dataSetWriterIds;
if(!dataSetWriterIds){
return UA_STATUSCODE_BADENCODINGERROR;
}
UA_Boolean commaNeeded = UA_FALSE;
rv = writeKey(&ctx, "Messages", UA_TRUE);
encodingJsonStartArray(&ctx);
for (UA_UInt16 i = 0; i < count; i++) {
writeComma(&ctx, commaNeeded);
commaNeeded = UA_TRUE;
rv = UA_DataSetMessage_encodeJson(&(src->payload.dataSetPayload.dataSetMessages[i]), dataSetWriterIds[i], &ctx.pos, ctx.end, useReversible);
if(rv != UA_STATUSCODE_GOOD)
return rv;
}
encodingJsonEndArray(&ctx);
}
encodingJsonEndObject(&ctx);
*bufPos = ctx.pos;
bufEnd = ctx.end;
} else {
rv = UA_STATUSCODE_BADNOTIMPLEMENTED;
}
return rv;
}
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);
}
const char * UA_DECODEKEY_DS_TYPE = ("Type");
static status DataSetPayload_decodeJsonInternal(void* dsmP, const UA_DataType *type, CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken){
UA_DataSetMessage* dsm = (UA_DataSetMessage*)dsmP;
dsm->header.dataSetMessageValid = UA_TRUE;
if(isJsonNull(ctx, parseCtx)){
(*parseCtx->index)++;
//TODO: set size, etc.
return UA_STATUSCODE_GOOD;
}
size_t length = (size_t)parseCtx->tokenArray[*parseCtx->index].size;
//UA_Variant *var = (UA_Variant*)UA_calloc(length, sizeof(UA_Variant));
//dsm->data.keyFrameData
UA_String *fieldNames = (UA_String*)UA_calloc(length, sizeof(UA_String));
//parseCtx->fieldNames = fieldNames;
//parseCtx->fieldNamesCount = length;
dsm->data.keyFrameData.fieldNames = fieldNames;
//UA_KeyValuePair *keyValuePairs = (UA_KeyValuePair*)UA_Array_new(dsm->data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_KEYVALUEPAIR]);
dsm->data.keyFrameData.fieldCount = (UA_UInt16)length;
dsm->data.keyFrameData.dataSetFields =
(UA_DataValue *)UA_Array_new(dsm->data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_DATAVALUE]);
status ret = UA_STATUSCODE_GOOD;
(*parseCtx->index)++; // We go to first Object key!
for(size_t i = 0; i < length; ++i) {
ret = getDecodeSignature(UA_TYPES_STRING)(&fieldNames[i], type, ctx, parseCtx, UA_TRUE);
//ret = getDecodeSignature(UA_TYPES_STRING)(&keyValuePairs[i].key.name, type, ctx, parseCtx, UA_TRUE);
if(ret != UA_STATUSCODE_GOOD){
//TODO: handle error, free mem
}
//Is field a variant or datavalue?
UA_Boolean isVariant = UA_TRUE;
size_t searchResultBody = 0;
lookAheadForKey(UA_DECODEKEY_DS_TYPE, ctx, parseCtx, &searchResultBody);
if(searchResultBody == 0){
isVariant = UA_FALSE;
dsm->header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
}else{
dsm->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
}
UA_DataValue_init(&dsm->data.keyFrameData.dataSetFields[i]);
if(isVariant){
ret = getDecodeSignature(UA_TYPES_VARIANT)(&dsm->data.keyFrameData.dataSetFields[i].value, type, ctx, parseCtx, UA_TRUE);
dsm->data.keyFrameData.dataSetFields[i].hasValue = UA_TRUE;
}else{
ret = getDecodeSignature(UA_TYPES_DATAVALUE)(&dsm->data.keyFrameData.dataSetFields[i], type, ctx, parseCtx, UA_TRUE);
dsm->data.keyFrameData.dataSetFields[i].hasValue = UA_TRUE;
}
if(ret != UA_STATUSCODE_GOOD){
//TODO: handle error, free mem
return ret;
}
}
return ret;
}
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");
static status
DatasetMessage_Payload_decodeJsonInternal(UA_DataSetMessage* dsm, const UA_DataType *type, CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken) {
UA_ConfigurationVersionDataType cvd;
UA_UInt16 dataSetWriterId; //TODO: Where to store?
dsm->header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
const char* fieldNames[6] = {
UA_DECODEKEY_DATASETWRITERID,
UA_DECODEKEY_SEQUENCENUMBER,
UA_DECODEKEY_METADATAVERSION,
UA_DECODEKEY_TIMESTAMP,
UA_DECODEKEY_DSM_STATUS,
UA_DECODEKEY_PAYLOAD};
UA_Boolean found[6] = {UA_FALSE, UA_FALSE, UA_FALSE, UA_FALSE, UA_FALSE, UA_FALSE};
void *fieldPointer[6] = {
&dataSetWriterId,
&dsm->header.dataSetMessageSequenceNr,
&cvd,
&dsm->header.timestamp,
&dsm->header.status,
dsm};
decodeJsonSignature functions[] = {
getDecodeSignature(UA_TYPES_UINT16),
getDecodeSignature(UA_TYPES_UINT16),
&MetaDataVersion_decodeJsonInternal,
getDecodeSignature(UA_TYPES_DATETIME),
getDecodeSignature(UA_TYPES_UINT16),
&DataSetPayload_decodeJsonInternal
};
DecodeContext decodeCtx = {fieldNames, fieldPointer, functions, found, 6};
status ret = decodeFields(ctx, parseCtx, &decodeCtx, NULL);
if(!found[0]){
/* no dataSetwriterid. Is mandatory. Abort. */
ret = UA_STATUSCODE_BADDECODINGERROR;
goto cleanup;
}else{
if(parseCtx->custom != NULL){
UA_UInt16* dataSetWriterIdsArray = (UA_UInt16*)parseCtx->custom;
if(*parseCtx->currentCustomIndex < parseCtx->numCustom){
dataSetWriterIdsArray[*parseCtx->currentCustomIndex] = dataSetWriterId;
(*parseCtx->currentCustomIndex)++;
}else{
ret = UA_STATUSCODE_BADDECODINGERROR;
goto cleanup;
}
}else{
ret = UA_STATUSCODE_BADDECODINGERROR;
goto cleanup;
}
}
dsm->header.dataSetMessageSequenceNrEnabled = found[1];
dsm->header.configVersionMajorVersion = cvd.majorVersion;
dsm->header.configVersionMinorVersion = cvd.minorVersion;
dsm->header.configVersionMajorVersionEnabled = found[2];
dsm->header.configVersionMinorVersionEnabled = found[2];
dsm->header.timestampEnabled = found[3];
dsm->header.statusEnabled = found[4];
if(!found[5]){
//No payload found
ret = UA_STATUSCODE_BADDECODINGERROR;
goto cleanup;
}
dsm->header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME;
dsm->header.picoSecondsIncluded = UA_FALSE;
dsm->header.dataSetMessageValid = UA_TRUE;
dsm->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
cleanup:
//UA_ConfigurationVersionDataType_delete(cvd);
return ret;
}
static status
DatasetMessage_Array_decodeJsonInternal(void *UA_RESTRICT dst, const UA_DataType *type, CtxJson *ctx, ParseCtx *parseCtx, UA_Boolean moveToken) {
/* Array! */
if(getJsmnType(parseCtx) != JSMN_ARRAY){
return UA_STATUSCODE_BADDECODINGERROR;
}
status ret;
size_t length = (size_t)parseCtx->tokenArray[*parseCtx->index].size;
/* Return early for empty arrays */
if(length == 0) {
return UA_STATUSCODE_GOOD;
}
/* Allocate memory */
UA_DataSetMessage *dsm = (UA_DataSetMessage*)UA_calloc(length, sizeof(UA_DataSetMessage));
if(dsm == NULL)
return UA_STATUSCODE_BADOUTOFMEMORY;
memcpy(dst, &dsm, sizeof(void*)); //Copy new Pointer do dest
(*parseCtx->index)++; // We go to first Array member!
/* Decode array members */
for(size_t i = 0; i < length; ++i) {
//ret = decodeJsonJumpTable[decode_index]((void*)dsm[i], type, ctx, parseCtx, UA_TRUE);
ret = DatasetMessage_Payload_decodeJsonInternal(&dsm[i], NULL, ctx, parseCtx, UA_TRUE);
if(ret != UA_STATUSCODE_GOOD){
//TODO: handle error, free mem
}
}
return ret;
}
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");
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;
/* Look forward for publisheId, if present check if type if primitve (Number) or String. */
u8 publishIdTypeIndex = UA_TYPES_STRING;
{
size_t searchResultPublishIdType = 0;
lookAheadForKey(UA_DECODEKEY_MESSAGES, ctx, parseCtx, &searchResultPublishIdType);
if(searchResultPublishIdType != 0){
jsmntok_t publishIdToken = parseCtx->tokenArray[searchResultPublishIdType];
//size_t sizeOfPublishId = (size_t)(parseCtx->tokenArray[searchResultPublishIdType].end
// - parseCtx->tokenArray[searchResultPublishIdType].start);
if(publishIdToken.type == JSMN_PRIMITIVE){
publishIdTypeIndex = UA_TYPES_UINT64;
dst->publisherIdType = UA_PUBLISHERDATATYPE_UINT64; //store in biggest possible
}else if(publishIdToken.type == JSMN_STRING){
//String
publishIdTypeIndex = UA_TYPES_STRING;
dst->publisherIdType = UA_PUBLISHERDATATYPE_STRING;
/*if(sizeOfPublishId == 36){
char *buf = (char*)(ctx->pos + parseCtx->tokenArray[*parseCtx->index].start);
// 8 13 18 23 should be -
if(buf[8] == '-' && buf[13] == '-' && buf[18] == '-' && buf[23] == '-'){
publishIdTypeIndex = UA_TYPES_GUID;
}
}*/
}
}
}
size_t messageCount = 0;
{
//Is Messages an Array? How big?
size_t searchResultMessages = 0;
lookAheadForKey(UA_DECODEKEY_MESSAGES, ctx, parseCtx, &searchResultMessages);
if(searchResultMessages != 0){
jsmntok_t bodyToken = parseCtx->tokenArray[searchResultMessages];
if(bodyToken.type == JSMN_ARRAY){
messageCount = (size_t)parseCtx->tokenArray[searchResultMessages].size;
}
}else{
//DataSetmessages are in a Array!
return UA_STATUSCODE_BADNOTIMPLEMENTED;
}
}
//Set up custom context for the dataSetwriterId
size_t currentCustomIndex = 0;
parseCtx->custom = (void*)UA_calloc(messageCount, sizeof(UA_UInt16));
parseCtx->currentCustomIndex = &currentCustomIndex;
parseCtx->numCustom = messageCount;
/* MessageType */
UA_Boolean isUaData = UA_TRUE;
size_t searchResultMessageType = 0;
lookAheadForKey(UA_DECODEKEY_MESSAGETYPE, ctx, parseCtx, &searchResultMessageType);
if(searchResultMessageType == 0){
return UA_STATUSCODE_BADDECODINGERROR;
}else{
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;
}else{
isUaData = UA_TRUE;
}
}else if(size == 11){ //ua-metadata
if(strncmp(msgType, "ua-metadata", size) != 0){
return UA_STATUSCODE_BADDECODINGERROR;
}else{
isUaData = UA_FALSE;
}
}else{
return UA_STATUSCODE_BADDECODINGERROR;
}
}
if(isUaData){
/* Network Message */
status ret = UA_STATUSCODE_GOOD;
UA_String messageType;
const char* fieldNames[5] = {UA_DECODEKEY_MESSAGEID,
UA_DECODEKEY_MESSAGETYPE,
UA_DECODEKEY_PUBLISHERID,
UA_DECODEKEY_DATASETCLASSID,
UA_DECODEKEY_MESSAGES};
UA_Boolean found[5] = {UA_FALSE, UA_FALSE, UA_FALSE, UA_FALSE, UA_FALSE};
void *fieldPointer[5] = {&dst->messageId, &messageType, &dst->publisherId.publisherIdString, &dst->dataSetClassId, &dst->payload.dataSetPayload.dataSetMessages};
//Store publisherId in correct union
if(publishIdTypeIndex == UA_TYPES_UINT64)
fieldPointer[2] = &dst->publisherId.publisherIdUInt64;
decodeJsonSignature functions[5] = {
getDecodeSignature(UA_TYPES_STRING),
NULL,
getDecodeSignature(publishIdTypeIndex),
getDecodeSignature(UA_TYPES_GUID),
&DatasetMessage_Array_decodeJsonInternal
};
DecodeContext decodeCtx = {fieldNames, fieldPointer, functions, found, 5};
ret = decodeFields(ctx, parseCtx, &decodeCtx, NULL);
dst->messageIdEnabled = found[0];
dst->publisherIdEnabled = found[2];
if(dst->publisherIdEnabled){
dst->publisherIdType = UA_PUBLISHERDATATYPE_STRING;
}
dst->dataSetClassIdEnabled = found[3];
dst->payloadHeaderEnabled = UA_TRUE;
dst->payloadHeader.dataSetPayloadHeader.count = (UA_Byte)messageCount;
//Set the dataSetWriterIds. They are filled in the dataSet decoding.
dst->payloadHeader.dataSetPayloadHeader.dataSetWriterIds = (UA_UInt16*)parseCtx->custom;
return ret;
}else{
//TODO: MetaData
return UA_STATUSCODE_BADNOTIMPLEMENTED;
}
}
status UA_NetworkMessage_decodeJson(UA_NetworkMessage *dst, UA_ByteString *src){
/* Set up the context */
CtxJson ctx;
ParseCtx parseCtx;
memset(&parseCtx, 0, sizeof(ParseCtx));
parseCtx.tokenArray = (jsmntok_t*)malloc(sizeof(jsmntok_t) * TOKENCOUNT);
memset(parseCtx.tokenArray, 0, sizeof(jsmntok_t) * TOKENCOUNT);
status ret = UA_STATUSCODE_GOOD;
UA_UInt16 tokenIndex = 0;
ret = tokenize(&parseCtx, &ctx, src, &tokenIndex);
if(ret != UA_STATUSCODE_GOOD){
return ret;
}
ret = NetworkMessage_decodeJsonInternal(dst, &ctx, &parseCtx);
free(parseCtx.tokenArray);
return ret;
}
/* Calc size */
static status
UA_DataSetMessage_calcSizeJson(const UA_DataSetMessage* src, UA_UInt16 dataSetWriterId, CtxJson *ctx) {
status rv = UA_STATUSCODE_GOOD;
//encodingJsonStartObject(&ctx);
ctx->pos++;
/* DataSetWriterId */
rv = calcWriteKey(ctx, "DataSetWriterId", UA_FALSE);
rv = calcJsonInternal(&dataSetWriterId, &UA_TYPES[UA_TYPES_UINT16], ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
/* DataSetMessageSequenceNr */
if(src->header.dataSetMessageSequenceNrEnabled) {
rv = calcWriteKey(ctx, "SequenceNumber", UA_TRUE);
rv = calcJsonInternal(&(src->header.dataSetMessageSequenceNr), &UA_TYPES[UA_TYPES_UINT16],ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
}
/* MetaDataVersion */
if(src->header.configVersionMajorVersionEnabled || src->header.configVersionMinorVersionEnabled) {
rv = calcWriteKey(ctx, "MetaDataVersion", UA_TRUE);
UA_ConfigurationVersionDataType cvd;
cvd.majorVersion = src->header.configVersionMajorVersion;
cvd.minorVersion = src->header.configVersionMinorVersion;
rv = calcJsonInternal(&cvd, &UA_TYPES[UA_TYPES_CONFIGURATIONVERSIONDATATYPE], ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
}
/* Timestamp */
if(src->header.timestampEnabled) {
rv = calcWriteKey(ctx, "Timestamp", UA_TRUE);
rv = calcJsonInternal(&(src->header.timestamp), &UA_TYPES[UA_TYPES_DATETIME], ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
}
/* Status */
if(src->header.statusEnabled) {
rv = calcWriteKey(ctx, "Status", UA_TRUE);
rv = calcJsonInternal(&(src->header.status), &UA_TYPES[UA_TYPES_STATUSCODE], ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
}
rv = calcWriteKey(ctx, "Payload", UA_TRUE);
//encodingJsonStartObject(&ctx); //Payload
ctx->pos++;
/* TODO: currently no difference between delta and key frames. Own dataSetMessageType for json?*/
if(src->header.dataSetMessageType == UA_DATASETMESSAGE_DATAKEYFRAME) {
if(src->data.keyFrameData.fieldNames == NULL){
return UA_STATUSCODE_BADENCODINGERROR;
}
if(src->header.fieldEncoding == UA_FIELDENCODING_VARIANT) {
for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) {
calcWriteKey_UA_String(ctx, &src->data.keyFrameData.fieldNames[i], i==0?UA_FALSE: UA_TRUE);
rv = calcJsonInternal(&(src->data.keyFrameData.dataSetFields[i].value),
&UA_TYPES[UA_TYPES_VARIANT], ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
}
} else if(src->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) {
return UA_STATUSCODE_BADNOTIMPLEMENTED;
} else if(src->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) {
for (UA_UInt16 i = 0; i < src->data.keyFrameData.fieldCount; i++) {
calcWriteKey_UA_String(ctx, &src->data.keyFrameData.fieldNames[i], i==0?UA_FALSE: UA_TRUE);
rv = calcJsonInternal(&(src->data.keyFrameData.dataSetFields[i]), &UA_TYPES[UA_TYPES_DATAVALUE], ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
}
}
}else if(src->header.dataSetMessageType == UA_DATASETMESSAGE_DATADELTAFRAME){
if(src->data.deltaFrameData.fieldNames == NULL){
return 0;
}
if(src->header.fieldEncoding == UA_FIELDENCODING_VARIANT) {
for (UA_UInt16 i = 0; i < src->data.deltaFrameData.fieldCount; i++) {
calcWriteKey_UA_String(ctx, &src->data.deltaFrameData.fieldNames[i], i==0?UA_FALSE: UA_TRUE);
rv = calcJsonInternal(&(src->data.deltaFrameData.deltaFrameFields[i].fieldValue.value), &UA_TYPES[UA_TYPES_VARIANT], ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
}
} else if(src->header.fieldEncoding == UA_FIELDENCODING_RAWDATA) {
return UA_STATUSCODE_BADNOTIMPLEMENTED;
} else if(src->header.fieldEncoding == UA_FIELDENCODING_DATAVALUE) {
for (UA_UInt16 i = 0; i < src->data.deltaFrameData.fieldCount; i++) {
calcWriteKey_UA_String(ctx, &src->data.deltaFrameData.fieldNames[i], i==0?UA_FALSE: UA_TRUE);
rv = calcJsonInternal(&(src->data.deltaFrameData.deltaFrameFields[i].fieldValue), &UA_TYPES[UA_TYPES_DATAVALUE], ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
}
}
}
//encodingJsonEndObject(&ctx); /* Payload */
ctx->pos++;
//encodingJsonEndObject(&ctx); /* DataSetMessage */
ctx->pos++;
return rv;
}
static status
UA_NetworkMessage_calcSizeJson_internal(const UA_NetworkMessage* src, CtxJson *ctx) {
status rv = UA_STATUSCODE_GOOD;
if(src->networkMessageType == UA_NETWORKMESSAGE_DATASET) {
//encodingJsonStartObject(&ctx);
ctx->pos++;
/* MessageId */
rv = calcWriteKey(ctx, "MessageId", UA_FALSE);
UA_Guid guid = UA_Guid_random();
rv = calcJsonInternal(&guid, &UA_TYPES[UA_TYPES_GUID], ctx);
/* MessageType */
rv = calcWriteKey(ctx, "MessageType", UA_TRUE);
UA_String s = UA_STRING("ua-data");
rv = calcJsonInternal(&s, &UA_TYPES[UA_TYPES_STRING], ctx);
/* PublisherId */
if(src->publisherIdEnabled) {
rv = calcWriteKey(ctx, "PublisherId", UA_TRUE);
switch (src->publisherIdType) {
case UA_PUBLISHERDATATYPE_BYTE:
rv = calcJsonInternal(&src->publisherId.publisherIdByte, &UA_TYPES[UA_TYPES_BYTE], ctx);
break;
case UA_PUBLISHERDATATYPE_UINT16:
rv = calcJsonInternal(&src->publisherId.publisherIdUInt16, &UA_TYPES[UA_TYPES_UINT16], ctx);
break;
case UA_PUBLISHERDATATYPE_UINT32:
rv = calcJsonInternal(&src->publisherId.publisherIdUInt32, &UA_TYPES[UA_TYPES_UINT32], ctx);
break;
case UA_PUBLISHERDATATYPE_UINT64:
rv = calcJsonInternal(&src->publisherId.publisherIdUInt64, &UA_TYPES[UA_TYPES_UINT64], ctx);
break;
case UA_PUBLISHERDATATYPE_STRING:
rv = calcJsonInternal(&src->publisherId.publisherIdString, &UA_TYPES[UA_TYPES_STRING], ctx);
break;
default:
rv = 0;
break;
}
}
if(rv != UA_STATUSCODE_GOOD)
return 0;
/* DataSetClassId */
if(src->dataSetClassIdEnabled) {
rv = calcWriteKey(ctx, "DataSetClassId", UA_TRUE);
rv = calcJsonInternal(&src->dataSetClassId, &UA_TYPES[UA_TYPES_GUID], ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
}
/* Payload: DataSetMessages */
UA_Byte count = src->payloadHeader.dataSetPayloadHeader.count;
if(count > 0){
UA_UInt16 *dataSetWriterIds = src->payloadHeader.dataSetPayloadHeader.dataSetWriterIds;
if(!dataSetWriterIds){
return 0;
}
UA_Boolean commaNeeded = UA_FALSE;
rv = calcWriteKey(ctx, "Messages", UA_TRUE);
//encodingJsonStartArray(&ctx);
ctx->pos++;
for (UA_UInt16 i = 0; i < count; i++) {
calcWriteComma(ctx, commaNeeded);
commaNeeded = UA_TRUE;
rv = UA_DataSetMessage_calcSizeJson(&(src->payload.dataSetPayload.dataSetMessages[i]), dataSetWriterIds[i], ctx);
if(rv != UA_STATUSCODE_GOOD)
return 0;
}
//encodingJsonEndArray(&ctx);
ctx->pos++;
}
//encodingJsonEndObject(&ctx);
ctx->pos++;
return rv;
} else {
return UA_STATUSCODE_BADNOTIMPLEMENTED;
}
}
size_t
UA_NetworkMessage_calcSizeJson(const UA_NetworkMessage* src, UA_Boolean useReversible){
CtxJson ctx;
memset(&ctx, 0, sizeof(ctx));
ctx.pos = 0;
ctx.depth = 0;
ctx.useReversible = useReversible;
//ctx.namespaces = namespaces;
//ctx.namespacesSize = namespaceSize;
//ctx.serverUris = serverUris;
//ctx.serverUrisSize = serverUriSize;
status rv = UA_STATUSCODE_GOOD;
UA_NetworkMessage_calcSizeJson_internal(src, &ctx);
if(rv != UA_STATUSCODE_GOOD){
return 0;
}else{
return (size_t)ctx.pos;
}
}
#endif /* UA_ENABLE_PUBSUB */

View File

@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) 2017 - 2018 Fraunhofer IOSB (Author: Tino Bischoff)
* Copyright (c) 2018 Fraunhofer IOSB (Author: Lukas Meling)
*/
#ifndef UA_PUBSUB_NETWORKMESSAGE_H_
@ -71,6 +72,8 @@ UA_DataSetMessageHeader_calcSizeBinary(const UA_DataSetMessageHeader* p);
typedef struct {
UA_UInt16 fieldCount;
UA_DataValue* dataSetFields;
/* Json keys for the dataSetFields: TODO: own dataSetMessageType for json? */
UA_String* fieldNames;
} UA_DataSetMessage_DataKeyFrameData;
typedef struct {
@ -81,6 +84,8 @@ typedef struct {
typedef struct {
UA_UInt16 fieldCount;
UA_DataSetMessage_DeltaFrameField* deltaFrameFields;
/* Json keys for the dataSetFields: TODO: own dataSetMessageType for json? */
UA_String* fieldNames;
} UA_DataSetMessage_DataDeltaFrameData;
typedef struct {
@ -156,6 +161,8 @@ typedef struct {
* ^^^^^^^^^^^^^^^^^ */
typedef struct {
UA_Byte version;
UA_Boolean messageIdEnabled;
UA_String messageId; //For Json NetworkMessage
UA_Boolean publisherIdEnabled;
UA_Boolean groupHeaderEnabled;
UA_Boolean payloadHeaderEnabled;
@ -215,6 +222,16 @@ UA_NetworkMessage_deleteMembers(UA_NetworkMessage* p);
void
UA_NetworkMessage_delete(UA_NetworkMessage* p);
/* Json */
UA_StatusCode
UA_NetworkMessage_encodeJson(const UA_NetworkMessage* src,
UA_Byte **bufPos, const UA_Byte *bufEnd, UA_Boolean useReversible);
UA_StatusCode UA_NetworkMessage_decodeJson(UA_NetworkMessage *dst, UA_ByteString *src);
size_t
UA_NetworkMessage_calcSizeJson(const UA_NetworkMessage* src, UA_Boolean useReversible);
_UA_END_DECLS
#endif /* UA_PUBSUB_NETWORKMESSAGE_H_ */

View File

@ -73,7 +73,6 @@ extern const decodeJsonSignature decodeJsonJumpTable[UA_BUILTIN_TYPES_COUNT + 1]
/* Forward declarations */
static status encodeJsonInternal(const void *src, const UA_DataType *type, CtxJson *ctx);
static status calcJsonInternal(const void *src, const UA_DataType *type, CtxJson *ctx);
UA_String UA_DateTime_toJSON(UA_DateTime t);
ENCODE_JSON(ByteString);
@ -1336,7 +1335,7 @@ const calcSizeJsonSignature calcJsonJumpTable[UA_BUILTIN_TYPES_COUNT + 1] = {
(calcSizeJsonSignature) calcJsonInternal,
};
static status
status
calcJsonInternal(const void *src, const UA_DataType *type, CtxJson *ctx) {
if(!type || !ctx){
return UA_STATUSCODE_BADENCODINGERROR;

View File

@ -87,12 +87,19 @@ status encodingCalcJsonStartArray(CtxJson *ctx);
size_t encodingCalcJsonEndArray(CtxJson *ctx);
status calcWriteComma(CtxJson *ctx, UA_Boolean commaNeeded);
status calcWriteNull(CtxJson *ctx);
status calcJsonInternal(const void *src, const UA_DataType *type, CtxJson *ctx);
typedef struct {
jsmntok_t *tokenArray;
UA_Int32 tokenCount;
UA_UInt16 *index;
/* Additonal data for special cases such as networkmessage/datasetmessage
* Currently only used for dataSetWriterIds*/
size_t numCustom;
void * custom;
size_t* currentCustomIndex;
} ParseCtx;
typedef status(*encodeJsonSignature)(const void *UA_RESTRICT src, const UA_DataType *type,

View File

@ -105,10 +105,9 @@ if(UA_ENABLE_JSON_ENCODING)
add_test_valgrind(types_builtin_json ${TESTS_BINARY_DIR}/check_types_builtin_json)
if(UA_ENABLE_PUBSUB)
#later batch
#add_executable(check_pubsub_encoding_json pubsub/check_pubsub_encoding_json.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
#target_link_libraries(check_pubsub_encoding_json ${LIBS})
#add_test_valgrind(pubsub_encoding_json ${TESTS_BINARY_DIR}/check_pubsub_encoding_json)
add_executable(check_pubsub_encoding_json pubsub/check_pubsub_encoding_json.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
target_link_libraries(check_pubsub_encoding_json ${LIBS})
add_test_valgrind(pubsub_encoding_json ${TESTS_BINARY_DIR}/check_pubsub_encoding_json)
endif()
endif()

View File

@ -0,0 +1,318 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) 2018 Fraunhofer IOSB (Author: Lukas Meling)
*/
#include "ua_types.h"
#include "ua_client.h"
#include "ua_util.h"
#include "ua_pubsub_networkmessage.h"
#include "check.h"
START_TEST(UA_PubSub_EnDecode) {
UA_NetworkMessage m;
memset(&m, 0, sizeof(UA_NetworkMessage));
m.version = 1;
m.networkMessageType = UA_NETWORKMESSAGE_DATASET;
m.payloadHeaderEnabled = true;
m.payloadHeader.dataSetPayloadHeader.count = 2;
UA_UInt16 dsWriter1 = 4;
UA_UInt16 dsWriter2 = 7;
m.payloadHeader.dataSetPayloadHeader.dataSetWriterIds = (UA_UInt16 *)UA_Array_new(m.payloadHeader.dataSetPayloadHeader.count, &UA_TYPES[UA_TYPES_UINT16]);
m.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0] = dsWriter1;
m.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[1] = dsWriter2;
size_t memsize = m.payloadHeader.dataSetPayloadHeader.count * sizeof(UA_DataSetMessage);
m.payload.dataSetPayload.dataSetMessages = (UA_DataSetMessage*)UA_malloc(memsize);
memset(m.payload.dataSetPayload.dataSetMessages, 0, memsize);
m.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageValid = true;
m.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding = UA_FIELDENCODING_VARIANT;
m.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME;
UA_UInt16 fieldCountDS1 = 1;
m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount = fieldCountDS1;
m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields =
(UA_DataValue*)UA_Array_new(m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_DATAVALUE]);
UA_DataValue_init(&m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0]);
/* Set fieldnames */
m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames =
(UA_String*)UA_Array_new(m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount, &UA_TYPES[UA_TYPES_STRING]);
m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0] = UA_STRING_ALLOC("Field1");
UA_UInt32 iv = 27;
UA_Variant_setScalarCopy(&m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value, &iv, &UA_TYPES[UA_TYPES_UINT32]);
m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue = true;
m.payload.dataSetPayload.dataSetMessages[1].header.dataSetMessageValid = true;
m.payload.dataSetPayload.dataSetMessages[1].header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
m.payload.dataSetPayload.dataSetMessages[1].header.dataSetMessageType = UA_DATASETMESSAGE_DATADELTAFRAME;
UA_UInt16 fieldCountDS2 = 2;
m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.fieldCount = fieldCountDS2;
memsize = sizeof(UA_DataSetMessage_DeltaFrameField) * m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.fieldCount;
m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields = (UA_DataSetMessage_DeltaFrameField*)UA_malloc(memsize);
/* Set fieldnames */
m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.fieldNames =
(UA_String*)UA_Array_new(m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.fieldCount, &UA_TYPES[UA_TYPES_STRING]);
m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.fieldNames[0] = UA_STRING_ALLOC("Field2.1");
m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.fieldNames[1] = UA_STRING_ALLOC("Field2.2");
UA_Guid gv = UA_Guid_random();
UA_UInt16 fieldIndex1 = 2;
m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[0].fieldIndex = fieldIndex1;
UA_DataValue_init(&m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[0].fieldValue);
UA_Variant_setScalarCopy(&m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[0].fieldValue.value, &gv, &UA_TYPES[UA_TYPES_GUID]);
m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[0].fieldValue.hasValue = true;
UA_UInt16 fieldIndex2 = 5;
m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[1].fieldIndex = fieldIndex2;
UA_DataValue_init(&m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[1].fieldValue);
UA_Int64 iv64 = 152478978534;
UA_Variant_setScalarCopy(&m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[1].fieldValue.value, &iv64, &UA_TYPES[UA_TYPES_INT64]);
m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[1].fieldValue.hasValue = true;
UA_StatusCode rv = UA_STATUSCODE_UNCERTAININITIALVALUE;
UA_ByteString buffer;
size_t msgSize = 1000;
rv = UA_ByteString_allocBuffer(&buffer, msgSize);
ck_assert_int_eq(rv, UA_STATUSCODE_GOOD);
UA_Byte *bufPos = buffer.data;
memset(bufPos, 0, msgSize);
const UA_Byte *bufEnd = &(buffer.data[buffer.length]);
size_t size = UA_NetworkMessage_calcSizeJson(&m, UA_TRUE);
ck_assert(size == 318);
rv = UA_NetworkMessage_encodeJson(&m, &bufPos, bufEnd, UA_TRUE);
*bufPos = 0;
// then
ck_assert_int_eq(rv, UA_STATUSCODE_GOOD);
UA_NetworkMessage m2;
memset(&m2, 0, sizeof(UA_NetworkMessage));
rv = UA_NetworkMessage_decodeJson(&m2, &buffer);
ck_assert_int_eq(rv, UA_STATUSCODE_GOOD);
ck_assert(m.networkMessageType == m2.networkMessageType);
ck_assert(m.timestampEnabled == m2.timestampEnabled);
ck_assert(m.dataSetClassIdEnabled == m2.dataSetClassIdEnabled);
ck_assert(m.groupHeaderEnabled == m2.groupHeaderEnabled);
ck_assert(m.picosecondsEnabled == m2.picosecondsEnabled);
ck_assert(m.promotedFieldsEnabled == m2.promotedFieldsEnabled);
ck_assert(m.publisherIdEnabled == m2.publisherIdEnabled);
ck_assert(m.securityEnabled == m2.securityEnabled);
ck_assert(m.chunkMessage == m2.chunkMessage);
ck_assert(m.payloadHeaderEnabled == m2.payloadHeaderEnabled);
ck_assert_uint_eq(m2.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0], dsWriter1);
ck_assert_uint_eq(m2.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[1], dsWriter2);
ck_assert(m.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageValid == m2.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageValid);
ck_assert(m.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding == m2.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding);
ck_assert_int_eq(m2.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldCount, fieldCountDS1);
ck_assert(m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue == m2.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue);
ck_assert_uint_eq((uintptr_t)m2.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value.type, (uintptr_t)&UA_TYPES[UA_TYPES_UINT32]);
ck_assert_uint_eq(*(UA_UInt32 *)m2.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value.data, iv);
ck_assert(m.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasSourceTimestamp == m2.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasSourceTimestamp);
ck_assert(m.payload.dataSetPayload.dataSetMessages[1].header.dataSetMessageValid == m2.payload.dataSetPayload.dataSetMessages[1].header.dataSetMessageValid);
ck_assert(m.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[0].fieldValue.hasSourceTimestamp == m2.payload.dataSetPayload.dataSetMessages[1].data.deltaFrameData.deltaFrameFields[0].fieldValue.hasSourceTimestamp);
UA_ByteString_deleteMembers(&buffer);
UA_NetworkMessage_deleteMembers(&m);
UA_NetworkMessage_deleteMembers(&m2);
}
END_TEST
START_TEST(UA_NetworkMessage_oneMessage_twoFields_json_decode) {
// given
UA_NetworkMessage out;
UA_ByteString buf = UA_STRING("{\"MessageId\":\"5ED82C10-50BB-CD07-0120-22521081E8EE\",\"MessageType\":\"ua-data\",\"Messages\":[{\"DataSetWriterId\":62541,\"MetaDataVersion\":{\"MajorVersion\":1478393530,\"MinorVersion\":12345},\"SequenceNumber\":4711,\"Payload\":{\"Test\":{\"Type\":5,\"Body\":42},\"Server localtime\":{\"Type\":13,\"Body\":\"2018-06-05T05:58:36.000Z\"}}}]}");
// when
UA_StatusCode retval = UA_NetworkMessage_decodeJson(&out, &buf);
// then
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
//NetworkMessage
ck_assert_int_eq(out.chunkMessage, false);
ck_assert_int_eq(out.dataSetClassIdEnabled, false);
ck_assert_int_eq(out.groupHeaderEnabled, false);
ck_assert_int_eq(out.networkMessageType, UA_NETWORKMESSAGE_DATASET);
ck_assert_int_eq(out.picosecondsEnabled, false);
ck_assert_int_eq(out.promotedFieldsEnabled, false);
ck_assert_int_eq(out.securityEnabled, false);
ck_assert_int_eq(out.timestampEnabled, false);
ck_assert_int_eq(out.publisherIdEnabled, false);
ck_assert_int_eq(out.payloadHeaderEnabled, true);
ck_assert_int_eq(out.payloadHeader.dataSetPayloadHeader.count, 1);
ck_assert_int_eq(out.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0], 62541);
//dataSetMessage
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNrEnabled, true);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNr, 4711);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageType, UA_DATASETMESSAGE_DATAKEYFRAME);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding, UA_FIELDENCODING_VARIANT);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.picoSecondsIncluded, false);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMinorVersionEnabled, true);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMajorVersionEnabled, true);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMinorVersion, 12345);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMajorVersion, 1478393530);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNr, 4711);
//ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetWriterId, 62541);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue, 1);
ck_assert_int_eq(*((UA_UInt16*)out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value.data), 42);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[1].hasValue, 1);
UA_DateTime *dt = (UA_DateTime*)out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[1].value.data;
UA_DateTimeStruct dts = UA_DateTime_toStruct(*dt);
ck_assert_int_eq(dts.year, 2018);
ck_assert_int_eq(dts.month, 6);
ck_assert_int_eq(dts.day, 5);
ck_assert_int_eq(dts.hour, 5);
ck_assert_int_eq(dts.min, 58);
ck_assert_int_eq(dts.sec, 36);
ck_assert_int_eq(dts.milliSec, 0);
ck_assert_int_eq(dts.microSec, 0);
ck_assert_int_eq(dts.nanoSec, 0);
UA_NetworkMessage_deleteMembers(&out);
}
END_TEST
START_TEST(UA_NetworkMessage_json_decode) {
// given
UA_NetworkMessage out;
memset(&out,0,sizeof(UA_NetworkMessage));
UA_ByteString buf = UA_STRING("{\"MessageId\":\"5ED82C10-50BB-CD07-0120-22521081E8EE\",\"MessageType\":\"ua-data\",\"Messages\":[{\"MetaDataVersion\":{\"MajorVersion\": 47, \"MinorVersion\": 47},\"DataSetWriterId\":62541,\"Status\":22,\"SequenceNumber\":4711,\"Payload\":{\"Test\":{\"Type\":5,\"Body\":42},\"Server localtime\":{\"Type\":1,\"Body\":true}}}]}");
// when
UA_StatusCode retval = UA_NetworkMessage_decodeJson(&out, &buf);
// then
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
//NetworkMessage
ck_assert_int_eq(out.chunkMessage, false);
ck_assert_int_eq(out.dataSetClassIdEnabled, false);
ck_assert_int_eq(out.groupHeaderEnabled, false);
ck_assert_int_eq(out.networkMessageType, UA_NETWORKMESSAGE_DATASET);
ck_assert_int_eq(out.picosecondsEnabled, false);
ck_assert_int_eq(out.promotedFieldsEnabled, false);
ck_assert_int_eq(out.securityEnabled, false);
ck_assert_int_eq(out.timestampEnabled, false);
ck_assert_int_eq(out.publisherIdEnabled, false);
ck_assert_int_eq(out.payloadHeaderEnabled, true);
ck_assert_int_eq(out.payloadHeader.dataSetPayloadHeader.count, 1);
ck_assert_int_eq(out.payloadHeader.dataSetPayloadHeader.dataSetWriterIds[0], 62541);
//dataSetMessage
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNrEnabled, true);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageSequenceNr, 4711);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.dataSetMessageType, UA_DATASETMESSAGE_DATAKEYFRAME);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.fieldEncoding, UA_FIELDENCODING_VARIANT);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.picoSecondsIncluded, false);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.statusEnabled, true);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.status, 22);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMinorVersionEnabled, true);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMajorVersionEnabled, true);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMinorVersion, 47);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].header.configVersionMajorVersion, 47);
//dataSetFields
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].hasValue, true);
ck_assert_int_eq(*((UA_UInt16*)out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[0].value.data), 42);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[1].hasValue, true);
ck_assert_int_eq(*((UA_Boolean*)out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.dataSetFields[1].value.data), 1);
UA_NetworkMessage_deleteMembers(&out);
}
END_TEST
START_TEST(UA_Networkmessage_DataSetFieldsNull_json_decode) {
// given
UA_NetworkMessage out;
memset(&out, 0, sizeof(UA_NetworkMessage));
UA_ByteString buf = UA_STRING("{ \"MessageId\": \"32235546-05d9-4fd7-97df-ea3ff3408574\", "
"\"MessageType\": \"ua-data\", \"PublisherId\": \"MQTT-Localhost\", "
"\"DataSetClassId\": \"00000005-cab9-4470-8f8a-2c1ead207e0e\", \"Messages\": "
"[ { \"DataSetWriterId\": 1, \"SequenceNumber\": 224, \"MetaDataVersion\": "
"{ \"MajorVersion\": 1, \"MinorVersion\": 1 },\"Payload\":null}]}");
// when
UA_StatusCode retval = UA_NetworkMessage_decodeJson(&out, &buf);
// then
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_int_eq(out.dataSetClassId.data1, 5);
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages->header.dataSetMessageSequenceNr, 224);
ck_assert_ptr_eq(out.payload.dataSetPayload.dataSetMessages->data.keyFrameData.dataSetFields, NULL);
UA_NetworkMessage_deleteMembers(&out);
}
END_TEST
START_TEST(UA_NetworkMessage_fieldNames_json_decode) {
// given
UA_NetworkMessage out;
UA_ByteString buf = UA_STRING("{\"MessageId\":\"5ED82C10-50BB-CD07-0120-22521081E8EE\","
"\"MessageType\":\"ua-data\",\"Messages\":"
"[{\"DataSetWriterId\":62541,\"MetaDataVersion\":"
"{\"MajorVersion\":1478393530,\"MinorVersion\":12345},"
"\"SequenceNumber\":4711,\"Payload\":{\"Test\":{\"Type\":5,\"Body\":42},\"Test2\":"
"{\"Type\":13,\"Body\":\"2018-06-05T05:58:36.000Z\"}}}]}");
// when
UA_StatusCode retval = UA_NetworkMessage_decodeJson(&out, &buf);
// then
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
//NetworkMessage
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0].data[0], 'T');
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0].data[1], 'e');
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0].data[2], 's');
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[0].data[3], 't');
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[1].data[0], 'T');
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[1].data[1], 'e');
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[1].data[2], 's');
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[1].data[3], 't');
ck_assert_int_eq(out.payload.dataSetPayload.dataSetMessages[0].data.keyFrameData.fieldNames[1].data[4], '2');
UA_NetworkMessage_deleteMembers(&out);
}
END_TEST
static Suite *testSuite_networkmessage(void) {
Suite *s = suite_create("Built-in Data Types 62541-6 Json");
TCase *tc_json_networkmessage = tcase_create("networkmessage_json");
tcase_add_test(tc_json_networkmessage, UA_PubSub_EnDecode);
tcase_add_test(tc_json_networkmessage, UA_NetworkMessage_oneMessage_twoFields_json_decode);
tcase_add_test(tc_json_networkmessage, UA_NetworkMessage_json_decode);
tcase_add_test(tc_json_networkmessage, UA_Networkmessage_DataSetFieldsNull_json_decode);
tcase_add_test(tc_json_networkmessage, UA_NetworkMessage_fieldNames_json_decode);
suite_add_tcase(s, tc_json_networkmessage);
return s;
}
int main(void) {
int number_failed = 0;
Suite *s;
SRunner *sr;
s = testSuite_networkmessage();
sr = srunner_create(s);
srunner_set_fork_status(sr, CK_NOFORK);
srunner_run_all(sr, CK_NORMAL);
number_failed += srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -24,3 +24,6 @@ BrokerConnectionTransportDataType
BrokerWriterGroupTransportDataType
BrokerDataSetWriterTransportDataType
BrokerWriterGroupTransportType
JsonDataSetWriterMessageDataType
JsonDataSetMessageContentMask
JsonNetworkMessageContentMask