/* Create a signed nonce */ static UA_StatusCode nonceAndSignCreateSessionResponse(UA_Server *server, UA_SecureChannel *channel, UA_Session *session, const UA_CreateSessionRequest *request, UA_CreateSessionResponse *response) { if(channel->securityMode != UA_MESSAGESECURITYMODE_SIGN && channel->securityMode != UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) return UA_STATUSCODE_GOOD; const UA_SecurityPolicy *const securityPolicy = channel->securityPolicy; UA_SignatureData *signatureData = &response->serverSignature; /* Generate Nonce * FIXME: remove magic number??? */ UA_StatusCode retval = UA_SecureChannel_generateNonce(channel, 32, &response->serverNonce); retval |= UA_ByteString_copy(&response->serverNonce, &session->serverNonce); if(retval != UA_STATUSCODE_GOOD) { UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken); return retval; } size_t signatureSize = securityPolicy->asymmetricModule.cryptoModule. getLocalSignatureSize(securityPolicy, channel->channelContext); retval |= UA_ByteString_allocBuffer(&signatureData->signature, signatureSize); if(retval != UA_STATUSCODE_GOOD) { UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken); return retval; } UA_ByteString dataToSign; retval |= UA_ByteString_allocBuffer(&dataToSign, request->clientCertificate.length + request->clientNonce.length); if(retval != UA_STATUSCODE_GOOD) { UA_SignatureData_deleteMembers(signatureData); UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken); return retval; } memcpy(dataToSign.data, request->clientCertificate.data, request->clientCertificate.length); memcpy(dataToSign.data + request->clientCertificate.length, request->clientNonce.data, request->clientNonce.length); retval |= UA_String_copy(&securityPolicy->asymmetricModule.cryptoModule. signatureAlgorithmUri, &signatureData->algorithm); retval |= securityPolicy->asymmetricModule.cryptoModule. sign(securityPolicy, channel->channelContext, &dataToSign, &signatureData->signature); UA_ByteString_deleteMembers(&dataToSign); if(retval != UA_STATUSCODE_GOOD) { UA_SignatureData_deleteMembers(signatureData); UA_SessionManager_removeSession(&server->sessionManager, &session->authenticationToken); } return retval; }
END_TEST START_TEST(encodeShallYieldDecode) { /* floating point types may change the representaton due to several possible NaN values. */ if(_i != UA_TYPES_FLOAT || _i != UA_TYPES_DOUBLE || _i != UA_TYPES_CREATESESSIONREQUEST || _i != UA_TYPES_CREATESESSIONRESPONSE || _i != UA_TYPES_VARIABLEATTRIBUTES || _i != UA_TYPES_READREQUEST || _i != UA_TYPES_MONITORINGPARAMETERS || _i != UA_TYPES_MONITOREDITEMCREATERESULT || _i != UA_TYPES_CREATESUBSCRIPTIONREQUEST || _i != UA_TYPES_CREATESUBSCRIPTIONRESPONSE) return; // given UA_ByteString msg1, msg2; void *obj1 = UA_new(&UA_TYPES[_i]); UA_StatusCode retval = UA_ByteString_allocBuffer(&msg1, 65000); // fixed buf size ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); UA_Byte *pos = msg1.data; const UA_Byte *end = &msg1.data[msg1.length]; retval = UA_encodeBinary(obj1, &UA_TYPES[_i], &pos, &end, NULL, NULL); if(retval != UA_STATUSCODE_GOOD) { UA_delete(obj1, &UA_TYPES[_i]); UA_ByteString_deleteMembers(&msg1); return; } // when void *obj2 = UA_new(&UA_TYPES[_i]); size_t offset = 0; retval = UA_decodeBinary(&msg1, &offset, obj2, &UA_TYPES[_i], 0, NULL); ck_assert_msg(retval == UA_STATUSCODE_GOOD, "could not decode idx=%d,nodeid=%i", _i, UA_TYPES[_i].typeId.identifier.numeric); ck_assert(!memcmp(obj1, obj2, UA_TYPES[_i].memSize)); // bit identical decoding retval = UA_ByteString_allocBuffer(&msg2, 65000); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); pos = msg2.data; end = &msg2.data[msg2.length]; retval = UA_encodeBinary(obj2, &UA_TYPES[_i], &pos, &end, NULL, NULL); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); // then msg1.length = offset; msg2.length = offset; ck_assert_msg(UA_ByteString_equal(&msg1, &msg2) == true, "messages differ idx=%d,nodeid=%i", _i, UA_TYPES[_i].typeId.identifier.numeric); // finally UA_delete(obj1, &UA_TYPES[_i]); UA_delete(obj2, &UA_TYPES[_i]); UA_ByteString_deleteMembers(&msg1); UA_ByteString_deleteMembers(&msg2); }
END_TEST START_TEST(calcSizeBinaryShallBeCorrect) { /* Empty variants (with no type defined) cannot be encoded. This is intentional. Discovery configuration is just a base class and void * */ if(_i == UA_TYPES_VARIANT || _i == UA_TYPES_VARIABLEATTRIBUTES || _i == UA_TYPES_VARIABLETYPEATTRIBUTES || _i == UA_TYPES_FILTEROPERAND || _i == UA_TYPES_MONITORINGFILTER || _i == UA_TYPES_DISCOVERYCONFIGURATION || _i == UA_TYPES_UNION || _i == UA_TYPES_HISTORYREADDETAILS || _i == UA_TYPES_NOTIFICATIONDATA || _i == UA_TYPES_MONITORINGFILTERRESULT) return; void *obj = UA_new(&UA_TYPES[_i]); size_t predicted_size = UA_calcSizeBinary(obj, &UA_TYPES[_i]); ck_assert_int_ne(predicted_size, 0); UA_ByteString msg; UA_StatusCode retval = UA_ByteString_allocBuffer(&msg, predicted_size); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); UA_Byte *pos = msg.data; const UA_Byte *end = &msg.data[msg.length]; retval = UA_encodeBinary(obj, &UA_TYPES[_i], &pos, &end, NULL, NULL); if(retval) printf("%i\n",_i); ck_assert_int_eq(retval, UA_STATUSCODE_GOOD); ck_assert_int_eq((uintptr_t)(pos - msg.data), predicted_size); UA_delete(obj, &UA_TYPES[_i]); UA_ByteString_deleteMembers(&msg); }
END_TEST START_TEST(decodeComplexTypeFromRandomBufferShallSurvive) { // given UA_ByteString msg1; UA_Int32 retval = UA_STATUSCODE_GOOD; UA_Int32 buflen = 256; retval = UA_ByteString_allocBuffer(&msg1, buflen); // fixed size #ifdef _WIN32 srand(42); #else srandom(42); #endif // when for(int n = 0;n < RANDOM_TESTS;n++) { for(UA_Int32 i = 0;i < buflen;i++) { #ifdef _WIN32 UA_UInt32 rnd; rnd = rand(); msg1.data[i] = rnd; #else msg1.data[i] = (UA_Byte)random(); // when #endif } size_t pos = 0; void *obj1 = UA_new(&UA_TYPES[_i]); retval |= UA_decodeBinary(&msg1, &pos, obj1, &UA_TYPES[_i], 0, NULL); UA_delete(obj1, &UA_TYPES[_i]); } // finally UA_ByteString_deleteMembers(&msg1); }
static UA_StatusCode ClientNetworkLayerGetBuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) { if(length > connection->remoteConf.recvBufferSize) return UA_STATUSCODE_BADCOMMUNICATIONERROR; if(connection->state == UA_CONNECTION_CLOSED) return UA_STATUSCODE_BADCONNECTIONCLOSED; return UA_ByteString_allocBuffer(buf, connection->remoteConf.recvBufferSize); }
static void checkSignature(const UA_Server *server, const UA_SecureChannel *channel, UA_Session *session, const UA_ActivateSessionRequest *request, UA_ActivateSessionResponse *response) { if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { const UA_SecurityPolicy *const securityPolicy = channel->securityPolicy; const UA_ByteString *const localCertificate = &securityPolicy->localCertificate; UA_ByteString dataToVerify; UA_StatusCode retval = UA_ByteString_allocBuffer(&dataToVerify, localCertificate->length + session->serverNonce.length); if(retval != UA_STATUSCODE_GOOD) { response->responseHeader.serviceResult = retval; UA_LOG_DEBUG_SESSION(server->config.logger, session, "Failed to allocate buffer for signature verification! %#10x", retval); return; } memcpy(dataToVerify.data, localCertificate->data, localCertificate->length); memcpy(dataToVerify.data + localCertificate->length, session->serverNonce.data, session->serverNonce.length); retval = securityPolicy->asymmetricModule.cryptoModule. verify(securityPolicy, channel->channelContext, &dataToVerify, &request->clientSignature.signature); if(retval != UA_STATUSCODE_GOOD) { response->responseHeader.serviceResult = retval; UA_LOG_DEBUG_SESSION(server->config.logger, session, "Failed to verify the client signature! %#10x", retval); UA_ByteString_deleteMembers(&dataToVerify); return; } retval = UA_SecureChannel_generateNonce(channel, 32, &response->serverNonce); retval |= UA_ByteString_copy(&response->serverNonce, &session->serverNonce); if(retval != UA_STATUSCODE_GOOD) { response->responseHeader.serviceResult = retval; UA_LOG_DEBUG_SESSION(server->config.logger, session, "Failed to generate a new nonce! %#10x", retval); UA_ByteString_deleteMembers(&dataToVerify); return; } UA_ByteString_deleteMembers(&dataToVerify); } }
static UA_StatusCode requestSession(UA_Client *client, UA_UInt32 *requestId) { UA_CreateSessionRequest request; UA_CreateSessionRequest_init(&request); UA_StatusCode retval = UA_STATUSCODE_GOOD; if(client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGN || client->channel.securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) { if(client->channel.localNonce.length != UA_SESSION_LOCALNONCELENGTH) { UA_ByteString_deleteMembers(&client->channel.localNonce); retval = UA_ByteString_allocBuffer(&client->channel.localNonce, UA_SESSION_LOCALNONCELENGTH); if(retval != UA_STATUSCODE_GOOD) return retval; } retval = client->channel.securityPolicy->symmetricModule. generateNonce(client->channel.securityPolicy, &client->channel.localNonce); if(retval != UA_STATUSCODE_GOOD) return retval; } request.requestHeader.requestHandle = ++client->requestHandle; request.requestHeader.timestamp = UA_DateTime_now(); request.requestHeader.timeoutHint = 10000; UA_ByteString_copy(&client->channel.localNonce, &request.clientNonce); request.requestedSessionTimeout = client->config.requestedSessionTimeout; request.maxResponseMessageSize = UA_INT32_MAX; UA_String_copy(&client->config.endpoint.endpointUrl, &request.endpointUrl); UA_ApplicationDescription_copy(&client->config.clientDescription, &request.clientDescription); retval = UA_Client_sendAsyncRequest ( client, &request, &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST], (UA_ClientAsyncServiceCallback) responseSessionCallback, &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE], NULL, requestId); UA_CreateSessionRequest_deleteMembers(&request); client->connectStatus = retval; return client->connectStatus; }
END_TEST #define RANDOM_TESTS 1000 START_TEST(decodeScalarBasicTypeFromRandomBufferShallSucceed) { // given void *obj1 = NULL; UA_ByteString msg1; UA_Int32 retval = UA_STATUSCODE_GOOD; UA_Int32 buflen = 256; retval = UA_ByteString_allocBuffer(&msg1, buflen); // fixed size #ifdef _WIN32 srand(42); #else srandom(42); #endif for(int n = 0;n < RANDOM_TESTS;n++) { for(UA_Int32 i = 0;i < buflen;i++) { #ifdef _WIN32 UA_UInt32 rnd; rnd = rand(); msg1.data[i] = rnd; #else msg1.data[i] = (UA_Byte)random(); // when #endif } size_t pos = 0; obj1 = UA_new(&UA_TYPES[_i]); retval |= UA_decodeBinary(&msg1, &pos, obj1, &UA_TYPES[_i], 0, NULL); //then ck_assert_msg(retval == UA_STATUSCODE_GOOD, "Decoding %d from random buffer", UA_TYPES[_i].typeId.identifier.numeric); // finally UA_delete(obj1, &UA_TYPES[_i]); } UA_ByteString_deleteMembers(&msg1); }
END_TEST START_TEST(decodeShallFailWithTruncatedBufferButSurvive) { //Skip test for void* if (_i == UA_TYPES_DISCOVERYCONFIGURATION || _i == UA_TYPES_FILTEROPERAND || _i == UA_TYPES_MONITORINGFILTER || _i == UA_TYPES_UNION || _i == UA_TYPES_HISTORYREADDETAILS || _i == UA_TYPES_NOTIFICATIONDATA || _i == UA_TYPES_MONITORINGFILTERRESULT) return; // given UA_ByteString msg1; void *obj1 = UA_new(&UA_TYPES[_i]); UA_StatusCode retval = UA_ByteString_allocBuffer(&msg1, 65000); // fixed buf size UA_Byte *pos = msg1.data; const UA_Byte *end = &msg1.data[msg1.length]; retval |= UA_encodeBinary(obj1, &UA_TYPES[_i], &pos, &end, NULL, NULL); UA_delete(obj1, &UA_TYPES[_i]); if(retval != UA_STATUSCODE_GOOD) { UA_ByteString_deleteMembers(&msg1); return; // e.g. variants cannot be encoded after an init without failing (no datatype set) } size_t half = (uintptr_t)(pos - msg1.data) / 2; msg1.length = half; // when void *obj2 = UA_new(&UA_TYPES[_i]); size_t offset = 0; retval = UA_decodeBinary(&msg1, &offset, obj2, &UA_TYPES[_i], 0, NULL); ck_assert_int_ne(retval, UA_STATUSCODE_GOOD); UA_delete(obj2, &UA_TYPES[_i]); UA_ByteString_deleteMembers(&msg1); }
static UA_StatusCode ServerNetworkLayerGetSendBuffer(UA_Connection *connection, size_t length, UA_ByteString *buf) { if(length > connection->remoteConf.recvBufferSize) return UA_STATUSCODE_BADCOMMUNICATIONERROR; return UA_ByteString_allocBuffer(buf, length); }
static UA_StatusCode subscriberListen(UA_PubSubChannel *psc) { UA_ByteString buffer; UA_StatusCode retval = UA_ByteString_allocBuffer(&buffer, 512); if(retval != UA_STATUSCODE_GOOD) { UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Message buffer allocation failed!"); return retval; } /* Receive the message. Blocks for 100ms */ retval = psc->receive(psc, &buffer, NULL, 100); if(retval != UA_STATUSCODE_GOOD || buffer.length == 0) { /* Workaround!! Reset buffer length. Receive can set the length to zero. * Then the buffer is not deleted because no memory allocation is * assumed. * TODO: Return an error code in 'receive' instead of setting the buf * length to zero. */ buffer.length = 512; UA_ByteString_clear(&buffer); return UA_STATUSCODE_GOOD; } /* Decode the message */ UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Message length: %lu", (unsigned long) buffer.length); UA_NetworkMessage networkMessage; memset(&networkMessage, 0, sizeof(UA_NetworkMessage)); size_t currentPosition = 0; UA_NetworkMessage_decodeBinary(&buffer, ¤tPosition, &networkMessage); UA_ByteString_clear(&buffer); /* Is this the correct message type? */ if(networkMessage.networkMessageType != UA_NETWORKMESSAGE_DATASET) goto cleanup; /* At least one DataSetMessage in the NetworkMessage? */ if(networkMessage.payloadHeaderEnabled && networkMessage.payloadHeader.dataSetPayloadHeader.count < 1) goto cleanup; /* Is this a KeyFrame-DataSetMessage? */ for(size_t j = 0; j < networkMessage.payloadHeader.dataSetPayloadHeader.count; j++) { UA_DataSetMessage *dsm = &networkMessage.payload.dataSetPayload.dataSetMessages[j]; if(dsm->header.dataSetMessageType != UA_DATASETMESSAGE_DATAKEYFRAME) continue; /* Loop over the fields and print well-known content types */ for(int i = 0; i < dsm->data.keyFrameData.fieldCount; i++) { const UA_DataType *currentType = dsm->data.keyFrameData.dataSetFields[i].value.type; if(currentType == &UA_TYPES[UA_TYPES_BYTE]) { UA_Byte value = *(UA_Byte *)dsm->data.keyFrameData.dataSetFields[i].value.data; UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Message content: [Byte] \tReceived data: %i", value); } else if (currentType == &UA_TYPES[UA_TYPES_DATETIME]) { UA_DateTime value = *(UA_DateTime *)dsm->data.keyFrameData.dataSetFields[i].value.data; UA_DateTimeStruct receivedTime = UA_DateTime_toStruct(value); UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Message content: [DateTime] \t" "Received date: %02i-%02i-%02i Received time: %02i:%02i:%02i", receivedTime.year, receivedTime.month, receivedTime.day, receivedTime.hour, receivedTime.min, receivedTime.sec); } } } cleanup: UA_NetworkMessage_clear(&networkMessage); return retval; }
int main(int argc, char** argv) { signal(SIGINT, stopHandler); /* catches ctrl-c */ UA_ServerConfig config = UA_ServerConfig_standard; UA_ServerNetworkLayer nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, 16664, logger); config.logger = Logger_Stdout; config.networkLayers = &nl; config.networkLayersSize = 1; UA_Server *server = UA_Server_new(config); /* add a variable node to the address space */ UA_VariableAttributes attr; UA_VariableAttributes_init(&attr); UA_Int32 myInteger = 42; UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]); attr.description = UA_LOCALIZEDTEXT("en_US","the answer"); attr.displayName = UA_LOCALIZEDTEXT("en_US","the answer"); UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer"); UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer"); UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER); UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES); UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId, parentReferenceNodeId, myIntegerName, UA_NODEID_NULL, attr, NULL, NULL); UA_ReadRequest request; UA_ReadRequest_init(&request); UA_ReadValueId rvi; rvi.nodeId = myIntegerNodeId; rvi.attributeId = UA_ATTRIBUTEID_VALUE; rvi.indexRange = UA_STRING_NULL; rvi.dataEncoding = UA_QUALIFIEDNAME(0, "DefaultBinary"); request.timestampsToReturn = UA_TIMESTAMPSTORETURN_NEITHER; request.nodesToReadSize = 1; request.nodesToRead = &rvi; UA_StatusCode retval = UA_STATUSCODE_GOOD; UA_ByteString request_msg; retval |= UA_ByteString_allocBuffer(&request_msg, 1000); UA_ByteString response_msg; retval |= UA_ByteString_allocBuffer(&response_msg, 1000); size_t offset = 0; retval |= UA_encodeBinary(&request, &UA_TYPES[UA_TYPES_READREQUEST], &request_msg, &offset); clock_t begin, end; begin = clock(); UA_ReadRequest rq; UA_ReadResponse rr; for(int i = 0; i < 900000; i++) { offset = 0; retval |= UA_decodeBinary(&request_msg, &offset, &rq, &UA_TYPES[UA_TYPES_READREQUEST]); UA_ReadResponse_init(&rr); Service_Read(server, &adminSession, &rq, &rr); offset = 0; retval |= UA_encodeBinary(&rr, &UA_TYPES[UA_TYPES_READRESPONSE], &response_msg, &offset); UA_ReadRequest_deleteMembers(&rq); UA_ReadResponse_deleteMembers(&rr); } end = clock(); double time_spent = (double)(end - begin) / CLOCKS_PER_SEC; printf("duration was %f s\n", time_spent); printf("retval is %i\n", retval); UA_ByteString_deleteMembers(&request_msg); UA_ByteString_deleteMembers(&response_msg); retval |= UA_Server_run(server, &running); UA_Server_delete(server); nl.deleteMembers(&nl); return (int)retval; }
/** * Initialize a client instance which is used for calling the registerServer service. * If the given endpoint has securityMode NONE, a client with default configuration * is returned. * If it is using SignAndEncrypt, the client certificates must be provided as a * command line argument and then the client is initialized using these certificates. * @param endpointRegister The remote endpoint where this server should register * @param argc from the main method * @param argv from the main method * @return NULL or the initialized non-connected client */ static UA_Client *getRegisterClient(UA_EndpointDescription *endpointRegister, int argc, char **argv) { if (endpointRegister->securityMode == UA_MESSAGESECURITYMODE_NONE) { UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Using LDS endpoint with security None"); return UA_Client_new(UA_ClientConfig_default); } #ifdef UA_ENABLE_ENCRYPTION if (endpointRegister->securityMode == UA_MESSAGESECURITYMODE_SIGN) { UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "LDS endpoint which only supports Sign is currently not supported"); return NULL; } UA_Client *clientRegister; UA_LOG_INFO(logger, UA_LOGCATEGORY_SERVER, "Using LDS endpoint with security SignAndEncrypt"); UA_ByteString certificate = UA_BYTESTRING_NULL; UA_ByteString privateKey = UA_BYTESTRING_NULL; UA_ByteString *trustList = NULL; size_t trustListSize = 0; UA_ByteString *revocationList = NULL; size_t revocationListSize = 0; if (argc < 3) { UA_LOG_FATAL(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "The Certificate and key is missing." "The required arguments are " "<client-certificate.der> <client-private-key.der> " "[<trustlist1.crl>, ...]"); return NULL; } certificate = loadFile(argv[1]); privateKey = loadFile(argv[2]); /* Load the trustList. Load revocationList is not supported now */ if (argc > 3) { trustListSize = (size_t) argc - 3; UA_StatusCode retval = UA_ByteString_allocBuffer(trustList, trustListSize); if (retval != UA_STATUSCODE_GOOD) { UA_ByteString_deleteMembers(&certificate); UA_ByteString_deleteMembers(&privateKey); return NULL; } for (size_t trustListCount = 0; trustListCount < trustListSize; trustListCount++) { trustList[trustListCount] = loadFile(argv[trustListCount + 3]); } } /* Secure client initialization */ clientRegister = UA_Client_secure_new(UA_ClientConfig_default, certificate, privateKey, &endpointRegister->serverCertificate, trustList, trustListSize, revocationList, revocationListSize, UA_SecurityPolicy_Basic128Rsa15); UA_ByteString_deleteMembers(&certificate); UA_ByteString_deleteMembers(&privateKey); for (size_t deleteCount = 0; deleteCount < trustListSize; deleteCount++) { UA_ByteString_deleteMembers(&trustList[deleteCount]); } return clientRegister; #else return NULL; #endif }
/* When a change is detected, encoding contains the heap-allocated binary * encoded value. The default for changed is false. */ static UA_StatusCode detectValueChangeWithFilter(UA_Server *server, UA_MonitoredItem *mon, UA_DataValue *value, UA_ByteString *encoding, UA_Boolean *changed) { if(UA_DataType_isNumeric(value->value.type) && (mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE || mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) { if(mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_ABSOLUTE) { if(!updateNeededForFilteredValue(&value->value, &mon->lastValue, mon->filter.dataChangeFilter.deadbandValue)) return UA_STATUSCODE_GOOD; } #ifdef UA_ENABLE_DA else if(mon->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_PERCENT) { UA_QualifiedName qn = UA_QUALIFIEDNAME(0, "EURange"); UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, mon->monitoredNodeId, 1, &qn); if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) { //if branch is not entried, property has been found UA_BrowsePathResult_deleteMembers(&bpr); return UA_STATUSCODE_GOOD; } const UA_VariableNode* node = (const UA_VariableNode*) UA_Nodestore_getNode(server->nsCtx, &bpr.targets->targetId.nodeId); UA_Range* euRange = (UA_Range*) node->value.data.value.value.data; if(!updateNeededForFilteredPercentValue(&value->value, &mon->lastValue, mon->filter.dataChangeFilter.deadbandValue, euRange)) { if(!updateNeededForStatusCode(value, mon)) //when same value, but different status code is written return UA_STATUSCODE_GOOD; } } #endif } /* Stack-allocate some memory for the value encoding. We might heap-allocate * more memory if needed. This is just enough for scalars and small * structures. */ UA_STACKARRAY(UA_Byte, stackValueEncoding, UA_VALUENCODING_MAXSTACK); UA_ByteString valueEncoding; valueEncoding.data = stackValueEncoding; valueEncoding.length = UA_VALUENCODING_MAXSTACK; /* Encode the value */ UA_Byte *bufPos = valueEncoding.data; const UA_Byte *bufEnd = &valueEncoding.data[valueEncoding.length]; UA_StatusCode retval = UA_encodeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE], &bufPos, &bufEnd, NULL, NULL); if(retval == UA_STATUSCODE_BADENCODINGERROR) { size_t binsize = UA_calcSizeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE]); if(binsize == 0) return UA_STATUSCODE_BADENCODINGERROR; if(binsize > UA_VALUENCODING_MAXSTACK) { retval = UA_ByteString_allocBuffer(&valueEncoding, binsize); if(retval == UA_STATUSCODE_GOOD) { bufPos = valueEncoding.data; bufEnd = &valueEncoding.data[valueEncoding.length]; retval = UA_encodeBinary(value, &UA_TYPES[UA_TYPES_DATAVALUE], &bufPos, &bufEnd, NULL, NULL); } } } if(retval != UA_STATUSCODE_GOOD) { if(valueEncoding.data != stackValueEncoding) UA_ByteString_deleteMembers(&valueEncoding); return retval; } /* Has the value changed? */ valueEncoding.length = (uintptr_t)bufPos - (uintptr_t)valueEncoding.data; *changed = (!mon->lastSampledValue.data || !UA_String_equal(&valueEncoding, &mon->lastSampledValue)); /* No change */ if(!(*changed)) { if(valueEncoding.data != stackValueEncoding) UA_ByteString_deleteMembers(&valueEncoding); return UA_STATUSCODE_GOOD; } /* Change detected. Copy encoding on the heap if necessary. */ if(valueEncoding.data == stackValueEncoding) return UA_ByteString_copy(&valueEncoding, encoding); *encoding = valueEncoding; return UA_STATUSCODE_GOOD; }