Merge branch 'master' into feature/architectures

This commit is contained in:
Jose Cabral 2018-06-27 09:05:30 +02:00
commit ae149eb6d5
66 changed files with 2372 additions and 1299 deletions

View File

@ -1,7 +1,7 @@
language: c
# using c for language overwrites our compilers
language: generic
# use new build environment (docker)
sudo: required
sudo: false
env:
global:
@ -16,43 +16,173 @@ dist: trusty
matrix:
fast_finish: true
include:
#
- os: linux
compiler: gcc
addons:
apt:
sources:
# see https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
- ubuntu-toolchain-r-test
packages:
- binutils-mingw-w64-i686
- build-essential
- check
- cmake
- gcc-multilib
- g++-mingw-w64-i686
- g++-mingw-w64-x86-64
- g++-multilib
- graphviz
- libsubunit-dev
- libx11-dev
- mingw-w64
- python-six
- python3-six
- texlive-fonts-recommended
- texlive-latex-extra
- texlive-latex-recommended
- valgrind
- wget
- xutils-dev
- zip
env:
- ANALYZE=false
- CC=gcc-4.8
- CXX=g++-4.8
- PYTHON=python2
- MINGW=true
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- check
- gcc-8
- gcc-8-multilib
- g++-8
- g++-8-multilib
- graphviz
- linux-libc-dev:i386
- python-six
- python3-six
- texlive-fonts-recommended
- texlive-latex-extra
- texlive-latex-recommended
- valgrind
env:
- ANALYZE=false
- CC=gcc-8
- CXX=g++-8
- PYTHON=python2
- os: linux
compiler: clang
addons:
apt:
sources:
- llvm-toolchain-trusty-6.0
- ubuntu-toolchain-r-test
packages:
- check
- clang-6.0
- clang-tidy-6.0
- graphviz
- python-six
- texlive-fonts-recommended
- texlive-latex-extra
- texlive-latex-recommended
- valgrind
env:
- ANALYZE=false
- CC=clang-6.0
- CXX=clang++-6.0
- PYTHON=python2
- os: linux
compiler: clang
addons:
apt:
sources:
- llvm-toolchain-trusty-6.0
- ubuntu-toolchain-r-test
packages:
- check
- clang-6.0
- clang-tidy-6.0
- graphviz
- python3-six
- texlive-fonts-recommended
- texlive-latex-extra
- texlive-latex-recommended
- valgrind
env:
- ANALYZE=false
- CC=clang-6.0
- CXX=clang++-6.0
- PYTHON=python3
- os: linux
compiler: tcc
addons:
apt:
packages:
- check
- graphviz
- python-six
- python3-six
- texlive-fonts-recommended
- texlive-latex-extra
- texlive-latex-recommended
env:
- ANALYZE=false
- CC=tcc
- PYTHON=python2
- os: linux
compiler: gcc
env: ANALYZE=true
addons:
apt:
packages:
- cppcheck
env:
- ANALYZE=true
- CC=gcc-4.8
- CXX=g++-4.8
- os: linux
compiler: clang
env: ANALYZE=true
addons:
apt:
sources:
- llvm-toolchain-trusty-6.0
- ubuntu-toolchain-r-test
packages:
- check
- clang-6.0
- clang-tidy-6.0
- graphviz
- python-six
- python3-six
- texlive-fonts-recommended
- texlive-latex-extra
- texlive-latex-recommended
env:
- ANALYZE=true
- PYTHON=python3
- CC=clang-6.0
- CXX=clang++-6.0
#- os: linux
# compiler: gcc
# env: LINT=true
- os: linux
compiler: gcc
env: DOCKER=true
services:
- docker
env:
- DOCKER=true
- os: linux
compiler: clang
env: FUZZER=true
addons:
apt:
sources:
- llvm-toolchain-trusty-6.0
- ubuntu-toolchain-r-test
packages:
- check
- clang-6.0
- clang-tidy-6.0
- libfuzzer-6.0-dev
- python-six
- python3-six
env:
- FUZZER=true
- os: osx
compiler: clang
# disable homebrew auto update which takes a lot of time
@ -61,7 +191,6 @@ matrix:
directories:
- $HOME/Library/Caches/Homebrew
- os: linux
compiler: gcc
addons:
sonarcloud:
organization: open62541
@ -69,43 +198,19 @@ matrix:
- master
- sonarcloud
env:
- CC=gcc-4.9
- CXX=g++-4.9
- SONAR=true
- PYTHON=python2
cache:
directories:
- '$HOME/.sonar/cache'
addons:
apt:
sources:
# see https://github.com/travis-ci/apt-source-whitelist/blob/master/ubuntu.json
packages:
- binutils-mingw-w64-i686
- build-essential
- check
- cmake
- cppcheck
- gcc-multilib
- g++-mingw-w64-i686
- g++-mingw-w64-x86-64
- g++-multilib
- graphviz
- libsubunit-dev
- libx11-dev
- mingw-w64
- python-six
- python3-six
- texlive-fonts-recommended
- texlive-latex-extra
- texlive-latex-recommended
- valgrind
- wget
- xutils-dev
- zip
coverity_scan:
project:
name: "open62541/open62541"
description: "Build submitted by Travis"
notification_email: null@plt.rwth-aachen.de
notification_email: noreply@open62541.org
build_command_prepend: "mkdir build && cd build && cmake .."
build_command: "make"
branch_pattern: coverity_scan
@ -120,6 +225,23 @@ cache:
# combine all the commands into one single command. See https://github.com/travis-ci/travis-ci/issues/1066
before_install: |
set -e
# set paths for locally installed libs (like liburcu)
export LOCAL_PKG=$HOME/install
mkdir -p $LOCAL_PKG/lib
mkdir -p $LOCAL_PKG/include
mkdir -p $LOCAL_PKG/bin
export LIBRARY_PATH=$LOCAL_PKG/lib:$LIBRARY_PATH
export C_INCLUDE_PATH=$LOCAL_PKG/include:$C_INCLUDE_PATH
export CPLUS_INCLUDE_PATH=$LOCAL_PKG/include:$CPLUS_INCLUDE_PATH
export PKG_CONFIG_PATH=$LOCAL_PKG/lib/pkgconfig:$PKG_CONFIG_PATH
export PATH=$LOCAL_PKG:$LOCAL_PKG/bin:$PATH
export CMAKE_PREFIX_PATH=$LOCAL_PKG:CMAKE_PREFIX_PATH
# set local path for python packages
export PATH=$PATH:$HOME/.local/bin # linux
export PATH=$PATH:$HOME/Library/Python #OS X
export PATH=$PATH:$HOME/Library/Python/2.7/bin #OS X
# Exit travis if on coverity_scan branch and not first build
test $TRAVIS_BRANCH != coverity_scan -o ${TRAVIS_JOB_NUMBER##*.} = 1 || exit 0
if [ ${TRAVIS_OS_NAME} == "linux" ]; then echo -n | openssl s_client -connect scan.coverity.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | sudo tee -a /etc/ssl/certs/ca-; fi

View File

@ -114,10 +114,11 @@ endif(${UA_ARCHITECTURE} STREQUAL "None")
# Options
set(UA_LOGLEVEL 300 CACHE STRING "Level at which logs shall be reported")
option(UA_ENABLE_HISTORIZING "Enable server to provide historical access." ON)
option(UA_ENABLE_METHODCALLS "Enable the Method service set" ON)
option(UA_ENABLE_NODEMANAGEMENT "Enable dynamic addition and removal of nodes at runtime" ON)
option(UA_ENABLE_SUBSCRIPTIONS "Enable subscriptions support." ON)
option(UA_ENABLE_SUBSCRIPTIONS_EVENTS "Enable the use of events." OFF)
option(UA_ENABLE_SUBSCRIPTIONS_EVENTS "Enable the use of events. (EXPERIMENTAL)" OFF)
option(UA_ENABLE_DISCOVERY "Enable Discovery Service (LDS)" ON)
option(UA_ENABLE_DISCOVERY_MULTICAST "Enable Discovery Service with multicast support (LDS-ME)" OFF)
option(UA_ENABLE_COVERAGE "Enable gcov coverage" OFF)
@ -162,6 +163,8 @@ endif()
option(UA_ENABLE_PUBSUB "Enable publish/subscribe (experimental)" OFF)
mark_as_advanced(UA_ENABLE_PUBSUB)
option(UA_ENABLE_PUBSUB_DELTAFRAMES "Enable sending of delta frames with only the changes" ON)
mark_as_advanced(UA_ENABLE_PUBSUB_DELTAFRAMES)
option(UA_ENABLE_PUBSUB_INFORMATIONMODEL "Enable PubSub information model twin" OFF)
mark_as_advanced(UA_ENABLE_PUBSUB_INFORMATIONMODEL)
if(UA_ENABLE_PUBSUB_INFORMATIONMODEL)
@ -641,12 +644,12 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_statuscodes.h
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_statuscode_descriptions.py
${UA_FILE_STATUSCODES})
# nodeid explanation
# Header containing defines for all NodeIds
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_nodeid_descriptions.py
${UA_FILE_NODEIDS} ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_nodeid_descriptions.py
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_nodeid_header.py
${UA_FILE_NODEIDS} ${PROJECT_BINARY_DIR}/src_generated/ua_nodeids NS0
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/tools/generate_nodeid_header.py
${UA_FILE_NODEIDS})
# we need a custom target to avoid that the generator is called concurrently and

View File

@ -1,51 +1,69 @@
open62541 Supported Features
============================
| __**Service**__ | | | Comment |
|:----------------------------|:--------------------------------|:--------------------:|:---------------------|
| Discovery Service Set | | | |
| | FindServers() | :heavy_check_mark: | |
OPC UA Stack
------------
| | | |
| --------------------------------------- |:--------------------:| -------------------- |
| **Encoding ** | | |
| OPC UA Binary | :heavy_check_mark: | |
| OPC UA JSON | :new_moon: | WIP for Release 0.4 |
| OPC UA XML | :new_moon: | |
| **Transport** | | |
| UA-TCP UA-SC UA Binary | :heavy_check_mark: | |
| OPC UA HTTPS | :new_moon: | |
| SOAP-HTTP WS-SC UA Binary | :new_moon: | |
| SOAP-HTTP WS-SC UA XML | :new_moon: | |
| SOAP-HTTP WS-SC UA XML-UA Binary | :new_moon: | |
| **Encryption** | | |
| None | :heavy_check_mark: | |
| Basic128Rsa15 | :heavy_check_mark: | master, Release 0.3 |
| Basic256 | :heavy_check_mark: | master, Release 0.3 |
| Basic256Sha256 | :heavy_check_mark: | master, Release 0.3 |
| **Authentication** | | |
| Anonymous | :heavy_check_mark: | |
| User Name Password | :heavy_check_mark: | |
| X509 Certificate | :new_moon: | |
OPC UA Server
-------------
| **Service-Set** | **Service** | **Support** | **Comment** |
| --------------------------- | ------------------------------- |:--------------------:| -------------------- |
| Discovery Service Set | FindServers() | :heavy_check_mark: | |
| | FindServersOnNetwork() | :full_moon: | master, Release 0.3 |
| | GetEndpoints() | :heavy_check_mark: | |
| | RegisterServer() | :heavy_check_mark: | |
| | RegisterServer2() | :full_moon: | master, Release 0.3 |
| Secure Channel Service Set | | | |
| | OpenSecureChannel() | :heavy_check_mark: | |
| Secure Channel Service Set | OpenSecureChannel() | :heavy_check_mark: | |
| | CloseSecureChannel() | :heavy_check_mark: | |
| Session Service Set | | | |
| | CreateSession() | :heavy_check_mark: | |
| Session Service Set | CreateSession() | :heavy_check_mark: | |
| | CloseSession() | :heavy_check_mark: | |
| | ActivateSession() | :heavy_check_mark: | |
| | Cancel() | :new_moon: | |
| Node Management Service Set | | | |
| | AddNodes() | :heavy_check_mark: | |
| Node Management Service Set | AddNodes() | :heavy_check_mark: | |
| | AddReferences() | :heavy_check_mark: | |
| | DeleteNodes() | :heavy_check_mark: | |
| | DeleteReferences() | :heavy_check_mark: | |
| View Service Set | | | |
| | Browse() | :heavy_check_mark: | |
| View Service Set | Browse() | :heavy_check_mark: | |
| | BrowseNext() | :heavy_check_mark: | |
| | TranslateBrowsePathsToNodeIds() | :heavy_check_mark: | |
| | RegisterNodes() | :heavy_check_mark: | |
| | UnregisterNodes() | :heavy_check_mark: | |
| Query Service Set | | | |
| | QueryFirst() | :new_moon: | |
| Query Service Set | QueryFirst() | :new_moon: | |
| | QueryNext() | :new_moon: | |
| Attribute Service Set | | | |
| | Read() | :heavy_check_mark: | |
| Attribute Service Set | Read() | :heavy_check_mark: | |
| | Write() | :heavy_check_mark: | |
| | HistoryRead() | :waning_gibbous_moon: | [WIP](https://github.com/open62541/open62541/pull/1740), Release 0.4 |
| | HistoryUpdate() | :waning_gibbous_moon: | [WIP](https://github.com/open62541/open62541/pull/1740), Release 0.4 |
| Method Service Set | | | |
| | Call() | :heavy_check_mark: | |
| MonitoredItems Service Set | | | |
| | CreateMonitoredItems() | :heavy_check_mark: | |
| Method Service Set | Call() | :heavy_check_mark: | |
| MonitoredItems Service Set | CreateMonitoredItems() | :heavy_check_mark: | See below for Events |
| | DeleteMonitoredItems() | :heavy_check_mark: | |
| | ModifyMonitoredItems() | :heavy_check_mark: | |
| | SetMonitoringMode() | :heavy_check_mark: | |
| | SetTriggering() | :new_moon: | |
| Subscription Service Set | | | |
| | CreateSubscription() | :heavy_check_mark: | |
| Subscription Service Set | CreateSubscription() | :heavy_check_mark: | |
| | ModifySubscription() | :heavy_check_mark: | |
| | SetPublishingMode() | :heavy_check_mark: | |
| | Publish() | :heavy_check_mark: | |
@ -53,45 +71,40 @@ open62541 Supported Features
| | DeleteSubscriptions() | :heavy_check_mark: | |
| | TransferSubscriptions() | :new_moon: | |
| **Subscriptions** | | |
| --------------------------------------- |:--------------------:| -------------------- |
| DataChange MonitoredItems | :heavy_check_mark: | master, Release 0.3 |
| DataChange Filters | :heavy_check_mark: | master |
| Event MonitoredItems | :heavy_check_mark: | master |
| Event Filters | :new_moon: | |
| | | |
|:----------------------------------------|:--------------------:|:---------------------|
| **Transport** | | |
| UA-TCP UA-SC UA Binary | :heavy_check_mark: | OPC.TCP - Binary |
| SOAP-HTTP WS-SC UA Binary | :new_moon: | HTTP/HTTPS - Binary |
| SOAP-HTTP WS-SC UA XML | :new_moon: | |
| SOAP-HTTP WS-SC UA XML-UA Binary | :new_moon: | |
| **Encryption** | | |
| None | :heavy_check_mark: | |
| Basic128Rsa15 | :heavy_check_mark: | master, Release 0.3 |
| Basic256 | :heavy_check_mark: | master |
| Basic256Sha256 | :heavy_check_mark: | master |
| **Authentication** | | |
| Anonymous | :heavy_check_mark: | |
| User Name Password | :heavy_check_mark: | |
| X509 Certificate | :new_moon: | |
| **Server Facets** | | |
| Core Server | :heavy_check_mark: | |
| Data Access Server | :heavy_check_mark: | |
| Embedded Server | :heavy_check_mark: | |
| Nano Embedded Device Server | :heavy_check_mark: | |
| Micro Embedded Device Server | :heavy_check_mark: | |
| Method Server | :heavy_check_mark: | |
| Embedded DataChange Subscription Server | :heavy_check_mark: | |
| Node Management Server | :heavy_check_mark: | |
| Standard DataChange Subscription Server | :waning_gibbous_moon: | Only Deadband Filter missing |
| Event Subscription Server | :full_moon: | master |
| **Client Facets** | | |
| Base Client Behaviour | :heavy_check_mark: | |
| AddressSpace Lookup | :heavy_check_mark: | |
| Attribute Read | :heavy_check_mark: | |
| DataChange Subscription | :heavy_check_mark: | |
| DataAccess | :heavy_check_mark: | |
| Discovery | :heavy_check_mark: | |
| Event Subscription | :heavy_check_mark: | |
| Method call | :heavy_check_mark: | |
| Advanced Type | :heavy_check_mark: | |
| **Discovery** | | See Discovery Service Set |
| --------------------------------------- |:--------------------:| -------------------- |
| Local Disovery Server | :heavy_check_mark: | master, Release 0.3 |
| Local Discovery Server Multicast Ext. | :heavy_check_mark: | master, Release 0.3 |
| Global Discovery Server | :new_moon: | |
OPC UA Client
-------------
- All services are supported
- Handling of subscriptions in the background
OPC UA PubSub
-------------
| | | |
| ------------------------------------------------- |:---------------------:| ---------------------- |
| **NetworkMessage decoding/encoding** | | |
| Binary (UADP) | :heavy_check_mark: | |
| JSON | :new_moon: | WIP |
| **PubSub Transport** | | |
| UDP/multicast (send and receive) | :heavy_check_mark: | |
| Ethernet (TSN) | :waning_gibbous_moon: | Defined API to plug in custom networking implementation |
| MQTT | :new_moon: | WIP |
| AMQP | :new_moon: | |
| **Publisher Configuration** | | |
| Configure (server-side) Publisher at runtime | :heavy_check_mark: | |
| Configuration representation in information model | :heavy_check_mark: | Runtime configuration changes by editing the information model representation are possible |
| Security Key Service Model | :new_moon: | |
| **Subscriber Configuration** | :waning_gibbous_moon: | Manual Subscriber only |

View File

@ -179,7 +179,7 @@ This group contains build options related to the supported OPC UA features.
**UA_ENABLE_SUBSCRIPTIONS**
Enable subscriptions
**UA_ENABLE_SUBSCRIPTIONS_EVENTS**
Enable the use of events for subscriptions
Enable the use of events for subscriptions. This is a new feature and currently marked as EXPERIMENTAL.
**UA_ENABLE_METHODCALLS**
Enable the Method service set
**UA_ENABLE_NODEMANAGEMENT**

View File

@ -110,12 +110,12 @@ endif()
if(UA_BUILD_SELFSIGNED_CERTIFICATE)
find_package(OpenSSL REQUIRED)
add_custom_command(OUTPUT server_cert.der ca.crt
add_custom_command(OUTPUT server_cert.der
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/certs/create_self-signed.py ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${PROJECT_SOURCE_DIR}/tools/certs/create_self-signed.py
${PROJECT_SOURCE_DIR}/tools/certs/localhost.cnf)
add_custom_target(selfsigned ALL DEPENDS server_cert.der ca.crt)
add_executable(server_certificate server_certificate.c ${STATIC_OBJECTS} server_cert.der ca.crt)
add_custom_target(selfsigned ALL DEPENDS server_cert.der)
add_executable(server_certificate server_certificate.c ${STATIC_OBJECTS} server_cert.der)
target_link_libraries(server_certificate open62541 ${open62541_LIBRARIES})
endif()

View File

@ -25,7 +25,17 @@ add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
${PROJECT_SOURCE_DIR}/examples/nodeset/server_nodeset.xml)
add_example(server_nodeset server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c)
# The .csv file can be created from within UaModeler or manually
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/example_nodeset_ids.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_nodeid_header.py
${PROJECT_SOURCE_DIR}/examples/nodeset/server_nodeset.csv ${PROJECT_BINARY_DIR}/src_generated/example_nodeset_ids EXAMPLE_NS
DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_nodeid_header.py
${PROJECT_SOURCE_DIR}/examples/nodeset/server_nodeset.csv)
add_example(server_nodeset server_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c ${PROJECT_BINARY_DIR}/src_generated/example_nodeset_ids.h)
if(UA_COMPILE_AS_CXX)
set_source_files_properties(${PROJECT_BINARY_DIR}/src_generated/example_nodeset.c PROPERTIES LANGUAGE CXX)
endif()

View File

@ -7,6 +7,7 @@
/* Files example_namespace.h and example_namespace.c are created from server_nodeset.xml in the
* /src_generated directory by CMake */
#include "example_nodeset.h"
#include "example_nodeset_ids.h"
UA_Boolean running = true;
@ -29,8 +30,20 @@ int main(int argc, char** argv) {
"Check previous output for any error.");
retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
} else {
// Do some additional stuff with the nodes
// this will just get the namespace index, since it is already added to the server
UA_UInt16 nsIdx = UA_Server_addNamespace(server, "http://yourorganisation.org/test/");
UA_NodeId testInstanceId = UA_NODEID_NUMERIC(nsIdx, UA_EXAMPLE_NSID_TESTINSTANCE);
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "The testInstance has ns=%d;id=%d",
testInstanceId.namespaceIndex, testInstanceId.identifier.numeric);
retval = UA_Server_run(server, &running);
}
UA_Server_delete(server);
UA_ServerConfig_delete(config);
return (int)retval;

View File

@ -0,0 +1,5 @@
testType,1001,ObjectType
testInstance,5001,Object
testFolder,5002,Object
testType_Var1,6001,Variable
testInstance_Var1,6002,Variable
1 testType 1001 ObjectType
2 testInstance 5001 Object
3 testFolder 5002 Object
4 testType_Var1 6001 Variable
5 testInstance_Var1 6002 Variable

View File

