Bladeren bron

Merge branch '0.3' into master

# Conflicts:
#	CMakeLists.txt
#	doc/building.rst
#	src/server/ua_server.c
#	src/server/ua_services_subscription.c
#	src/server/ua_subscription.h
#	src/server/ua_subscription_datachange.c
#	src/ua_types.c
Stefan Profanter 5 jaren geleden
bovenliggende
commit
af8daa874d

+ 1 - 1
deps/mdnsd

@@ -1 +1 @@
-Subproject commit f4aee59d4bbed766a9a8ef3c44492e98edfdd654
+Subproject commit e98eab03631bdf57f39b3cb9acce1ee1786aa411

+ 9 - 1
doc/building.rst

@@ -198,6 +198,12 @@ Detailed SDK Features
 
 **UA_ENABLE_COVERAGE**
    Measure the coverage of unit tests
+**UA_ENABLE_DISCOVERY**
+   Enable Discovery Service (LDS)
+**UA_ENABLE_DISCOVERY_MULTICAST**
+   Enable Discovery Service with multicast support (LDS-ME)
+**UA_ENABLE_DISCOVERY_SEMAPHORE**
+   Enable Discovery Semaphore support
 
 **UA_NAMESPACE_ZERO**
 
@@ -221,7 +227,9 @@ be visible in the cmake GUIs.
 
 **UA_ENABLE_STATUSCODE_DESCRIPTIONS**
    Compile the human-readable name of the StatusCodes into the binary. Enabled by default.
-
+**UA_ENABLE_FULL_NS0**
+   Use the full NS0 instead of a minimal Namespace 0 nodeset
+   ``UA_FILE_NS0`` is used to specify the file for NS0 generation from namespace0 folder. Default value is ``Opc.Ua.NodeSet2.xml``
 **UA_ENABLE_NONSTANDARD_UDP**
    Enable udp extension
 

+ 0 - 1
include/ua_config.h.in

@@ -45,7 +45,6 @@ extern "C" {
 #cmakedefine UA_ENABLE_STATUSCODE_DESCRIPTIONS
 #cmakedefine UA_ENABLE_TYPENAMES
 #cmakedefine UA_ENABLE_DETERMINISTIC_RNG
-#cmakedefine UA_ENABLE_GENERATE_NAMESPACE0
 #cmakedefine UA_ENABLE_NONSTANDARD_UDP
 #cmakedefine UA_ENABLE_DISCOVERY
 #cmakedefine UA_ENABLE_DISCOVERY_MULTICAST

+ 1 - 4
src/server/ua_server.c

@@ -20,9 +20,6 @@
 #include "ua_types.h"
 #include "ua_server_internal.h"
 
-#ifdef UA_ENABLE_GENERATE_NAMESPACE0
-#include "ua_namespaceinit_generated.h"
-#endif
 #ifdef UA_ENABLE_PUBSUB_INFORMATIONMODEL
 #include "ua_pubsub_ns0.h"
 #endif
@@ -67,7 +64,7 @@ UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name) {
     return addNamespace(server, nameString);
 }
 
