Kaynağa Gözat

Support of PercentDeadband feature

Nik625 5 yıl önce
ebeveyn
işleme
81958f67ca

+ 6 - 0
CMakeLists.txt

@@ -194,6 +194,9 @@ endif()
 option(UA_ENABLE_PUBSUB "Enable publish/subscribe" OFF)
 mark_as_advanced(UA_ENABLE_PUBSUB)
 
+option(UA_ENABLE_DA "Enable data access" ON)
+mark_as_advanced(UA_ENABLE_DA)
+
 option(UA_ENABLE_PUBSUB_ETH_UADP "Enable publish/subscribe UADP over Ethernet" OFF)
 mark_as_advanced(UA_ENABLE_PUBSUB_ETH_UADP)
 if(UA_ENABLE_PUBSUB_ETH_UADP)
@@ -735,6 +738,9 @@ else()
             set(UA_FILE_NSPUBSUB ${PROJECT_SOURCE_DIR}/tools/schema/Opc.Ua.NodeSet2.PubSubMinimal.xml)
         endif()
     endif()
+	if(UA_ENABLE_DA)
+		list(APPEND UA_FILE_DATATYPES ${PROJECT_SOURCE_DIR}/tools/schema/datatypes_dataaccess.txt)
+    endif()
 endif()
 
 if(NOT EXISTS "${UA_FILE_NS0}")

+ 1 - 0
include/ua_config.h.in

@@ -31,6 +31,7 @@
 #cmakedefine UA_ENABLE_PUBSUB_DELTAFRAMES
 #cmakedefine UA_ENABLE_PUBSUB_INFORMATIONMODEL
 #cmakedefine UA_ENABLE_PUBSUB_INFORMATIONMODEL_METHODS
+#cmakedefine UA_ENABLE_DA
 #cmakedefine UA_ENABLE_ENCRYPTION
 #cmakedefine UA_ENABLE_HISTORIZING
 #cmakedefine UA_ENABLE_EXPERIMENTAL_HISTORIZING

+ 4 - 0
src/server/ua_subscription.h

@@ -127,6 +127,10 @@ struct UA_MonitoredItem {
 #ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
     UA_MonitoredItem *next;
 #endif
+
+#ifdef UA_ENABLE_DA
+    UA_StatusCode lastStatus;
+#endif
 };
 
 void UA_MonitoredItem_init(UA_MonitoredItem *mon, UA_Subscription *sub);

+ 155 - 18
src/server/ua_subscription_datachange.c

@@ -13,6 +13,10 @@
 #include "ua_subscription.h"
 #include "ua_types_encoding_binary.h"
 
+#ifdef UA_ENABLE_DA
+#include <math.h> // fabs
+#endif
+
 #ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
 
 #define UA_VALUENCODING_MAXSTACK 512
@@ -70,6 +74,65 @@ outOfDeadBand(const void *data1, const void *data2, const size_t arrayPos,
     return true;
 }
 