@ -55,7 +55,7 @@ typedef void (*UA_ClientAsyncServiceCallback)(UA_Client *client, void *userdata,
/*
* Repeated Callbacks
* ------------------ */
typedef UA_StatusCode (*UA_ClientCallback)(UA_Client *client, void *data);
typedef void (*UA_ClientCallback)(UA_Client *client, void *data);
UA_StatusCode
UA_Client_addRepeatedCallback(UA_Client *Client, UA_ClientCallback callback,

View File

@ -28,8 +28,10 @@ extern "C" {
#cmakedefine UA_ENABLE_NODEMANAGEMENT
#cmakedefine UA_ENABLE_SUBSCRIPTIONS
#cmakedefine UA_ENABLE_PUBSUB
#cmakedefine UA_ENABLE_PUBSUB_DELTAFRAMES
#cmakedefine UA_ENABLE_PUBSUB_INFORMATIONMODEL
#cmakedefine UA_ENABLE_ENCRYPTION
#cmakedefine UA_ENABLE_HISTORIZING
#cmakedefine UA_ENABLE_SUBSCRIPTIONS_EVENTS
/* Multithreading */

View File

@ -428,6 +428,19 @@ UA_BrowsePathResult UA_EXPORT
UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
const UA_BrowsePath *browsePath);
/* A simplified TranslateBrowsePathsToNodeIds based on the
* SimpleAttributeOperand type (Part 4, 7.4.4.5).
*
* This specifies a relative path using a list of BrowseNames instead of the
* RelativePath structure. The list of BrowseNames is equivalent to a
* RelativePath that specifies forward references which are subtypes of the
* HierarchicalReferences ReferenceType. All Nodes followed by the browsePath
* shall be of the NodeClass Object or Variable. */
UA_BrowsePathResult UA_EXPORT
UA_Server_browseSimplifiedBrowsePath(UA_Server *server, const UA_NodeId origin,
size_t browsePathSize,
const UA_QualifiedName *browsePath);
#ifndef HAVE_NODEITER_CALLBACK
#define HAVE_NODEITER_CALLBACK
/* Iterate over all nodes referenced by parentNodeId by calling the callback
@ -668,22 +681,26 @@ typedef struct {
* `value->value.storageType` to `UA_VARIANT_DATA_NODELETE` to prevent the
* memory being cleaned up. Don't forget to also set `value->hasValue` to
* true to indicate the presence of a value.
*
* @param handle An optional pointer to user-defined data for the
* specific data source
* @param nodeid Id of the read node
*
* @param server The server executing the callback
* @param sessionId The identifier of the session
* @param sessionContext Additional data attached to the session in the
* access control layer
* @param nodeId The identifier of the node being read from
* @param nodeContext Additional data attached to the node by the user
* @param includeSourceTimeStamp If true, then the datasource is expected to
* set the source timestamp in the returned value
* @param range If not null, then the datasource shall return only a
* selection of the (nonscalar) data. Set
* UA_STATUSCODE_BADINDEXRANGEINVALID in the value if this does not
* apply.
* apply
* @param value The (non-null) DataValue that is returned to the client. The
* data source sets the read data, the result status and optionally a
* sourcetimestamp.
* @return Returns a status code for logging. Error codes intended for the
* original caller are set in the value. If an error is returned,
* then no releasing of the value is done. */
* then no releasing of the value is done
*/
UA_StatusCode (*read)(UA_Server *server, const UA_NodeId *sessionId,
void *sessionContext, const UA_NodeId *nodeId,
void *nodeContext, UA_Boolean includeSourceTimeStamp,
@ -691,14 +708,24 @@ typedef struct {
/* Write into a data source. This method pointer can be NULL if the
* operation is unsupported.
*
* @param handle An optional pointer to user-defined data for the
* specific data source
* @param nodeid Id of the node being written to
* @param data The data to be written into the data source
* @param range An optional data range. If the data source is scalar or does
* not support writing of ranges, then an error code is returned.
* @return Returns a status code that is returned to the user */
*
* @param server The server executing the callback
* @param sessionId The identifier of the session
* @param sessionContext Additional data attached to the session in the
* access control layer
* @param nodeId The identifier of the node being written to
* @param nodeContext Additional data attached to the node by the user
* @param range If not NULL, then the datasource shall return only a
* selection of the (nonscalar) data. Set
* UA_STATUSCODE_BADINDEXRANGEINVALID in the value if this does not
* apply
* @param value The (non-NULL) DataValue that has been written by the client.
* The data source contains the written data, the result status and
* optionally a sourcetimestamp
* @return Returns a status code for logging. Error codes intended for the
* original caller are set in the value. If an error is returned,
* then no releasing of the value is done
*/
UA_StatusCode (*write)(UA_Server *server, const UA_NodeId *sessionId,
void *sessionContext, const UA_NodeId *nodeId,
void *nodeContext, const UA_NumericRange *range,
@ -1216,6 +1243,11 @@ UA_Server_triggerEvent(UA_Server *server, const UA_NodeId eventNodeId, const UA_
/* Add a new namespace to the server. Returns the index of the new namespace */
UA_UInt16 UA_EXPORT UA_Server_addNamespace(UA_Server *server, const char* name);
/* Get namespace by name from the server. */
UA_StatusCode UA_EXPORT
UA_Server_getNamespaceByName(UA_Server *server, const UA_String namespaceUri,
size_t* foundIndex);
#ifdef __cplusplus
}
#endif

View File

@ -5,6 +5,7 @@
* Copyright 2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
* Copyright 2017 (c) Stefan Profanter, fortiss GmbH
* Copyright 2017 (c) Henrik Norrman
* Copyright 2018 (c) Fabian Arndt, Root-Core
*/
#ifndef UA_SERVER_CONFIG_H_
@ -165,6 +166,29 @@ struct UA_ServerConfig {
* state of the semaphore file. */
UA_UInt32 discoveryCleanupTimeout;
#endif
/* Historical Access */
#ifdef UA_ENABLE_HISTORIZING
UA_Boolean accessHistoryDataCapability;
UA_UInt32 maxReturnDataValues; /* 0 -> unlimited size */
UA_Boolean accessHistoryEventsCapability;
UA_UInt32 maxReturnEventValues; /* 0 -> unlimited size */
UA_Boolean insertDataCapability;
UA_Boolean insertEventCapability;
UA_Boolean insertAnnotationsCapability;
UA_Boolean replaceDataCapability;
UA_Boolean replaceEventCapability;
UA_Boolean updateDataCapability;
UA_Boolean updateEventCapability;
UA_Boolean deleteRawCapability;
UA_Boolean deleteEventCapability;
UA_Boolean deleteAtTimeDataCapability;
#endif
};
#ifdef __cplusplus

View File

@ -497,6 +497,10 @@ typedef struct {
UA_NumericRangeDimension *dimensions;
} UA_NumericRange;
UA_StatusCode UA_EXPORT
UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str);
/**
* .. _variant:
*

View File

@ -7,6 +7,7 @@
* Copyright 2017 (c) Stefan Profanter, fortiss GmbH
* Copyright 2017 (c) Thomas Stalder, Blue Time Concept SA
* Copyright 2018 (c) Daniel Feist, Precitec GmbH & Co. KG
* Copyright 2018 (c) Fabian Arndt, Root-Core
*/
#include "ua_plugin_securitypolicy.h"
@ -280,6 +281,28 @@ createDefaultConfig(void) {
conf->discoveryCleanupTimeout = 60 * 60;
#endif
#ifdef UA_ENABLE_HISTORIZING
/* conf->accessHistoryDataCapability = UA_FALSE; */
/* conf->maxReturnDataValues = 0; */
/* conf->accessHistoryEventsCapability = UA_FALSE; */
/* conf->maxReturnEventValues = 0; */
/* conf->insertDataCapability = UA_FALSE; */
/* conf->insertEventCapability = UA_FALSE; */
/* conf->insertAnnotationsCapability = UA_FALSE; */
/* conf->replaceDataCapability = UA_FALSE; */
/* conf->replaceEventCapability = UA_FALSE; */
/* conf->updateDataCapability = UA_FALSE; */
/* conf->updateEventCapability = UA_FALSE; */
/* conf->deleteRawCapability = UA_FALSE; */
/* conf->deleteEventCapability = UA_FALSE; */
/* conf->deleteAtTimeDataCapability = UA_FALSE; */
#endif
/* --> Finish setting the default static config <-- */
return conf;
@ -637,6 +660,11 @@ UA_ServerConfig_delete(UA_ServerConfig *config) {
/* Default Client Settings */
/***************************/
static UA_INLINE void UA_ClientConnectionTCP_poll_callback(UA_Client *client, void *data) {
UA_ClientConnectionTCP_poll(client, data);
}
const UA_ClientConfig UA_ClientConfig_default = {
5000, /* .timeout, 5 seconds */
10 * 60 * 1000, /* .secureChannelLifeTime, 10 minutes */
@ -650,7 +678,7 @@ const UA_ClientConfig UA_ClientConfig_default = {
},
UA_ClientConnectionTCP, /* .connectionFunc (for sync connection) */
UA_ClientConnectionTCP_init, /* .initConnectionFunc (for async client) */
UA_ClientConnectionTCP_poll, /* .pollConnectionFunc (for async connection) */
UA_ClientConnectionTCP_poll_callback, /* .pollConnectionFunc (for async connection) */
0, /* .customDataTypesSize */
NULL, /* .customDataTypes */

View File

@ -1,3 +1,4 @@
/* This work is licensed under a Creative Commons CCZero 1.0 Universal License.
* See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
*
@ -8,6 +9,8 @@
#include <ctype.h>
#include <stdio.h>
#ifdef UA_DEBUG_DUMP_PKGS
void UA_dump_hex_pkg(UA_Byte* buffer, size_t bufferLen) {
printf("--------------- HEX Package Start ---------------\n");
char ascii[17];
@ -37,3 +40,4 @@ void UA_dump_hex_pkg(UA_Byte* buffer, size_t bufferLen) {
printf(" |%s|\n%08zx\n", ascii, bufferLen);
printf("--------------- HEX Package END ---------------\n");
}
#endif

View File

@ -133,16 +133,6 @@ UA_PubSubChannelUDPMC_open(const UA_PubSubConnectionConfig *connectionConfig) {
UA_String hostname, path;
UA_UInt16 networkPort;
//TODO replace fallback to use the existing parseEndpointUrl function. Extend parseEndpointUrl for UDP or create own parseEndpointUrl function for PubSub.
if(strncmp((char*)&address.url.data, "opc.udp://", 10) != 0){
strncpy((char*)address.url.data, "opc.tcp://", 10);
} else {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
"PubSub Connection creation failed. Invalid URL.");
UA_free(channelDataUDPMC);
UA_free(newChannel);
return NULL;
}
if(UA_parseEndpointUrl(&address.url, &hostname, &networkPort, &path) != UA_STATUSCODE_GOOD){
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER,
"PubSub Connection creation failed. Invalid URL.");

View File

@ -41,7 +41,7 @@
/* Open the Connection */
/***********************/
static UA_StatusCode
openSecureChannelAsync(UA_Client *client, UA_Boolean renew);
openSecureChannelAsync(UA_Client *client/*, UA_Boolean renew*/);
static UA_StatusCode
requestSession(UA_Client *client, UA_UInt32 *requestId);
@ -92,7 +92,7 @@ processACKResponseAsync(void *application, UA_Connection *connection,
/* Open a SecureChannel. TODO: Select with endpoint */
client->channel.connection = &client->connection;
client->connectStatus = openSecureChannelAsync(client, false);
client->connectStatus = openSecureChannelAsync(client/*, false*/);
return client->connectStatus;
}
@ -235,10 +235,10 @@ static UA_StatusCode processOPNResponse
/* OPN messges to renew the channel are sent asynchronous */
static UA_StatusCode
openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
openSecureChannelAsync(UA_Client *client/*, UA_Boolean renew*/) {
/* Check if sc is still valid */
if(renew && client->nextChannelRenewal - UA_DateTime_nowMonotonic () > 0)
return UA_STATUSCODE_GOOD;
/*if(renew && client->nextChannelRenewal - UA_DateTime_nowMonotonic () > 0)
return UA_STATUSCODE_GOOD;*/
UA_Connection *conn = &client->connection;
if(conn->state != UA_CONNECTION_ESTABLISHED)
@ -249,15 +249,15 @@ openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
UA_OpenSecureChannelRequest_init(&opnSecRq);
opnSecRq.requestHeader.timestamp = UA_DateTime_now();
opnSecRq.requestHeader.authenticationToken = client->authenticationToken;
if(renew) {
/*if(renew) {
opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_RENEW;
UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
"Requesting to renew the SecureChannel");
} else {
} else {*/
opnSecRq.requestType = UA_SECURITYTOKENREQUESTTYPE_ISSUE;
UA_LOG_DEBUG(client->config.logger, UA_LOGCATEGORY_SECURECHANNEL,
"Requesting to open a SecureChannel");
}
//}
opnSecRq.securityMode = client->channel.securityMode;
opnSecRq.clientNonce = client->channel.localNonce;
@ -265,7 +265,7 @@ openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
/* Prepare the entry for the linked list */
UA_UInt32 requestId = ++client->requestId;
AsyncServiceCall *ac = NULL;
/*AsyncServiceCall *ac = NULL;
if(renew) {
ac = (AsyncServiceCall*)UA_malloc(sizeof(AsyncServiceCall));
if (!ac)
@ -275,7 +275,7 @@ openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
ac->responseType = &UA_TYPES[UA_TYPES_OPENSECURECHANNELRESPONSE];
ac->requestId = requestId;
ac->userdata = NULL;
}
}*/
/* Send the OPN message */
UA_StatusCode retval = UA_SecureChannel_sendAsymmetricOPNMessage (
@ -289,8 +289,8 @@ openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
"Sending OPN message failed with error %s",
UA_StatusCode_name(retval));
UA_Client_close(client);
if(renew)
UA_free(ac);
//if(renew)
// UA_free(ac);
return retval;
}
@ -298,10 +298,10 @@ openSecureChannelAsync(UA_Client *client, UA_Boolean renew) {
"OPN message sent");
/* Store the entry for async processing and return */
if(renew) {
/*if(renew) {
LIST_INSERT_HEAD(&client->asyncServiceCalls, ac, pointers);
return retval;
}
}*/
return retval;
}

View File

@ -393,12 +393,15 @@ UA_DataSetWriter_deleteMembers(UA_Server *server, UA_DataSetWriter *dataSetWrite
UA_NodeId_deleteMembers(&dataSetWriter->linkedWriterGroup);
UA_NodeId_deleteMembers(&dataSetWriter->connectedDataSet);
LIST_REMOVE(dataSetWriter, listEntry);
#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
//delete lastSamples store
for(size_t i = 0; i < dataSetWriter->lastSamplesCount; i++){
UA_DataValue_delete(dataSetWriter->lastSamples[i].value);
UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[i].value);
}
LIST_REMOVE(dataSetWriter, listEntry);
UA_free(dataSetWriter->lastSamples);
dataSetWriter->lastSamples = NULL;
dataSetWriter->lastSamplesCount = 0;
#endif
}
/**********************************************/
@ -527,25 +530,19 @@ UA_Server_addDataSetWriter(UA_Server *server,
newDataSetWriter->config = tmpDataSetWriterConfig;
//save the current version of the connected PublishedDataSet
newDataSetWriter->connectedDataSetVersion = currentDataSetContext->dataSetMetaData.configurationVersion;
#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
//initialize the queue for the last values
newDataSetWriter->lastSamplesCount = currentDataSetContext->fieldSize;
newDataSetWriter->lastSamples = (UA_DataSetWriterSample * )
UA_calloc(newDataSetWriter->lastSamplesCount, sizeof(UA_DataSetWriterSample));
UA_calloc(currentDataSetContext->fieldSize, sizeof(UA_DataSetWriterSample));
if(!newDataSetWriter->lastSamples) {
UA_DataSetWriterConfig_deleteMembers(&newDataSetWriter->config);
UA_free(newDataSetWriter);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
for(size_t i = 0; i < newDataSetWriter->lastSamplesCount; i++) {
newDataSetWriter->lastSamples[i].value = (UA_DataValue *) UA_calloc(1, sizeof(UA_DataValue));
if(!newDataSetWriter->lastSamples[i].value) {
for(size_t j = 0; j < i; j++)
UA_free(newDataSetWriter->lastSamples[j].value);
UA_DataSetWriterConfig_deleteMembers(&newDataSetWriter->config);
UA_free(newDataSetWriter);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
}
newDataSetWriter->lastSamplesCount = currentDataSetContext->fieldSize;
#endif
//connect PublishedDataSet with DataSetWriter
newDataSetWriter->connectedDataSet = currentDataSetContext->identifier;
newDataSetWriter->linkedWriterGroup = wg->identifier;
@ -642,7 +639,6 @@ void UA_DataSetField_deleteMembers(UA_DataSetField *field) {
UA_NodeId_deleteMembers(&field->identifier);
UA_NodeId_deleteMembers(&field->publishedDataSet);
UA_FieldMetaData_deleteMembers(&field->fieldMetaData);
UA_DataValue_deleteMembers(&field->lastValue);
LIST_REMOVE(field, listEntry);
}
@ -655,6 +651,7 @@ void UA_DataSetField_deleteMembers(UA_DataSetField *field) {
*
* @return UA_TRUE if the value has changed
*/
#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
static UA_Boolean
valueChangedVariant(UA_Variant *oldValue, UA_Variant *newValue){
if(! (oldValue && newValue))
@ -695,32 +692,33 @@ valueChangedVariant(UA_Variant *oldValue, UA_Variant *newValue){
UA_ByteString_delete(newValueEncoding);
return compareResult;
}
#endif
/**
* Obtain the latest value for a specific DataSetField. This method is currently
* called inside the DataSetMessage generation process.
*/
static UA_StatusCode
UA_PubSubDataSetField_sampleValue(UA_Server *server, UA_DataSetField *field) {
static void
UA_PubSubDataSetField_sampleValue(UA_Server *server, UA_DataSetField *field,
UA_DataValue *value) {
/* Read the value */
UA_ReadValueId rvid;
UA_ReadValueId_init(&rvid);
rvid.nodeId = field->config.field.variable.publishParameters.publishedVariable;
rvid.attributeId = field->config.field.variable.publishParameters.attributeId;
rvid.indexRange = field->config.field.variable.publishParameters.indexRange;
UA_DataValue value = UA_Server_read(server, &rvid, UA_TIMESTAMPSTORETURN_BOTH);
UA_DataValue_deleteMembers(&field->lastValue);
field->lastValue = value;
return UA_STATUSCODE_GOOD;
*value = UA_Server_read(server, &rvid, UA_TIMESTAMPSTORETURN_BOTH);
}
static UA_StatusCode
UA_PubSubDataSetWriter_generateKeyFrameMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage,
UA_DataSetWriter *dataSetWriter) {
UA_PublishedDataSet *currentDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
UA_PublishedDataSet *currentDataSet =
UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
if(!currentDataSet)
return UA_STATUSCODE_BADNOTFOUND;
//prepare DataSetMessageContent
/* Prepare DataSetMessageContent */
dataSetMessage->header.dataSetMessageValid = true;
dataSetMessage->header.dataSetMessageType = UA_DATASETMESSAGE_DATAKEYFRAME;
dataSetMessage->data.keyFrameData.fieldCount = currentDataSet->fieldSize;
@ -729,90 +727,116 @@ UA_PubSubDataSetWriter_generateKeyFrameMessage(UA_Server *server, UA_DataSetMess
if(!dataSetMessage->data.keyFrameData.dataSetFields)
return UA_STATUSCODE_BADOUTOFMEMORY;
UA_DataSetField *tmpDataSetField;
/* Loop over the fields */
size_t counter = 0;
LIST_FOREACH(tmpDataSetField, &currentDataSet->fields, listEntry){
if(UA_PubSubDataSetField_sampleValue(server, tmpDataSetField) == UA_STATUSCODE_GOOD){
//include field into DSM
UA_DataValue_init(&dataSetMessage->data.keyFrameData.dataSetFields[counter]);
UA_DataValue_copy(&tmpDataSetField->lastValue, &dataSetMessage->data.keyFrameData.dataSetFields[counter]);
if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0){
dataSetMessage->data.keyFrameData.dataSetFields[counter].hasStatus = UA_FALSE;
}
if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0){
dataSetMessage->data.keyFrameData.dataSetFields[counter].hasSourceTimestamp = UA_FALSE;
if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0){
dataSetMessage->data.keyFrameData.dataSetFields[counter].hasServerPicoseconds = UA_FALSE;
}
}
if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0){
dataSetMessage->data.keyFrameData.dataSetFields[counter].hasServerTimestamp = UA_FALSE;
}
//Update lastValue store
UA_DataValue_deleteMembers(dataSetWriter->lastSamples[counter].value);
UA_DataValue_copy(&tmpDataSetField->lastValue, dataSetWriter->lastSamples[counter++].value);
}
UA_DataSetField *dsf;
LIST_FOREACH(dsf, &currentDataSet->fields, listEntry) {
/* Sample the value */
UA_DataValue *dfv = &dataSetMessage->data.keyFrameData.dataSetFields[counter];
UA_PubSubDataSetField_sampleValue(server, dsf, dfv);
/* Deactivate statuscode? */
if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0)
dfv->hasStatus = false;
/* Deactivate timestamps */
if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0)
dfv->hasSourceTimestamp = false;
if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0)
dfv->hasSourcePicoseconds = false;
if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0)
dfv->hasServerTimestamp = false;
if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS) == 0)
dfv->hasServerPicoseconds = false;
#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
/* Update lastValue store */
UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[counter].value);
UA_DataValue_copy(dfv, &dataSetWriter->lastSamples[counter].value);
#endif
counter++;
}
return UA_STATUSCODE_GOOD;
}
#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
static UA_StatusCode
UA_PubSubDataSetWriter_generateDeltaFrameMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage,
UA_PubSubDataSetWriter_generateDeltaFrameMessage(UA_Server *server,
UA_DataSetMessage *dataSetMessage,
UA_DataSetWriter *dataSetWriter) {
UA_PublishedDataSet *currentDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
UA_PublishedDataSet *currentDataSet =
UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
if(!currentDataSet)
return UA_STATUSCODE_BADNOTFOUND;
//prepare DataSetMessageContent
/* Prepare DataSetMessageContent */
memset(dataSetMessage, 0, sizeof(UA_DataSetMessage));
dataSetMessage->header.dataSetMessageValid = true;
dataSetMessage->header.dataSetMessageType = UA_DATASETMESSAGE_DATADELTAFRAME;
UA_DataSetField *tmpDataSetField;
UA_DataSetField *dsf;
size_t counter = 0;
LIST_FOREACH(tmpDataSetField, &currentDataSet->fields, listEntry) {
if(UA_PubSubDataSetField_sampleValue(server, tmpDataSetField) == UA_STATUSCODE_GOOD) {
//check if the value has changed
if(valueChangedVariant(&dataSetWriter->lastSamples[counter].value->value, &tmpDataSetField->lastValue.value)){
//increase fieldCount for current delta message
dataSetMessage->data.deltaFrameData.fieldCount++;
dataSetWriter->lastSamples[counter].valeChanged = UA_TRUE;
} else {
dataSetWriter->lastSamples[counter].valeChanged = UA_FALSE;
}
//update last stored sample
UA_DataValue_init(dataSetWriter->lastSamples[counter].value);
UA_DataValue_copy(&tmpDataSetField->lastValue, dataSetWriter->lastSamples[counter++].value);
LIST_FOREACH(dsf, &currentDataSet->fields, listEntry) {
/* Sample the value */
UA_DataValue value;
UA_DataValue_init(&value);
UA_PubSubDataSetField_sampleValue(server, dsf, &value);
/* Check if the value has changed */
if(valueChangedVariant(&dataSetWriter->lastSamples[counter].value.value, &value.value)) {
/* increase fieldCount for current delta message */
dataSetMessage->data.deltaFrameData.fieldCount++;
dataSetWriter->lastSamples[counter].valueChanged = UA_TRUE;
/* Update last stored sample */
UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[counter].value);
dataSetWriter->lastSamples[counter].value = value;
} else {
UA_DataValue_deleteMembers(&value);
dataSetWriter->lastSamples[counter].valueChanged = UA_FALSE;
}
counter++;
}
//allocate DeltaFrameFields
UA_DataSetMessage_DeltaFrameField * deltaFields = (UA_DataSetMessage_DeltaFrameField *)
/* Allocate DeltaFrameFields */
UA_DataSetMessage_DeltaFrameField *deltaFields = (UA_DataSetMessage_DeltaFrameField *)
UA_calloc(dataSetMessage->data.deltaFrameData.fieldCount, sizeof(UA_DataSetMessage_DeltaFrameField));
if(!deltaFields)
return UA_STATUSCODE_BADOUTOFMEMORY;
dataSetMessage->data.deltaFrameData.deltaFrameFields = deltaFields;
size_t currentDeltaField = 0;
for(size_t i = 0; i < currentDataSet->fieldSize; i++){
if(dataSetWriter->lastSamples[i].valeChanged){
deltaFields[currentDeltaField].fieldIndex = (UA_UInt16) i;
UA_DataValue_copy(dataSetWriter->lastSamples[i].value, &deltaFields[currentDeltaField].fieldValue);
dataSetWriter->lastSamples[i].valeChanged = false;
if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0){
dataSetMessage->data.deltaFrameData.deltaFrameFields[currentDeltaField].fieldValue.hasStatus = UA_FALSE;
}
if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0){
dataSetMessage->data.deltaFrameData.deltaFrameFields[currentDeltaField].fieldValue.hasSourceTimestamp = UA_FALSE;
if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0){
dataSetMessage->data.deltaFrameData.deltaFrameFields[currentDeltaField].fieldValue.hasServerPicoseconds = UA_FALSE;
}
}
if((dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0){
dataSetMessage->data.deltaFrameData.deltaFrameFields[currentDeltaField].fieldValue.hasServerTimestamp = UA_FALSE;
}
currentDeltaField++;
}
for(size_t i = 0; i < currentDataSet->fieldSize; i++) {
if(!dataSetWriter->lastSamples[i].valueChanged)
continue;
UA_DataSetMessage_DeltaFrameField *dff = &deltaFields[currentDeltaField];
dff->fieldIndex = (UA_UInt16) i;
UA_DataValue_copy(&dataSetWriter->lastSamples[i].value, &dff->fieldValue);
dataSetWriter->lastSamples[i].valueChanged = false;
/* Deactivate statuscode? */
if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_STATUSCODE) == 0)
dff->fieldValue.hasStatus = UA_FALSE;
/* Deactivate timestamps? */
if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP) == 0)
dff->fieldValue.hasSourceTimestamp = UA_FALSE;
if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS) == 0)
dff->fieldValue.hasServerPicoseconds = UA_FALSE;
if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SERVERTIMESTAMP) == 0)
dff->fieldValue.hasServerTimestamp = UA_FALSE;
if((dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS) == 0)
dff->fieldValue.hasServerPicoseconds = UA_FALSE;
currentDeltaField++;
}
return UA_STATUSCODE_GOOD;
}
#endif
/**
* Generate a DataSetMessage for the given writer.
@ -823,102 +847,128 @@ UA_PubSubDataSetWriter_generateDeltaFrameMessage(UA_Server *server, UA_DataSetMe
static UA_StatusCode
UA_DataSetWriter_generateDataSetMessage(UA_Server *server, UA_DataSetMessage *dataSetMessage,
UA_DataSetWriter *dataSetWriter) {
UA_PublishedDataSet *currentDataSet = UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
UA_PublishedDataSet *currentDataSet =
UA_PublishedDataSet_findPDSbyId(server, dataSetWriter->connectedDataSet);
if(!currentDataSet)
return UA_STATUSCODE_BADNOTFOUND;
/* Reset the message */
memset(dataSetMessage, 0, sizeof(UA_DataSetMessage));
//currently is only UADP supported. The configuration Flags are included inside the std. defined UA_UadpDataSetWriterMessageDataType
/* Currently is only UADP supported. The configuration Flags are included
* inside the std. defined UA_UadpDataSetWriterMessageDataType */
UA_UadpDataSetWriterMessageDataType defaultUadpConfiguration;
UA_UadpDataSetWriterMessageDataType *dataSetWriterMessageDataType = NULL;
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_UADPDATASETWRITERMESSAGEDATATYPE])) {
dataSetWriterMessageDataType = (UA_UadpDataSetWriterMessageDataType *) dataSetWriter->config.messageSettings.content.decoded.data;
dataSetWriterMessageDataType = (UA_UadpDataSetWriterMessageDataType *)
dataSetWriter->config.messageSettings.content.decoded.data;
} else {
//create default flag configuration if no UadpDataSetWriterMessageDataType was passed in
UA_UadpDataSetWriterMessageDataType defaultUadpConfiguration;
/* create default flag configuration if no
* UadpDataSetWriterMessageDataType was passed in */
memset(&defaultUadpConfiguration, 0, sizeof(UA_UadpDataSetWriterMessageDataType));
defaultUadpConfiguration.dataSetMessageContentMask = (UA_UadpDataSetMessageContentMask) ((unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP |
(unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION |
(unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION);
defaultUadpConfiguration.dataSetMessageContentMask = (UA_UadpDataSetMessageContentMask)
(UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP | UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION |
UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION);
dataSetWriterMessageDataType = &defaultUadpConfiguration;
}
if(dataSetWriterMessageDataType->networkMessageNumber != 0 || dataSetWriterMessageDataType->dataSetOffset != 0 ||
dataSetWriterMessageDataType->configuredSize !=0 ){
UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "Static DSM configuration not supported. Using defaults");
/* Sanity-test the configuration */
if(dataSetWriterMessageDataType->networkMessageNumber != 0 ||
dataSetWriterMessageDataType->dataSetOffset != 0 ||
dataSetWriterMessageDataType->configuredSize !=0 ) {
UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
"Static DSM configuration not supported. Using defaults");
dataSetWriterMessageDataType->networkMessageNumber = 0;
dataSetWriterMessageDataType->dataSetOffset = 0;
dataSetWriterMessageDataType->configuredSize = 0;
}
//The encoding depends on the flags inside the writer config.
if(dataSetWriter->config.dataSetFieldContentMask & (unsigned int) UA_DATASETFIELDCONTENTMASK_RAWDATAENCODING) {
/* The field encoding depends on the flags inside the writer config.
* TODO: This can be moved to the encoding layer. */
if(dataSetWriter->config.dataSetFieldContentMask & UA_DATASETFIELDCONTENTMASK_RAWDATAENCODING) {
dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_RAWDATA;
} else if (dataSetWriter->config.dataSetFieldContentMask &
((unsigned int) UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP |
(unsigned int) UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS |
(unsigned int) UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS |
(unsigned int) UA_DATASETFIELDCONTENTMASK_STATUSCODE)) {
(UA_DATASETFIELDCONTENTMASK_SOURCETIMESTAMP | UA_DATASETFIELDCONTENTMASK_SERVERPICOSECONDS |
UA_DATASETFIELDCONTENTMASK_SOURCEPICOSECONDS | UA_DATASETFIELDCONTENTMASK_STATUSCODE)) {
dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_DATAVALUE;
} else {
dataSetMessage->header.fieldEncoding = UA_FIELDENCODING_VARIANT;
}
//Std: 'The DataSetMessageContentMask defines the flags for the content of the DataSetMessage header.'
if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_MAJORVERSION){
/* 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;
dataSetMessage->header.configVersionMajorVersion =
currentDataSet->dataSetMetaData.configurationVersion.majorVersion;
}
if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION){
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_MINORVERSION){
dataSetMessage->header.configVersionMinorVersionEnabled = UA_TRUE;
dataSetMessage->header.configVersionMinorVersion = currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
dataSetMessage->header.configVersionMinorVersion =
currentDataSet->dataSetMetaData.configurationVersion.minorVersion;
}
if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_SEQUENCENUMBER) {
dataSetMessage->header.dataSetMessageSequenceNrEnabled = UA_TRUE;
dataSetMessage->header.dataSetMessageSequenceNr = dataSetWriter->actualDataSetMessageSequenceCount;
dataSetMessage->header.dataSetMessageSequenceNr =
dataSetWriter->actualDataSetMessageSequenceCount;
}
if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) {
if(dataSetWriterMessageDataType->dataSetMessageContentMask & UA_UADPDATASETMESSAGECONTENTMASK_TIMESTAMP) {
dataSetMessage->header.timestampEnabled = UA_TRUE;
dataSetMessage->header.timestamp = UA_DateTime_now();
if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_PICOSECONDS) {
dataSetMessage->header.picoSecondsIncluded = UA_FALSE;
UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "DSM picosecond field is currently not supported. Using defaults");
}
}
if(dataSetWriterMessageDataType->dataSetMessageContentMask & (unsigned int) UA_UADPDATASETMESSAGECONTENTMASK_STATUS){
/* 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;
UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER, "DSM status field is currently not supported. Using defaults");
}
if(dataSetWriter->actualDataSetMessageSequenceCount < UA_UINT16_MAX){
dataSetWriter->actualDataSetMessageSequenceCount++;
} else {
dataSetWriter->actualDataSetMessageSequenceCount = 0;
}
//check if the PublishedDataSet version has changed -> if yes flush the lastValue store and send a KeyFrame.
/* Set the sequence count. Automatically rolls over to zero */
dataSetWriter->actualDataSetMessageSequenceCount++;
#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 ||
dataSetWriter->connectedDataSetVersion.minorVersion != currentDataSet->dataSetMetaData.configurationVersion.minorVersion) {
/* Remove old samples */
for(size_t i = 0; i < dataSetWriter->lastSamplesCount; i++)
UA_DataValue_deleteMembers(&dataSetWriter->lastSamples[i].value);
//realloc pds dependent memory
/* Realloc pds dependent memory */
dataSetWriter->lastSamplesCount = currentDataSet->fieldSize;
dataSetWriter->lastSamples = (UA_DataSetWriterSample * ) UA_realloc(dataSetWriter->lastSamples,
sizeof(UA_DataSetWriterSample) * dataSetWriter->lastSamplesCount);
if(!dataSetWriter->lastSamples)
UA_DataSetWriterSample *newSamplesArray = (UA_DataSetWriterSample * )
UA_realloc(dataSetWriter->lastSamples, sizeof(UA_DataSetWriterSample) * dataSetWriter->lastSamplesCount);
if(!newSamplesArray)
return UA_STATUSCODE_BADOUTOFMEMORY;
dataSetWriter->lastSamples = newSamplesArray;
memset(dataSetWriter->lastSamples, 0, sizeof(UA_DataSetWriterSample) * dataSetWriter->lastSamplesCount);
for (size_t i = 0; i < dataSetWriter->lastSamplesCount; i++) {
dataSetWriter->lastSamples[i].value = (UA_DataValue *) UA_calloc(1, sizeof(UA_DataValue));
if(!dataSetWriter->lastSamples[i].value)
return UA_STATUSCODE_BADOUTOFMEMORY;
}
dataSetWriter->connectedDataSetVersion = currentDataSet->dataSetMetaData.configurationVersion;
UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter);
dataSetWriter->deltaFrameCounter = 0;
} else if (currentDataSet->fieldSize == 1 || dataSetWriter->deltaFrameCounter == 0 || dataSetWriter->deltaFrameCounter > dataSetWriter->config.keyFrameCount){
//@info the standard defines: if a PDS contains only one fields no delta messages should be generated
//because they need more memory than a keyframe with 1 field.
UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter);
dataSetWriter->deltaFrameCounter = 1;
} else {
return UA_STATUSCODE_GOOD;
}
/* The standard defines: if a PDS contains only one fields no delta messages
* should be generated because they need more memory than a keyframe with 1
* field. */
if(currentDataSet->fieldSize > 1 && dataSetWriter->deltaFrameCounter > 0 &&
dataSetWriter->deltaFrameCounter <= dataSetWriter->config.keyFrameCount) {
UA_PubSubDataSetWriter_generateDeltaFrameMessage(server, dataSetMessage, dataSetWriter);
dataSetWriter->deltaFrameCounter++;
return UA_STATUSCODE_GOOD;
}
dataSetWriter->deltaFrameCounter = 1;
#endif
UA_PubSubDataSetWriter_generateKeyFrameMessage(server, dataSetMessage, dataSetWriter);
return UA_STATUSCODE_GOOD;
}

