<commit_before>/* 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 2017-2020 (c) Fraunhofer IOSB (Author: <NAME>)
 *    Copyright 2017 (c) <NAME>, fortiss GmbH
 *    Copyright 2018 (c) <NAME>, fortiss GmbH
 *    Copyright 2018 (c) <NAME>, Blue Time Concept SA
 *    Copyright 2018 (c) <NAME>, Root-Core
 */

#include "ua_server_internal.h"
#include "ua_subscription.h"
#include "ua_types_encoding_binary.h"

#ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */

#define UA_VALUENCODING_MAXSTACK 512

/* Convert to double first. We might loose differences for large Int64 that
 * cannot be precisely expressed as double. */
static UA_Boolean
outOfDeadBand(const void *data1, const void *data2,
              const UA_DataType *type, const UA_Double deadband) {
    UA_Double v;
    if(type == &UA_TYPES[UA_TYPES_BOOLEAN]) {
        v = (UA_Double)*(const UA_Boolean*)data1 - (UA_Double)*(const UA_Boolean*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_SBYTE]) {
        v = (UA_Double)*(const UA_SByte*)data1 - (UA_Double)*(const UA_SByte*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_BYTE]) {
        v = (UA_Double)*(const UA_Byte*)data1 - (UA_Double)*(const UA_Byte*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_INT16]) {
        v = (UA_Double)*(const UA_Int16*)data1 - (UA_Double)*(const UA_Int16*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_UINT16]) {
        v = (UA_Double)*(const UA_UInt16*)data1 - (UA_Double)*(const UA_UInt16*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_INT32]) {
        v = (UA_Double)*(const UA_Int32*)data1 - (UA_Double)*(const UA_Int32*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_UINT32]) {
        v = (UA_Double)*(const UA_UInt32*)data1 - (UA_Double)*(const UA_UInt32*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_INT64]) {
        v = (UA_Double)*(const UA_Int64*)data1 - (UA_Double)*(const UA_Int64*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_UINT64]) {
        v = (UA_Double)*(const UA_UInt64*)data1 - (UA_Double)*(const UA_UInt64*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_FLOAT]) {
        v = (UA_Double)*(const UA_Float*)data1 - (UA_Double)*(const UA_Float*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_DOUBLE]) {
        v = (UA_Double)*(const UA_Double*)data1 - (UA_Double)*(const UA_Double*)data2;
    } else {
        return false;
    }
    if(v < 0.0)
        v = -v;
    return (v > deadband);
}

static UA_Boolean
updateNeededForFilteredValue(const UA_Variant *value, const UA_Variant *oldValue,
                             const UA_Double deadbandValue) {
    if(value->arrayLength != oldValue->arrayLength)
        return true;

    if(value->type != oldValue->type)
        return true;

    size_t length = 1;
    if(!UA_Variant_isScalar(value))
        length = value->arrayLength;
    uintptr_t data = (uintptr_t)value->data;
    for(size_t i = 0; i < length; ++i) {
        if(outOfDeadBand((const void*)data, oldValue->data, value->type, deadbandValue))
            return true;
        data += value->type->memSize;
    }

    return false;
}