+#ifdef UA_ENABLE_DA
+static UA_INLINE UA_Boolean
+outOfPercentDeadBand(const void *data1, const void *data2, const size_t index,
+                     const UA_DataType *type, const UA_Double deadbandValue, UA_Range* range) {
+    if(type == &UA_TYPES[UA_TYPES_SBYTE]) {
+        if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_SByte*)data1)[index],
+                                         ((const UA_SByte*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
+           *(const UA_SByte*)data1 > range->high)
+            return false;
+    } else if(type == &UA_TYPES[UA_TYPES_BYTE]) {
+        if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Byte*)data1)[index],
+                                         ((const UA_Byte*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
+           *(const UA_Byte*)data1 > range->high)
+            return false;
+    } else if(type == &UA_TYPES[UA_TYPES_INT16]) {
+        if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int16*)data1)[index],
+                                         ((const UA_Int16*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
+           *(const UA_Int16*)data1 > range->high)
+            return false;
+    } else if(type == &UA_TYPES[UA_TYPES_UINT16]) {
+        if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt16*)data1)[index],
+                                         ((const UA_UInt16*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
+           *(const UA_UInt16*)data1 > range->high)
+            return false;
+    } else if(type == &UA_TYPES[UA_TYPES_INT32]) {
+        if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int32*)data1)[index],
+                                         ((const UA_Int32*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
+           *(const UA_Int32*)data1 > range->high)
+            return false;
+    } else if(type == &UA_TYPES[UA_TYPES_UINT32]) {
+        if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt32*)data1)[index],
+                                         ((const UA_UInt32*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
+           *(const UA_UInt32*)data1 > range->high)
+            return false;
+    } else if(type == &UA_TYPES[UA_TYPES_INT64]) {
+        if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Int64*)data1)[index],
+                                         ((const UA_Int64*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
+           *(const UA_Int64*)data1 > range->high)
+            return false;
+    } else if(type == &UA_TYPES[UA_TYPES_UINT64]) {
+        if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_UInt64*)data1)[index],
+                                         ((const UA_UInt64*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
+           *(const UA_UInt64*)data1 > range->high)
+            return false;
+    } else if(type == &UA_TYPES[UA_TYPES_FLOAT]) {
+        if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Float*)data1)[index],
+                                         ((const UA_Float*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
+           *(const UA_Float*)data1 > range->high)
+            return false;
+    } else if(type == &UA_TYPES[UA_TYPES_DOUBLE]) {
+        if(ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_Double*)data1)[index],
+                                         ((const UA_Double*)data2)[index]) <= (deadbandValue/100.0)*(fabs(range->high - range->low)) ||
+           *(const UA_Double*)data1 > range->high)
+            return false;
+    }
+    return true;
+}
+#endif /* UA_ENABLE_DA */
+
 static UA_INLINE UA_Boolean
 updateNeededForFilteredValue(const UA_Variant *value, const UA_Variant *oldValue,
                              const UA_Double deadbandValue) {
@@ -90,10 +153,49 @@ updateNeededForFilteredValue(const UA_Variant *value, const UA_Variant *oldValue
     return false;
 }
 
+#ifdef UA_ENABLE_DA
+static UA_INLINE UA_Boolean
+updateNeededForFilteredPercentValue(const UA_Variant *value, const UA_Variant *oldValue,
+                                    const UA_Double deadbandValue, UA_Range* euRange) {
+    if(value->arrayLength != oldValue->arrayLength)
+        return true;
+
+    if(value->type != oldValue->type)
+        return true;
+
+    if (UA_Variant_isScalar(value)) {
+        return outOfPercentDeadBand(value->data, oldValue->data, 0, value->type, deadbandValue, euRange);
+    } else {
+        for (size_t i = 0; i < value->arrayLength; ++i) {
+            if (outOfPercentDeadBand(value->data, oldValue->data, i, value->type, deadbandValue, euRange))
+                return true;
+        }
+    }
+    return false;
+}
+
+static UA_Boolean
+updateNeededForStatusCode(const UA_DataValue *value, const UA_MonitoredItem *mon) {
+    if (UA_Variant_isScalar(&value->value)) {
+        if(value->status != mon->lastStatus)
+          return true;
+    }
+    return false;
+}
+#endif
+
+
 /* When a change is detected, encoding contains the heap-allocated binary encoded value */
 static UA_Boolean
 detectValueChangeWithFilter(UA_Server *server, UA_Subscription *sub, UA_MonitoredItem *mon,
                             UA_DataValue *value, UA_ByteString *encoding) {
+
+#ifdef UA_ENABLE_DA
+  	if(mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUS) {
+      	if(!updateNeededForStatusCode(value, mon))
+          	return false;
+  	}
+#endif
     if(UA_DataType_isNumeric(value->value.type) &&
        (mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE ||
         mon->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) {
@@ -102,12 +204,23 @@ detectValueChangeWithFilter(UA_Server *server, UA_Subscription *sub, UA_Monitore
                                              mon->filter.dataChangeFilter.deadbandValue))
                 return false;
         }
-        /* else if (mon->filter.deadbandType == UA_DEADBANDTYPE_PERCENT) {
-            // TODO where do this EURange come from ?
-            UA_Double deadbandValue = fabs(mon->filter.deadbandValue * (EURange.high-EURange.low));
-            if (!updateNeededForFilteredValue(value->value, mon->lastValue, deadbandValue))
-                return false;
-        }*/
+#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 false;
+			}
+			const UA_VariableNode* node = (const UA_VariableNode*) UA_Nodestore_get(server, &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 false;
+			}
+		}
+#endif
     }
 
     /* Stack-allocate some memory for the value encoding. We might heap-allocate
@@ -227,15 +340,33 @@ sampleCallbackWithValue(UA_Server *server, UA_MonitoredItem *monitoredItem,
             return false;
         }
 
-        if(value->value.storageType == UA_VARIANT_DATA) {
-            newNotification->data.value = *value; /* Move the value to the notification */
-            storedValue = true;
-        } else { /* => (value->value.storageType == UA_VARIANT_DATA_NODELETE) */
-            UA_StatusCode retval = UA_DataValue_copy(value, &newNotification->data.value);
-            if(retval != UA_STATUSCODE_GOOD) {
-                UA_ByteString_deleteMembers(&binValueEncoding);
-                UA_free(newNotification);
-                return false;
+        if(monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUS){
+            if(value->value.storageType == UA_VARIANT_DATA) {
+                newNotification->data.value.hasStatus = true;
+                UA_StatusCode_copy(&value->status, &newNotification->data.value.status);
+                storedValue = true;
+            }
+            else { /* => (value->value.storageType == UA_VARIANT_DATA_NODELETE) */
+                UA_StatusCode retval = UA_DataValue_copy(value, &newNotification->data.value);
+                if(retval != UA_STATUSCODE_GOOD) {
+                    UA_ByteString_deleteMembers(&binValueEncoding);
+                    UA_free(newNotification);
+                    return false;
+                }
+            }
+        }
+        else if(monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE ||
+                monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP) {
+            if(value->value.storageType == UA_VARIANT_DATA) {
+                newNotification->data.value = *value; /* Move the value to the notification */
+                storedValue = true;
+            } else { /* => (value->value.storageType == UA_VARIANT_DATA_NODELETE) */
+                UA_StatusCode retval = UA_DataValue_copy(value, &newNotification->data.value);
+                if(retval != UA_STATUSCODE_GOOD) {
+                    UA_ByteString_deleteMembers(&binValueEncoding);
+                    UA_free(newNotification);
+                    return false;
+                }
             }
         }
 
@@ -281,12 +412,18 @@ sampleCallbackWithValue(UA_Server *server, UA_MonitoredItem *monitoredItem,
 
     /* Store the value for filter comparison (we don't want to decode
      * lastSampledValue in every iteration) */
-    if((monitoredItem->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_PERCENT ||
-        monitoredItem->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_ABSOLUTE) &&
-       (monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE ||
+    if((monitoredItem->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_NONE ||
+        monitoredItem->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_ABSOLUTE ||
+        monitoredItem->filter.dataChangeFilter.deadbandType == UA_DEADBANDTYPE_PERCENT) &&
+       (monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUS ||
+        monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUE ||
         monitoredItem->filter.dataChangeFilter.trigger == UA_DATACHANGETRIGGER_STATUSVALUETIMESTAMP)) {
         UA_Variant_deleteMembers(&monitoredItem->lastValue);
         UA_Variant_copy(&value->value, &monitoredItem->lastValue);
+#ifdef UA_ENABLE_DA
+        UA_StatusCode_deleteMembers(&monitoredItem->lastStatus);
+        UA_StatusCode_copy(&value->status, &monitoredItem->lastStatus);
+#endif
         /* Don't test the return code here. If this fails, lastValue is empty
          * and a notification will be forced for the next deadband comparison. */
     }

+ 2 - 0
tools/appveyor/build.ps1

@@ -60,6 +60,7 @@ try {
     & cmake $cmake_cnf `
             -DCMAKE_BUILD_TYPE=RelWithDebInfo `
             -DUA_BUILD_EXAMPLES:BOOL=ON `
+            -DUA_ENABLE_DA:BOOL=ON `
             -DUA_ENABLE_JSON_ENCODING:BOOL=ON `
             -DUA_ENABLE_PUBSUB:BOOL=ON `
             -DUA_ENABLE_PUBSUB_DELTAFRAMES:BOOL=ON `
@@ -137,6 +138,7 @@ try {
                 -DCMAKE_BUILD_TYPE=Debug `
                 -DUA_BUILD_EXAMPLES=OFF `
                 -DUA_BUILD_UNIT_TESTS=ON `
+                -DUA_ENABLE_DA=ON `
                 -DUA_ENABLE_DISCOVERY=ON `
                 -DUA_ENABLE_DISCOVERY_MULTICAST=ON `
                 -DUA_ENABLE_ENCRYPTION:BOOL=$build_encryption `

+ 1 - 0
tools/schema/datatypes_dataaccess.txt

@@ -0,0 +1 @@
+Range

+ 1 - 1
tools/schema/datatypes_minimal.txt

@@ -115,4 +115,4 @@ UtcTime
 LocaleId
 RedundancySupport
 ServerDiagnosticsSummaryDataType
-EnumValueType
+EnumValueType

+ 2 - 0
tools/travis/travis_linux_script.sh

@@ -373,6 +373,7 @@ else
         -DUA_BUILD_EXAMPLES=ON \
         -DUA_BUILD_UNIT_TESTS=ON \
         -DUA_ENABLE_COVERAGE=OFF \
+        -DUA_ENABLE_DA=ON \
         -DUA_ENABLE_DISCOVERY=ON \
         -DUA_ENABLE_DISCOVERY_MULTICAST=ON \
         -DUA_ENABLE_ENCRYPTION=ON \
@@ -409,6 +410,7 @@ else
             -DUA_BUILD_EXAMPLES=ON \
             -DUA_BUILD_UNIT_TESTS=ON \
             -DUA_ENABLE_COVERAGE=ON \
+            -DUA_ENABLE_DA=ON \
             -DUA_ENABLE_DISCOVERY=ON \
             -DUA_ENABLE_DISCOVERY_MULTICAST=ON \
             -DUA_ENABLE_ENCRYPTION=ON \