mirror of
https://github.com/open62541/open62541.git
synced 2025-06-03 04:00:21 +00:00
Merge branch 'master' into feature/architectures
This commit is contained in:
commit
ae149eb6d5
212
.travis.yml
212
.travis.yml
@ -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
|
||||
|
@ -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
|
||||
|
131
FEATURES.md
131
FEATURES.md
@ -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 |
|
||||
|
@ -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**
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
5
examples/nodeset/server_nodeset.csv
Normal file
5
examples/nodeset/server_nodeset.csv
Normal file
@ -0,0 +1,5 @@
|
||||
testType,1001,ObjectType
|
||||
testInstance,5001,Object
|
||||
testFolder,5002,Object
|
||||
testType_Var1,6001,Variable
|
||||
testInstance_Var1,6002,Variable
|
|
@ -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,
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
*
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.");
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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, ¤tDataSet->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, ¤tDataSet->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, ¤tDataSet->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, ¤tDataSet->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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
/*********************************/
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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(¤t[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,
|
||||
¤t, ¤tSize, ¤tCount,
|
||||
&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) {
|
||||
|
@ -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(¬ification->data.event.result); */
|
||||
UA_EventFieldList_deleteMembers(¬ification->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(¬ification->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(¬ification->data.event.fields);
|
||||
efl->clientHandle = mon->clientHandle;
|
||||
/* EventFilterResult currently isn't being used
|
||||
UA_EventFilterResult_deleteMembers(¬ification->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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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(¬ification->fields);
|
||||
UA_EventFilterResult_deleteMembers(¬ification->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(¬ification->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,
|
||||
¬ification->fields, &whereClauseResult);
|
||||
if (retval == UA_STATUSCODE_BADNOTSUPPORTED)
|
||||
whereClausesUsed = UA_TRUE;
|
||||
|
||||
if(whereClauseResult) {
|
||||
retval = UA_Server_readValue(server, bpr.targets[0].targetId.nodeId,
|
||||
¬ification->fields.eventFields[i]);
|
||||
if(retval != UA_STATUSCODE_GOOD)
|
||||
UA_Variant_init(¬ification->fields.eventFields[i]);
|
||||
|
||||
if(whereClausesUsed)
|
||||
return UA_STATUSCODE_BADNOTSUPPORTED;
|
||||
} else {
|
||||
UA_Variant_init(¬ification->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],
|
||||
¬ification->fields.eventFields[i]);
|
||||
}
|
||||
/* UA_Boolean whereClauseResult = UA_TRUE; */
|
||||
/* return whereClausesApply(server, filter->whereClause, ¬ification->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,
|
||||
¬ification->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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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 ..
|
||||
|
172
tests/nodeset-compiler/CMakeLists.txt
Normal file
172
tests/nodeset-compiler/CMakeLists.txt
Normal 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()
|
45
tests/nodeset-compiler/check_nodeset_objecttype.c
Normal file
45
tests/nodeset-compiler/check_nodeset_objecttype.c
Normal 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;
|
||||
}
|
61
tests/nodeset-compiler/objecttype.xml
Normal file
61
tests/nodeset-compiler/objecttype.xml
Normal 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>
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
448
tests/server/check_subscription_events.c
Normal file
448
tests/server/check_subscription_events.c
Normal 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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 ... ***"
|
||||
|
@ -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])
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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()
|
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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
|
@ -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)
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user