/* 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_Session *session, UA_MonitoredItem *mon,
                            UA_DataValue *value, UA_ByteString *encoding,
                            UA_Boolean *changed) {
    if(!value->value.type) {
        *changed = !(UA_ByteString_equal(encoding, &mon->lastSampledValue));
        return UA_STATUSCODE_GOOD;
    }

    /* Test absolute deadband */
    if(UA_DataType_isNumeric(value->value.type) &&
       mon->parameters.filter.content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGEFILTER]) {
        UA_DataChangeFilter *filter = (UA_DataChangeFilter*)
            mon->parameters.filter.content.decoded.data;
        if(filter->deadbandType == UA_DEADBANDTYPE_ABSOLUTE &&
           (filter->trigger == UA_DATACHANGETRIGGER_STATUSVALUE ||
            filter->trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) {
            *changed = updateNeededForFilteredValue(&value->value,
                                                    &mon->lastValue.value,
                                                    filter->deadbandValue);
            return UA_STATUSCODE_GOOD;
        }
    }

    /* 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_Byte stackValueEncoding[UA_VALUENCODING_MAXSTACK];
    UA_ByteString valueEncoding;
    valueEncoding.data = stackValueEncoding;
    valueEncoding.length = UA_VALUENCODING_MAXSTACK;
    UA_Byte *bufPos = stackValueEncoding;
    const UA_Byte *bufEnd = &stackValueEncoding[UA_VALUENCODING_MAXSTACK];<commit_msg>fix(core): MonitoredItem sampling callback handle Bad_StatusCodes (#4610)<commit_after>/* 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 2017-2020 (c) Fraunhofer IOSB (Author: <NAME>)
 *    Copyright 2017 (c) <NAME>, fortiss GmbH
 *    Copyright 2018 (c) <NAME>, fortiss GmbH
 *    Copyright 2018 (c) <NAME>, Blue Time Concept SA
 *    Copyright 2018 (c) <NAME>, Root-Core
 */

#include "ua_server_internal.h"
#include "ua_subscription.h"
#include "ua_types_encoding_binary.h"

#ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */

#define UA_VALUENCODING_MAXSTACK 512

/* Convert to double first. We might loose differences for large Int64 that
 * cannot be precisely expressed as double. */
static UA_Boolean
outOfDeadBand(const void *data1, const void *data2,
              const UA_DataType *type, const UA_Double deadband) {
    UA_Double v;
    if(type == &UA_TYPES[UA_TYPES_BOOLEAN]) {
        v = (UA_Double)*(const UA_Boolean*)data1 - (UA_Double)*(const UA_Boolean*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_SBYTE]) {
        v = (UA_Double)*(const UA_SByte*)data1 - (UA_Double)*(const UA_SByte*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_BYTE]) {
        v = (UA_Double)*(const UA_Byte*)data1 - (UA_Double)*(const UA_Byte*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_INT16]) {
        v = (UA_Double)*(const UA_Int16*)data1 - (UA_Double)*(const UA_Int16*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_UINT16]) {
        v = (UA_Double)*(const UA_UInt16*)data1 - (UA_Double)*(const UA_UInt16*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_INT32]) {
        v = (UA_Double)*(const UA_Int32*)data1 - (UA_Double)*(const UA_Int32*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_UINT32]) {
        v = (UA_Double)*(const UA_UInt32*)data1 - (UA_Double)*(const UA_UInt32*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_INT64]) {
        v = (UA_Double)*(const UA_Int64*)data1 - (UA_Double)*(const UA_Int64*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_UINT64]) {
        v = (UA_Double)*(const UA_UInt64*)data1 - (UA_Double)*(const UA_UInt64*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_FLOAT]) {
        v = (UA_Double)*(const UA_Float*)data1 - (UA_Double)*(const UA_Float*)data2;
    } else if(type == &UA_TYPES[UA_TYPES_DOUBLE]) {
        v = (UA_Double)*(const UA_Double*)data1 - (UA_Double)*(const UA_Double*)data2;
    } else {
        return false;
    }
    if(v < 0.0)
        v = -v;
    return (v > deadband);
}

static UA_Boolean
updateNeededForFilteredValue(const UA_Variant *value, const UA_Variant *oldValue,
                             const UA_Double deadbandValue) {
    if(value->arrayLength != oldValue->arrayLength)
        return true;

    if(value->type != oldValue->type)
        return true;

    size_t length = 1;
    if(!UA_Variant_isScalar(value))
        length = value->arrayLength;
    uintptr_t data = (uintptr_t)value->data;
    for(size_t i = 0; i < length; ++i) {
        if(outOfDeadBand((const void*)data, oldValue->data, value->type, deadbandValue))
            return true;
        data += value->type->memSize;
    }

    return false;
}

/* 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_Session *session, UA_MonitoredItem *mon,
                            UA_DataValue *value, UA_ByteString *encoding,
                            UA_Boolean *changed) {
    /* Handle status change for instance Bad_NodeIdUnknown */
    if(value->hasStatus != mon->lastValue.hasStatus ||
       value->status != mon->lastValue.status) {
        *changed = true;
        return UA_STATUSCODE_GOOD;
    }

    /* Test absolute deadband */
    if(value->value.type != NULL &&
       UA_DataType_isNumeric(value->value.type) &&
       mon->parameters.filter.content.decoded.type == &UA_TYPES[UA_TYPES_DATACHANGEFILTER]) {
        UA_DataChangeFilter *filter = (UA_DataChangeFilter*)
            mon->parameters.filter.content.decoded.data;
        if(filter->deadbandType == UA_DEADBANDTYPE_ABSOLUTE &&
           (filter->trigger == UA_DATACHANGETRIGGER_STATUSVALUE ||
            filter->trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) {
            *changed = updateNeededForFilteredValue(&value->value,
                                                    &mon->lastValue.value,
                                                    filter->deadbandValue);
            return UA_STATUSCODE_GOOD;
        }
    }

    /* 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_Byte stackValueEncoding[UA_VALUENCODING_MAXSTACK];
    UA_ByteString valueEncoding;
    valueEncoding.data = stackValueEncoding;
    valueEncoding.length = UA_VALUENCODING_MAXSTACK;
    UA_Byte *bufPos = stackValueEncoding;
    const UA_Byte *bufEnd = &stackValueEncoding[UA_VALUENCODING_MAXSTACK];
{'fraction_non_alphanumeric': 0.1195758884743766, 'fraction_numerical': 0.019438444924406047, 'mean_word_length': 3.1176232821341956, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 60, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3939892', 'n_tokens_mistral': 3566, 'n_tokens_neox': 3408, 'n_words': 725}