-UA_StatusCode 
+UA_StatusCode
 UA_Server_getNamespaceByName(UA_Server *server, const UA_String namespaceUri,
                              size_t* foundIndex) {
   for(size_t idx = 0; idx < server->namespacesSize; idx++)

+ 1 - 0
src/server/ua_services_discovery_multicast.c

@@ -33,6 +33,7 @@
 # endif
 #else
 # include <unistd.h> //access
+# include <sys/time.h> // struct timeval
 #endif
 
 #include <fcntl.h>

+ 30 - 0
src/server/ua_services_subscription.c

@@ -162,6 +162,34 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
                          // Then numeric type will be detected from this value. Set null as defaut.
                          const UA_DataType* dataType) {
 
+    /* Filter */
+    if(params->filter.encoding != UA_EXTENSIONOBJECT_DECODED) {
+        UA_DataChangeFilter_init(&(mon->filter));
+        mon->filter.trigger = UA_DATACHANGETRIGGER_STATUSVALUE;
+    } else if(params->filter.content.decoded.type != &UA_TYPES[UA_TYPES_DATACHANGEFILTER]) {
+        return UA_STATUSCODE_BADMONITOREDITEMFILTERINVALID;
+    } else {
+        UA_DataChangeFilter *filter = (UA_DataChangeFilter *)params->filter.content.decoded.data;
+        // TODO implement EURange to support UA_DEADBANDTYPE_PERCENT
+        if (filter->deadbandType == UA_DEADBANDTYPE_PERCENT) {
+            return UA_STATUSCODE_BADMONITOREDITEMFILTERUNSUPPORTED;
+        }
+        if (UA_Variant_isEmpty(&mon->lastValue)) {
+            if (!dataType || !isDataTypeNumeric(dataType))
+                return UA_STATUSCODE_BADFILTERNOTALLOWED;
+        } else
+        if (!isDataTypeNumeric(mon->lastValue.type)) {
+            return UA_STATUSCODE_BADFILTERNOTALLOWED;
+        }
+        UA_DataChangeFilter_copy(filter, &(mon->filter));
+    }
+
+    MonitoredItem_unregisterSampleCallback(server, mon);
+                         const UA_MonitoringParameters *params,
+                         // This parameter is optional and used only if mon->lastValue is not set yet.
+                         // Then numeric type will be detected from this value. Set null as defaut.
+                         const UA_DataType* dataType) {
+
     /* Filter */
     if(params->filter.encoding != UA_EXTENSIONOBJECT_DECODED) {
         UA_DataChangeFilter_init(&(mon->filter.dataChangeFilter));
@@ -229,6 +257,8 @@ setMonitoredItemSettings(UA_Server *server, UA_MonitoredItem *mon,
 
     /* Register sample callback if reporting is enabled */
     if(monitoringMode == UA_MONITORINGMODE_REPORTING)
+        MonitoredItem_registerSampleCallback(server, mon);
+    return UA_STATUSCODE_GOOD;
         UA_MonitoredItem_registerSampleCallback(server, mon);
     return UA_STATUSCODE_GOOD;
 }

+ 68 - 0
src/server/ua_subscription_datachange.c

@@ -81,6 +81,8 @@ void UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem)
 #endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
 
     /* Remove the monitored item */
+    if(monitoredItem->listEntry.le_prev != NULL)
+        LIST_REMOVE(monitoredItem, listEntry);
     UA_String_deleteMembers(&monitoredItem->indexRange);
     UA_ByteString_deleteMembers(&monitoredItem->lastSampledValue);
     UA_Variant_deleteMembers(&monitoredItem->lastValue);
@@ -267,6 +269,72 @@ updateNeededForFilteredValue(const UA_Variant *value, const UA_Variant *oldValue
     return false;
 }
 
+#define ABS_SUBTRACT_TYPE_INDEPENDENT(a,b) ((a)>(b)?(a)-(b):(b)-(a))
+
+static UA_INLINE UA_Boolean
+outOfDeadBand(const void *data1, const void *data2, const size_t index, const UA_DataType *type, const UA_Double deadbandValue) {
+    if (type == &UA_TYPES[UA_TYPES_SBYTE]) {
+        if (ABS_SUBTRACT_TYPE_INDEPENDENT(((const UA_SByte*)data1)[index], ((const UA_SByte*)data2)[index]) <= deadbandValue)
+            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)
+                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)
+            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)
+            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)
+            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)
+            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)
+            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)
+            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)
+            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)
+            return false;
+    }
+    return true;
+}
+
+static UA_INLINE 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;
+    }
+    if (UA_Variant_isScalar(value)) {
+        return outOfDeadBand(value->data, oldValue->data, 0, value->type, deadbandValue);
+    } else {
+        for (size_t i = 0; i < value->arrayLength; ++i) {
+            if (outOfDeadBand(value->data, oldValue->data, i, value->type, deadbandValue))
+                return true;
+        }
+    }
+    return false;
+}
+
 /* When a change is detected, encoding contains the heap-allocated binary encoded value */
 static UA_Boolean
 detectValueChangeWithFilter(UA_Server *server, UA_MonitoredItem *mon, UA_DataValue *value,

+ 68 - 43
src/ua_securechannel.c

@@ -33,9 +33,9 @@ const UA_ByteString
     UA_SECURITY_POLICY_NONE_URI = {47, (UA_Byte *)"http://opcfoundation.org/UA/SecurityPolicy#None"};
 
 #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS
-UA_THREAD_LOCAL UA_StatusCode decrypt_verifySignatureFailure;
-UA_THREAD_LOCAL UA_StatusCode sendAsym_sendFailure;
-UA_THREAD_LOCAL UA_StatusCode processSym_seqNumberFailure;
+UA_StatusCode decrypt_verifySignatureFailure;
+UA_StatusCode sendAsym_sendFailure;
+UA_StatusCode processSym_seqNumberFailure;
 #endif
 
 UA_StatusCode
@@ -108,11 +108,15 @@ UA_SecureChannel_deleteMembersCleanup(UA_SecureChannel *channel) {
     }
 
     /* Remove the buffered chunks */
-    struct ChunkEntry *ch, *temp_ch;
-    LIST_FOREACH_SAFE(ch, &channel->chunks, pointers, temp_ch) {
-        UA_ByteString_deleteMembers(&ch->bytes);
-        LIST_REMOVE(ch, pointers);
-        UA_free(ch);
+    struct MessageEntry *me, *temp_me;
+    LIST_FOREACH_SAFE(me, &channel->chunks, pointers, temp_me) {
+        struct ChunkPayload *cp, *temp_cp;
+        SIMPLEQ_FOREACH_SAFE(cp, &me->chunkPayload, pointers, temp_cp) {
+            UA_ByteString_deleteMembers(&cp->bytes);
+            UA_free(cp);
+        }
+        LIST_REMOVE(me, pointers);
+        UA_free(me);
     }
 }
 
@@ -725,77 +729,98 @@ UA_SecureChannel_sendSymmetricMessage(UA_SecureChannel *channel, UA_UInt32 reque
 
 static void
 UA_SecureChannel_removeChunks(UA_SecureChannel *channel, UA_UInt32 requestId) {
-    struct ChunkEntry *ch;
-    LIST_FOREACH(ch, &channel->chunks, pointers) {
-        if(ch->requestId == requestId) {
-            UA_ByteString_deleteMembers(&ch->bytes);
-            LIST_REMOVE(ch, pointers);
-            UA_free(ch);
+    struct MessageEntry *me;
+    LIST_FOREACH(me, &channel->chunks, pointers) {
+        if(me->requestId == requestId) {
+            struct ChunkPayload *cp, *temp_cp;
+            SIMPLEQ_FOREACH_SAFE(cp, &me->chunkPayload, pointers, temp_cp) {
+                UA_ByteString_deleteMembers(&cp->bytes);
+                UA_free(cp);
+            }
+            LIST_REMOVE(me, pointers);
+            UA_free(me);
             return;
         }
     }
 }
 
 static UA_StatusCode
-appendChunk(struct ChunkEntry *chunkEntry, const UA_ByteString *chunkBody) {
-    UA_Byte *new_bytes = (UA_Byte *)
-        UA_realloc(chunkEntry->bytes.data, chunkEntry->bytes.length + chunkBody->length);
-    if(!new_bytes) {
-        UA_ByteString_deleteMembers(&chunkEntry->bytes);
-        return UA_STATUSCODE_BADOUTOFMEMORY;
-    }
-    chunkEntry->bytes.data = new_bytes;
-    memcpy(&chunkEntry->bytes.data[chunkEntry->bytes.length], chunkBody->data, chunkBody->length);
-    chunkEntry->bytes.length += chunkBody->length;
+appendChunk(struct MessageEntry *messageEntry, const UA_ByteString *chunkBody) {
+    
+    struct ChunkPayload* cp = (struct ChunkPayload*)UA_malloc(sizeof(struct ChunkPayload));
+    UA_StatusCode retval = UA_ByteString_copy(chunkBody, &cp->bytes);
+    if (retval != UA_STATUSCODE_GOOD)
+        return retval;
+    
+    SIMPLEQ_INSERT_TAIL(&messageEntry->chunkPayload, cp, pointers);
+    messageEntry->chunkPayloadSize += chunkBody->length;
     return UA_STATUSCODE_GOOD;
 }
 
 static UA_StatusCode
 UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
                              const UA_ByteString *chunkBody) {
-    struct ChunkEntry *ch;
-    LIST_FOREACH(ch, &channel->chunks, pointers) {
-        if(ch->requestId == requestId)
+    struct MessageEntry *me;
+    LIST_FOREACH(me, &channel->chunks, pointers) {
+        if(me->requestId == requestId)
             break;
     }
 
     /* No chunkentry on the channel, create one */
-    if(!ch) {
-        ch = (struct ChunkEntry *)UA_malloc(sizeof(struct ChunkEntry));
-        if(!ch)
+    if(!me) {
+        me = (struct MessageEntry *)UA_malloc(sizeof(struct MessageEntry));
+        if(!me)
             return UA_STATUSCODE_BADOUTOFMEMORY;
-        ch->requestId = requestId;
-        UA_ByteString_init(&ch->bytes);
-        LIST_INSERT_HEAD(&channel->chunks, ch, pointers);
+        memset(me, 0, sizeof(struct MessageEntry));
+        me->requestId = requestId;
+        SIMPLEQ_INIT(&me->chunkPayload);
+        LIST_INSERT_HEAD(&channel->chunks, me, pointers);
     }
 
-    return appendChunk(ch, chunkBody);
+    return appendChunk(me, chunkBody);
 }
 
 static UA_StatusCode
 UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
                                const UA_ByteString *chunkBody, UA_MessageType messageType,
                                UA_ProcessMessageCallback callback, void *application) {
-    struct ChunkEntry *chunkEntry;
-    LIST_FOREACH(chunkEntry, &channel->chunks, pointers) {
-        if(chunkEntry->requestId == requestId)
+    struct MessageEntry *messageEntry;
+    LIST_FOREACH(messageEntry, &channel->chunks, pointers) {
+        if(messageEntry->requestId == requestId)
             break;
     }
 
     UA_ByteString bytes;
-    if(!chunkEntry) {
+    if(!messageEntry) {
         bytes = *chunkBody;
     } else {
-        UA_StatusCode retval = appendChunk(chunkEntry, chunkBody);
+        UA_StatusCode retval = appendChunk(messageEntry, chunkBody);
         if(retval != UA_STATUSCODE_GOOD)
             return retval;
-        bytes = chunkEntry->bytes;
-        LIST_REMOVE(chunkEntry, pointers);
-        UA_free(chunkEntry);
+        
+        UA_ByteString_init(&bytes);
+
+        bytes.data = (UA_Byte*) UA_malloc(messageEntry->chunkPayloadSize);
+        if (!bytes.data)
+            return UA_STATUSCODE_BADOUTOFMEMORY;
+
+        struct ChunkPayload *cp, *temp_cp;
+        size_t curPos = 0;
+        SIMPLEQ_FOREACH_SAFE(cp, &messageEntry->chunkPayload, pointers, temp_cp) {
+            memcpy(&bytes.data[curPos], cp->bytes.data, cp->bytes.length);
+            curPos += cp->bytes.length;
+            UA_ByteString_deleteMembers(&cp->bytes);
+            UA_free(cp);
+        }
+
+        bytes.length = messageEntry->chunkPayloadSize;
+
+        LIST_REMOVE(messageEntry, pointers);
+        UA_free(messageEntry);
     }
 
     UA_StatusCode retval = callback(application, channel, messageType, requestId, &bytes);
-    if(chunkEntry)
+    if(messageEntry)
         UA_ByteString_deleteMembers(&bytes);
     return retval;
 }

+ 13 - 7
src/ua_securechannel.h

@@ -27,9 +27,9 @@ extern "C" {
 
 /* Thread-local variables to force failure modes during testing */
 #ifdef UA_ENABLE_UNIT_TEST_FAILURE_HOOKS
-extern UA_THREAD_LOCAL UA_StatusCode decrypt_verifySignatureFailure;
-extern UA_THREAD_LOCAL UA_StatusCode sendAsym_sendFailure;
-extern UA_THREAD_LOCAL UA_StatusCode processSym_seqNumberFailure;
+extern UA_StatusCode decrypt_verifySignatureFailure;
+extern UA_StatusCode sendAsym_sendFailure;
+extern UA_StatusCode processSym_seqNumberFailure;
 #endif
 
 /* The Session implementation differs between client and server. Still, it is
@@ -44,12 +44,18 @@ typedef struct UA_SessionHeader {
 } UA_SessionHeader;
 
 /* For chunked requests */
-struct ChunkEntry {
-    LIST_ENTRY(ChunkEntry) pointers;
-    UA_UInt32 requestId;
+struct ChunkPayload {
+    SIMPLEQ_ENTRY(ChunkPayload) pointers;
     UA_ByteString bytes;
 };
 
+struct MessageEntry {
+    LIST_ENTRY(MessageEntry) pointers;
+    UA_UInt32 requestId;
+    SIMPLEQ_HEAD(chunkpayload_pointerlist, ChunkPayload) chunkPayload;
+    size_t chunkPayloadSize;
+};
+
 typedef enum {
     UA_SECURECHANNELSTATE_FRESH,
     UA_SECURECHANNELSTATE_OPEN,
@@ -79,7 +85,7 @@ struct UA_SecureChannel {
     UA_UInt32 sendSequenceNumber;
 
     LIST_HEAD(session_pointerlist, UA_SessionHeader) sessions;
-    LIST_HEAD(chunk_pointerlist, ChunkEntry) chunks;
+    LIST_HEAD(chunk_pointerlist, MessageEntry) chunks;
 };
 
 UA_StatusCode

+ 2 - 1
src/ua_types.c

@@ -71,7 +71,8 @@ UA_findDataType(const UA_NodeId *typeId) {
 /* Random Number Generator */
 /***************************/
 
-static UA_THREAD_LOCAL pcg32_random_t UA_rng = PCG32_INITIALIZER;
+//TODO is this safe for multithreading?
+static pcg32_random_t UA_rng = PCG32_INITIALIZER;
 
 void
 UA_random_seed(u64 seed) {

+ 0 - 18
src/ua_util.h

@@ -26,24 +26,6 @@ extern "C" {
 /* Macro-Expand for MSVC workarounds */
 #define UA_MACRO_EXPAND(x) x
 
-/* Thread-Local Storage
- * --------------------
- * Thread-local storage is not required by the main library functionality. It is
- * only used for some testing strategies. ``UA_THREAD_LOCAL`` is empty if the
- * feature is not available. */
-
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
-# define UA_THREAD_LOCAL _Thread_local /* C11 */
-#elif defined(__cplusplus) && __cplusplus > 199711L
-# define UA_THREAD_LOCAL thread_local /* C++11 */
-#elif defined(__GNUC__)
-# define UA_THREAD_LOCAL __thread /* GNU extension */
-#elif defined(_MSC_VER)
-# define UA_THREAD_LOCAL __declspec(thread) /* MSVC extension */
-#else
-# define UA_THREAD_LOCAL
-#endif
-
 /* Integer Shortnames
  * ------------------
  * These are not exposed on the public API, since many user-applications make

+ 1 - 1
tools/clang-format_precommit_hook

@@ -9,7 +9,7 @@
 # and make sure that `clang-format` is installed on your system
 
 maj_min=1
-maj_max=3
+maj_max=8
 
 base=clang-format
 format=""