View File

@ -68,10 +68,12 @@ UA_PubSubConnection_deleteMembers(UA_Server *server, UA_PubSubConnection *connec
/* DataSetWriter */
/**********************************************/
#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
typedef struct UA_DataSetWriterSample{
UA_Boolean valeChanged;
UA_DataValue *value;
UA_Boolean valueChanged;
UA_DataValue value;
} UA_DataSetWriterSample;
#endif
typedef struct UA_DataSetWriter{
UA_DataSetWriterConfig config;
@ -81,9 +83,11 @@ typedef struct UA_DataSetWriter{
UA_NodeId linkedWriterGroup;
UA_NodeId connectedDataSet;
UA_ConfigurationVersionDataType connectedDataSetVersion;
#ifdef UA_ENABLE_PUBSUB_DELTAFRAMES
UA_UInt16 deltaFrameCounter; //actual count of sent deltaFrames
size_t lastSamplesCount;
UA_DataSetWriterSample *lastSamples;
#endif
UA_UInt16 actualDataSetMessageSequenceCount;
} UA_DataSetWriter;
@ -130,7 +134,6 @@ typedef struct UA_DataSetField{
UA_FieldMetaData fieldMetaData;
UA_UInt64 sampleCallbackId;
UA_Boolean sampleCallbackIsRegistered;
UA_DataValue lastValue;
} UA_DataSetField;
UA_StatusCode

View File

@ -76,7 +76,7 @@ UA_NetworkMessage_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos,
// ExtendedFlags1
if(UA_NetworkMessage_ExtendedFlags1Enabled(src)) {
v = src->publisherIdType;
v = (UA_Byte)src->publisherIdType;
if(src->dataSetClassIdEnabled)
v |= NM_DATASET_CLASSID_ENABLED_MASK;
@ -99,7 +99,7 @@ UA_NetworkMessage_encodeBinary(const UA_NetworkMessage* src, UA_Byte **bufPos,
// ExtendedFlags2
if(UA_NetworkMessage_ExtendedFlags2Enabled(src)) {
v = src->networkMessageType;
v = (UA_Byte)src->networkMessageType;
// shift left 2 bit
v = (UA_Byte) (v << NM_SHIFT_LEN);
@ -856,7 +856,7 @@ UA_DataSetMessageHeader_encodeBinary(const UA_DataSetMessageHeader* src, UA_Byte
UA_Byte v;
// DataSetFlags1
v = src->fieldEncoding;
v = (UA_Byte)src->fieldEncoding;
// shift left 1 bit
v = (UA_Byte)(v << DS_MH_SHIFT_LEN);
@ -884,7 +884,7 @@ UA_DataSetMessageHeader_encodeBinary(const UA_DataSetMessageHeader* src, UA_Byte
// DataSetFlags2
if(UA_DataSetMessageHeader_DataSetFlags2Enabled(src)) {
v = src->dataSetMessageType;
v = (UA_Byte)src->dataSetMessageType;
if(src->timestampEnabled)
v |= DS_MESSAGEHEADER_TIMESTAMP_ENABLED_MASK;

View File

@ -471,8 +471,8 @@ void mdns_set_address_record(UA_Server *server, const char *fullServiceDomain,
std::string ipv6_str(str_buffer);
// Detect and skip non-external addresses
bool is_link_local(false);
bool is_special_use(false);
UA_Boolean is_link_local(false);
UA_Boolean is_special_use(false);
if(0 == ipv6_str.find("fe")) {
char c = ipv6_str[2];

View File

@ -1,3 +1,4 @@
/* 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/.
@ -13,6 +14,7 @@
* Copyright 2016 (c) Lorenz Haas
* Copyright 2017 (c) frax2222
* Copyright 2017 (c) Mark Giraud, Fraunhofer IOSB
* Copyright 2018 (c) Hilscher Gesellschaft für Systemautomation mbH (Author: Martin Lang)
*/
#include "ua_types.h"
@ -65,6 +67,21 @@ UA_UInt16 UA_Server_addNamespace(UA_Server *server, const char* name) {
return addNamespace(server, nameString);
}
UA_StatusCode
UA_Server_getNamespaceByName(UA_Server *server, const UA_String namespaceUri,
size_t* foundIndex) {
for(size_t idx = 0; idx < server->namespacesSize; idx++)
{
if(UA_String_equal(&server->namespaces[idx], &namespaceUri) == true)
{
(*foundIndex) = idx;
return UA_STATUSCODE_GOOD;
}
}
return UA_STATUSCODE_BADNOTFOUND;
}
UA_StatusCode
UA_Server_forEachChildNodeCall(UA_Server *server, UA_NodeId parentNodeId,
UA_NodeIteratorCallback callback, void *handle) {

View File

@ -77,6 +77,7 @@ typedef enum {
static void
getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
const UA_DataType **responseType, UA_Service *service,
UA_InSituService *serviceInsitu,
UA_Boolean *requiresSession, UA_ServiceType *serviceType) {
switch(requestTypeId) {
case UA_NS0ID_GETENDPOINTSREQUEST_ENCODING_DEFAULTBINARY:
@ -114,14 +115,14 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
break;
#endif
case UA_NS0ID_CREATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_CreateSession;
*service = NULL; //(UA_Service)Service_CreateSession;
*requestType = &UA_TYPES[UA_TYPES_CREATESESSIONREQUEST];
*responseType = &UA_TYPES[UA_TYPES_CREATESESSIONRESPONSE];
*requiresSession = false;
*serviceType = UA_SERVICETYPE_CUSTOM;
break;
case UA_NS0ID_ACTIVATESESSIONREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_ActivateSession;
*service = NULL; //(UA_Service)Service_ActivateSession;
*requestType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONREQUEST];
*responseType = &UA_TYPES[UA_TYPES_ACTIVATESESSIONRESPONSE];
*serviceType = UA_SERVICETYPE_CUSTOM;
@ -132,7 +133,8 @@ getServicePointers(UA_UInt32 requestTypeId, const UA_DataType **requestType,
*responseType = &UA_TYPES[UA_TYPES_CLOSESESSIONRESPONSE];
break;
case UA_NS0ID_READREQUEST_ENCODING_DEFAULTBINARY:
*service = (UA_Service)Service_Read;
*service = NULL;
*serviceInsitu = (UA_InSituService)Service_Read;
*requestType = &UA_TYPES[UA_TYPES_READREQUEST];
*responseType = &UA_TYPES[UA_TYPES_READRESPONSE];
*serviceType = UA_SERVICETYPE_INSITU;
@ -400,12 +402,13 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
/* Get the service pointers */
UA_Service service = NULL;
UA_InSituService serviceInsitu = NULL;
const UA_DataType *requestType = NULL;
const UA_DataType *responseType = NULL;
UA_Boolean sessionRequired = true;
UA_ServiceType serviceType = UA_SERVICETYPE_NORMAL;
getServicePointers(requestTypeId.identifier.numeric, &requestType,
&responseType, &service, &sessionRequired, &serviceType);
&responseType, &service, &serviceInsitu, &sessionRequired, &serviceType);
if(!requestType) {
if(requestTypeId.identifier.numeric == 787) {
UA_LOG_INFO_CHANNEL(server->config.logger, channel,
@ -561,7 +564,7 @@ processMSG(UA_Server *server, UA_SecureChannel *channel,
retval = UA_MessageContext_encode(&mc, response, responseType);
break;
case UA_SERVICETYPE_INSITU:
retval = ((UA_InSituService)service)
retval = serviceInsitu
(server, session, &mc, request, (UA_ResponseHeader*)response);
break;
case UA_SERVICETYPE_NORMAL:

View File

@ -236,9 +236,7 @@ UA_Server_workerCallback(UA_Server *server, UA_ServerCallback callback, void *da
/* A few global NodeId definitions */
extern const UA_NodeId subtypeId;
UA_StatusCode
UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str);
extern const UA_NodeId hierarchicalReferences;
UA_UInt16 addNamespace(UA_Server *server, const UA_String name);

View File

@ -7,6 +7,7 @@
* Copyright 2017 (c) Thomas Bender
* Copyright 2017 (c) Julian Grothoff
* Copyright 2017 (c) Henrik Norrman
* Copyright 2018 (c) Fabian Arndt, Root-Core
*/
#include "ua_server_internal.h"
@ -583,7 +584,7 @@ UA_Server_initNS0(UA_Server *server) {
&maxBrowseContinuationPoints, &UA_TYPES[UA_TYPES_UINT16]);
/* ServerProfileArray */
UA_String profileArray[4];
UA_String profileArray[5];
UA_UInt16 profileArraySize = 0;
#define ADDPROFILEARRAY(x) profileArray[profileArraySize++] = UA_STRING_ALLOC(x)
ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/NanoEmbeddedDevice");
@ -596,6 +597,9 @@ UA_Server_initNS0(UA_Server *server) {
#ifdef UA_ENABLE_SUBSCRIPTIONS
ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/EmbeddedDataChangeSubscription");
#endif
#ifdef UA_ENABLE_HISTORIZING
ADDPROFILEARRAY("http://opcfoundation.org/UA-Profile/Server/HistoricalRawData");
#endif
retVal |= writeNs0VariableArray(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_SERVERPROFILEARRAY,
profileArray, profileArraySize, &UA_TYPES[UA_TYPES_STRING]);
@ -737,6 +741,64 @@ UA_Server_initNS0(UA_Server *server) {
retVal |= writeNs0Variable(server, UA_NS0ID_SERVER_SERVERCAPABILITIES_OPERATIONLIMITS_MAXMONITOREDITEMSPERCALL,
&server->config.maxMonitoredItemsPerCall, &UA_TYPES[UA_TYPES_UINT32]);
#ifdef UA_ENABLE_HISTORIZING
/* ServerCapabilities - HistoryServerCapabilities - AccessHistoryDataCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_ACCESSHISTORYDATACAPABILITY,
&server->config.accessHistoryDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - MaxReturnDataValues */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_MAXRETURNDATAVALUES,
&server->config.maxReturnDataValues, &UA_TYPES[UA_TYPES_UINT32]);
/* ServerCapabilities - HistoryServerCapabilities - AccessHistoryEventsCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_ACCESSHISTORYEVENTSCAPABILITY,
&server->config.accessHistoryEventsCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - MaxReturnEventValues */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_MAXRETURNEVENTVALUES,
&server->config.maxReturnEventValues, &UA_TYPES[UA_TYPES_UINT32]);
/* ServerCapabilities - HistoryServerCapabilities - InsertDataCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTDATACAPABILITY,
&server->config.insertDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - InsertEventCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTEVENTCAPABILITY,
&server->config.insertEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - InsertAnnotationsCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_INSERTANNOTATIONCAPABILITY,
&server->config.insertAnnotationsCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - ReplaceDataCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_REPLACEDATACAPABILITY,
&server->config.replaceDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - ReplaceEventCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_REPLACEEVENTCAPABILITY,
&server->config.replaceEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - UpdateDataCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_UPDATEDATACAPABILITY,
&server->config.updateDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - UpdateEventCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_UPDATEEVENTCAPABILITY,
&server->config.updateEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - DeleteRawCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETERAWCAPABILITY,
&server->config.deleteRawCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - DeleteEventCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETEEVENTCAPABILITY,
&server->config.deleteEventCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
/* ServerCapabilities - HistoryServerCapabilities - DeleteAtTimeDataCapability */
retVal |= writeNs0Variable(server, UA_NS0ID_HISTORYSERVERCAPABILITIES_DELETEATTIMECAPABILITY,
&server->config.deleteAtTimeDataCapability, &UA_TYPES[UA_TYPES_BOOLEAN]);
#endif
#if defined(UA_ENABLE_METHODCALLS) && defined(UA_ENABLE_SUBSCRIPTIONS)
retVal |= UA_Server_setMethodNode_callback(server,
UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_GETMONITOREDITEMS), readMonitoredItems);

View File

@ -14,83 +14,6 @@
#define UA_MAX_TREE_RECURSE 50 /* How deep up/down the tree do we recurse at most? */
/**********************/
/* Parse NumericRange */
/**********************/
static size_t
readDimension(UA_Byte *buf, size_t buflen, UA_NumericRangeDimension *dim) {
size_t progress = UA_readNumber(buf, buflen, &dim->min);
if(progress == 0)
return 0;
if(buflen <= progress + 1 || buf[progress] != ':') {
dim->max = dim->min;
return progress;
}
++progress;
size_t progress2 = UA_readNumber(&buf[progress], buflen - progress, &dim->max);
if(progress2 == 0)
return 0;
/* invalid range */
if(dim->min >= dim->max)
return 0;
return progress + progress2;
}
UA_StatusCode
UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str) {
size_t idx = 0;
size_t dimensionsMax = 0;
UA_NumericRangeDimension *dimensions = NULL;
UA_StatusCode retval = UA_STATUSCODE_GOOD;
size_t offset = 0;
while(true) {
/* alloc dimensions */
if(idx >= dimensionsMax) {
UA_NumericRangeDimension *newds;
size_t newdssize = sizeof(UA_NumericRangeDimension) * (dimensionsMax + 2);
newds = (UA_NumericRangeDimension*)UA_realloc(dimensions, newdssize);
if(!newds) {
retval = UA_STATUSCODE_BADOUTOFMEMORY;
break;
}
dimensions = newds;
dimensionsMax = dimensionsMax + 2;
}
/* read the dimension */
size_t progress = readDimension(&str->data[offset], str->length - offset,
&dimensions[idx]);
if(progress == 0) {
retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
break;
}
offset += progress;
++idx;
/* loop into the next dimension */
if(offset >= str->length)
break;
if(str->data[offset] != ',') {
retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
break;
}
++offset;
}
if(retval == UA_STATUSCODE_GOOD && idx > 0) {
range->dimensions = dimensions;
range->dimensionsSize = idx;
} else
UA_free(dimensions);
return retval;
}
/********************************/
/* Information Model Operations */
/********************************/
@ -415,6 +338,10 @@ UA_Server_processServiceOperations(UA_Server *server, UA_Session *session,
return UA_STATUSCODE_GOOD;
}
/* A few global NodeId definitions */
const UA_NodeId subtypeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}};
const UA_NodeId hierarchicalReferences = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HIERARCHICALREFERENCES}};
/*********************************/
/* Default attribute definitions */
/*********************************/

View File

@ -570,7 +570,6 @@ typeEquivalence(const UA_DataType *t) {
return TYPE_EQUIVALENCE_NONE;
}
const UA_NodeId subtypeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASSUBTYPE}};
static const UA_NodeId enumNodeId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ENUMERATION}};
UA_Boolean

View File

@ -4,7 +4,7 @@
*
* Copyright 2015 (c) Chris Iatrou
* Copyright 2015-2017 (c) Florian Palm
* Copyright 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
* Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
* Copyright 2015-2016 (c) Sten Grüner
* Copyright 2015 (c) Oleksiy Vasylyev
* Copyright 2016 (c) LEvertz
@ -48,9 +48,11 @@ getArgumentsVariableNode(UA_Server *server, const UA_MethodNode *ofMethod,
return NULL;
}
/* inputArgumentResults has the length request->inputArgumentsSize */
static UA_StatusCode
typeCheckArguments(UA_Server *server, const UA_VariableNode *argRequirements,
size_t argsSize, UA_Variant *args) {
size_t argsSize, UA_Variant *args,
UA_StatusCode *inputArgumentResults) {
/* Verify that we have a Variant containing UA_Argument (scalar or array) in
* the "InputArguments" node */
if(argRequirements->valueSource != UA_VALUESOURCE_DATA)
@ -71,33 +73,38 @@ typeCheckArguments(UA_Server *server, const UA_VariableNode *argRequirements,
return UA_STATUSCODE_BADTOOMANYARGUMENTS;
/* Type-check every argument against the definition */
UA_StatusCode retval = UA_STATUSCODE_GOOD;
UA_Argument *argReqs = (UA_Argument*)argRequirements->value.data.value.value.data;
for(size_t i = 0; i < argReqsSize; ++i) {
if(!compatibleValue(server, &argReqs[i].dataType, argReqs[i].valueRank,
argReqs[i].arrayDimensionsSize, argReqs[i].arrayDimensions,
&args[i], NULL))
return UA_STATUSCODE_BADTYPEMISMATCH;
&args[i], NULL)) {
inputArgumentResults[i] = UA_STATUSCODE_BADTYPEMISMATCH;
retval = UA_STATUSCODE_BADINVALIDARGUMENT;
}
}
return UA_STATUSCODE_GOOD;
return retval;
}
/* inputArgumentResults has the length request->inputArgumentsSize */
static UA_StatusCode
validMethodArguments(UA_Server *server, const UA_MethodNode *method,
const UA_CallMethodRequest *request) {
const UA_CallMethodRequest *request,
UA_StatusCode *inputArgumentResults) {
/* Get the input arguments node */
const UA_VariableNode *inputArguments =
getArgumentsVariableNode(server, method, UA_STRING("InputArguments"));
UA_StatusCode retval = UA_STATUSCODE_GOOD;
if(!inputArguments) {
if(request->inputArgumentsSize > 0)
retval = UA_STATUSCODE_BADINVALIDARGUMENT;
return retval;
return UA_STATUSCODE_BADTOOMANYARGUMENTS;
return UA_STATUSCODE_GOOD;
}
/* Verify the request */
retval = typeCheckArguments(server, inputArguments,
request->inputArgumentsSize,
request->inputArguments);
UA_StatusCode retval = typeCheckArguments(server, inputArguments,
request->inputArgumentsSize,
request->inputArguments,
inputArgumentResults);
/* Release the input arguments node */
server->config.nodestore.releaseNode(server->config.nodestore.context,
@ -168,8 +175,27 @@ callWithMethodAndObject(UA_Server *server, UA_Session *session,
return;
}
/* Allocate the inputArgumentResults array */
result->inputArgumentResults = (UA_StatusCode*)
UA_Array_new(request->inputArgumentsSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
if(!result->inputArgumentResults) {
result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
return;
}
result->inputArgumentResultsSize = request->inputArgumentsSize;
/* Verify Input Arguments */
result->statusCode = validMethodArguments(server, method, request);
result->statusCode = validMethodArguments(server, method, request, result->inputArgumentResults);
/* Return inputArgumentResults only for BADINVALIDARGUMENT */
if(result->statusCode != UA_STATUSCODE_BADINVALIDARGUMENT) {
UA_Array_delete(result->inputArgumentResults, result->inputArgumentResultsSize,
&UA_TYPES[UA_TYPES_STATUSCODE]);
result->inputArgumentResults = NULL;
result->inputArgumentResultsSize = 0;
}
/* Error during type-checking? */
if(result->statusCode != UA_STATUSCODE_GOOD)
return;
@ -178,22 +204,20 @@ callWithMethodAndObject(UA_Server *server, UA_Session *session,
getArgumentsVariableNode(server, method, UA_STRING("OutputArguments"));
/* Allocate the output arguments array */
if(outputArguments) {
if(outputArguments->value.data.value.value.arrayLength > 0) {
result->outputArguments = (UA_Variant*)
UA_Array_new(outputArguments->value.data.value.value.arrayLength,
&UA_TYPES[UA_TYPES_VARIANT]);
if(!result->outputArguments) {
result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
return;
}
result->outputArgumentsSize = outputArguments->value.data.value.value.arrayLength;
}
/* Release the output arguments node */
server->config.nodestore.releaseNode(server->config.nodestore.context,
(const UA_Node*)outputArguments);
size_t outputArgsSize = 0;
if(outputArguments)
outputArgsSize = outputArguments->value.data.value.value.arrayLength;
result->outputArguments = (UA_Variant*)
UA_Array_new(outputArgsSize, &UA_TYPES[UA_TYPES_VARIANT]);
if(!result->outputArguments) {
result->statusCode = UA_STATUSCODE_BADOUTOFMEMORY;
return;
}
result->outputArgumentsSize = outputArgsSize;
/* Release the output arguments node */
server->config.nodestore.releaseNode(server->config.nodestore.context,
(const UA_Node*)outputArguments);
/* Call the method */
result->statusCode = method->method(server, &session->sessionId, session->sessionHandle,
@ -212,7 +236,7 @@ Operation_CallMethod(UA_Server *server, UA_Session *session, void *context,
server->config.nodestore.getNode(server->config.nodestore.context,
&request->methodId);
if(!method) {
result->statusCode = UA_STATUSCODE_BADMETHODINVALID;
result->statusCode = UA_STATUSCODE_BADNODEIDUNKNOWN;
return;
}

View File

@ -63,7 +63,6 @@ const UA_NodeId parentReferences[UA_PARENT_REFERENCES_COUNT] = {
{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}
};
/* Check if the requested parent node exists, has the right node class and is
* referenced with an allowed (hierarchical) reference type. For "type" nodes,
* only hasSubType references are allowed. */
@ -137,10 +136,8 @@ checkParentReference(UA_Server *server, UA_Session *session, UA_NodeClass nodeCl
}
/* Test if the referencetype is hierarchical */
const UA_NodeId hierarchicalReference =
UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
if(!isNodeInTree(&server->config.nodestore, referenceTypeId,
&hierarchicalReference, &subtypeId, 1)) {
&hierarchicalReferences, &subtypeId, 1)) {
UA_LOG_INFO_SESSION(server->config.logger, session,
"AddNodes: Reference type to the parent is not hierarchical");
return UA_STATUSCODE_BADREFERENCETYPEIDINVALID;
@ -643,7 +640,7 @@ static UA_StatusCode callConstructors(UA_Server *server, UA_Session *session,
static UA_StatusCode
addRef(UA_Server *server, UA_Session *session, const UA_NodeId *nodeId,
const UA_NodeId *referenceTypeId, const UA_NodeId *parentNodeId,
bool forward) {
UA_Boolean forward) {
UA_StatusCode retval = UA_STATUSCODE_GOOD;
UA_AddReferencesItem ref_item;
UA_AddReferencesItem_init(&ref_item);

View File

@ -71,7 +71,7 @@ Service_CreateSubscription(UA_Server *server, UA_Session *session,
UA_CreateSubscriptionResponse *response) {
/* Check limits for the number of subscriptions */
if((server->config.maxSubscriptionsPerSession != 0) &&
(session->numPublishReq >= server->config.maxSubscriptionsPerSession)) {
(session->numSubscriptions >= server->config.maxSubscriptionsPerSession)) {
response->responseHeader.serviceResult = UA_STATUSCODE_BADTOOMANYSUBSCRIPTIONS;
return;
}
@ -510,12 +510,8 @@ Operation_SetMonitoringMode(UA_Server *server, UA_Session *session,
/* Setting the mode to DISABLED or SAMPLING causes all queued Notifications to be deleted */
UA_Notification *notification, *notification_tmp;
TAILQ_FOREACH_SAFE(notification, &mon->queue, listEntry, notification_tmp) {
TAILQ_REMOVE(&mon->queue, notification, listEntry);
TAILQ_REMOVE(&smc->sub->notificationQueue, notification, globalEntry);
--smc->sub->notificationQueueSize;
UA_Notification_delete(notification);
UA_Notification_delete(smc->sub, mon, notification);
}
mon->queueSize = 0;
/* Initialize lastSampledValue */
UA_ByteString_deleteMembers(&mon->lastSampledValue);

View File

@ -19,6 +19,10 @@
#include "ua_server_internal.h"
#include "ua_services.h"
/**********/
/* Browse */
/**********/
/* Target node on top of the stack */
static UA_StatusCode
fillReferenceDescription(UA_Server *server, const UA_Node *curr,
@ -68,6 +72,14 @@ relevantReference(UA_Server *server, UA_Boolean includeSubtypes,
return isNodeInTree(&server->config.nodestore, testRef, rootRef, &hasSubType, 1);
}
static UA_Boolean
matchClassMask(const UA_Node *node, UA_UInt32 nodeClassMask) {
if(nodeClassMask != UA_NODECLASS_UNSPECIFIED &&
(node->nodeClass & nodeClassMask) == 0)
return false;
return true;
}
/* Returns whether the node / continuationpoint is done */
static UA_Boolean
browseReferences(UA_Server *server, const UA_Node *node,
@ -133,7 +145,7 @@ browseReferences(UA_Server *server, const UA_Node *node,
continue;
/* Test if the node class matches */
if(descr->nodeClassMask != 0 && (target->nodeClass & descr->nodeClassMask) == 0) {
if(!matchClassMask(target, descr->nodeClassMask)) {
UA_Nodestore_release(server, target);
continue;
}
@ -445,7 +457,7 @@ walkBrowsePathElementReferenceTargets(UA_BrowsePathResult *result, size_t *targe
}
static void
walkBrowsePathElement(UA_Server *server, UA_Session *session,
walkBrowsePathElement(UA_Server *server, UA_Session *session, UA_UInt32 nodeClassMask,
UA_BrowsePathResult *result, size_t *targetsSize,
const UA_RelativePathElement *elem, UA_UInt32 elemDepth,
const UA_QualifiedName *targetName,
@ -474,8 +486,14 @@ walkBrowsePathElement(UA_Server *server, UA_Session *session,
continue;
}
/* Test whether the current node has the target name required in the
* previous path element */
/* Test whether the node fits the class mask */
if(!matchClassMask(node, nodeClassMask)) {
UA_Nodestore_release(server, node);
continue;
}
/* Test whether the node has the target name required in the previous
* path element */
if(targetName && (targetName->namespaceIndex != node->browseName.namespaceIndex ||
!UA_String_equal(&targetName->name, &node->browseName.name))) {
UA_Nodestore_release(server, node);
@ -507,7 +525,7 @@ walkBrowsePathElement(UA_Server *server, UA_Session *session,
/* This assumes that result->targets has enough room for all currentCount elements */
static void
addBrowsePathTargets(UA_Server *server, UA_Session *session,
addBrowsePathTargets(UA_Server *server, UA_Session *session, UA_UInt32 nodeClassMask,
UA_BrowsePathResult *result, const UA_QualifiedName *targetName,
UA_NodeId *current, size_t currentCount) {
for(size_t i = 0; i < currentCount; i++) {
@ -517,14 +535,18 @@ addBrowsePathTargets(UA_Server *server, UA_Session *session,
continue;
}
/* Test whether the current node has the target name required in the
/* Test whether the node fits the class mask */
UA_Boolean skip = !matchClassMask(node, nodeClassMask);
/* Test whether the node has the target name required in the
* previous path element */
UA_Boolean valid = targetName->namespaceIndex == node->browseName.namespaceIndex &&
UA_String_equal(&targetName->name, &node->browseName.name);
if(targetName->namespaceIndex != node->browseName.namespaceIndex ||
!UA_String_equal(&targetName->name, &node->browseName.name))
skip = true;
UA_Nodestore_release(server, node);
if(!valid) {
if(skip) {
UA_NodeId_deleteMembers(&current[i]);
continue;
}
@ -539,7 +561,7 @@ addBrowsePathTargets(UA_Server *server, UA_Session *session,
static void
walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path,
UA_BrowsePathResult *result, size_t targetsSize,
UA_UInt32 nodeClassMask, UA_BrowsePathResult *result, size_t targetsSize,
UA_NodeId **current, size_t *currentSize, size_t *currentCount,
UA_NodeId **next, size_t *nextSize, size_t *nextCount) {
UA_assert(*currentCount == 1);
@ -551,7 +573,7 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path
/* Iterate over path elements */
UA_assert(path->relativePath.elementsSize > 0);
for(UA_UInt32 i = 0; i < path->relativePath.elementsSize; ++i) {
walkBrowsePathElement(server, session, result, &targetsSize,
walkBrowsePathElement(server, session, nodeClassMask, result, &targetsSize,
&path->relativePath.elements[i], i, targetName,
*current, *currentCount, next, nextSize, nextCount);
@ -599,13 +621,13 @@ walkBrowsePath(UA_Server *server, UA_Session *session, const UA_BrowsePath *path
}
/* Move the elements of current to the targets */
addBrowsePathTargets(server, session, result, targetName, *current, *currentCount);
addBrowsePathTargets(server, session, nodeClassMask, result, targetName, *current, *currentCount);
*currentCount = 0;
}
static void
Operation_TranslateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
void *context, const UA_BrowsePath *path,
UA_UInt32 *nodeClassMask, const UA_BrowsePath *path,
UA_BrowsePathResult *result) {
if(path->relativePath.elementsSize <= 0) {
result->statusCode = UA_STATUSCODE_BADNOTHINGTODO;
@ -662,7 +684,7 @@ Operation_TranslateBrowsePathToNodeIds(UA_Server *server, UA_Session *session,
currentCount = 1;
/* Walk the path elements */
walkBrowsePath(server, session, path, result, targetsSize,
walkBrowsePath(server, session, path, *nodeClassMask, result, targetsSize,
&current, &currentSize, &currentCount,
&next, &nextSize, &nextCount);
@ -690,7 +712,9 @@ UA_Server_translateBrowsePathToNodeIds(UA_Server *server,
const UA_BrowsePath *browsePath) {
UA_BrowsePathResult result;
UA_BrowsePathResult_init(&result);
Operation_TranslateBrowsePathToNodeIds(server, &adminSession, NULL, browsePath, &result);
UA_UInt32 nodeClassMask = 0; /* All node classes */
Operation_TranslateBrowsePathToNodeIds(server, &adminSession, &nodeClassMask,
browsePath, &result);
return result;
}
@ -707,13 +731,44 @@ Service_TranslateBrowsePathsToNodeIds(UA_Server *server, UA_Session *session,
return;
}
UA_UInt32 nodeClassMask = 0; /* All node classes */
response->responseHeader.serviceResult =
UA_Server_processServiceOperations(server, session,
(UA_ServiceOperation)Operation_TranslateBrowsePathToNodeIds,
NULL, &request->browsePathsSize, &UA_TYPES[UA_TYPES_BROWSEPATH],
&nodeClassMask,
&request->browsePathsSize, &UA_TYPES[UA_TYPES_BROWSEPATH],
&response->resultsSize, &UA_TYPES[UA_TYPES_BROWSEPATHRESULT]);
}
UA_BrowsePathResult
UA_Server_browseSimplifiedBrowsePath(UA_Server *server, const UA_NodeId origin,
size_t browsePathSize, const UA_QualifiedName *browsePath) {
/* Construct the BrowsePath */
UA_BrowsePath bp;
UA_BrowsePath_init(&bp);
bp.startingNode = origin;
UA_STACKARRAY(UA_RelativePathElement, rpe, browsePathSize);
memset(rpe, 0, sizeof(UA_RelativePathElement) * browsePathSize);
for(size_t j = 0; j < browsePathSize; j++) {
rpe[j].referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HIERARCHICALREFERENCES);
rpe[j].includeSubtypes = true;
rpe[j].targetName = browsePath[j];
}
bp.relativePath.elements = rpe;
bp.relativePath.elementsSize = browsePathSize;
/* Browse */
UA_BrowsePathResult bpr;
UA_BrowsePathResult_init(&bpr);
UA_UInt32 nodeClassMask = UA_NODECLASS_OBJECT | UA_NODECLASS_VARIABLE;
Operation_TranslateBrowsePathToNodeIds(server, &adminSession, &nodeClassMask, &bp, &bpr);
return bpr;
}
/************/
/* Register */
/************/
void Service_RegisterNodes(UA_Server *server, UA_Session *session,
const UA_RegisterNodesRequest *request,
UA_RegisterNodesResponse *response) {

View File

@ -2,7 +2,7 @@
* 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 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
* Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
* Copyright 2015 (c) Chris Iatrou
* Copyright 2015-2016 (c) Sten Grüner
* Copyright 2017-2018 (c) Thomas Stalder, Blue Time Concept SA
@ -20,16 +20,50 @@
#ifdef UA_ENABLE_SUBSCRIPTIONS /* conditional compilation */
void UA_Notification_delete(UA_Notification *n) {
if(n->mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
UA_DataValue_deleteMembers(&n->data.value);
} else if (n->mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
UA_Array_delete(n->data.event.fields.eventFields, n->data.event.fields.eventFieldsSize,
&UA_TYPES[UA_TYPES_VARIANT]);
/* EventFilterResult currently isn't being used
* UA_EventFilterResult_delete(notification->data.event->result);
*/
void
UA_Notification_enqueue(UA_Server *server, UA_Subscription *sub,
UA_MonitoredItem *mon, UA_Notification *n) {
/* Add to the MonitoredItem */
TAILQ_INSERT_TAIL(&mon->queue, n, listEntry);
++mon->queueSize;
/* Add to the subscription */
TAILQ_INSERT_TAIL(&sub->notificationQueue, n, globalEntry);
++sub->notificationQueueSize;
if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
++sub->dataChangeNotifications;
} else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
++sub->eventNotifications;
} else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_STATUSNOTIFY) {
++sub->statusChangeNotifications;
}
/* Ensure enough space is available in the MonitoredItem. Do this only after
* adding the new Notification. */
MonitoredItem_ensureQueueSpace(server, mon);
}
void
UA_Notification_delete(UA_Subscription *sub, UA_MonitoredItem *mon,
UA_Notification *n) {
if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
UA_DataValue_deleteMembers(&n->data.value);
--sub->dataChangeNotifications;
} else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
UA_EventFieldList_deleteMembers(&n->data.event.fields);
/* EventFilterResult currently isn't being used
* UA_EventFilterResult_delete(notification->data.event->result); */
--sub->eventNotifications;
} else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_STATUSNOTIFY) {
--sub->statusChangeNotifications;
}
TAILQ_REMOVE(&mon->queue, n, listEntry);
--mon->queueSize;
TAILQ_REMOVE(&sub->notificationQueue, n, globalEntry);
--sub->notificationQueueSize;
UA_free(n);
}
@ -151,145 +185,164 @@ UA_Subscription_removeRetransmissionMessage(UA_Subscription *sub, UA_UInt32 sequ
return UA_STATUSCODE_GOOD;
}
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
/* EventChange: Iterate over the monitoredItems of the subscription, starting at mon, and
* move notifications into the response. */
static void
Events_moveNotificationsFromMonitoredItems(UA_Server *server, UA_Subscription *sub, UA_EventFieldList *efls,
size_t eflsSize) {
UA_StatusCode retval;
size_t pos = 0;
UA_Notification *notification, *notification_tmp;
TAILQ_FOREACH_SAFE(notification, &sub->notificationQueue, globalEntry, notification_tmp) {
if (pos >= eflsSize) {
return;
}
UA_MonitoredItem *mon = notification->mon;
/* Remove the notification from the queues */
TAILQ_REMOVE(&sub->notificationQueue, notification, globalEntry);
TAILQ_REMOVE(&mon->queue, notification, listEntry);
/* removing an overflowEvent should not reduce the queueSize */
UA_NodeId overflowId = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE);
if (!(notification->data.event.fields.eventFieldsSize == 1
&& notification->data.event.fields.eventFields->type == &UA_TYPES[UA_TYPES_NODEID]
&& UA_NodeId_equal((UA_NodeId *)notification->data.event.fields.eventFields->data, &overflowId))) {
--mon->queueSize;
--sub->notificationQueueSize;
}
/* Move the content to the response */
UA_EventFieldList *efl = &efls[pos];
efl->clientHandle = mon->clientHandle;
efl->eventFieldsSize = notification->data.event.fields.eventFieldsSize;
retval = UA_Array_copy(notification->data.event.fields.eventFields,
notification->data.event.fields.eventFieldsSize,
(void **) &efl->eventFields, &UA_TYPES[UA_TYPES_VARIANT]);
if (retval != UA_STATUSCODE_GOOD) {
return;
}
/* EventFilterResult currently isn't being used
UA_EventFilterResult_deleteMembers(&notification->data.event.result); */
UA_EventFieldList_deleteMembers(&notification->data.event.fields);
UA_free(notification);
}
}
#endif
/* DataChange: Iterate over the monitoreditems of the subscription, starting at mon, and
* move notifications into the response. */
static void
DataChange_moveNotificationsFromMonitoredItems(UA_Subscription *sub, UA_MonitoredItemNotification *mins,
size_t minsSize) {
size_t pos = 0;
UA_Notification *notification, *notification_tmp;
TAILQ_FOREACH_SAFE(notification, &sub->notificationQueue, globalEntry, notification_tmp) {
if(pos >= minsSize)
return;
UA_MonitoredItem *mon = notification->mon;
/* Remove the notification from the queues */
TAILQ_REMOVE(&sub->notificationQueue, notification, globalEntry);
TAILQ_REMOVE(&mon->queue, notification, listEntry);
--mon->queueSize;
--sub->notificationQueueSize;
/* Move the content to the response */
UA_MonitoredItemNotification *min = &mins[pos];
min->clientHandle = mon->clientHandle;
min->value = notification->data.value;
UA_free(notification);
++pos;
}
}
static UA_StatusCode
prepareNotificationMessage(UA_Server *server, UA_Subscription *sub, UA_NotificationMessage *message,
size_t notifications) {
/* Array of ExtensionObject to hold different kinds of notifications
* (currently only DataChangeNotifications) */
message->notificationData = UA_ExtensionObject_new();
prepareNotificationMessage(UA_Server *server, UA_Subscription *sub,
UA_NotificationMessage *message, size_t notifications) {
UA_assert(notifications > 0);
/* Allocate an ExtensionObject for events and data */
message->notificationData = (UA_ExtensionObject*)
UA_Array_new(2, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
if(!message->notificationData)
return UA_STATUSCODE_BADOUTOFMEMORY;
message->notificationDataSize = 1;
message->notificationDataSize = 2;
UA_ExtensionObject *data = message->notificationData;
data->encoding = UA_EXTENSIONOBJECT_DECODED;
/* TODO: basing type of notificationtype off of first monitoredItem in subscription which isnt very good */
/* Allocate Notification */
if (LIST_FIRST(&sub->monitoredItems)->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
UA_DataChangeNotification *dcn = UA_DataChangeNotification_new();
if (!dcn) {
/* Pre-allocate DataChangeNotifications */
size_t notificationDataIdx = 0;
UA_DataChangeNotification *dcn = NULL;
if(sub->dataChangeNotifications > 0) {
dcn = UA_DataChangeNotification_new();
if(!dcn) {
UA_NotificationMessage_deleteMembers(message);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
data->content.decoded.data = dcn;
data->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION];
message->notificationData->encoding = UA_EXTENSIONOBJECT_DECODED;
message->notificationData->content.decoded.data = dcn;
message->notificationData->content.decoded.type = &UA_TYPES[UA_TYPES_DATACHANGENOTIFICATION];
/* Allocate array of notifications */
dcn->monitoredItems = (UA_MonitoredItemNotification *)
UA_Array_new(notifications,
&UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]);
size_t dcnSize = sub->dataChangeNotifications;
if(dcnSize > notifications)
dcnSize = notifications;
dcn->monitoredItems = (UA_MonitoredItemNotification*)
UA_Array_new(dcnSize, &UA_TYPES[UA_TYPES_MONITOREDITEMNOTIFICATION]);
if(!dcn->monitoredItems) {
UA_NotificationMessage_deleteMembers(message);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
dcn->monitoredItemsSize = notifications;
/* Move notifications into the response .. the point of no return */
DataChange_moveNotificationsFromMonitoredItems(sub, dcn->monitoredItems, notifications);
dcn->monitoredItemsSize = dcnSize;
notificationDataIdx++;
}
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
else if (LIST_FIRST(&sub->monitoredItems)->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
UA_EventNotificationList *enl = UA_EventNotificationList_new();
if (!enl) {
UA_EventNotificationList *enl = NULL;
UA_StatusChangeNotification *scn = NULL;
/* Pre-allocate either StatusChange or EventNotifications. Sending a
* (single) StatusChangeNotification has priority. */
if(sub->statusChangeNotifications > 0) {
scn = UA_StatusChangeNotification_new();
if(!scn) {
UA_NotificationMessage_deleteMembers(message);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
UA_EventNotificationList_init(enl);
data->content.decoded.data = enl;
data->content.decoded.type = &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST];
/* Allocate array of notifications */
enl->events = (UA_EventFieldList *) UA_Array_new(notifications, &UA_TYPES[UA_TYPES_EVENTFIELDLIST]);
if (!enl->events) {
message->notificationData[notificationDataIdx].encoding = UA_EXTENSIONOBJECT_DECODED;
message->notificationData[notificationDataIdx].content.decoded.data = scn;
message->notificationData[notificationDataIdx].content.decoded.type = &UA_TYPES[UA_TYPES_STATUSCHANGENOTIFICATION];
notificationDataIdx++;
} else if(sub->eventNotifications > 0) {
enl = UA_EventNotificationList_new();
if(!enl) {
UA_NotificationMessage_deleteMembers(message);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
enl->eventsSize = notifications;
message->notificationData[notificationDataIdx].encoding = UA_EXTENSIONOBJECT_DECODED;
message->notificationData[notificationDataIdx].content.decoded.data = enl;
message->notificationData[notificationDataIdx].content.decoded.type = &UA_TYPES[UA_TYPES_EVENTNOTIFICATIONLIST];
/* Move the list into the response .. the point of no return */
Events_moveNotificationsFromMonitoredItems(server, sub, enl->events, notifications);
size_t enlSize = sub->eventNotifications;
if(enlSize > notifications)
enlSize = notifications;
enl->events = (UA_EventFieldList*) UA_Array_new(enlSize, &UA_TYPES[UA_TYPES_EVENTFIELDLIST]);
if(!enl->events) {
UA_NotificationMessage_deleteMembers(message);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
enl->eventsSize = enlSize;
notificationDataIdx++;
}
#endif
else {
return UA_STATUSCODE_BADNOTIMPLEMENTED;
UA_assert(notificationDataIdx > 0);
message->notificationDataSize = notificationDataIdx;
/* <-- The point of no return --> */
size_t totalNotifications = 0; /* How many notifications were moved to the response overall? */
size_t dcnPos = 0; /* How many DataChangeNotifications were put into the list? */
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
size_t enlPos = 0; /* How many EventNotifications were moved into the list */
#endif
UA_Notification *notification, *notification_tmp;
TAILQ_FOREACH_SAFE(notification, &sub->notificationQueue, globalEntry, notification_tmp) {
if(totalNotifications >= notifications)
break;
UA_MonitoredItem *mon = notification->mon;
/* Move the content to the response */
if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
UA_assert(dcn != NULL); /* Have at least one change notification */
/* Move the content to the response */
UA_MonitoredItemNotification *min = &dcn->monitoredItems[dcnPos];
min->clientHandle = mon->clientHandle;
min->value = notification->data.value;
UA_DataValue_init(&notification->data.value); /* Reset after the value has been moved */
dcnPos++;
}
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_STATUSNOTIFY && scn) {
// TODO: Handling of StatusChangeNotifications
scn = NULL; /* At most one per PublishReponse */
} else if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY && enl) {
UA_assert(enl != NULL); /* Have at least one event notification */
/* TODO: The following lead to crashes when we assumed notifications to be ready... */
/* /\* removing an overflowEvent should not reduce the queueSize *\/ */
/* UA_NodeId overflowId = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE); */
/* if (!(notification->data.event.fields.eventFieldsSize == 1 */
/* && notification->data.event.fields.eventFields->type == &UA_TYPES[UA_TYPES_NODEID] */
/* && UA_NodeId_equal((UA_NodeId *)notification->data.event.fields.eventFields->data, &overflowId))) { */
/* --mon->queueSize; */
/* --sub->notificationQueueSize; */
/* } */
/* Move the content to the response */
UA_EventFieldList *efl = &enl->events[enlPos];
*efl = notification->data.event.fields;
UA_EventFieldList_init(&notification->data.event.fields);
efl->clientHandle = mon->clientHandle;
/* EventFilterResult currently isn't being used
UA_EventFilterResult_deleteMembers(&notification->data.event.result); */
enlPos++;
}
#endif
else {
continue; /* Nothing to do */
}
/* Remove the notification from the queues */
UA_Notification_delete(sub, mon, notification);
totalNotifications++;
}
/* Set sizes */
if(dcn) {
dcn->monitoredItemsSize = dcnPos;
if(dcnPos == 0) {
UA_free(dcn->monitoredItems);
dcn->monitoredItems = NULL;
}
}
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
if(enl) {
enl->eventsSize = enlPos;
if(enlPos == 0) {
UA_free(enl->events);
enl->events = NULL;
}
}
#endif
return UA_STATUSCODE_GOOD;
}
@ -333,7 +386,8 @@ UA_Subscription_publish(UA_Server *server, UA_Subscription *sub) {
}
}
if (sub->readyNotifications > sub->notificationQueueSize)
/* If there are several late publish responses... */
if(sub->readyNotifications > sub->notificationQueueSize)
sub->readyNotifications = sub->notificationQueueSize;
/* Count the available notifications */
@ -360,7 +414,7 @@ UA_Subscription_publish(UA_Server *server, UA_Subscription *sub) {
sub->subscriptionId);
}
/* We want to send a response. Is it possible? */
/* We want to send a response. Is the channel open? */
UA_SecureChannel *channel = sub->session->header.channel;
if(!channel || !pre) {
UA_LOG_DEBUG_SESSION(server->config.logger, sub->session,
@ -417,7 +471,7 @@ UA_Subscription_publish(UA_Server *server, UA_Subscription *sub) {
* no notifications (and this is a keepalive message). */
message->sequenceNumber = UA_Subscription_nextSequenceNumber(sub->sequenceNumber);
if(notifications != 0) {
if(notifications > 0) {
/* There are notifications. So we can't reuse the sequence number. */
sub->sequenceNumber = message->sequenceNumber;

View File

@ -2,7 +2,7 @@
* 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 2015-2017 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
* Copyright 2015-2018 (c) Fraunhofer IOSB (Author: Julius Pfrommer)
* Copyright 2015 (c) Chris Iatrou
* Copyright 2015-2016 (c) Sten Grüner
* Copyright 2015 (c) Oleksiy Vasylyev
@ -41,7 +41,6 @@ typedef enum {
UA_MONITOREDITEMTYPE_EVENTNOTIFY = 4
} UA_MonitoredItemType;
struct UA_MonitoredItem;
typedef struct UA_MonitoredItem UA_MonitoredItem;
@ -64,8 +63,14 @@ typedef struct UA_Notification {
} data;
} UA_Notification;
/* Clean up the notification. Must be removed from the lists first. */
void UA_Notification_delete(UA_Notification *n);
/* Ensure enough space is available; Add notification to the linked lists;
* Increase the counters */
void UA_Notification_enqueue(UA_Server *server, UA_Subscription *sub,
UA_MonitoredItem *mon, UA_Notification *n);
/* Delete the notification. Also removes it from the linked lists. */
void UA_Notification_delete(UA_Subscription *sub, UA_MonitoredItem *mon,
UA_Notification *n);
typedef TAILQ_HEAD(NotificationQueue, UA_Notification) NotificationQueue;
@ -165,8 +170,16 @@ struct UA_Subscription {
/* Global list of notifications from the MonitoredItems */
NotificationQueue notificationQueue;
UA_UInt32 notificationQueueSize;
UA_UInt32 readyNotifications; /* Notifications to be sent out now (already late) */
UA_UInt32 notificationQueueSize; /* Total queue size */
UA_UInt32 dataChangeNotifications;
UA_UInt32 eventNotifications;
UA_UInt32 statusChangeNotifications;
/* Notifications to be sent out now (already late). In a regular publish
* callback, all queued notifications are sent out. In a late publish
* response, only the notifications left from the last regular publish
* callback are sent. */
UA_UInt32 readyNotifications;
/* Retransmission Queue */
ListOfNotificationMessages retransmissionQueue;

View File

@ -65,13 +65,9 @@ void UA_MonitoredItem_delete(UA_Server *server, UA_MonitoredItem *monitoredItem)
UA_Notification *notification, *notification_tmp;
TAILQ_FOREACH_SAFE(notification, &monitoredItem->queue,
listEntry, notification_tmp) {
/* Remove the item from the queues */
TAILQ_REMOVE(&monitoredItem->queue, notification, listEntry);
TAILQ_REMOVE(&sub->notificationQueue, notification, globalEntry);
--sub->notificationQueueSize;
UA_Notification_delete(notification);
/* Remove the item from the queues and free the memory */
UA_Notification_delete(sub, monitoredItem, notification);
}
monitoredItem->queueSize = 0;
}
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
@ -128,67 +124,53 @@ MonitoredItem_ensureQueueSpace(UA_Server *server, UA_MonitoredItem *mon) {
TAILQ_REMOVE(&sub->notificationQueue, after_del, globalEntry);
TAILQ_INSERT_AFTER(&sub->notificationQueue, del, after_del, globalEntry);
/* Remove the notification from the queues */
TAILQ_REMOVE(&mon->queue, del, listEntry);
TAILQ_REMOVE(&sub->notificationQueue, del, globalEntry);
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
/* TODO: provide additional protection for overflowEvents according to specification */
/* removing an overflowEvent should not reduce the queueSize */
UA_NodeId overflowId = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE);
if(!(del->data.event.fields.eventFieldsSize == 1 &&
del->data.event.fields.eventFields->type == &UA_TYPES[UA_TYPES_NODEID] &&
UA_NodeId_equal((UA_NodeId *)del->data.event.fields.eventFields->data, &overflowId))) {
--mon->queueSize;
--sub->notificationQueueSize;
}
#else
--mon->queueSize;
--sub->notificationQueueSize;
#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
/* Create an overflow notification */
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) {
/* EventFilterResult currently isn't being used
UA_EventFilterResult_deleteMembers(&del->data.event->result); */
UA_EventFieldList_deleteMembers(&del->data.event.fields);
/* if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_EVENTNOTIFY) { */
/* /\* EventFilterResult currently isn't being used */
/* UA_EventFilterResult_deleteMembers(&del->data.event->result); *\/ */
/* UA_EventFieldList_deleteMembers(&del->data.event.fields); */
/* cause an overflowEvent */
/* an overflowEvent does not care about event filters and as such
* will not be "triggered" correctly. Instead, a notification will
* be inserted into the queue which includes only the nodeId of the
* overflowEventType. It is up to the client to check for possible
* overflows. */
UA_Notification *overflowNotification = (UA_Notification *) UA_malloc(sizeof(UA_Notification));
if(!overflowNotification)
return UA_STATUSCODE_BADOUTOFMEMORY;
/* /\* cause an overflowEvent *\/ */
/* /\* an overflowEvent does not care about event filters and as such */
/* * will not be "triggered" correctly. Instead, a notification will */
/* * be inserted into the queue which includes only the nodeId of the */
/* * overflowEventType. It is up to the client to check for possible */
/* * overflows. *\/ */
/* UA_Notification *overflowNotification = (UA_Notification *) UA_malloc(sizeof(UA_Notification)); */
/* if(!overflowNotification) */
/* return UA_STATUSCODE_BADOUTOFMEMORY; */
UA_EventFieldList_init(&overflowNotification->data.event.fields);
overflowNotification->data.event.fields.eventFields = UA_Variant_new();
if(!overflowNotification->data.event.fields.eventFields) {
UA_EventFieldList_deleteMembers(&overflowNotification->data.event.fields);
UA_free(overflowNotification);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
/* UA_EventFieldList_init(&overflowNotification->data.event.fields); */
/* overflowNotification->data.event.fields.eventFields = UA_Variant_new(); */
/* if(!overflowNotification->data.event.fields.eventFields) { */
/* UA_EventFieldList_deleteMembers(&overflowNotification->data.event.fields); */
/* UA_free(overflowNotification); */
/* return UA_STATUSCODE_BADOUTOFMEMORY; */
/* } */
UA_Variant_init(overflowNotification->data.event.fields.eventFields);
overflowNotification->data.event.fields.eventFieldsSize = 1;
UA_Variant_setScalarCopy(overflowNotification->data.event.fields.eventFields,
&overflowId, &UA_TYPES[UA_TYPES_NODEID]);
overflowNotification->mon = mon;
if(mon->discardOldest) {
TAILQ_INSERT_HEAD(&mon->queue, overflowNotification, listEntry);
TAILQ_INSERT_HEAD(&mon->subscription->notificationQueue, overflowNotification, globalEntry);
} else {
TAILQ_INSERT_TAIL(&mon->queue, overflowNotification, listEntry);
TAILQ_INSERT_TAIL(&mon->subscription->notificationQueue, overflowNotification, globalEntry);
}
}
/* UA_Variant_init(overflowNotification->data.event.fields.eventFields); */
/* overflowNotification->data.event.fields.eventFieldsSize = 1; */
/* UA_NodeId overflowId = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE); */
/* UA_Variant_setScalarCopy(overflowNotification->data.event.fields.eventFields, */
/* &overflowId, &UA_TYPES[UA_TYPES_NODEID]); */
/* overflowNotification->mon = mon; */
/* if(mon->discardOldest) { */
/* TAILQ_INSERT_HEAD(&mon->queue, overflowNotification, listEntry); */
/* TAILQ_INSERT_HEAD(&mon->subscription->notificationQueue, overflowNotification, globalEntry); */
/* } else { */
/* TAILQ_INSERT_TAIL(&mon->queue, overflowNotification, listEntry); */
/* TAILQ_INSERT_TAIL(&mon->subscription->notificationQueue, overflowNotification, globalEntry); */
/* } */
/* ++mon->queueSize; */
/* ++sub->notificationQueueSize; */
/* ++sub->eventNotifications; */
/* } */
#endif /* UA_ENABLE_SUBSCRIPTIONS_EVENTS */
/* Free the notification */
UA_Notification_delete(del);
/* Delete the notification. This also removes the notification from the
* linked lists. */
UA_Notification_delete(sub, mon, del);
}
if(mon->monitoredItemType == UA_MONITOREDITEMTYPE_CHANGENOTIFY) {
@ -432,14 +414,8 @@ sampleCallbackWithValue(UA_Server *server, UA_MonitoredItem *monitoredItem,
newNotification->data.value = *value; /* Move the value to the notification */
storedValue = true;
/* Add the notification to the end of local and global queue */
TAILQ_INSERT_TAIL(&monitoredItem->queue, newNotification, listEntry);
TAILQ_INSERT_TAIL(&sub->notificationQueue, newNotification, globalEntry);
++monitoredItem->queueSize;
++sub->notificationQueueSize;
/* Remove some notifications if the queue is beyond maximum capacity */
MonitoredItem_ensureQueueSpace(server, monitoredItem);
/* Enqueue the new notification */
UA_Notification_enqueue(server, sub, monitoredItem, newNotification);
} else {
/* Call the local callback if not attached to a subscription */
UA_LocalMonitoredItem *localMon = (UA_LocalMonitoredItem*) monitoredItem;

View File

@ -30,7 +30,6 @@ UA_Event_generateEventId(UA_Server *server, UA_ByteString *generatedId) {
if(!generatedId->data) {
UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_USERLAND,
"Server unable to allocate memory for EventId data.");
UA_free(generatedId);
return UA_STATUSCODE_BADOUTOFMEMORY;
}
@ -44,145 +43,76 @@ UA_Event_generateEventId(UA_Server *server, UA_ByteString *generatedId) {
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
findAllSubtypesNodeIteratorCallback(UA_NodeId parentId, UA_Boolean isInverse,
UA_NodeId referenceTypeId, void *handle) {
/* only subtypes of hasSubtype */
UA_NodeId hasSubtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
if(isInverse || !UA_NodeId_equal(&referenceTypeId, &hasSubtypeId))
return UA_STATUSCODE_GOOD;
Events_nodeListElement *entry = (Events_nodeListElement *) UA_malloc(sizeof(Events_nodeListElement));
if(!entry)
return UA_STATUSCODE_BADOUTOFMEMORY;
UA_StatusCode retval = UA_NodeId_copy(&parentId, &entry->nodeId);
if(retval != UA_STATUSCODE_GOOD) {
UA_free(entry);
return retval;
}
LIST_INSERT_HEAD(&((struct getNodesHandle *) handle)->nodes, entry, listEntry);
/* recursion */
UA_Server_forEachChildNodeCall(((struct getNodesHandle *) handle)->server,
parentId, findAllSubtypesNodeIteratorCallback, handle);
return UA_STATUSCODE_GOOD;
}
/* Searches for an attribute of an event with the name 'name' and the depth from the event relativePathSize.
* Returns the browsePathResult of searching for that node */
static void
UA_Event_findVariableNode(UA_Server *server, UA_QualifiedName *name, size_t relativePathSize,
const UA_NodeId *event, UA_BrowsePathResult *out) {
/* get a list with all subtypes of aggregates */
struct getNodesHandle handle;
handle.server = server;
LIST_INIT(&handle.nodes);
UA_StatusCode retval =
UA_Server_forEachChildNodeCall(server, UA_NODEID_NUMERIC(0, UA_NS0ID_AGGREGATES),
findAllSubtypesNodeIteratorCallback, &handle);
if(retval != UA_STATUSCODE_GOOD)
out->statusCode = retval;
/* check if you can find the node with any of the subtypes of aggregates */
UA_Boolean nodeFound = UA_FALSE;
Events_nodeListElement *iter, *tmp_iter;
LIST_FOREACH_SAFE(iter, &handle.nodes, listEntry, tmp_iter) {
if (!nodeFound) {
UA_RelativePathElement rpe;
UA_RelativePathElement_init(&rpe);
rpe.referenceTypeId = iter->nodeId;
rpe.isInverse = false;
rpe.includeSubtypes = false;
rpe.targetName = *name;
/* TODO: test larger browsepath perhaps put browsepath in a loop */
UA_BrowsePath bp;
UA_BrowsePath_init(&bp);
bp.relativePath.elementsSize = relativePathSize;
bp.startingNode = *event;
bp.relativePath.elements = &rpe;
*out = UA_Server_translateBrowsePathToNodeIds(server, &bp);
if(out->statusCode == UA_STATUSCODE_GOOD)
nodeFound = UA_TRUE;
}
LIST_REMOVE(iter, listEntry);
UA_NodeId_deleteMembers(&iter->nodeId);
UA_free(iter);
}
}
UA_StatusCode UA_EXPORT
UA_StatusCode
UA_Server_createEvent(UA_Server *server, const UA_NodeId eventType, UA_NodeId *outNodeId) {
if (!outNodeId) {
if(!outNodeId) {
UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND, "outNodeId cannot be NULL!");
return UA_STATUSCODE_BADINVALIDARGUMENT;
}
/* make sure the eventType is a subtype of BaseEventType */
/* Make sure the eventType is a subtype of BaseEventType */
UA_NodeId hasSubtypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE);
UA_NodeId baseEventTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
if (!isNodeInTree(&server->config.nodestore, &eventType, &baseEventTypeId, &hasSubtypeId, 1)) {
UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND, "Event type must be a subtype of BaseEventType!");
if(!isNodeInTree(&server->config.nodestore, &eventType, &baseEventTypeId, &hasSubtypeId, 1)) {
UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND,
"Event type must be a subtype of BaseEventType!");
return UA_STATUSCODE_BADINVALIDARGUMENT;
}
UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
oAttr.displayName.locale = UA_STRING_NULL;
oAttr.displayName.text = UA_STRING_NULL;
oAttr.description.locale = UA_STRING_NULL;
oAttr.description.text = UA_STRING_NULL;
/* Create an ObjectNode which represents the event */
UA_QualifiedName name;
UA_QualifiedName_init(&name);
/* create an ObjectNode which represents the event */
UA_NodeId newNodeId = UA_NODEID_NULL;
UA_ObjectAttributes oAttr = UA_ObjectAttributes_default;
UA_StatusCode retval =
UA_Server_addObjectNode(server,
UA_NODEID_NULL, /* the user may not have control over the nodeId */
UA_NODEID_NULL, /* an event does not have a parent */
UA_NODEID_NULL, /* an event does not have any references */
UA_NODEID_NULL, /* Set a random unused NodeId */
UA_NODEID_NULL, /* No parent */
UA_NODEID_NULL, /* No parent reference */
name, /* an event does not have a name */
eventType, /* the type of the event */
oAttr, /* default attributes are fine */
NULL, /* no node context */
outNodeId);
&newNodeId);
if (retval != UA_STATUSCODE_GOOD) {
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND,
"Adding event failed. StatusCode %s", UA_StatusCode_name(retval));
return retval;
}
/* find the eventType variableNode */
/* Find the eventType variable */
name = UA_QUALIFIEDNAME(0, "EventType");
UA_BrowsePathResult bpr;
UA_BrowsePathResult_init(&bpr);
UA_Event_findVariableNode(server, &name, 1, outNodeId, &bpr);
UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, newNodeId, 1, &name);
if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
retval = bpr.statusCode;
UA_BrowsePathResult_deleteMembers(&bpr);
return bpr.statusCode;
UA_Server_deleteNode(server, newNodeId, true);
UA_NodeId_deleteMembers(&newNodeId);
return retval;
}
/* Set the EventType */
UA_Variant value;
UA_Variant_init(&value);
UA_Variant_setScalarCopy(&value, &eventType, &UA_TYPES[UA_TYPES_NODEID]);
UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
UA_Variant_deleteMembers(&value);
UA_Variant_setScalar(&value, (void*)(uintptr_t)&eventType, &UA_TYPES[UA_TYPES_NODEID]);
retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
UA_BrowsePathResult_deleteMembers(&bpr);
if(retval != UA_STATUSCODE_GOOD) {
UA_Server_deleteNode(server, newNodeId, true);
UA_NodeId_deleteMembers(&newNodeId);
return retval;
}
/* the object is not put in any queues until it is triggered */
return retval;
*outNodeId = newNodeId;
return UA_STATUSCODE_GOOD;
}
static UA_Boolean
isValidEvent(UA_Server *server, const UA_NodeId *validEventParent, const UA_NodeId *eventId) {
/* find the eventType variableNode */
UA_BrowsePathResult bpr;
UA_BrowsePathResult_init(&bpr);
UA_QualifiedName findName = UA_QUALIFIEDNAME(0, "EventType");
UA_Event_findVariableNode(server, &findName, 1, eventId, &bpr);
UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, *eventId, 1, &findName);
if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
UA_BrowsePathResult_deleteMembers(&bpr);
return UA_FALSE;
@ -194,26 +124,69 @@ isValidEvent(UA_Server *server, const UA_NodeId *validEventParent, const UA_Node
return tmp;
}
/* static UA_StatusCode */
/* whereClausesApply(UA_Server *server, const UA_ContentFilter whereClause, */
/* UA_EventFieldList *efl, UA_Boolean *result) { */
/* /\* if the where clauses aren't specified leave everything as is *\/ */
/* if(whereClause.elementsSize == 0) { */
/* *result = UA_TRUE; */
/* return UA_STATUSCODE_GOOD; */
/* } */
/* /\* where clauses were specified *\/ */
/* UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_USERLAND, */
/* "Where clauses are not supported by the server."); */
/* *result = UA_TRUE; */
/* return UA_STATUSCODE_BADNOTSUPPORTED; */
/* } */
/* Part 4: 7.4.4.5 SimpleAttributeOperand
* The clause can point to any attribute of nodes. Either a child of the event
* node and also the event type. */
static UA_StatusCode
whereClausesApply(UA_Server *server, const UA_ContentFilter whereClause,
UA_EventFieldList *efl, UA_Boolean *result) {
/* if the where clauses aren't specified leave everything as is */
if(whereClause.elementsSize == 0) {
*result = UA_TRUE;
return UA_STATUSCODE_GOOD;
resolveSimpleAttributeOperand(UA_Server *server, UA_Session *session, const UA_NodeId *origin,
const UA_SimpleAttributeOperand *sao, UA_Variant *value) {
/* Prepare the ReadValueId */
UA_ReadValueId rvi;
UA_ReadValueId_init(&rvi);
rvi.indexRange = sao->indexRange;
rvi.attributeId = sao->attributeId;
/* If this list (browsePath) is empty the Node is the instance of the
* TypeDefinition. */
if(sao->browsePathSize == 0) {
rvi.nodeId = sao->typeDefinitionId;
UA_DataValue v = UA_Server_readWithSession(server, session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
if(v.status == UA_STATUSCODE_GOOD && v.hasValue)
*value = v.value;
return v.status;
}
/* where clauses were specified */
UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_USERLAND,
"Where clauses are not supported by the server.");
*result = UA_TRUE;
return UA_STATUSCODE_BADNOTSUPPORTED;
/* Resolve the browse path */
UA_BrowsePathResult bpr =
UA_Server_browseSimplifiedBrowsePath(server, *origin, sao->browsePathSize, sao->browsePath);
if(bpr.targetsSize == 0 && bpr.statusCode == UA_STATUSCODE_GOOD)
bpr.statusCode = UA_STATUSCODE_BADNOTFOUND;
if(bpr.statusCode != UA_STATUSCODE_GOOD) {
UA_StatusCode retval = bpr.statusCode;
UA_BrowsePathResult_deleteMembers(&bpr);
return retval;
}
/* Read the first matching element. Move the value to the output. */
rvi.nodeId = bpr.targets[0].targetId.nodeId;
UA_DataValue v = UA_Server_readWithSession(server, session, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
if(v.status == UA_STATUSCODE_GOOD && v.hasValue)
*value = v.value;
UA_BrowsePathResult_deleteMembers(&bpr);
return v.status;
}
/* filters the given event with the given filter and writes the results into a notification */
static UA_StatusCode
UA_Server_filterEvent(UA_Server *server, const UA_NodeId *eventNode, UA_EventFilter *filter,
UA_Server_filterEvent(UA_Server *server, UA_Session *session,
const UA_NodeId *eventNode, UA_EventFilter *filter,
UA_EventNotification *notification) {
if (filter->selectClausesSize == 0)
return UA_STATUSCODE_BADEVENTFILTERINVALID;
@ -234,8 +207,8 @@ UA_Server_filterEvent(UA_Server *server, const UA_NodeId *eventNode, UA_EventFil
}
/* EventFilterResult currently isn't being used
notification->result.selectClauseResultsSize = filter->selectClausesSize;
notification->result.selectClauseResults = (UA_StatusCode *) UA_Array_new(filter->selectClausesSize,
&UA_TYPES[UA_TYPES_VARIANT]);
notification->result.selectClauseResults = (UA_StatusCode *)
UA_Array_new(filter->selectClausesSize, &UA_TYPES[UA_TYPES_STATUSCODE]);
if (!notification->result->selectClauseResults) {
UA_EventFieldList_deleteMembers(&notification->fields);
UA_EventFilterResult_deleteMembers(&notification->result);
@ -256,117 +229,99 @@ UA_Server_filterEvent(UA_Server *server, const UA_NodeId *eventNode, UA_EventFil
continue;
}
/* type is correct */
/* find the variable node with the data being looked for */
UA_BrowsePathResult bpr;
UA_BrowsePathResult_init(&bpr);
UA_Event_findVariableNode(server, filter->selectClauses[i].browsePath,
filter->selectClauses[i].browsePathSize, eventNode, &bpr);
if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
UA_Variant_init(&notification->fields.eventFields[i]);
continue;
}
/* copy the value */
UA_Boolean whereClauseResult = UA_TRUE;
UA_Boolean whereClausesUsed = UA_FALSE; /* placeholder until whereClauses are implemented */
UA_StatusCode retval = whereClausesApply(server, filter->whereClause,
&notification->fields, &whereClauseResult);
if (retval == UA_STATUSCODE_BADNOTSUPPORTED)
whereClausesUsed = UA_TRUE;
if(whereClauseResult) {
retval = UA_Server_readValue(server, bpr.targets[0].targetId.nodeId,
&notification->fields.eventFields[i]);
if(retval != UA_STATUSCODE_GOOD)
UA_Variant_init(&notification->fields.eventFields[i]);
if(whereClausesUsed)
return UA_STATUSCODE_BADNOTSUPPORTED;
} else {
UA_Variant_init(&notification->fields.eventFields[i]);
/* TODO: better statuscode for failing at where clauses */
/* EventFilterResult currently isn't being used
notification->result.selectClauseResults[i] = UA_STATUSCODE_BADDATAUNAVAILABLE; */
}
UA_BrowsePathResult_deleteMembers(&bpr);
/* TODO: Put the result into the selectClausResults */
resolveSimpleAttributeOperand(server, session, eventNode,
&filter->selectClauses[i],
&notification->fields.eventFields[i]);
}
/* UA_Boolean whereClauseResult = UA_TRUE; */
/* return whereClausesApply(server, filter->whereClause, &notification->fields, &whereClauseResult); */
return UA_STATUSCODE_GOOD;
}
static UA_StatusCode
eventSetConstants(UA_Server *server, const UA_NodeId *event,
const UA_NodeId *origin, UA_ByteString *outEventId) {
UA_BrowsePathResult bpr;
UA_BrowsePathResult_init(&bpr);
/* set the source */
eventSetStandardFields(UA_Server *server, const UA_NodeId *event,
const UA_NodeId *origin, UA_ByteString *outEventId) {
/* Set the SourceNode */
UA_StatusCode retval;
UA_QualifiedName name = UA_QUALIFIEDNAME(0, "SourceNode");
UA_Event_findVariableNode(server, &name, 1, event, &bpr);
if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
UA_StatusCode tmp = bpr.statusCode;
UA_BrowsePathResult bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
retval = bpr.statusCode;
UA_BrowsePathResult_deleteMembers(&bpr);
return tmp;
return retval;
}
UA_Variant value;
UA_Variant_init(&value);
UA_Variant_setScalarCopy(&value, origin, &UA_TYPES[UA_TYPES_NODEID]);
UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
UA_Variant_deleteMembers(&value);
UA_BrowsePathResult_deleteMembers(&bpr);
if(retval != UA_STATUSCODE_GOOD)
return retval;
/* set the receive time */
/* Set the ReceiveTime */
name = UA_QUALIFIEDNAME(0, "ReceiveTime");
UA_Event_findVariableNode(server, &name, 1, event, &bpr);
if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
UA_StatusCode tmp = bpr.statusCode;
bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
retval = bpr.statusCode;
UA_BrowsePathResult_deleteMembers(&bpr);
return tmp;
return retval;
}
UA_DateTime time = UA_DateTime_now();
UA_Variant_setScalar(&value, &time, &UA_TYPES[UA_TYPES_DATETIME]);
UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
UA_BrowsePathResult_deleteMembers(&bpr);
/* set the eventId attribute */
UA_ByteString eventId;
UA_ByteString_init(&eventId);
UA_StatusCode retval = UA_Event_generateEventId(server, &eventId);
if (retval != UA_STATUSCODE_GOOD) {
UA_ByteString_deleteMembers(&eventId);
if(retval != UA_STATUSCODE_GOOD)
return retval;
/* Set the EventId */
UA_ByteString eventId = UA_BYTESTRING_NULL;
retval = UA_Event_generateEventId(server, &eventId);
if(retval != UA_STATUSCODE_GOOD)
return retval;
}
if (outEventId) {
UA_ByteString_copy(&eventId, outEventId);
}
name = UA_QUALIFIEDNAME(0, "EventId");
UA_Event_findVariableNode(server, &name, 1, event, &bpr);
if (bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
UA_StatusCode tmp = bpr.statusCode;
bpr = UA_Server_browseSimplifiedBrowsePath(server, *event, 1, &name);
if(bpr.statusCode != UA_STATUSCODE_GOOD || bpr.targetsSize < 1) {
retval = bpr.statusCode;
UA_ByteString_deleteMembers(&eventId);
UA_BrowsePathResult_deleteMembers(&bpr);
return tmp;
return retval;
}
UA_Variant_init(&value);
UA_Variant_setScalar(&value, &eventId, &UA_TYPES[UA_TYPES_BYTESTRING]);
UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
UA_ByteString_deleteMembers(&eventId);
retval = UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
UA_BrowsePathResult_deleteMembers(&bpr);
if(retval != UA_STATUSCODE_GOOD) {
UA_ByteString_deleteMembers(&eventId);
return retval;
}
/* Return the EventId */
if(outEventId)
*outEventId = eventId;
else
UA_ByteString_deleteMembers(&eventId);
return UA_STATUSCODE_GOOD;
}
/* insert each node into the list (passed as handle) */
/* Insert each node into the list (passed as handle) */
static UA_StatusCode
getParentsNodeIteratorCallback(UA_NodeId parentId, UA_Boolean isInverse,
UA_NodeId referenceTypeId, void *handle) {
/* parents have an inverse reference */
UA_NodeId referenceTypeId, struct getNodesHandle *handle) {
/* Parents have an inverse reference */
if(!isInverse)
return UA_STATUSCODE_GOOD;
/* Is this a hierarchical reference? */
if(!isNodeInTree(&handle->server->config.nodestore, &referenceTypeId,
&hierarchicalReferences, &subtypeId, 1))
return UA_STATUSCODE_GOOD;
Events_nodeListElement *entry = (Events_nodeListElement *) UA_malloc(sizeof(Events_nodeListElement));
if (!entry) {
if(!entry)
return UA_STATUSCODE_BADOUTOFMEMORY;
}
UA_StatusCode retval = UA_NodeId_copy(&parentId, &entry->nodeId);
if(retval != UA_STATUSCODE_GOOD) {
@ -374,15 +329,15 @@ getParentsNodeIteratorCallback(UA_NodeId parentId, UA_Boolean isInverse,
return retval;
}
LIST_INSERT_HEAD(&((struct getNodesHandle *) handle)->nodes, entry, listEntry);
LIST_INSERT_HEAD(&handle->nodes, entry, listEntry);
/* recursion */
UA_Server_forEachChildNodeCall(((struct getNodesHandle *) handle)->server,
parentId, getParentsNodeIteratorCallback, handle);
/* Recursion */
UA_Server_forEachChildNodeCall(handle->server, parentId, (UA_NodeIteratorCallback)getParentsNodeIteratorCallback, handle);
return UA_STATUSCODE_GOOD;
}
/* filters an event according to the filter specified by mon and then adds it to mons notification queue */
/* Filters an event according to the filter specified by mon and then adds it to
* mons notification queue */
static UA_StatusCode
UA_Event_addEventToMonitoredItem(UA_Server *server, const UA_NodeId *event,
UA_MonitoredItem *mon) {
@ -390,78 +345,87 @@ UA_Event_addEventToMonitoredItem(UA_Server *server, const UA_NodeId *event,
if(!notification)
return UA_STATUSCODE_BADOUTOFMEMORY;
/* apply the filter */
UA_StatusCode retval = UA_Server_filterEvent(server, event, &mon->filter.eventFilter,
/* Get the session */
UA_Subscription *sub = mon->subscription;
UA_Session *session = sub->session;
/* Apply the filter */
UA_StatusCode retval = UA_Server_filterEvent(server, session, event,
&mon->filter.eventFilter,
&notification->data.event);
if (retval != UA_STATUSCODE_GOOD) {
if(retval != UA_STATUSCODE_GOOD) {
UA_free(notification);
return retval;
}
notification->mon = mon;
/* add to the monitored item queue */
MonitoredItem_ensureQueueSpace(server, mon);
TAILQ_INSERT_TAIL(&mon->queue, notification, listEntry);
++mon->queueSize;
/* add to the subscription queue */
TAILQ_INSERT_TAIL(&mon->subscription->notificationQueue, notification, globalEntry);
++mon->subscription->notificationQueueSize;
/* Enqueue the notification */
notification->mon = mon;
UA_Notification_enqueue(server, mon->subscription, mon, notification);
return UA_STATUSCODE_GOOD;
}
UA_StatusCode UA_EXPORT
static const UA_NodeId objectsFolderId = {0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_OBJECTSFOLDER}};
static const UA_NodeId parentReferences_events[2] =
{{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}},
{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}};
UA_StatusCode
UA_Server_triggerEvent(UA_Server *server, const UA_NodeId eventNodeId, const UA_NodeId origin,
UA_ByteString *outEventId, const UA_Boolean deleteEventNode) {
/* make sure the origin is in the ObjectsFolder (TODO: or in the ViewsFolder) */
UA_NodeId objectsFolderId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId references[2] = {
{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_ORGANIZES}},
{0, UA_NODEIDTYPE_NUMERIC, {UA_NS0ID_HASCOMPONENT}}
};
if(!isNodeInTree(&server->config.nodestore, &origin, &objectsFolderId, references, 2)) {
/* Make sure the origin is in the ObjectsFolder (TODO: or in the ViewsFolder) */
if(!isNodeInTree(&server->config.nodestore, &origin, &objectsFolderId, parentReferences_events, 2)) {
UA_LOG_ERROR(server->config.logger, UA_LOGCATEGORY_USERLAND,
"Node for event must be in ObjectsFolder!");
return UA_STATUSCODE_BADINVALIDARGUMENT;
}
UA_StatusCode retval = eventSetConstants(server, &eventNodeId, &origin, outEventId);
if(retval != UA_STATUSCODE_GOOD)
UA_StatusCode retval = eventSetStandardFields(server, &eventNodeId, &origin, outEventId);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
"Events: Could not set the standard event fields with StatusCode %s",
UA_StatusCode_name(retval));
return retval;
}
/* get an array with all parents */
/* Get an array with all parents. The first call to
* getParentsNodeIteratorCallback adds the emitting node itself. */
struct getNodesHandle parentHandle;
parentHandle.server = server;
LIST_INIT(&parentHandle.nodes);
retval = getParentsNodeIteratorCallback(origin, UA_TRUE, UA_NODEID_NULL, &parentHandle);
if(retval != UA_STATUSCODE_GOOD)
retval = getParentsNodeIteratorCallback(origin, UA_TRUE, parentReferences_events[1], &parentHandle);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
"Events: Could not create the list of nodes listening on the event with StatusCode %s",
UA_StatusCode_name(retval));
return retval;
}
/* add the event to each node's monitored items */
/* Add the event to each node's monitored items */
Events_nodeListElement *parentIter, *tmp_parentIter;
LIST_FOREACH_SAFE(parentIter, &parentHandle.nodes, listEntry, tmp_parentIter) {
const UA_ObjectNode *node = (const UA_ObjectNode *) UA_Nodestore_get(server, &parentIter->nodeId);
/* SLIST_FOREACH */
for (UA_MonitoredItem *monIter = node->monitoredItemQueue; monIter != NULL; monIter = monIter->next) {
retval = UA_Event_addEventToMonitoredItem(server, &eventNodeId, monIter);
if (retval != UA_STATUSCODE_GOOD) {
UA_Nodestore_release(server, (const UA_Node *) node);
return retval;
if(node->nodeClass == UA_NODECLASS_OBJECT) {
for(UA_MonitoredItem *monIter = node->monitoredItemQueue; monIter != NULL; monIter = monIter->next) {
retval = UA_Event_addEventToMonitoredItem(server, &eventNodeId, monIter);
if(retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
"Events: Could not add the event to a listening node with StatusCode %s",
UA_StatusCode_name(retval));
}
}
}
UA_Nodestore_release(server, (const UA_Node *) node);
LIST_REMOVE(parentIter, listEntry);
UA_NodeId_delete(&parentIter->nodeId);
UA_NodeId_deleteMembers(&parentIter->nodeId);
UA_free(parentIter);
}
/* delete the node representation of the event */
if (deleteEventNode) {
/* Delete the node representation of the event */
if(deleteEventNode) {
retval = UA_Server_deleteNode(server, eventNodeId, UA_TRUE);
if (retval != UA_STATUSCODE_GOOD) {
UA_LOG_WARNING(server->config.logger,
UA_LOGCATEGORY_SERVER,
UA_LOG_WARNING(server->config.logger, UA_LOGCATEGORY_SERVER,
"Attempt to remove event using deleteNode failed. StatusCode %s",
UA_StatusCode_name(retval));
return retval;

View File

@ -550,7 +550,7 @@ computeStrides(const UA_Variant *v, const UA_NumericRange range,
*stride = v->arrayLength; /* So it can be copied as a contiguous block. */
*first = 0;
size_t running_dimssize = 1;
bool found_contiguous = false;
UA_Boolean found_contiguous = false;
for(size_t k = dims_count; k > 0;) {
--k;
size_t dimrange = 1 + realmax[k] - range.dimensions[k].min;
@ -567,7 +567,7 @@ computeStrides(const UA_Variant *v, const UA_NumericRange range,
}
/* Is the type string-like? */
static bool
static UA_Boolean
isStringLike(const UA_DataType *type) {
if(type == &UA_TYPES[UA_TYPES_STRING] ||
type == &UA_TYPES[UA_TYPES_BYTESTRING] ||
@ -604,8 +604,8 @@ UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst,
const UA_NumericRange range) {
if(!src->type)
return UA_STATUSCODE_BADINVALIDARGUMENT;
bool isScalar = UA_Variant_isScalar(src);
bool stringLike = isStringLike(src->type);
UA_Boolean isScalar = UA_Variant_isScalar(src);
UA_Boolean stringLike = isStringLike(src->type);
UA_Variant arraySrc;
/* Extract the range for copying at this level. The remaining range is dealt
@ -733,7 +733,7 @@ UA_Variant_copyRange(const UA_Variant *src, UA_Variant *dst,
* variant and strings. This is already possible for reading... */
static UA_StatusCode
Variant_setRange(UA_Variant *v, void *array, size_t arraySize,
const UA_NumericRange range, bool copy) {
const UA_NumericRange range, UA_Boolean copy) {
/* Compute the strides */
size_t count, block, stride, first;
UA_StatusCode retval = computeStrides(v, range, &count,
@ -1109,3 +1109,80 @@ isDataTypeNumeric(const UA_DataType *type) {
return true;
return false;
}
/**********************/
/* Parse NumericRange */
/**********************/
static size_t
readDimension(UA_Byte *buf, size_t buflen, UA_NumericRangeDimension *dim) {
size_t progress = UA_readNumber(buf, buflen, &dim->min);
if(progress == 0)
return 0;
if(buflen <= progress + 1 || buf[progress] != ':') {
dim->max = dim->min;
return progress;
}
++progress;
size_t progress2 = UA_readNumber(&buf[progress], buflen - progress, &dim->max);
if(progress2 == 0)
return 0;
/* invalid range */
if(dim->min >= dim->max)
return 0;
return progress + progress2;
}
UA_StatusCode
UA_NumericRange_parseFromString(UA_NumericRange *range, const UA_String *str) {
size_t idx = 0;
size_t dimensionsMax = 0;
UA_NumericRangeDimension *dimensions = NULL;
UA_StatusCode retval = UA_STATUSCODE_GOOD;
size_t offset = 0;
while(true) {
/* alloc dimensions */
if(idx >= dimensionsMax) {
UA_NumericRangeDimension *newds;
size_t newdssize = sizeof(UA_NumericRangeDimension) * (dimensionsMax + 2);
newds = (UA_NumericRangeDimension*)UA_realloc(dimensions, newdssize);
if(!newds) {
retval = UA_STATUSCODE_BADOUTOFMEMORY;
break;
}
dimensions = newds;
dimensionsMax = dimensionsMax + 2;
}
/* read the dimension */
size_t progress = readDimension(&str->data[offset], str->length - offset,
&dimensions[idx]);
if(progress == 0) {
retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
break;
}
offset += progress;
++idx;
/* loop into the next dimension */
if(offset >= str->length)
break;
if(str->data[offset] != ',') {
retval = UA_STATUSCODE_BADINDEXRANGEINVALID;
break;
}
++offset;
}
if(retval == UA_STATUSCODE_GOOD && idx > 0) {
range->dimensions = dimensions;
range->dimensionsSize = idx;
} else
UA_free(dimensions);
return retval;
}

View File

@ -183,7 +183,7 @@ UA_decode64(const u8 buf[8], u64 *v) {
/* Boolean */
ENCODE_BINARY(Boolean) {
if(ctx->pos + sizeof(bool) > ctx->end)
if(ctx->pos + sizeof(UA_Boolean) > ctx->end)
return UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED;
*ctx->pos = *(const u8*)src;
++ctx->pos;
@ -191,7 +191,7 @@ ENCODE_BINARY(Boolean) {
}
DECODE_BINARY(Boolean) {
if(ctx->pos + sizeof(bool) > ctx->end)
if(ctx->pos + sizeof(UA_Boolean) > ctx->end)
return UA_STATUSCODE_BADDECODINGERROR;
*dst = (*ctx->pos > 0) ? true : false;
++ctx->pos;
@ -968,7 +968,7 @@ DECODE_BINARY(ExtensionObject) {
/* Never returns UA_STATUSCODE_BADENCODINGLIMITSEXCEEDED */
static status
Variant_encodeBinaryWrapExtensionObject(const UA_Variant *src, const bool isArray, Ctx *ctx) {
Variant_encodeBinaryWrapExtensionObject(const UA_Variant *src, const UA_Boolean isArray, Ctx *ctx) {
/* Default to 1 for a scalar. */
size_t length = 1;
@ -1016,8 +1016,8 @@ ENCODE_BINARY(Variant) {
return ENCODE_DIRECT(&encoding, Byte);
/* Set the content type in the encoding mask */
const bool isBuiltin = src->type->builtin;
const bool isAlias = src->type->membersSize == 1
const UA_Boolean isBuiltin = src->type->builtin;
const UA_Boolean isAlias = src->type->membersSize == 1
&& UA_TYPES[src->type->members[0].memberTypeIndex].builtin;
if(isBuiltin)
encoding |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(src->type->typeIndex + 1);
@ -1027,8 +1027,8 @@ ENCODE_BINARY(Variant) {
encoding |= UA_VARIANT_ENCODINGMASKTYPE_TYPEID_MASK & (u8)(UA_TYPES_EXTENSIONOBJECT + 1);
/* Set the array type in the encoding mask */
const bool isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
const bool hasDimensions = isArray && src->arrayDimensionsSize > 0;
const UA_Boolean isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
const UA_Boolean hasDimensions = isArray && src->arrayDimensionsSize > 0;
if(isArray) {
encoding |= UA_VARIANT_ENCODINGMASKTYPE_ARRAY;
if(hasDimensions)
@ -1111,7 +1111,7 @@ DECODE_BINARY(Variant) {
return UA_STATUSCODE_GOOD;
/* Does the variant contain an array? */
const bool isArray = (encodingByte & UA_VARIANT_ENCODINGMASKTYPE_ARRAY) > 0;
const UA_Boolean isArray = (encodingByte & UA_VARIANT_ENCODINGMASKTYPE_ARRAY) > 0;
/* Get the datatype of the content. The type must be a builtin data type.
* All not-builtin types are wrapped in an ExtensionObject. */
@ -1644,9 +1644,9 @@ CALCSIZE_BINARY(Variant) {
if(!src->type)
return s;
bool isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
bool hasDimensions = isArray && src->arrayDimensionsSize > 0;
bool isBuiltin = src->type->builtin;
UA_Boolean isArray = src->arrayLength > 0 || src->data <= UA_EMPTY_ARRAY_SENTINEL;
UA_Boolean hasDimensions = isArray && src->arrayDimensionsSize > 0;
UA_Boolean isBuiltin = src->type->builtin;
size_t encode_index = src->type->typeIndex;

View File

@ -31,9 +31,18 @@ UA_readNumber(u8 *buf, size_t buflen, u32 *number) {
UA_StatusCode
UA_parseEndpointUrl(const UA_String *endpointUrl, UA_String *outHostname,
u16 *outPort, UA_String *outPath) {
/* Url must begin with "opc.tcp://" */
if(endpointUrl->length < 11 || strncmp((char*)endpointUrl->data, "opc.tcp://", 10) != 0)
/* Url must begin with "opc.tcp://" or opc.udp:// (if pubsub enabled) */
if(endpointUrl->length < 11) {
return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
} else if (strncmp((char*)endpointUrl->data, "opc.tcp://", 10) != 0) {
#ifdef UA_ENABLE_PUBSUB
if (strncmp((char*)endpointUrl->data, "opc.udp://", 10) != 0) {
return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
}
#else
return UA_STATUSCODE_BADTCPENDPOINTURLINVALID;
#endif
}
/* Where does the hostname end? */
size_t curr = 10;

View File

@ -2,7 +2,7 @@
find_package(Check REQUIRED)
set(LIBS ${CHECK_LIBRARIES} ${open62541_LIBRARIES})
include_directories(${CHECK_INCLUDE_DIRS})
find_package(Threads REQUIRED)
#find_package(Threads REQUIRED)
if(NOT MSVC AND UA_ENABLE_UNIT_TESTS_MEMCHECK)
find_package(Valgrind REQUIRED)
endif()
@ -145,6 +145,10 @@ add_executable(check_monitoreditem_filter server/check_monitoreditem_filter.c $<
target_link_libraries(check_monitoreditem_filter ${LIBS})
add_test_valgrind(monitoreditem_filter ${TESTS_BINARY_DIR}/check_monitoreditem_filter)
add_executable(check_subscription_events server/check_subscription_events.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
target_link_libraries(check_subscription_events ${LIBS})
add_test_valgrind(subscription_events ${TESTS_BINARY_DIR}/check_subscription_events)
add_executable(check_nodestore server/check_nodestore.c $<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
target_link_libraries(check_nodestore ${LIBS})
add_test_valgrind(nodestore ${TESTS_BINARY_DIR}/check_nodestore)
@ -233,155 +237,5 @@ if(UA_ENABLE_ENCRYPTION)
add_test_valgrind(encryption_basic256sha256 ${TESTS_BINARY_DIR}/check_encryption_basic256sha256)
endif()
#############################
# #
# Test for Nodeset Compiler #
# #
#############################
# can only be tested if UA_ENABLE_FULL_NS0
if (UA_ENABLE_FULL_NS0)
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/src_generated/tests")
# Generate types for DI namespace
set(UA_TYPES_OUT "ua_types_di")
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.h
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_handling.h
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_encoding_binary.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
--namespace=2
--type-csv=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv
--type-bsd=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd
--no-builtin
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}
DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd)
add_custom_target(open62541-generator-tests-types-di DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c)
# Generate types for ADI namespace
set(UA_TYPES_OUT "ua_types_adi")
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.h
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_handling.h
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_encoding_binary.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
--namespace=3
--type-csv=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/OpcUaAdiModel.csv
--type-bsd=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd
--no-builtin
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}
DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/OpcUaAdiModel.csv
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd)
add_custom_target(open62541-generator-tests-types-adi DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c)
# generate DI namespace
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
--internal-headers
--types-array=UA_TYPES
--types-array=UA_TYPES_DI
--existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
--xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di
DEPENDS ${UA_NAMESPACE0_XML}
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
)
add_custom_target(open62541-generator-tests-ns-di DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c)
add_dependencies(open62541-generator-tests-ns-di open62541-generator-tests-types-di)
# generate ADI namespace which is using DI
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
--internal-headers
--types-array=UA_TYPES
--types-array=UA_TYPES_DI
--types-array=UA_TYPES_ADI
--existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
--existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
--xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi
DEPENDS ${UA_NAMESPACE0_XML}
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml
)
add_custom_target(open62541-generator-tests-ns-adi DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c)
add_dependencies(open62541-generator-tests-ns-adi open62541-generator-tests-types-adi open62541-generator-tests-ns-di)
# generate PLCopen namespace which is using DI
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
--internal-headers
--types-array=UA_TYPES
--types-array=UA_TYPES_DI
# PLCopen has no specific type definition, thus use the default UA_TYPES to ignore it
--types-array=UA_TYPES
--existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
--existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
--xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/PLCopen/Opc.Ua.Plc.NodeSet2.xml
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc
DEPENDS ${UA_NAMESPACE0_XML}
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/PLCopen/Opc.Ua.Plc.NodeSet2.xml
)
add_custom_target(open62541-generator-tests-ns-plc DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c)
add_dependencies(open62541-generator-tests-ns-plc open62541-generator-tests-ns-di)
add_executable(check_nodeset_compiler_adi
server/check_nodeset_compiler_adi.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_di_generated.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_adi_generated.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c
$<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
add_dependencies(check_nodeset_compiler_adi open62541-generator-tests-ns-adi)
target_link_libraries(check_nodeset_compiler_adi ${LIBS})
add_test_valgrind(nodeset_compiler_adi ${TESTS_BINARY_DIR}/check_nodeset_compiler_adi)
add_executable(check_nodeset_compiler_plc
server/check_nodeset_compiler_plc.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_di_generated.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c
$<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
add_dependencies(check_nodeset_compiler_plc open62541-generator-tests-ns-plc)
target_link_libraries(check_nodeset_compiler_plc ${LIBS})
add_test_valgrind(nodeset_compiler_plc ${TESTS_BINARY_DIR}/check_nodeset_compiler_plc)
endif()
# Tests for Nodeset Compiler
add_subdirectory(nodeset-compiler)

View File

@ -98,7 +98,7 @@ START_TEST(parseCustomScalar) {
size_t offset = 0;
retval = UA_decodeBinary(&buf, &offset, &var2, &UA_TYPES[UA_TYPES_VARIANT], 1, &PointType);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_ptr_eq(var2.type, &PointType);
ck_assert(var2.type == &PointType);
Point *p2 = (Point*)var2.data;
ck_assert(p.x == p2->x);
@ -137,7 +137,7 @@ START_TEST(parseCustomScalarExtensionObject) {
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_int_eq(eo2.encoding, UA_EXTENSIONOBJECT_DECODED);
ck_assert_ptr_eq(eo2.content.decoded.type, &PointType);
ck_assert(eo2.content.decoded.type == &PointType);
Point *p2 = (Point*)eo2.content.decoded.data;
ck_assert(p.x == p2->x);
@ -173,13 +173,13 @@ START_TEST(parseCustomArray) {
size_t offset = 0;
retval = UA_decodeBinary(&buf, &offset, &var2, &UA_TYPES[UA_TYPES_VARIANT], 1, &PointType);
ck_assert_int_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_ptr_eq(var2.type, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
ck_assert(var2.type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]);
ck_assert_int_eq(var2.arrayLength, 10);
for (size_t i = 0; i < 10; i++) {
UA_ExtensionObject *eo = &((UA_ExtensionObject*)var2.data)[i];
ck_assert_int_eq(eo->encoding, UA_EXTENSIONOBJECT_DECODED);
ck_assert_ptr_eq(eo->content.decoded.type, &PointType);
ck_assert(eo->content.decoded.type == &PointType);
Point *p2 = (Point*)eo->content.decoded.data;
// we need to cast floats to int to avoid comparison of floats

View File

@ -27,9 +27,9 @@ if [ -z ${TRAVIS+x} ]; then
export CC=clang-5.0
export CXX=clang++-5.0
else
# Travis needs an older clang
export CC=clang-3.9
export CXX=clang++-3.9
# Travis needs a specific
export CC=clang-6.0
export CXX=clang++-6.0
fi
# First build and run the unit tests without any specific fuzz settings
cmake -DUA_BUILD_FUZZING_CORPUS=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_ENABLE_ENCRYPTION=ON ..

View File

@ -0,0 +1,172 @@
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/src_generated/tests")
include_directories("${PROJECT_BINARY_DIR}/src_generated/tests")
macro(generate_dataset xml source)
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/${source}.c
${PROJECT_BINARY_DIR}/src_generated/tests/${source}.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
--types-array=UA_TYPES
--existing ${UA_FILE_NS0}
--xml ${xml}
${PROJECT_BINARY_DIR}/src_generated/tests/${source}
DEPENDS ${UA_FILE_NS0} ${xml}
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py)
endmacro()
# Check ObjectType
generate_dataset(${PROJECT_SOURCE_DIR}/tests/nodeset-compiler/objecttype.xml check_nodeset_objecttype_generated)
add_executable(check_nodeset_objecttype check_nodeset_objecttype.c
${PROJECT_BINARY_DIR}/src_generated/tests/check_nodeset_objecttype_generated.c
$<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
target_link_libraries(check_nodeset_objecttype ${LIBS})
add_test_valgrind(nodeset_objecttype ${TESTS_BINARY_DIR}/check_nodeset_objecttype)
###############################################
# Test Companion Specs that need the full NS0 #
###############################################
if(UA_ENABLE_FULL_NS0)
# Generate types for DI namespace
set(UA_TYPES_OUT "ua_types_di")
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.h
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_handling.h
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_encoding_binary.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
--namespace=2
--type-csv=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv
--type-bsd=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd
--no-builtin
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}
DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/OpcUaDiModel.csv
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.Types.bsd)
add_custom_target(open62541-generator-tests-types-di DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c)
# Generate types for ADI namespace
set(UA_TYPES_OUT "ua_types_adi")
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.h
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_handling.h
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated_encoding_binary.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
--namespace=3
--type-csv=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/OpcUaAdiModel.csv
--type-bsd=${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd
--no-builtin
${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}
DEPENDS ${PROJECT_SOURCE_DIR}/tools/generate_datatypes.py
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/OpcUaAdiModel.csv
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.Types.bsd)
add_custom_target(open62541-generator-tests-types-adi DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/${UA_TYPES_OUT}_generated.c)
# generate DI namespace
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
--internal-headers
--types-array=UA_TYPES
--types-array=UA_TYPES_DI
--existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
--xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di
DEPENDS ${UA_NAMESPACE0_XML}
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
)
add_custom_target(open62541-generator-tests-ns-di DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c)
add_dependencies(open62541-generator-tests-ns-di open62541-generator-tests-types-di)
# generate ADI namespace which is using DI
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
--internal-headers
--types-array=UA_TYPES
--types-array=UA_TYPES_DI
--types-array=UA_TYPES_ADI
--existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
--existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
--xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi
DEPENDS ${UA_NAMESPACE0_XML}
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/ADI/Opc.Ua.Adi.NodeSet2.xml
)
add_custom_target(open62541-generator-tests-ns-adi DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c)
add_dependencies(open62541-generator-tests-ns-adi open62541-generator-tests-types-adi open62541-generator-tests-ns-di)
# generate PLCopen namespace which is using DI
add_custom_command(OUTPUT ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.h
PRE_BUILD
COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
--internal-headers
--types-array=UA_TYPES
--types-array=UA_TYPES_DI
# PLCopen has no specific type definition, thus use the default UA_TYPES to ignore it
--types-array=UA_TYPES
--existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/Schema/Opc.Ua.NodeSet2.xml
--existing ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
--xml ${PROJECT_SOURCE_DIR}/deps/ua-nodeset/PLCopen/Opc.Ua.Plc.NodeSet2.xml
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc
DEPENDS ${UA_NAMESPACE0_XML}
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset_compiler.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/nodeset.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/datatypes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_nodes.py
${PROJECT_SOURCE_DIR}/tools/nodeset_compiler/backend_open62541_datatypes.py
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/DI/Opc.Ua.Di.NodeSet2.xml
${PROJECT_SOURCE_DIR}/deps/ua-nodeset/PLCopen/Opc.Ua.Plc.NodeSet2.xml
)
add_custom_target(open62541-generator-tests-ns-plc DEPENDS ${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c)
add_dependencies(open62541-generator-tests-ns-plc open62541-generator-tests-ns-di)
add_executable(check_nodeset_compiler_adi check_nodeset_compiler_adi.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_di_generated.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_adi_generated.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_adi.c
$<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
add_dependencies(check_nodeset_compiler_adi open62541-generator-tests-ns-adi)
target_link_libraries(check_nodeset_compiler_adi ${LIBS})
add_test_valgrind(nodeset_compiler_adi ${TESTS_BINARY_DIR}/check_nodeset_compiler_adi)
add_executable(check_nodeset_compiler_plc check_nodeset_compiler_plc.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_types_di_generated.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_di.c
${PROJECT_BINARY_DIR}/src_generated/tests/ua_namespace_plc.c
$<TARGET_OBJECTS:open62541-object> $<TARGET_OBJECTS:open62541-testplugins>)
add_dependencies(check_nodeset_compiler_plc open62541-generator-tests-ns-plc)
target_link_libraries(check_nodeset_compiler_plc ${LIBS})
add_test_valgrind(nodeset_compiler_plc ${TESTS_BINARY_DIR}/check_nodeset_compiler_plc)
endif()

View File

@ -0,0 +1,45 @@
/* 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/. */
#include "open62541.h"
#include "check_nodeset_objecttype_generated.h"
#include "check.h"
static UA_Server *server = NULL;
static UA_ServerConfig *config = NULL;
static void setup(void) {
config = UA_ServerConfig_new_default();
server = UA_Server_new(config);
UA_StatusCode retval = check_nodeset_objecttype_generated(server);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
}
static void teardown(void) {
UA_Server_delete(server);
UA_ServerConfig_delete(config);
}
START_TEST(checkObjectTypeExists) {
UA_NodeClass nc = UA_NODECLASS_UNSPECIFIED;
UA_StatusCode retval = UA_Server_readNodeClass(server, UA_NODEID_NUMERIC(2, 1001), &nc);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_int_eq(nc, UA_NODECLASS_OBJECTTYPE);
} END_TEST
int main(void) {
Suite *s = suite_create("XML-Generated ObjectType");
TCase *tc_objecttype = tcase_create("objecttype tests");
tcase_add_checked_fixture(tc_objecttype, setup, teardown);
tcase_add_test(tc_objecttype, checkObjectTypeExists);
suite_add_tcase(s, tc_objecttype);
SRunner *sr = srunner_create(s);
srunner_set_fork_status(sr, CK_NOFORK);
srunner_run_all(sr, CK_NORMAL);
int number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -0,0 +1,61 @@
<UANodeSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:uax="http://opcfoundation.org/UA/2008/02/Types.xsd" xmlns="http://opcfoundation.org/UA/2011/03/UANodeSet.xsd" xmlns:s1="http://yourorganisation.org/test/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<NamespaceUris>
<Uri>http://yourorganisation.org/test/</Uri>
</NamespaceUris>
<Aliases>
<Alias Alias="Double">i=11</Alias>
<Alias Alias="Organizes">i=35</Alias>
<Alias Alias="HasModellingRule">i=37</Alias>
<Alias Alias="HasTypeDefinition">i=40</Alias>
<Alias Alias="HasSubtype">i=45</Alias>
<Alias Alias="HasComponent">i=47</Alias>
</Aliases>
<!-- Following object has only references to nodes defined after itself -->
<UAObject NodeId="ns=1;i=5001" BrowseName="1:testInstance">
<DisplayName>testInstance</DisplayName>
<References>
<Reference ReferenceType="Organizes" IsForward="false">ns=1;i=5002</Reference>
<Reference ReferenceType="HasTypeDefinition">ns=1;i=1001</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=6002</Reference>
</References>
</UAObject>
<UAObject NodeId="ns=1;i=5002" BrowseName="1:testFolder">
<DisplayName>testFolder</DisplayName>
<References>
<Reference ReferenceType="Organizes" IsForward="false">i=85</Reference>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
</References>
</UAObject>
<UAObjectType NodeId="ns=1;i=1001" BrowseName="1:testType">
<DisplayName>testType</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
<Reference ReferenceType="HasComponent">ns=1;i=6001</Reference>
</References>
</UAObjectType>
<UAVariable DataType="Double" ParentNodeId="ns=1;i=1001" NodeId="ns=1;i=6001" BrowseName="1:Var1" UserAccessLevel="3" AccessLevel="3">
<DisplayName>Var1</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasModellingRule">i=78</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=1001</Reference>
</References>
<Value>
<uax:Double>42.0</uax:Double>
</Value>
</UAVariable>
<UAVariable ParentNodeId="ns=1;i=5001" NodeId="ns=1;i=6002" BrowseName="1:Var1" DataType="i=7" UserAccessLevel="3" AccessLevel="3">
<DisplayName>Var2</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=63</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">ns=1;i=5001</Reference>
</References>
<Value>
<ListOfUInt32 xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">
<uax:UInt32>1</uax:UInt32>
<uax:UInt32>2</uax:UInt32>
<uax:UInt32>3</uax:UInt32>
</ListOfUInt32>
</Value>
</UAVariable>
</UANodeSet>

View File

@ -405,8 +405,10 @@ START_TEST(Client_find_on_network_registered) {
ck_assert_uint_eq(gethostname(hostname, 255), 0);
//DNS limits name to max 63 chars (+ \0)
snprintf(urls[0], 64, "LDS_test-%s", hostname);
snprintf(urls[1], 64, "Register_test-%s", hostname);
//We need this ugly casting, otherwise gcc >7.2 will complain about format-truncation, but we want it here
void *hostnameVoid = (void*)hostname;
snprintf(urls[0], 64, "LDS_test-%s", (char*)hostnameVoid);
snprintf(urls[1], 64, "Register_test-%s", (char*)hostnameVoid);
expectedUris[0] = UA_STRING(urls[0]);
expectedUris[1] = UA_STRING(urls[1]);
FindOnNetworkAndCheck(expectedUris, 2, NULL, NULL, NULL, 0);

View File

@ -41,7 +41,7 @@ START_TEST(Server_addNamespace_writeService) {
UA_Server_readValue(server, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACEARRAY),
&namespaces);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_ptr_eq(namespaces.type, &UA_TYPES[UA_TYPES_STRING]);
ck_assert(namespaces.type == &UA_TYPES[UA_TYPES_STRING]);
namespaces.data = UA_realloc(namespaces.data, (namespaces.arrayLength + 1) * sizeof(UA_String));
++namespaces.arrayLength;

View File

@ -164,7 +164,7 @@ START_TEST(ReadSingleAttributeValueWithoutTimestamp) {
ck_assert_int_eq(resp.status, UA_STATUSCODE_GOOD);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_INT32] == resp.value.type);
ck_assert_int_eq(42, *(UA_Int32* )resp.value.data);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -202,7 +202,7 @@ START_TEST(ReadSingleAttributeValueRangeWithoutTimestamp) {
UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
ck_assert_int_eq(4, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_INT32] == resp.value.type);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -216,7 +216,7 @@ START_TEST(ReadSingleAttributeNodeIdWithoutTimestamp) {
const UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_NODEID] == resp.value.type);
UA_NodeId* respval = (UA_NodeId*) resp.value.data;
ck_assert_int_eq(1, respval->namespaceIndex);
ck_assert(UA_String_equal(&myIntegerNodeId.identifier.string, &respval->identifier.string));
@ -232,7 +232,7 @@ START_TEST(ReadSingleAttributeNodeClassWithoutTimestamp) {
UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_NODECLASS],resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_NODECLASS] == resp.value.type);
ck_assert_int_eq(*(UA_Int32*)resp.value.data,UA_NODECLASS_VARIABLE);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -248,7 +248,7 @@ START_TEST(ReadSingleAttributeBrowseNameWithoutTimestamp) {
UA_QualifiedName* respval = (UA_QualifiedName*) resp.value.data;
const UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_QUALIFIEDNAME], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_QUALIFIEDNAME] == resp.value.type);
ck_assert_int_eq(1, respval->namespaceIndex);
ck_assert(UA_String_equal(&myIntegerName.name, &respval->name));
UA_DataValue_deleteMembers(&resp);
@ -266,7 +266,7 @@ START_TEST(ReadSingleAttributeDisplayNameWithoutTimestamp) {
const UA_LocalizedText comp = UA_LOCALIZEDTEXT("locale", "the answer");
UA_VariableNode* compNode = makeCompareSequence();
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT] == resp.value.type);
ck_assert(UA_String_equal(&comp.text, &respval->text));
ck_assert(UA_String_equal(&compNode->displayName.locale, &respval->locale));
UA_DataValue_deleteMembers(&resp);
@ -284,7 +284,7 @@ START_TEST(ReadSingleAttributeDescriptionWithoutTimestamp) {
UA_LocalizedText* respval = (UA_LocalizedText*) resp.value.data;
UA_VariableNode* compNode = makeCompareSequence();
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT] == resp.value.type);
ck_assert(UA_String_equal(&compNode->description.locale, &respval->locale));
ck_assert(UA_String_equal(&compNode->description.text, &respval->text));
UA_DataValue_deleteMembers(&resp);
@ -301,7 +301,7 @@ START_TEST(ReadSingleAttributeWriteMaskWithoutTimestamp) {
UA_UInt32* respval = (UA_UInt32*) resp.value.data;
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_UINT32] == resp.value.type);
ck_assert_int_eq(0,*respval);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -331,7 +331,7 @@ START_TEST(ReadSingleAttributeIsAbstractWithoutTimestamp) {
UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_BOOLEAN] == resp.value.type);
ck_assert(*(UA_Boolean* )resp.value.data==false);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -345,7 +345,7 @@ START_TEST(ReadSingleAttributeSymmetricWithoutTimestamp) {
UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_BOOLEAN] == resp.value.type);
ck_assert(*(UA_Boolean* )resp.value.data==false);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -361,7 +361,7 @@ START_TEST(ReadSingleAttributeInverseNameWithoutTimestamp) {
UA_LocalizedText* respval = (UA_LocalizedText*) resp.value.data;
const UA_LocalizedText comp = UA_LOCALIZEDTEXT("", "OrganizedBy");
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT],resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_LOCALIZEDTEXT] == resp.value.type);
ck_assert(UA_String_equal(&comp.text, &respval->text));
ck_assert(UA_String_equal(&comp.locale, &respval->locale));
UA_DataValue_deleteMembers(&resp);
@ -376,7 +376,7 @@ START_TEST(ReadSingleAttributeContainsNoLoopsWithoutTimestamp) {
UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_BOOLEAN] == resp.value.type);
ck_assert(*(UA_Boolean* )resp.value.data==false);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -391,7 +391,7 @@ START_TEST(ReadSingleAttributeEventNotifierWithoutTimestamp) {
ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BYTE],resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_BYTE] == resp.value.type);
ck_assert_int_eq(*(UA_Byte*)resp.value.data, 0);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -407,7 +407,7 @@ START_TEST(ReadSingleAttributeDataTypeWithoutTimestamp) {
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_int_eq(UA_STATUSCODE_GOOD, resp.status);
ck_assert_int_eq(true, resp.hasValue);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_NODEID], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_NODEID] == resp.value.type);
UA_NodeId* respval = (UA_NodeId*)resp.value.data;
ck_assert_int_eq(respval->namespaceIndex,0);
ck_assert_int_eq(respval->identifier.numeric, UA_NS0ID_BASEDATATYPE);
@ -423,7 +423,7 @@ START_TEST(ReadSingleAttributeValueRankWithoutTimestamp) {
UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_INT32], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_INT32] == resp.value.type);
ck_assert_int_eq(-2, *(UA_Int32* )resp.value.data);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -437,7 +437,7 @@ START_TEST(ReadSingleAttributeArrayDimensionsWithoutTimestamp) {
UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_UINT32], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_UINT32] == resp.value.type);
ck_assert_ptr_eq((UA_Int32*)resp.value.data,0);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -451,7 +451,7 @@ START_TEST(ReadSingleAttributeAccessLevelWithoutTimestamp) {
UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BYTE], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_BYTE] == resp.value.type);
ck_assert_int_eq(*(UA_Byte*)resp.value.data, UA_ACCESSLEVELMASK_READ); // set by default
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -485,7 +485,7 @@ START_TEST(ReadSingleAttributeMinimumSamplingIntervalWithoutTimestamp) {
UA_VariableNode *compNode = makeCompareSequence();
UA_Double comp = (UA_Double) compNode->minimumSamplingInterval;
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_DOUBLE], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_DOUBLE] == resp.value.type);
ck_assert(*respval == comp);
UA_DataValue_deleteMembers(&resp);
config->nodestore.deleteNode(config->nodestore.context, (UA_Node*)compNode);
@ -500,7 +500,7 @@ START_TEST(ReadSingleAttributeHistorizingWithoutTimestamp) {
UA_DataValue resp = UA_Server_read(server, &rvi, UA_TIMESTAMPSTORETURN_NEITHER);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_BOOLEAN] == resp.value.type);
ck_assert(*(UA_Boolean*)resp.value.data==false);
UA_DataValue_deleteMembers(&resp);
} END_TEST
@ -516,7 +516,7 @@ START_TEST(ReadSingleAttributeExecutableWithoutTimestamp) {
ck_assert_int_eq(true, resp.hasValue);
ck_assert_int_eq(0, resp.value.arrayLength);
ck_assert_ptr_eq(&UA_TYPES[UA_TYPES_BOOLEAN], resp.value.type);
ck_assert(&UA_TYPES[UA_TYPES_BOOLEAN] == resp.value.type);
ck_assert(*(UA_Boolean*)resp.value.data==true);
UA_DataValue_deleteMembers(&resp);
#endif

View File

@ -2,8 +2,6 @@
* 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/. */
/* TODO add event based testing */
#include "ua_server.h"
#include "server/ua_services.h"
#include "server/ua_server_internal.h"

View File

@ -173,6 +173,19 @@ START_TEST(Service_TranslateBrowsePathsToNodeIds) {
}
END_TEST
START_TEST(BrowseSimplifiedBrowsePath) {
UA_QualifiedName objectsName = UA_QUALIFIEDNAME(0, "Objects");
UA_BrowsePathResult bpr =
UA_Server_browseSimplifiedBrowsePath(server_translate_browse,
UA_NODEID_NUMERIC(0, UA_NS0ID_ROOTFOLDER),
1, &objectsName);
ck_assert_int_eq(bpr.targetsSize, 1);
UA_BrowsePathResult_deleteMembers(&bpr);
}
END_TEST
static Suite *testSuite_Service_TranslateBrowsePathsToNodeIds(void) {
Suite *s = suite_create("Service_TranslateBrowsePathsToNodeIds");
TCase *tc_browse = tcase_create("Browse Service");
@ -183,6 +196,7 @@ static Suite *testSuite_Service_TranslateBrowsePathsToNodeIds(void) {
TCase *tc_translate = tcase_create("TranslateBrowsePathsToNodeIds");
tcase_add_unchecked_fixture(tc_translate, setup_server, teardown_server);
tcase_add_test(tc_translate, Service_TranslateBrowsePathsToNodeIds);
tcase_add_test(tc_translate, BrowseSimplifiedBrowsePath);
suite_add_tcase(s, tc_translate);
return s;

View File

@ -0,0 +1,448 @@
/* 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/. */
#include "ua_client_subscriptions.h"
#include "ua_server.h"
#include "server/ua_services.h"
#include "server/ua_server_internal.h"
#include "server/ua_subscription.h"
#include "ua_config_default.h"
#include "thread_wrapper.h"
#include "check.h"
#include "testing_clock.h"
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
static UA_Server *server;
static UA_ServerConfig *config;
static UA_Boolean *running;
static THREAD_HANDLE server_thread;
UA_Client *client;
static UA_UInt32 subscriptionId;
static UA_UInt32 monitoredItemId;
static UA_NodeId eventType;
static size_t nSelectClauses = 4;
static UA_Boolean notificationReceived;
static UA_SimpleAttributeOperand *selectClauses;
UA_Double publishingInterval = 500.0;
static void addNewEventType(void) {
UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US", "SimpleEventType");
attr.description = UA_LOCALIZEDTEXT_ALLOC("en-US", "The simple event type we created");
UA_Server_addObjectTypeNode(server, UA_NODEID_NULL,
UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE),
UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
UA_QUALIFIEDNAME(0, "SimpleEventType"),
attr, NULL, &eventType);
UA_LocalizedText_deleteMembers(&attr.displayName);
UA_LocalizedText_deleteMembers(&attr.description);
}
static void setupSelectClauses(void) {
// check for severity (set manually), message (set manually), eventType (automatic) and sourceNode (automatic)
selectClauses = (UA_SimpleAttributeOperand *)
UA_Array_new(nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
if (!selectClauses)
return;
for (size_t i = 0; i < nSelectClauses; ++i) {
UA_SimpleAttributeOperand_init(&selectClauses[i]);
selectClauses[i].typeDefinitionId = UA_NODEID_NUMERIC(0, UA_NS0ID_BASEEVENTTYPE);
selectClauses[i].browsePathSize = 1;
selectClauses[i].attributeId = UA_ATTRIBUTEID_VALUE;
selectClauses[i].browsePath = (UA_QualifiedName *)
UA_Array_new(selectClauses[i].browsePathSize, &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
if (!selectClauses[i].browsePathSize) {
UA_Array_delete(selectClauses, nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
}
}
selectClauses[0].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "Severity");
selectClauses[1].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "Message");
selectClauses[2].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "EventType");
selectClauses[3].browsePath[0] = UA_QUALIFIEDNAME_ALLOC(0, "SourceNode");
}
static void
handler_events_simple(UA_Client *lclient, UA_UInt32 subId, void *subContext,
UA_UInt32 monId, void *monContext,
size_t nEventFields, UA_Variant *eventFields) {
UA_Boolean foundSeverity = UA_FALSE;
UA_Boolean foundMessage = UA_FALSE;
UA_Boolean foundType = UA_FALSE;
UA_Boolean foundSource = UA_FALSE;
ck_assert_uint_eq(*(UA_UInt32 *) monContext, monitoredItemId);
ck_assert_uint_eq(nEventFields, nSelectClauses);
// check all event fields
for (unsigned int i = 0; i < nEventFields; i++) {
// find out which attribute of the event is being looked at
if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_UINT16])) {
// Severity
ck_assert_uint_eq(*((UA_UInt16 *) (eventFields[i].data)), 1000);
foundSeverity = UA_TRUE;
} else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])) {
// Message
UA_LocalizedText comp = UA_LOCALIZEDTEXT("en-US", "Generated Event");
ck_assert(UA_String_equal(&((UA_LocalizedText *) eventFields[i].data)->locale, &comp.locale));
ck_assert(UA_String_equal(&((UA_LocalizedText *) eventFields[i].data)->text, &comp.text));
foundMessage = UA_TRUE;
} else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_NODEID])) {
// either SourceNode or EventType
UA_NodeId serverId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER);
if (UA_NodeId_equal((UA_NodeId *)eventFields[i].data, &eventType)) {
// EventType
foundType = UA_TRUE;
} else if (UA_NodeId_equal((UA_NodeId *)eventFields[i].data, &serverId)) {
// SourceNode
foundSource = UA_TRUE;
} else {
ck_assert_msg(UA_FALSE, "NodeId doesn't match");
}
} else {
ck_assert_msg(UA_FALSE, "Field doesn't match");
}
}
ck_assert_uint_eq(foundMessage, UA_TRUE);
ck_assert_uint_eq(foundSeverity, UA_TRUE);
ck_assert_uint_eq(foundType, UA_TRUE);
ck_assert_uint_eq(foundSource, UA_TRUE);
notificationReceived = true;
}
// create a subscription and add a monitored item to it
static void setupSubscription(void) {
// Create subscription
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request,
NULL, NULL, NULL);
subscriptionId = response.subscriptionId;
}
static void removeSubscription(void) {
UA_DeleteSubscriptionsRequest deleteSubscriptionsRequest;
UA_DeleteSubscriptionsRequest_init(&deleteSubscriptionsRequest);
UA_UInt32 removeId = subscriptionId;
deleteSubscriptionsRequest.subscriptionIdsSize = 1;
deleteSubscriptionsRequest.subscriptionIds = &removeId;
UA_DeleteSubscriptionsResponse deleteSubscriptionsResponse;
UA_DeleteSubscriptionsResponse_init(&deleteSubscriptionsResponse);
Service_DeleteSubscriptions(server, &adminSession, &deleteSubscriptionsRequest,
&deleteSubscriptionsResponse);
UA_DeleteSubscriptionsResponse_deleteMembers(&deleteSubscriptionsResponse);
}
THREAD_CALLBACK(serverloop) {
while (*running)
UA_Server_run_iterate(server, true);
return 0;
}
static void setup(void) {
running = UA_Boolean_new();
*running = true;
config = UA_ServerConfig_new_default();
config->maxPublishReqPerSession = 5;
server = UA_Server_new(config);
UA_Server_run_startup(server);
addNewEventType();
setupSelectClauses();
THREAD_CREATE(server_thread, serverloop);
client = UA_Client_new(UA_ClientConfig_default);
UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
setupSubscription();
}
static void teardown(void) {
removeSubscription();
*running = false;
THREAD_JOIN(server_thread);
UA_Server_run_shutdown(server);
UA_Boolean_delete(running);
UA_Server_delete(server);
UA_ServerConfig_delete(config);
UA_Array_delete(selectClauses, nSelectClauses, &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]);
UA_Client_disconnect(client);
UA_Client_delete(client);
}
static UA_StatusCode eventSetup(UA_NodeId *eventNodeId) {
UA_StatusCode retval;
retval = UA_Server_createEvent(server, eventType, eventNodeId);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
// add a severity to the event
UA_Variant value;
UA_RelativePathElement rpe;
UA_RelativePathElement_init(&rpe);
rpe.referenceTypeId = UA_NODEID_NUMERIC(0, UA_NS0ID_HASPROPERTY);
rpe.isInverse = false;
rpe.includeSubtypes = false;
UA_BrowsePath bp;
UA_BrowsePath_init(&bp);
bp.startingNode = *eventNodeId;
bp.relativePath.elementsSize = 1;
bp.relativePath.elements = &rpe;
rpe.targetName = UA_QUALIFIEDNAME(0, "Severity");
UA_BrowsePathResult bpr = UA_Server_translateBrowsePathToNodeIds(server, &bp);
// number with no special meaning
UA_UInt16 eventSeverity = 1000;
UA_Variant_setScalar(&value, &eventSeverity, &UA_TYPES[UA_TYPES_UINT16]);
UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
UA_BrowsePathResult_deleteMembers(&bpr);
//add a message to the event
rpe.targetName = UA_QUALIFIEDNAME(0, "Message");
bpr = UA_Server_translateBrowsePathToNodeIds(server, &bp);
UA_LocalizedText message = UA_LOCALIZEDTEXT("en-US", "Generated Event");
UA_Variant_setScalar(&value, &message, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
UA_Server_writeValue(server, bpr.targets[0].targetId.nodeId, value);
UA_BrowsePathResult_deleteMembers(&bpr);
return retval;
}
static UA_MonitoredItemCreateResult addMonitoredItem(UA_Client_EventNotificationCallback handler) {
UA_MonitoredItemCreateRequest item;
UA_MonitoredItemCreateRequest_init(&item);
item.itemToMonitor.nodeId = UA_NODEID_NUMERIC(0, 2253); // Root->Objects->Server
item.itemToMonitor.attributeId = UA_ATTRIBUTEID_EVENTNOTIFIER;
item.monitoringMode = UA_MONITORINGMODE_REPORTING;
UA_EventFilter filter;
UA_EventFilter_init(&filter);
filter.selectClauses = selectClauses;
filter.selectClausesSize = nSelectClauses;
item.requestedParameters.filter.encoding = UA_EXTENSIONOBJECT_DECODED;
item.requestedParameters.filter.content.decoded.data = &filter;
item.requestedParameters.filter.content.decoded.type = &UA_TYPES[UA_TYPES_EVENTFILTER];
item.requestedParameters.queueSize = 1;
item.requestedParameters.discardOldest = true;
return UA_Client_MonitoredItems_createEvent(client, subscriptionId,
UA_TIMESTAMPSTORETURN_BOTH, item,
&monitoredItemId, handler, NULL);
}
// ensure events are received with proper values
START_TEST(generateEvents)
{
UA_NodeId eventNodeId;
UA_StatusCode retval = eventSetup(&eventNodeId);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
// add a monitored item
UA_MonitoredItemCreateResult createResult = addMonitoredItem(handler_events_simple);
ck_assert_uint_eq(createResult.statusCode, UA_STATUSCODE_GOOD);
// trigger the event
retval = UA_Server_triggerEvent(server, eventNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), NULL, UA_TRUE);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
// let the client fetch the event and check if the correct values were received
notificationReceived = false;
UA_fakeSleep((UA_UInt32) publishingInterval + 100);
retval = UA_Client_run_iterate(client, 0);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(notificationReceived, true);
ck_assert_uint_eq(createResult.revisedQueueSize, 1);
// delete the monitoredItem
UA_DeleteMonitoredItemsRequest deleteRequest;
UA_DeleteMonitoredItemsRequest_init(&deleteRequest);
deleteRequest.subscriptionId = subscriptionId;
deleteRequest.monitoredItemIds = &monitoredItemId;
deleteRequest.monitoredItemIdsSize = 1;
UA_DeleteMonitoredItemsResponse deleteResponse =
UA_Client_MonitoredItems_delete(client, deleteRequest);
ck_assert_uint_eq(deleteResponse.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(deleteResponse.resultsSize, 1);
UA_DeleteMonitoredItemsResponse_deleteMembers(&deleteResponse);
}
END_TEST
static void
handler_events_propagate(UA_Client *lclient, UA_UInt32 subId, void *subContext,
UA_UInt32 monId, void *monContext,
size_t nEventFields, UA_Variant *eventFields) {
UA_Boolean foundSeverity = UA_FALSE;
UA_Boolean foundMessage = UA_FALSE;
UA_Boolean foundType = UA_FALSE;
UA_Boolean foundSource = UA_FALSE;
ck_assert_uint_eq(*(UA_UInt32 *) monContext, monitoredItemId);
ck_assert_uint_eq(nEventFields, nSelectClauses);
// check all event fields
for (unsigned int i = 0; i < nEventFields; i++) {
// find out which attribute of the event is being looked at
if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_UINT16])) {
// Severity
ck_assert_uint_eq(*((UA_UInt16 *) (eventFields[i].data)), 1000);
foundSeverity = UA_TRUE;
} else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])) {
// Message
UA_LocalizedText comp = UA_LOCALIZEDTEXT("en-US", "Generated Event");
ck_assert(UA_String_equal(&((UA_LocalizedText *) eventFields[i].data)->locale, &comp.locale));
ck_assert(UA_String_equal(&((UA_LocalizedText *) eventFields[i].data)->text, &comp.text));
foundMessage = UA_TRUE;
} else if (UA_Variant_hasScalarType(&eventFields[i], &UA_TYPES[UA_TYPES_NODEID])) {
// either SourceNode or EventType
UA_NodeId serverNameSpaceId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACES);
if (UA_NodeId_equal((UA_NodeId *)eventFields[i].data, &eventType)) {
// EventType
foundType = UA_TRUE;
} else if (UA_NodeId_equal((UA_NodeId *)eventFields[i].data, &serverNameSpaceId)) {
// SourceNode
foundSource = UA_TRUE;
} else {
ck_assert_msg(UA_FALSE, "NodeId doesn't match");
}
} else {
ck_assert_msg(UA_FALSE, "Field doesn't match");
}
}
ck_assert_uint_eq(foundMessage, UA_TRUE);
ck_assert_uint_eq(foundSeverity, UA_TRUE);
ck_assert_uint_eq(foundType, UA_TRUE);
ck_assert_uint_eq(foundSource, UA_TRUE);
notificationReceived = true;
}
START_TEST(uppropagation) {
// trigger first event
UA_NodeId eventNodeId;
UA_StatusCode retval = eventSetup(&eventNodeId);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
//add a monitored item
UA_MonitoredItemCreateResult createResult = addMonitoredItem(handler_events_propagate);
ck_assert_uint_eq(createResult.statusCode, UA_STATUSCODE_GOOD);
// trigger the event on a child of server, using namespaces in this case (no reason in particular)
retval = UA_Server_triggerEvent(server, eventNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_NAMESPACES), NULL,
UA_TRUE);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
// let the client fetch the event and check if the correct values were received
notificationReceived = false;
UA_fakeSleep((UA_UInt32) publishingInterval + 100);
retval = UA_Client_run_iterate(client, 0);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(notificationReceived, true);
ck_assert_uint_eq(createResult.revisedQueueSize, 1);
// delete the monitoredItem
UA_DeleteMonitoredItemsRequest deleteRequest;
UA_DeleteMonitoredItemsRequest_init(&deleteRequest);
deleteRequest.subscriptionId = subscriptionId;
deleteRequest.monitoredItemIds = &monitoredItemId;
deleteRequest.monitoredItemIdsSize = 1;
UA_DeleteMonitoredItemsResponse deleteResponse =
UA_Client_MonitoredItems_delete(client, deleteRequest);
ck_assert_uint_eq(deleteResponse.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(deleteResponse.resultsSize, 1);
UA_DeleteMonitoredItemsResponse_deleteMembers(&deleteResponse);
}
END_TEST
/*
static void
handler_events_overflow(UA_Client *lclient, UA_UInt32 subId, void *subContext,
UA_UInt32 monId, void *monContext,
size_t nEventFields, UA_Variant *eventFields) {
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "Event overflow was found");
ck_assert_uint_eq(*(UA_UInt32 *) monContext, monitoredItemId);
ck_assert_uint_eq(nEventFields, 1);
ck_assert(eventFields->type == &UA_TYPES[UA_TYPES_NODEID]);
UA_NodeId comp = UA_NODEID_NUMERIC(0, UA_NS0ID_SIMPLEOVERFLOWEVENTTYPE);
ck_assert((UA_NodeId_equal((UA_NodeId *)eventFields->data, &comp)));
notificationReceived = true;
}
// ensures an eventQueueOverflowEvent is published when appropriate
START_TEST(eventOverflow)
{
// add a monitored item
UA_MonitoredItemCreateResult createResult = addMonitoredItem(handler_events_overflow);
ck_assert_uint_eq(createResult.statusCode, UA_STATUSCODE_GOOD);
// trigger first event
UA_NodeId eventNodeId;
UA_StatusCode retval = UA_STATUSCODE_GOOD;
for (int i = 0; i < 3; i++) {
retval = eventSetup(&eventNodeId);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
retval = UA_Server_triggerEvent(server, eventNodeId, UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER), NULL, UA_TRUE);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
}
// fetch the events
notificationReceived = false;
UA_fakeSleep((UA_UInt32) publishingInterval + 100);
retval = UA_Client_run_iterate(client, 0);
ck_assert_uint_eq(retval, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(notificationReceived, true);
ck_assert_uint_eq(createResult.revisedQueueSize, 1);
// delete the monitoredItem
UA_DeleteMonitoredItemsRequest deleteRequest;
UA_DeleteMonitoredItemsRequest_init(&deleteRequest);
deleteRequest.subscriptionId = subscriptionId;
deleteRequest.monitoredItemIds = &monitoredItemId;
deleteRequest.monitoredItemIdsSize = 1;
UA_DeleteMonitoredItemsResponse deleteResponse =
UA_Client_MonitoredItems_delete(client, deleteRequest);
ck_assert_uint_eq(deleteResponse.responseHeader.serviceResult, UA_STATUSCODE_GOOD);
ck_assert_uint_eq(deleteResponse.resultsSize, 1);
UA_DeleteMonitoredItemsResponse_deleteMembers(&deleteResponse);
}
END_TEST
*/
#endif // UA_ENABLE_SUBSCRIPTIONS_EVENTS
// assumes subscriptions work fine with data change because of other unit test
static Suite *testSuite_Client(void) {
Suite *s = suite_create("Server Subscription Events");
TCase *tc_server = tcase_create("Server Subscription Events");
#ifdef UA_ENABLE_SUBSCRIPTIONS_EVENTS
tcase_add_checked_fixture(tc_server, setup, teardown);
tcase_add_test(tc_server, generateEvents);
tcase_add_test(tc_server, uppropagation);
// tcase_add_test(tc_server, eventOverflow);
#endif // UA_ENABLE_SUBSCRIPTIONS_EVENTS
suite_add_tcase(s, tc_server);
return s;
}
int main(void) {
Suite *s = testSuite_Client();
SRunner *sr = srunner_create(s);
srunner_set_fork_status(sr, CK_NOFORK);
srunner_run_all(sr, CK_NORMAL);
int number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -13,31 +13,31 @@ extern "C" {
#include "ua_plugin_log.h"
typedef struct funcs_called {
bool asym_enc;
bool asym_dec;
UA_Boolean asym_enc;
UA_Boolean asym_dec;
bool sym_enc;
bool sym_dec;
UA_Boolean sym_enc;
UA_Boolean sym_dec;
bool asym_sign;
bool asym_verify;
UA_Boolean asym_sign;
UA_Boolean asym_verify;
bool sym_sign;
bool sym_verify;
UA_Boolean sym_sign;
UA_Boolean sym_verify;
bool newContext;
bool deleteContext;
UA_Boolean newContext;
UA_Boolean deleteContext;
bool makeCertificateThumbprint;
bool generateKey;
bool generateNonce;
UA_Boolean makeCertificateThumbprint;
UA_Boolean generateKey;
UA_Boolean generateNonce;
bool setLocalSymEncryptingKey;
bool setLocalSymSigningKey;
bool setLocalSymIv;
bool setRemoteSymEncryptingKey;
bool setRemoteSymSigningKey;
bool setRemoteSymIv;
UA_Boolean setLocalSymEncryptingKey;
UA_Boolean setLocalSymSigningKey;
UA_Boolean setLocalSymIv;
UA_Boolean setRemoteSymEncryptingKey;
UA_Boolean setRemoteSymSigningKey;
UA_Boolean setRemoteSymIv;
} funcs_called;
typedef struct key_sizes {

View File

@ -1,4 +1,3 @@
$ErrorActionPreference = "Stop"
try {
cd $env:APPVEYOR_BUILD_FOLDER
@ -71,7 +70,7 @@ try {
Write-Host -ForegroundColor Green "`n##### Testing $env:CC_NAME with full NS0 #####`n"
New-Item -ItemType directory -Path "build"
cd build
& cmake -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_FULL_NS0:BOOL=ON -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -G"$env:CC_NAME" ..
& cmake -DUA_ENABLE_SUBSCRIPTIONS_EVENTS:BOOL=ON -DUA_BUILD_EXAMPLES:BOOL=ON -DUA_ENABLE_FULL_NS0:BOOL=ON -DUA_COMPILE_AS_CXX:BOOL=$env:FORCE_CXX -G"$env:CC_NAME" ..
Invoke-Expression $make_cmd
if ($LASTEXITCODE -and $LASTEXITCODE -ne 0) {
Write-Host -ForegroundColor Red "`n`n*** Make failed. Exiting ... ***"

View File

@ -25,49 +25,21 @@ os.environ['HOSTNAME'] = socket.gethostname()
openssl_conf = os.path.join(certsdir, "localhost.cnf")
os.chdir(os.path.abspath(sys.argv[1]))
os.system("""openssl genrsa -out ca.key {}""".format(keysize))
os.system("""openssl req \
-x509 \
-new \
-nodes \
-key ca.key \
-days 3650 \
-subj "/C=DE/O=open62541/CN=open62541.org" \
-out ca.crt""")
os.system("""openssl req \
-new \
-newkey rsa:{} \
-nodes \
-subj "/C=DE/O=open62541/CN=open62541Server@localhost" \
-keyout localhost.key \
-out localhost.csr""".format(keysize))
os.system("""openssl x509 -req \
-days 3650 \
-in localhost.csr \
-CA ca.crt \
-CAkey ca.key \
-CAcreateserial \
-out localhost.crt \
-extfile {} \
-extensions v3_ca""".format(openssl_conf))
-config {} \
-new \
-nodes \
-x509 -sha256 \
-newkey rsa:{} \
-keyout localhost.key -days 365 \
-subj "/C=DE/O=open62541/CN=open62541Server@localhost"\
-out localhost.crt""".format(openssl_conf, keysize))
os.system("openssl x509 -in localhost.crt -outform der -out server_cert.der")
os.system("openssl rsa -inform PEM -in localhost.key -outform DER -out server_key.der")
# Convert certificate authority(CA) file 'ca.crt' into DER encoded form
# to provide as trust list input
os.system("openssl x509 -in ca.crt -outform der -out ca_cert.der")
os.remove("localhost.key")
os.remove("localhost.crt")
os.remove("localhost.csr")
os.remove("ca.srl")
# os.remove("ca.key")
# os.remove("ca.crt")
# if os.path.isfile(os.path.join(sys.argv[1], "server_cert.der")):
# os.remove(os.path.join(sys.argv[1], "server_cert.der"))
# shutil.move("server_cert.der", sys.argv[1])
# if os.path.isfile(os.path.join(sys.argv[1], "ca.crt")):
# os.remove(os.path.join(sys.argv[1], "ca.crt"))
# shutil.move("ca.crt", sys.argv[1])
print("Certificates generated in " + sys.argv[1])

View File

@ -254,7 +254,7 @@ basicConstraints = CA:false
# left out by default.
# keyUsage = cRLSign, keyCertSign
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyCertSign
extendedKeyUsage = TLS Web Server Authentication, TLS Web Client Authentication
# Some might want this also

View File

@ -1,3 +1,11 @@
#check environment variable
if("$ENV{MBEDTLS_FOLDER_INCLUDE}")
set(MBEDTLS_FOLDER_INCLUDE "$ENV{MBEDTLS_FOLDER_INCLUDE}")
endif()
if("$ENV{MBEDTLS_FOLDER_LIBRARY}")
set(MBEDTLS_FOLDER_LIBRARY "$ENV{MBEDTLS_FOLDER_LIBRARY}")
endif()
find_path(MBEDTLS_INCLUDE_DIRS mbedtls/ssl.h HINTS ${MBEDTLS_FOLDER_INCLUDE})
if(UA_BUILD_OSS_FUZZ)

View File

@ -15,6 +15,7 @@ from io import open
parser = argparse.ArgumentParser()
parser.add_argument('statuscodes', help='path/to/Opc.Ua.NodeIds.csv')
parser.add_argument('outfile', help='outfile w/o extension')
parser.add_argument('namespace', help='NS0')
args = parser.parse_args()
rows = []
@ -33,23 +34,22 @@ def printh(string):
printh(u'''/*---------------------------------------------------------
* Autogenerated -- do not modify
* Generated from %s with script %s
* Generated from {0} with script {1}
*-------------------------------------------------------*/
#ifndef UA_NODEIDS_H_
#define UA_NODEIDS_H_
#ifndef UA_NODEIDS_{2}_H_
#define UA_NODEIDS_{2}_H_
/**
* Namespace Zero NodeIds
* ----------------------
* Numeric identifiers of standard-defined nodes in namespace zero. The
* following definitions are autogenerated from the ``NodeIds.csv`` file
* provided with the OPC UA standard. */
''' % (args.statuscodes, sys.argv[0]))
* following definitions are autogenerated from the ``{0}`` file */
'''.format(args.statuscodes, sys.argv[0], args.namespace))
for row in rows:
printh(u"#define UA_NS0ID_%s %s /* %s */" % (row[0].upper(), row[1], row[2]))
printh(u"#define UA_{namespace}ID_{name} {id} /* {description} */".format(namespace=args.namespace, name=row[0].upper(), id=row[1], description=row[2]))
printh(u'''#endif /* UA_NODEIDS_H_ */ ''')
printh(u'''#endif /* UA_NODEIDS_{0}_H_ */ '''.format(args.namespace))
fh.close()

View File

@ -30,7 +30,6 @@ except ImportError:
logger = logging.getLogger(__name__)
from constants import *
from nodes import *
from nodeset import *
from backend_open62541_nodes import generateNodeCode_begin, generateNodeCode_finish, generateReferenceCode

View File

@ -320,14 +320,14 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_
or (len(node.value) > 1
and (parentNode.valueRank != -2 or parentNode.valueRank != -3))):
# User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_GUID:
if isinstance(node.value[0], Guid):
logger.warn("Don't know how to print array of GUID in node " + str(parentNode.id))
elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
elif isinstance(node.value[0], DiagnosticInfo):
logger.warn("Don't know how to print array of DiagnosticInfo in node " + str(parentNode.id))
elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_STATUSCODE:
elif isinstance(node.value[0], StatusCode):
logger.warn("Don't know how to print array of StatusCode in node " + str(parentNode.id))
else:
if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
if isinstance(node.value[0], ExtensionObject):
for idx, v in enumerate(node.value):
logger.debug("Building extObj array index " + str(idx))
[code1, codeCleanup1] = generateExtensionObjectSubtypeCode(v, parent=parentNode, nodeset=nodeset, arrayIndex=idx, max_string_length=max_string_length,
@ -335,7 +335,7 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_
code = code + code1
codeCleanup = codeCleanup + codeCleanup1
code.append("UA_" + node.value[0].__class__.__name__ + " " + valueName + "[" + str(len(node.value)) + "];")
if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
if isinstance(node.value[0], ExtensionObject):
for idx, v in enumerate(node.value):
logger.debug("Printing extObj array index " + str(idx))
instanceName = generateNodeValueInstanceName(v, parentNode, 0, idx)
@ -353,21 +353,21 @@ def generateValueCode(node, parentNode, nodeset, bootstrapping=True, max_string_
getTypesArrayForValue(nodeset, node.value[0]) + ");")
else:
# User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_GUID:
if isinstance(node.value[0], Guid):
logger.warn("Don't know how to print scalar GUID in node " + str(parentNode.id))
elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_DIAGNOSTICINFO:
elif isinstance(node.value[0], DiagnosticInfo):
logger.warn("Don't know how to print scalar DiagnosticInfo in node " + str(parentNode.id))
elif node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_STATUSCODE:
elif isinstance(node.value[0], StatusCode):
logger.warn("Don't know how to print scalar StatusCode in node " + str(parentNode.id))
else:
# The following strategy applies to all other types, in particular strings and numerics.
if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
if isinstance(node.value[0], ExtensionObject):
[code1, codeCleanup1] = generateExtensionObjectSubtypeCode(node.value[0], parent=parentNode, nodeset=nodeset, max_string_length=max_string_length,
encode_binary_size=encode_binary_size)
code = code + code1
codeCleanup = codeCleanup + codeCleanup1
instanceName = generateNodeValueInstanceName(node.value[0], parentNode, 0, 0)
if node.value[0].numericRepresentation == BUILTINTYPE_TYPEID_EXTENSIONOBJECT:
if isinstance(node.value[0], ExtensionObject):
code.append("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " = " +
generateNodeValueCode(node.value[0], instanceName, max_string_length=max_string_length) + ";")
code.append(

View File

@ -1,64 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 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/.
###
### Author: Chris Iatrou (ichrispa@core-vector.net)
### Version: rev 13
###
### This program was created for educational purposes and has been
### contributed to the open62541 project by the author. All licensing
### terms for this source is inherited by the terms and conditions
### specified for by the open62541 project (see the projects readme
### file for more information on the MPLv2 terms and restrictions).
###
### This program is not meant to be used in a production environment. The
### author is not liable for any complications arising due to the use of
### this program.
###
NODE_CLASS_GENERERIC = 0
NODE_CLASS_OBJECT = 1
NODE_CLASS_VARIABLE = 2
NODE_CLASS_METHOD = 4
NODE_CLASS_OBJECTTYPE = 8
NODE_CLASS_VARIABLETYPE = 16
NODE_CLASS_REFERENCETYPE = 32
NODE_CLASS_DATATYPE = 64
NODE_CLASS_VIEW = 128
# Not in OPC-UA, but exists in XML
NODE_CLASS_METHODTYPE = 256
##
## Numeric codes used to encode binary type fields:
##
BUILTINTYPE_TYPEID_EXTENSIONOBJECT = 1
BUILTINTYPE_TYPEID_LOCALIZEDTEXT = 2
BUILTINTYPE_TYPEID_EXPANDEDNODEID = 3
BUILTINTYPE_TYPEID_NODEID = 4
BUILTINTYPE_TYPEID_DATETIME = 5
BUILTINTYPE_TYPEID_QUALIFIEDNAME = 6
BUILTINTYPE_TYPEID_STATUSCODE = 7
BUILTINTYPE_TYPEID_GUID = 8
BUILTINTYPE_TYPEID_BOOLEAN = 9
BUILTINTYPE_TYPEID_BYTE = 10
BUILTINTYPE_TYPEID_SBYTE = 11
BUILTINTYPE_TYPEID_INT16 = 12
BUILTINTYPE_TYPEID_UINT16 = 13
BUILTINTYPE_TYPEID_INT32 = 14
BUILTINTYPE_TYPEID_UINT32 = 15
BUILTINTYPE_TYPEID_INT64 = 16
BUILTINTYPE_TYPEID_UINT64 = 17
BUILTINTYPE_TYPEID_FLOAT = 18
BUILTINTYPE_TYPEID_DOUBLE = 19
BUILTINTYPE_TYPEID_STRING = 20
BUILTINTYPE_TYPEID_XMLELEMENT = 21
BUILTINTYPE_TYPEID_BYTESTRING = 22
BUILTINTYPE_TYPEID_DIAGNOSTICINFO = 23
BUILTINTYPE_TYPEID_NUMBER = 24
BUILTINTYPE_TYPEID_UINTEGER = 25
BUILTINTYPE_TYPEID_INTEGER = 26

View File

@ -23,7 +23,6 @@ from datetime import datetime
logger = logging.getLogger(__name__)
import xml.dom.minidom as dom
from constants import *
from base64 import *
import six
@ -52,7 +51,6 @@ def valueIsInternalType(valueTypeString):
class Value(object):
def __init__(self, xmlelement=None):
self.value = None
self.numericRepresentation = 0
self.alias = None
self.dataType = None
self.encodingRule = []
@ -293,7 +291,6 @@ class Value(object):
class Boolean(Value):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_BOOLEAN
if xmlelement:
self.parseXML(xmlelement)
@ -312,7 +309,6 @@ class Boolean(Value):
class Number(Value):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_NUMBER
if xmlelement:
self.parseXML(xmlelement)
@ -328,77 +324,66 @@ class Number(Value):
class Integer(Number):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_INTEGER
if xmlelement:
self.parseXML(xmlelement)
class UInteger(Number):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_UINTEGER
if xmlelement:
self.parseXML(xmlelement)
class Byte(UInteger):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_BYTE
if xmlelement:
self.parseXML(xmlelement)
class SByte(Integer):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_SBYTE
if xmlelement:
self.parseXML(xmlelement)
class Int16(Integer):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_INT16
if xmlelement:
self.parseXML(xmlelement)
class UInt16(UInteger):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_UINT16
if xmlelement:
self.parseXML(xmlelement)
class Int32(Integer):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_INT32
if xmlelement:
self.parseXML(xmlelement)
class UInt32(UInteger):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_UINT32
if xmlelement:
self.parseXML(xmlelement)
class Int64(Integer):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_INT64
if xmlelement:
self.parseXML(xmlelement)
class UInt64(UInteger):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_UINT64
if xmlelement:
self.parseXML(xmlelement)
class Float(Number):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_FLOAT
if xmlelement:
self.parseXML(xmlelement)
@ -414,14 +399,12 @@ class Float(Number):
class Double(Float):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_DOUBLE
if xmlelement:
self.parseXML(xmlelement)
class String(Value):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_STRING
if xmlelement:
self.parseXML(xmlelement)
@ -445,12 +428,10 @@ class String(Value):
class XmlElement(String):
def __init__(self, xmlelement=None):
Value.__init__(self, xmlelement)
self.numericRepresentation = BUILTINTYPE_TYPEID_XMLELEMENT
class ByteString(Value):
def __init__(self, xmlelement=None):
Value.__init__(self, xmlelement)
self.numericRepresentation = BUILTINTYPE_TYPEID_BYTESTRING
def parseXML(self, xmlvalue):
# Expect <ByteString>value</ByteString>
@ -466,7 +447,6 @@ class ByteString(Value):
class ExtensionObject(Value):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_EXTENSIONOBJECT
if xmlelement:
self.parseXML(xmlelement)
@ -474,12 +454,11 @@ class ExtensionObject(Value):
pass
def __str__(self):
return "'" + self.alias() + "':" + self.stringRepresentation + "(" + str(self.value) + ")"
return "'ExtensionObject'"
class LocalizedText(Value):
def __init__(self, xmlvalue=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_LOCALIZEDTEXT
self.locale = ''
self.text = ''
if xmlvalue:
@ -510,7 +489,6 @@ class LocalizedText(Value):
class NodeId(Value):
def __init__(self, idstring=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_NODEID
self.i = None
self.b = None
self.g = None
@ -555,19 +533,10 @@ class NodeId(Value):
# </Identifier>
# </NodeId> or </Alias>
if not isinstance(xmlvalue, dom.Element):
self.text = xmlvalue
self.text = xmlvalue # Alias
return
self.checkXML(xmlvalue)
if self.alias != None:
if not self.alias == xmlvalue.localName:
logger.warn(
"Expected an aliased XML field called " + self.alias + " but got " + xmlvalue.localName + " instead. This is a parsing error of Value.__parseXMLSingleValue(), will try to continue anyway.")
else:
if not self.stringRepresentation == xmlvalue.localName:
logger.warn(
"Expected XML field " + self.stringRepresentation + " but got " + xmlvalue.localName + " instead. This is a parsing error of Value.__parseXMLSingleValue(), will try to continue anyway.")
# Catch XML <NodeId />
if xmlvalue.firstChild == None:
logger.error("No value is given, which is illegal for Node Types...")
@ -608,7 +577,6 @@ class NodeId(Value):
class ExpandedNodeId(Value):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_EXPANDEDNODEID
if xmlelement:
self.parseXML(xmlelement)
@ -619,7 +587,6 @@ class ExpandedNodeId(Value):
class DateTime(Value):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_DATETIME
if xmlelement:
self.parseXML(xmlelement)
@ -653,7 +620,6 @@ class DateTime(Value):
class QualifiedName(Value):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_QUALIFIEDNAME
self.ns = 0
self.name = ''
if xmlelement:
@ -686,12 +652,10 @@ class QualifiedName(Value):
class StatusCode(UInt32):
def __init__(self, xmlelement=None):
Value.__init__(self, xmlelement)
self.numericRepresentation = BUILTINTYPE_TYPEID_STATUSCODE
class DiagnosticInfo(Value):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_DIAGNOSTICINFO
if xmlelement:
self.parseXML(xmlelement)
@ -702,7 +666,6 @@ class DiagnosticInfo(Value):
class Guid(Value):
def __init__(self, xmlelement=None):
Value.__init__(self)
self.numericRepresentation = BUILTINTYPE_TYPEID_GUID
if xmlelement:
self.parseXML(xmlelement)

View File

@ -19,7 +19,6 @@
import sys
import logging
from datatypes import *
from constants import *
logger = logging.getLogger(__name__)
@ -63,7 +62,6 @@ def RefOrAlias(s):
class Node(object):
def __init__(self):
self.id = NodeId()
self.nodeClass = NODE_CLASS_GENERERIC
self.browseName = QualifiedName()
self.displayName = LocalizedText()
self.description = LocalizedText()
@ -188,7 +186,6 @@ class Node(object):
class ReferenceTypeNode(Node):
def __init__(self, xmlelement=None):
Node.__init__(self)
self.nodeClass = NODE_CLASS_REFERENCETYPE
self.isAbstract = False
self.symmetric = False
self.inverseName = ""
@ -213,7 +210,6 @@ class ReferenceTypeNode(Node):
class ObjectNode(Node):
def __init__(self, xmlelement=None):
Node.__init__(self)
self.nodeClass = NODE_CLASS_OBJECT
self.eventNotifier = 0
if xmlelement:
self.parseXML(xmlelement)
@ -227,7 +223,6 @@ class ObjectNode(Node):
class VariableNode(Node):
def __init__(self, xmlelement=None):
Node.__init__(self)
self.nodeClass = NODE_CLASS_VARIABLE
self.dataType = NodeId()
self.valueRank = -2
self.arrayDimensions = []
@ -303,7 +298,6 @@ class VariableNode(Node):
class VariableTypeNode(VariableNode):
def __init__(self, xmlelement=None):
VariableNode.__init__(self)
self.nodeClass = NODE_CLASS_VARIABLETYPE
self.isAbstract = False
if xmlelement:
self.parseXML(xmlelement)
@ -317,7 +311,6 @@ class VariableTypeNode(VariableNode):
class MethodNode(Node):
def __init__(self, xmlelement=None):
Node.__init__(self)
self.nodeClass = NODE_CLASS_METHOD
self.executable = True
self.userExecutable = True
self.methodDecalaration = None
@ -337,7 +330,6 @@ class MethodNode(Node):
class ObjectTypeNode(Node):
def __init__(self, xmlelement=None):
Node.__init__(self)
self.nodeClass = NODE_CLASS_OBJECTTYPE
self.isAbstract = False
if xmlelement:
self.parseXML(xmlelement)
@ -382,7 +374,6 @@ class DataTypeNode(Node):
def __init__(self, xmlelement=None):
Node.__init__(self)
self.nodeClass = NODE_CLASS_DATATYPE
self.isAbstract = False
self.__xmlDefinition__ = None
self.__baseTypeEncoding__ = []
@ -615,7 +606,6 @@ class DataTypeNode(Node):
class ViewNode(Node):
def __init__(self, xmlelement=None):
Node.__init__(self)
self.nodeClass = NODE_CLASS_VIEW
self.containsNoLoops == False
self.eventNotifier == False
if xmlelement:

View File

@ -1202,6 +1202,7 @@
<Reference ReferenceType="HasProperty">i=3704</Reference>
<Reference ReferenceType="HasComponent">i=2996</Reference>
<Reference ReferenceType="HasComponent">i=2997</Reference>
<Reference ReferenceType="HasComponent">i=11192</Reference>
<Reference ReferenceType="HasTypeDefinition">i=2013</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2253</Reference>
</References>
@ -2059,4 +2060,137 @@ PC9vcGM6VHlwZURpY3Rpb25hcnk+</ByteString>
<String xmlns="http://opcfoundation.org/UA/2008/02/Types.xsd">EnumValueType</String>
</Value>
</UAVariable>
<UAObjectType NodeId="i=2330" BrowseName="HistoryServerCapabilitiesType">
<DisplayName>HistoryServerCapabilitiesType</DisplayName>
<References>
<Reference ReferenceType="HasSubtype" IsForward="false">i=58</Reference>
</References>
</UAObjectType>
<UAObject NodeId="i=11192" BrowseName="HistoryServerCapabilities">
<DisplayName>HistoryServerCapabilities</DisplayName>
<References>
<Reference ReferenceType="HasProperty">i=11193</Reference>
<Reference ReferenceType="HasProperty">i=11242</Reference>
<Reference ReferenceType="HasProperty">i=11273</Reference>
<Reference ReferenceType="HasProperty">i=11274</Reference>
<Reference ReferenceType="HasProperty">i=11196</Reference>
<Reference ReferenceType="HasProperty">i=11197</Reference>
<Reference ReferenceType="HasProperty">i=11198</Reference>
<Reference ReferenceType="HasProperty">i=11199</Reference>
<Reference ReferenceType="HasProperty">i=11200</Reference>
<Reference ReferenceType="HasProperty">i=11281</Reference>
<Reference ReferenceType="HasProperty">i=11282</Reference>
<Reference ReferenceType="HasProperty">i=11283</Reference>
<Reference ReferenceType="HasProperty">i=11502</Reference>
<Reference ReferenceType="HasProperty">i=11275</Reference>
<Reference ReferenceType="HasComponent">i=11201</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=2268</Reference>
<Reference ReferenceType="HasTypeDefinition">i=2330</Reference>
</References>
</UAObject>
<UAVariable NodeId="i=11193" BrowseName="AccessHistoryDataCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>AccessHistoryDataCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11242" BrowseName="AccessHistoryEventsCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>AccessHistoryEventsCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11273" BrowseName="MaxReturnDataValues" ParentNodeId="i=11192" DataType="UInt32">
<DisplayName>MaxReturnDataValues</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11274" BrowseName="MaxReturnEventValues" ParentNodeId="i=11192" DataType="UInt32">
<DisplayName>MaxReturnEventValues</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11196" BrowseName="InsertDataCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>InsertDataCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11197" BrowseName="ReplaceDataCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>ReplaceDataCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11198" BrowseName="UpdateDataCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>UpdateDataCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11199" BrowseName="DeleteRawCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>DeleteRawCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11200" BrowseName="DeleteAtTimeCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>DeleteAtTimeCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11281" BrowseName="InsertEventCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>InsertEventCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11282" BrowseName="ReplaceEventCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>ReplaceEventCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11283" BrowseName="UpdateEventCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>UpdateEventCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11502" BrowseName="DeleteEventCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>DeleteEventCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAVariable NodeId="i=11275" BrowseName="InsertAnnotationCapability" ParentNodeId="i=11192" DataType="Boolean">
<DisplayName>InsertAnnotationCapability</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=68</Reference>
<Reference ReferenceType="HasProperty" IsForward="false">i=11192</Reference>
</References>
</UAVariable>
<UAObject NodeId="i=11201" BrowseName="AggregateFunctions" ParentNodeId="i=11192">
<DisplayName>AggregateFunctions</DisplayName>
<References>
<Reference ReferenceType="HasTypeDefinition">i=61</Reference>
<Reference ReferenceType="HasComponent" IsForward="false">i=11192</Reference>
</References>
</UAObject>
</UANodeSet>

View File

@ -5,27 +5,36 @@ set -ev
if [ -z ${DOCKER+x} ] && [ -z ${SONAR+x} ]; then
# Only on non-docker builds required
echo "=== Installing from external package sources ===" && echo -en 'travis_fold:start:before_install.external\\r'
echo "=== Installing from external package sources in $LOCAL_PKG ===" && echo -en 'travis_fold:start:before_install.external\\r'
if [ "$CC" = "clang" ]; then
# the ubuntu repo has a somehow broken clang-3.9 compiler. We want to use the one from the llvm repo
# See https://github.com/openssl/openssl/commit/404c76f4ee1dc51c0d200e2b60a6340aadb44e38
sudo cp .travis-apt-pin.preferences /etc/apt/preferences.d/no-ubuntu-clang
curl -sSL "http://apt.llvm.org/llvm-snapshot.gpg.key" | sudo -E apt-key add -
echo "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main" | sudo tee -a /etc/apt/sources.list > /dev/null
sudo -E apt-add-repository -y "ppa:ubuntu-toolchain-r/test"
sudo -E apt-get -yq update
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install clang-3.9 clang-tidy-3.9 libfuzzer-3.9-dev
fi
# Increase the environment version to force a rebuild of the packages
# The version is writen to the cache file after every build of the dependencies
ENV_VERSION="1"
ENV_INSTALLED=""
if [ -e $LOCAL_PKG/.build_env ]; then
echo "=== No cached build environment ==="
read -r ENV_INSTALLED < $LOCAL_PKG/.build_env
fi
# travis caches the $LOCAL_PKG dir. If it is loaded, we don't need to reinstall the packages
if [ "$ENV_VERSION" = "$ENV_INSTALLED" ]; then
echo "=== The build environment is current ==="
exit 0
fi
echo "=== The build environment is outdated ==="
# Clean up
rm -rf $LOCAL_PKG/*
if [ "$CC" = "tcc" ]; then
mkdir tcc_install && cd tcc_install
wget https://download.savannah.gnu.org/releases/tinycc/tcc-0.9.27.tar.bz2
tar xf tcc-0.9.27.tar.bz2
cd tcc-0.9.27
./configure
./configure --prefix=$LOCAL_PKG
make
sudo make install
make install
cd ../..
rm -rf tcc_install
fi
@ -33,9 +42,9 @@ if [ -z ${DOCKER+x} ] && [ -z ${SONAR+x} ]; then
wget https://github.com/ARMmbed/mbedtls/archive/mbedtls-2.7.1.tar.gz
tar xf mbedtls-2.7.1.tar.gz
cd mbedtls-mbedtls-2.7.1
cmake -DENABLE_TESTING=Off .
cmake -DENABLE_TESTING=Off -DCMAKE_INSTALL_PREFIX=$LOCAL_PKG .
make -j
sudo make install
make install
echo -en 'travis_fold:end:script.before_install.external\\r'
@ -46,11 +55,4 @@ if [ -z ${DOCKER+x} ] && [ -z ${SONAR+x} ]; then
pip install --user cpplint
echo -en 'travis_fold:end:script.before_install.python\\r'
echo "=== Installed versions are ===" && echo -en 'travis_fold:start:before_install.versions\\r'
clang --version
g++ --version
cppcheck --version
valgrind --version
echo -en 'travis_fold:end:script.before_install.versions\\r'
fi

View File

@ -56,11 +56,11 @@ fi
if [ $ANALYZE = "true" ]; then
echo "=== Running static code analysis ===" && echo -en 'travis_fold:start:script.analyze\\r'
if [ "$CC" = "clang" ]; then
if ! case $CC in clang*) false;; esac; then
mkdir -p build
cd build
scan-build cmake -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_UNIT_TESTS=ON ..
scan-build -enable-checker security.FloatLoopCounter \
scan-build-6.0 cmake -DUA_BUILD_EXAMPLES=ON -DUA_BUILD_UNIT_TESTS=ON ..
scan-build-6.0 -enable-checker security.FloatLoopCounter \
-enable-checker security.insecureAPI.UncheckedReturn \
--status-bugs -v \
make -j
@ -68,8 +68,8 @@ if [ $ANALYZE = "true" ]; then
mkdir -p build
cd build
scan-build cmake -DUA_ENABLE_AMALGAMATION=ON ..
scan-build -enable-checker security.FloatLoopCounter \
scan-build-6.0 cmake -DUA_ENABLE_AMALGAMATION=ON ..
scan-build-6.0 -enable-checker security.FloatLoopCounter \
-enable-checker security.insecureAPI.UncheckedReturn \
--status-bugs -v \
make -j
@ -110,13 +110,15 @@ else
echo -e "\r\n== Full Namespace 0 Generation ==" && echo -en 'travis_fold:start:script.build.ns0\\r'
mkdir -p build
cd build
cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DCMAKE_BUILD_TYPE=Debug -DUA_ENABLE_FULL_NS0=ON -DUA_BUILD_EXAMPLES=ON ..
cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DCMAKE_BUILD_TYPE=Debug -DUA_ENABLE_FULL_NS0=ON -DUA_BUILD_EXAMPLES=ON \
-DUA_ENABLE_SUBSCRIPTIONS_EVENTS=ON ..
make -j
if [ $? -ne 0 ] ; then exit 1 ; fi
cd .. && rm build -rf
echo -en 'travis_fold:end:script.build.ns0\\r'
# cross compilation only with gcc
if [ "$CC" = "gcc" ]; then
if ! [ -z ${MINGW+x} ]; then
echo -e "\r\n== Cross compile release build for MinGW 32 bit ==" && echo -en 'travis_fold:start:script.build.cross_mingw32\\r'
mkdir -p build && cd build
cmake -DCMAKE_TOOLCHAIN_FILE=../tools/cmake/Toolchain-mingw32.cmake -DUA_ENABLE_AMALGAMATION=ON -DCMAKE_BUILD_TYPE=Release -DUA_BUILD_EXAMPLES=ON ..
@ -239,7 +241,7 @@ else
cmake -DPYTHON_EXECUTABLE:FILEPATH=/usr/bin/$PYTHON -DUA_ENABLE_FULL_NS0=ON \
-DCMAKE_BUILD_TYPE=Debug -DUA_BUILD_EXAMPLES=ON -DUA_ENABLE_PUBSUB=ON -DUA_ENABLE_ENCRYPTION=ON -DUA_ENABLE_DISCOVERY=ON \
-DUA_ENABLE_DISCOVERY_MULTICAST=ON -DUA_BUILD_UNIT_TESTS=ON -DUA_ENABLE_COVERAGE=OFF \
-DUA_ENABLE_UNIT_TESTS_MEMCHECK=OFF ..
-DUA_ENABLE_UNIT_TESTS_MEMCHECK=OFF -DUA_ENABLE_SUBSCRIPTIONS_EVENTS=ON ..
make -j && make test ARGS="-V"
if [ $? -ne 0 ] ; then exit 1 ; fi
cd .. && rm build -rf