diff --git a/.clang-tidy b/.clang-tidy index f4f2ce797a..0182705d92 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -109,13 +109,13 @@ CheckOptions: - key: readability-function-size.StatementThreshold value: '800' - key: readability-identifier-naming.AbstractClassCase - value: aNy_CasE + value: CamelCase - key: readability-identifier-naming.AbstractClassPrefix value: '' - key: readability-identifier-naming.AbstractClassSuffix value: '' - key: readability-identifier-naming.ClassCase - value: aNy_CasE + value: CamelCase - key: readability-identifier-naming.ClassConstantCase value: aNy_CasE - key: readability-identifier-naming.ClassConstantPrefix @@ -129,7 +129,7 @@ CheckOptions: - key: readability-identifier-naming.ClassMemberSuffix value: '' - key: readability-identifier-naming.ClassMethodCase - value: aNy_CasE + value: camelBack - key: readability-identifier-naming.ClassMethodPrefix value: '' - key: readability-identifier-naming.ClassMethodSuffix @@ -175,9 +175,9 @@ CheckOptions: - key: readability-identifier-naming.ConstexprVariableSuffix value: '' - key: readability-identifier-naming.EnumCase - value: aNy_CasE + value: lower_case - key: readability-identifier-naming.EnumConstantCase - value: aNy_CasE + value: lower_case - key: readability-identifier-naming.EnumConstantPrefix value: '' - key: readability-identifier-naming.EnumConstantSuffix @@ -187,7 +187,7 @@ CheckOptions: - key: readability-identifier-naming.EnumSuffix value: '' - key: readability-identifier-naming.FunctionCase - value: aNy_CasE + value: camelBack - key: readability-identifier-naming.FunctionPrefix value: '' - key: readability-identifier-naming.FunctionSuffix @@ -243,7 +243,7 @@ CheckOptions: - key: readability-identifier-naming.MethodSuffix value: '' - key: readability-identifier-naming.NamespaceCase - value: aNy_CasE + value: lowercase - key: readability-identifier-naming.NamespacePrefix value: '' - key: readability-identifier-naming.NamespaceSuffix diff --git a/.codacy.yml b/.codacy.yml index de72c4c943..dcb28f1d91 100644 --- a/.codacy.yml +++ b/.codacy.yml @@ -12,4 +12,8 @@ languages: exclude_paths: - ThirdParty/**/* - - config/cmake/test_compiler_cxx/** \ No newline at end of file + - ThirdParty/* + - config/cmake/test_compiler_cxx/** + - swig/**/* + - scripts/**/* + - src/helics/common/cppzmq/* \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index dc893407f1..6db577cd9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -69,7 +69,7 @@ jobs: - ubuntu-toolchain-r-test packages: - g++-6 - env: MATRIX_EVAL="COMPILER=g++-6 && CC=gcc-6 && CXX=g++-6 && SHARED_LIB_EXT=so" + env: MATRIX_EVAL="COMPILER=g++-6 && CC=gcc-6 && CXX=g++-6 && SHARED_LIB_EXT=so && USE_SWIG=true" - <<: *linux_base addons: @@ -79,7 +79,7 @@ jobs: packages: - g++-4.9 env: - - MATRIX_EVAL="COMPILER=g++-4.9 && CC=gcc-4.9 && CXX=g++-4.9 && SHARED_LIB_EXT=so" + - MATRIX_EVAL="COMPILER=g++-4.9 && CC=gcc-4.9 && CXX=g++-4.9 && SHARED_LIB_EXT=so && USE_SWIG=false" - MINIMUM_DEPENDENCIES=true - <<: *linux_base @@ -91,7 +91,7 @@ jobs: - llvm-toolchain-precise-3.6 packages: - clang-3.6 - env: MATRIX_EVAL="SHARED_LIB_EXT=so && COMPILER=clang++-3.6 && CC='ccache clang-3.6 -Qunused-arguments -fcolor-diagnostics' && CXX='ccache clang++-3.6 -Qunused-arguments -fcolor-diagnostics' + env: MATRIX_EVAL="SHARED_LIB_EXT=so && USE_SWIG=true && COMPILER=clang++-3.6 && CC='ccache clang-3.6 -Qunused-arguments -fcolor-diagnostics' && CXX='ccache clang++-3.6 -Qunused-arguments -fcolor-diagnostics' # ------------------------------------------------ @@ -99,11 +99,11 @@ jobs: # ------------------------------------------------ - <<: *daily_linux env: - - MATRIX_EVAL="COMPILER=g++-6 && CC=gcc-6 && CXX=g++-6 && SHARED_LIB_EXT=so" + - MATRIX_EVAL="COMPILER=g++-6 && CC=gcc-6 && CXX=g++-6 && SHARED_LIB_EXT=so && USE_SWIG=true" - RUN_VALGRIND=true - <<: *daily_linux env: - - MATRIX_EVAL="COMPILER=g++-6 && CC=gcc-6 && CXX=g++-6 && SHARED_LIB_EXT=so" + - MATRIX_EVAL="COMPILER=g++-6 && CC=gcc-6 && CXX=g++-6 && SHARED_LIB_EXT=so && USE_SWIG=true" - RUN_COVERAGE=true branches: @@ -121,6 +121,9 @@ script: - mkdir build && cd build - export HELICS_DEPENDENCY_FLAGS="-DZeroMQ_INSTALL_PATH=${TRAVIS_BUILD_DIR}/dependencies/zmq -DBOOST_ROOT=${TRAVIS_BUILD_DIR}/dependencies/boost" - export HELICS_OPTION_FLAGS="-DBUILD_C_SHARED_LIB=ON -DBUILD_HELICS_EXAMPLES=ON -DBUILD_PYTHON=ON -DTRAVIS_TESTS_ENABLE=ON -DEXAMPLES_WARNINGS_AS_ERROR=ON -DPYTHON_LIBRARY=$(python3-config --prefix)/lib/libpython3.6m.${SHARED_LIB_EXT} -DPYTHON_INCLUDE_DIR=$(python3-config --prefix)/include/python3.6m/" + - if [[ "$USE_SWIG" == 'false' ]] ; then HELICS_OPTION_FLAGS+=" -DGENERATE_PYTHON=OFF" ; fi + # - echo "$HELICS_OPTION_FLAGS" + # - echo "$HELICS_DEPENDENCY_FLAGS" - cmake .. ${HELICS_DEPENDENCY_FLAGS} ${HELICS_OPTION_FLAGS} - make -j2 # For controlling which tests get run: @@ -128,12 +131,16 @@ script: # 1 = common_tests # 2 = core tests # 3 = application api tests - # 4 = c interface tests + # 4 = shared_library tests # 5 = helics-apps tests # 6 = travis-tests - # - ctest --verbose --timeout 120 -I 0,0,0,1 + - ctest --verbose --timeout 120 -I 0,0,0,1 + # - ctest --verbose --timeout 120 -I 0,0,0,2 - ctest --verbose --timeout 120 -I 0,0,0,6 + # - ctest --verbose --timeout 360 -I 0,0,0,4 - export PYTHONPATH="$(pwd)/swig/python" + # - echo "$LD_LIBRARY_PATH" + # - echo "$DYLD_FALLBACK_LIBRARY_PATH" - python3 -m helics - python3 -m pytest -v ../tests/python_helics diff --git a/CMakeLists.txt b/CMakeLists.txt index 466ab8fc6f..7c79207c72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,9 @@ project (HELICS) set (HELICS_VERSION_MAJOR 1) set (HELICS_VERSION_MINOR 0) set (HELICS_VERSION_PATCH 0) -set (HELICS_VERSION_BUILD alpha2) -set (HELICS_DATE "02-02-18") +set (HELICS_VERSION_BUILD beta) +set (HELICS_DATE "02-23-18") +set (HELICS_VERSION_STRING "1.0.0-beta (02-23-18)") OPTION(BUILD_HELICS_TESTS "Enable the test Executables to be built" ON) # enable testing @@ -112,10 +113,9 @@ ELSE(UNIX) ENDIF(MINGW) ENDIF(UNIX) -IF (BUILD_MATLAB OR USE_BOOST_STATIC_LIBS) +IF (USE_BOOST_STATIC_LIBS) set(Boost_USE_STATIC_LIBS ON) set(BOOST_STATIC ON) - ENDIF () # ------------------------------------------------------------- # add coverage target @@ -188,7 +188,6 @@ endif(ZeroMQ_ENABLE) OPTION(MPI_ENABLE "Enable MPI networking library" OFF) IF (MPI_ENABLE) find_package(MPI) -set(MPI_C_FOUND FALSE) if (${MPI_C_FOUND}) set(HELICS_HAVE_MPI TRUE) include_directories("${MPI_C_INCLUDE_PATH}") @@ -206,7 +205,7 @@ add_library(helics-static STATIC $ $ ) -target_link_libraries(helics-static Threads::Threads) +target_link_libraries(helics-static Threads::Threads) IF (UNIX AND NOT APPLE) target_link_libraries(helics-static rt) ENDIF() @@ -380,6 +379,7 @@ endif(ENABLE_CLANG_TOOLS) add_subdirectory(swig) +add_subdirectory(docs) set(HELICS_CMAKECONFIG_INSTALL_DIR "cmake/${PROJECT_NAME}" CACHE STRING "install path for HELICSConfig.cmake") @@ -460,3 +460,10 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Import.cmake DESTINATION ${HELICS_CMAKECONFIG_INSTALL_DIR}) + +if (CMAKE_INSTALL_PREFIX) + message(STATUS "Using CMAKE_INSTALL_PREFIX: " ${CMAKE_INSTALL_PREFIX}) +endif() + + + diff --git a/README.md b/README.md index 29606abb1e..6a33a9c359 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# HELICS-SRC [![Build Status](https://travis-ci.org/GMLC-TDC/HELICS-src.svg?branch=master)](https://travis-ci.org/GMLC-TDC/HELICS-src) [![Build status](https://ci.appveyor.com/api/projects/status/afpa4mv0kgsjwvtn/branch/develop?svg=true)](https://ci.appveyor.com/project/nightlark/helics-src/branch/develop) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/83ba19b36b714c729ec3a3d18504505e)](https://www.codacy.com/app/phlptp/HELICS-src?utm_source=github.com&utm_medium=referral&utm_content=GMLC-TDC/HELICS-src&utm_campaign=Badge_Grade) [![Gitter chat](https://badges.gitter.im/GMLC-TDC/HELICS-src.png)](https://gitter.im/GMLC-TDC/HELICS-src) [![Documentation](https://img.shields.io/badge/docs-ready-blue.svg)](http://gmlc-tdc.github.io/HELICS-src) +# HELICS-SRC [![Build Status](https://travis-ci.org/GMLC-TDC/HELICS-src.svg?branch=master)](https://travis-ci.org/GMLC-TDC/HELICS-src) +[![Build status](https://ci.appveyor.com/api/projects/status/afpa4mv0kgsjwvtn/branch/develop?svg=true)](https://ci.appveyor.com/project/nightlark/helics-src/branch/develop) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/83ba19b36b714c729ec3a3d18504505e)](https://www.codacy.com/app/phlptp/HELICS-src?utm_source=github.com&utm_medium=referral&utm_content=GMLC-TDC/HELICS-src&utm_campaign=Badge_Grade) +[![Gitter chat](https://badges.gitter.im/GMLC-TDC/HELICS-src.png)](https://gitter.im/GMLC-TDC/HELICS-src) +[![Documentation](https://img.shields.io/badge/docs-ready-blue.svg)](http://gmlc-tdc.github.io/HELICS-src)

diff --git a/ThirdParty/libguarded/staged_guarded.hpp b/ThirdParty/libguarded/staged_guarded.hpp index 45024bdb6b..91ad1fed6e 100644 --- a/ThirdParty/libguarded/staged_guarded.hpp +++ b/ThirdParty/libguarded/staged_guarded.hpp @@ -1,34 +1,34 @@ /* - Copyright (C) 2017, Battelle Memorial Institute All rights reserved. -This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. +This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial +Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the +Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. */ /*********************************************************************** -* -* Copyright (c) 2015-2017 Ansel Sermersheim -* All rights reserved. -* -* This file is part of libguarded -* -* libguarded is free software, released under the BSD 2-Clause license. -* For license details refer to LICENSE provided with this project. -* -***********************************************************************/ + * + * Copyright (c) 2015-2017 Ansel Sermersheim + * All rights reserved. + * + * This file is part of libguarded + * + * libguarded is free software, released under the BSD 2-Clause license. + * For license details refer to LICENSE provided with this project. + * + ***********************************************************************/ #ifndef STAGED_GUARDED_HPP #define STAGED_GUARDED_HPP +#include #include #include #include -#include namespace libguarded { - /** \headerfile staged_guarded.hpp @@ -46,11 +46,13 @@ namespace libguarded template class guarded { - private: +private: class deleter; + class shared_deleter; - public: +public: using handle = std::unique_ptr; + using shared_handle = std::unique_ptr; /** Construct a guarded object. This constructor will accept any @@ -58,7 +60,7 @@ class guarded constructor of T. */ template - staged_guarded(Us &&... data); + staged_guarded (Us &&... data); /** Acquire a handle to the protected object. As a side effect, the @@ -66,7 +68,8 @@ class guarded thread. The lock will be automatically released when the handle is destroyed. */ - handle lock(); + handle lock (); + shared_handle lock() const; /** Attempt to acquire a handle to the protected object. Returns a @@ -75,7 +78,7 @@ class guarded thread. The lock will be automatically released when the handle is destroyed. */ - handle try_lock(); + handle try_lock (); /** Attempt to acquire a handle to the protected object. As a side @@ -92,7 +95,7 @@ class guarded default std::mutex. */ template - handle try_lock_for(const Duration &duration); + handle try_lock_for (const Duration &duration); /** Attempt to acquire a handle to the protected object. As a side @@ -108,70 +111,36 @@ class guarded default std::mutex. */ template - handle try_lock_until(const TimePoint &timepoint); + handle try_lock_until (const TimePoint &timepoint); - /** generate a copy of the protected object - */ - std::enable_if_t::value, T> load() - { - if (constant) - { - return m_obj; - } - else - { - std::lock_guard lock(m_mutex); - return m_obj; - } - } - /** generate a copy of the protected object - */ - std::enable_if_t::value, T> operator*() - { - return load(); - } + // shared access, note "shared" in method names + shared_handle lock_shared() const; + shared_handle try_lock_shared() const; - /** store an updated value into the object*/ - template - std::enable_if_t::value> store(objType &&newObj) + void transition () { if (!constant) { + //acquire the lock then alter the std::lock_guard lock(m_mutex); - m_obj = std::forward(newObj); - } - else - { - throw(std::exception("cannot change after constant flag set")); + constant = true; } + } - /** store an updated value into the object*/ - template - std::enable_if_t::value> operator=(objType &&newObj) - { - store(std::forward(newObj)); - } - - void transition() - { - std::lock_guard lock(m_mutex); - constant = true; - } private: class deleter { public: using pointer = T *; - deleter(std::unique_lock lock) : m_lock(std::move(lock)) - { - } + deleter (std::unique_lock lock) : m_lock (std::move (lock)) {} - void operator()(T *ptr) + void operator() (T *ptr) { - if (m_lock.owns_lock()) { - m_lock.unlock(); + if (m_lock.owns_lock ()) + { + m_lock.unlock (); } } @@ -179,87 +148,151 @@ class guarded std::unique_lock m_lock; }; + class shared_deleter + { + public: + using pointer = const T *; + + shared_deleter(M & mutex) : m_deleter_mutex(mutex) + { + } + + void operator()(const T * ptr) + { + if (ptr) { + m_deleter_mutex.unlock_shared(); + } + } + + private: + M & m_deleter_mutex; + }; + T m_obj; M m_mutex; - std::atomic constant{ false }; + std::atomic constant{false}; }; template template -staged_guarded::guarded(Us &&... data) : m_obj(std::forward(data)...) +staged_guarded::guarded (Us &&... data) : m_obj (std::forward (data)...) +{ +} + +template +auto staged_guarded::lock () -> handle +{ + std::unique_lock lock = + (constant) ? std::unique_lock (m_mutex, std::defer_lock) : std::unique_lock (m_mutex); + return handle (&m_obj, deleter (std::move (lock))); +} + +template +auto staged_guarded::lock() const -> shared_handle +{ + std::unique_lock lock = + (constant) ? std::unique_lock(m_mutex, std::defer_lock) : std::unique_lock(m_mutex); + return shared_handle(&m_obj, deleter(std::move(lock))); +} + +template +auto staged_guarded::lock_shared() const -> shared_handle { + std::unique_lock lock = + (constant) ? std::unique_lock(m_mutex, std::defer_lock) : std::unique_lock(m_mutex); + return shared_handle(&m_obj, deleter(std::move(lock))); } template -auto staged_guarded::lock() -> handle +auto staged_guarded::try_lock () -> handle { - std::unique_lock lock=(constant)? std::unique_lock(m_mutex, std::defer_lock) : - std::unique_lock(m_mutex); - return handle(&m_obj, deleter(std::move(lock))); + if (!constant) + { + std::unique_lock lock (m_mutex, std::try_to_lock); + + if (lock.owns_lock ()) + { + return handle (&m_obj, deleter (std::move (lock))); + } + else + { + return handle (nullptr, deleter (std::move (lock))); + } + } + else + { + return handle (&m_obj, deleter (std::unique_lock (m_mutex, std::defer_lock))); + } } + template -auto staged_guarded::try_lock() -> handle +auto staged_guarded::try_lock_shared() -> shared_handle { if (!constant) { std::unique_lock lock(m_mutex, std::try_to_lock); - if (lock.owns_lock()) { + if (lock.owns_lock()) + { return handle(&m_obj, deleter(std::move(lock))); } - else { + else + { return handle(nullptr, deleter(std::move(lock))); } } else { - return handle(&m_obj, deleter(std::unique_lock(m_mutex,std::defer_lock))); + return handle(&m_obj, deleter(std::unique_lock(m_mutex, std::defer_lock))); } } template template -auto staged_guarded::try_lock_for(const Duration &d) -> handle +auto staged_guarded::try_lock_for (const Duration &d) -> handle { if (!constant) { - std::unique_lock lock(m_mutex, d); + std::unique_lock lock (m_mutex, d); - if (lock.owns_lock()) { - return handle(&m_obj, deleter(std::move(lock))); + if (lock.owns_lock ()) + { + return handle (&m_obj, deleter (std::move (lock))); } - else { - return handle(nullptr, deleter(std::move(lock))); + else + { + return handle (nullptr, deleter (std::move (lock))); } } else { - return handle(&m_obj, deleter(std::unique_lock(m_mutex, std::defer_lock))); + return handle (&m_obj, deleter (std::unique_lock (m_mutex, std::defer_lock))); } } template template -auto staged_guarded::try_lock_until(const TimePoint &tp) -> handle +auto staged_guarded::try_lock_until (const TimePoint &tp) -> handle { if (!constant) { - std::unique_lock lock(m_mutex, tp); + std::unique_lock lock (m_mutex, tp); - if (lock.owns_lock()) { - return handle(&m_obj, deleter(std::move(lock))); + if (lock.owns_lock ()) + { + return handle (&m_obj, deleter (std::move (lock))); } - else { - return handle(nullptr, deleter(std::move(lock))); + else + { + return handle (nullptr, deleter (std::move (lock))); } } else { - return handle(&m_obj, deleter(std::unique_lock(m_mutex, std::defer_lock))); + return handle (&m_obj, deleter (std::unique_lock (m_mutex, std::defer_lock))); } } - } #endif /*STAGED_GUARDED_HPP*/ diff --git a/appveyor.yml b/appveyor.yml index 790b1ac1ec..959a05402f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,6 @@ shallow_clone: true -version: 1.0.0.alpha2.{build} +version: 1.0.0.beta.{build} image: Visual Studio 2015 @@ -33,12 +33,12 @@ test_script: # 1 = common_tests # 2 = core tests # 3 = application api tests - # 4 = c interface tests + # 4 = shared library tests # 5 = app tests # 6 = travis_tests - ctest -C Release --verbose --timeout 120 -I 0,0,0,1 - ctest -C Release --verbose --timeout 90 -I 0,0,0,2 - ctest -C Release --verbose --timeout 240 -I 0,0,0,3 - - ctest -C Release --verbose --timeout 90 -I 0,0,0,5 + #- ctest -C Release --verbose --timeout 90 -I 0,0,0,5 artifacts: diff --git a/config/Doxyfile.in b/config/Doxyfile.in index 09e25f4282..7431be57e7 100644 --- a/config/Doxyfile.in +++ b/config/Doxyfile.in @@ -31,7 +31,15 @@ PROJECT_NAME = helics # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = @HELICS_VERSION_MAJOR@.@HELICS_VERSION_MINOR@.@HELICS_VERSION_PATCH@.@HELICS_VERSION_BUILD@ +PROJECT_NUMBER = @HELICS_VERSION_MAJOR@.@HELICS_VERSION_MINOR@.@HELICS_VERSION_PATCH@-@HELICS_VERSION_BUILD@ + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = "@CMAKE_CURRENT_SOURCE_DIR@/docs/img/HELICS_48x48.png" + # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. @@ -569,7 +577,7 @@ WARN_LOGFILE = # directories like "/usr/src/myproject". Separate the files or directories # with spaces. -INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/src/" +INPUT = "@CMAKE_CURRENT_SOURCE_DIR@/" # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -612,7 +620,7 @@ EXCLUDE_SYMLINKS = NO # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* -EXCLUDE_PATTERNS = */test/* */ThirdParty/* */install/* */bin/* */build/* */config/* */build2/* */html/* */examples/* */cmake/* +EXCLUDE_PATTERNS = */test/* */ThirdParty/* */install/* */bin/* */build*/* */config/* */build2/* */html/* */examples/* */cmake/* */test/* */cppzmq/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -620,7 +628,7 @@ EXCLUDE_PATTERNS = */test/* */ThirdParty/* */install/* */bin/* */build/* * # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = zmq, json_helics # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see @@ -646,7 +654,7 @@ EXAMPLE_RECURSIVE = NO # directories that contain image that are included in the documentation (see # the \image command). -IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/docs/images" +IMAGE_PATH = "@CMAKE_CURRENT_SOURCE_DIR@/docs/img" # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program diff --git a/config/cmake/FindMatlab.cmake b/config/cmake/FindMatlab.cmake new file mode 100644 index 0000000000..d94d9917e1 --- /dev/null +++ b/config/cmake/FindMatlab.cmake @@ -0,0 +1,1540 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +#.rst: +# FindMatlab +# ---------- +# +# Finds Matlab installations and provides Matlab tools and libraries to cmake. +# +# This package first intention is to find the libraries associated with Matlab +# in order to be able to build Matlab extensions (mex files). It can also be +# used: +# +# * run specific commands in Matlab +# * declare Matlab unit test +# * retrieve various information from Matlab (mex extensions, versions and +# release queries, ...) +# +# The module supports the following components: +# +# * ``MX_LIBRARY``, ``ENG_LIBRARY`` and ``MAT_LIBRARY``: respectively the MX, +# ENG and MAT libraries of Matlab +# * ``MAIN_PROGRAM`` the Matlab binary program. +# * ``MEX_COMPILER`` the MEX compiler. +# * ``SIMULINK`` the Simulink environment. +# +# .. note:: +# +# The version given to the :command:`find_package` directive is the Matlab +# **version**, which should not be confused with the Matlab *release* name +# (eg. `R2014`). +# The :command:`matlab_get_version_from_release_name` and +# :command:`matlab_get_release_name_from_version` allow a mapping +# from the release name to the version. +# +# The variable :variable:`Matlab_ROOT_DIR` may be specified in order to give +# the path of the desired Matlab version. Otherwise, the behaviour is platform +# specific: +# +# * Windows: The installed versions of Matlab are retrieved from the +# Windows registry +# * OS X: The installed versions of Matlab are given by the MATLAB +# paths in ``/Application``. If no such application is found, it falls back +# to the one that might be accessible from the PATH. +# * Unix: The desired Matlab should be accessible from the PATH. +# +# Additional information is provided when :variable:`MATLAB_FIND_DEBUG` is set. +# When a Matlab binary is found automatically and the ``MATLAB_VERSION`` +# is not given, the version is queried from Matlab directly. +# On Windows, it can make a window running Matlab appear. +# +# The mapping of the release names and the version of Matlab is performed by +# defining pairs (name, version). The variable +# :variable:`MATLAB_ADDITIONAL_VERSIONS` may be provided before the call to +# the :command:`find_package` in order to handle additional versions. +# +# A Matlab scripts can be added to the set of tests using the +# :command:`matlab_add_unit_test`. By default, the Matlab unit test framework +# will be used (>= 2013a) to run this script, but regular ``.m`` files +# returning an exit code can be used as well (0 indicating a success). +# +# Module Input Variables +# ^^^^^^^^^^^^^^^^^^^^^^ +# +# Users or projects may set the following variables to configure the module +# behaviour: +# +# :variable:`Matlab_ROOT_DIR` +# the root of the Matlab installation. +# :variable:`MATLAB_FIND_DEBUG` +# outputs debug information +# :variable:`MATLAB_ADDITIONAL_VERSIONS` +# additional versions of Matlab for the automatic retrieval of the installed +# versions. +# +# Variables defined by the module +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +# Result variables +# """""""""""""""" +# +# ``Matlab_FOUND`` +# ``TRUE`` if the Matlab installation is found, ``FALSE`` +# otherwise. All variable below are defined if Matlab is found. +# ``Matlab_ROOT_DIR`` +# the final root of the Matlab installation determined by the FindMatlab +# module. +# ``Matlab_MAIN_PROGRAM`` +# the Matlab binary program. Available only if the component ``MAIN_PROGRAM`` +# is given in the :command:`find_package` directive. +# ``Matlab_INCLUDE_DIRS`` +# the path of the Matlab libraries headers +# ``Matlab_MEX_LIBRARY`` +# library for mex, always available. +# ``Matlab_MX_LIBRARY`` +# mx library of Matlab (arrays). Available only if the component +# ``MX_LIBRARY`` has been requested. +# ``Matlab_ENG_LIBRARY`` +# Matlab engine library. Available only if the component ``ENG_LIBRARY`` +# is requested. +# ``Matlab_MAT_LIBRARY`` +# Matlab matrix library. Available only if the component ``MAT_LIBRARY`` +# is requested. +# ``Matlab_LIBRARIES`` +# the whole set of libraries of Matlab +# ``Matlab_MEX_COMPILER`` +# the mex compiler of Matlab. Currently not used. +# Available only if the component ``MEX_COMPILER`` is asked +# +# Cached variables +# """""""""""""""" +# +# ``Matlab_MEX_EXTENSION`` +# the extension of the mex files for the current platform (given by Matlab). +# ``Matlab_ROOT_DIR`` +# the location of the root of the Matlab installation found. If this value +# is changed by the user, the result variables are recomputed. +# +# Provided macros +# ^^^^^^^^^^^^^^^ +# +# :command:`matlab_get_version_from_release_name` +# returns the version from the release name +# :command:`matlab_get_release_name_from_version` +# returns the release name from the Matlab version +# +# Provided functions +# ^^^^^^^^^^^^^^^^^^ +# +# :command:`matlab_add_mex` +# adds a target compiling a MEX file. +# :command:`matlab_add_unit_test` +# adds a Matlab unit test file as a test to the project. +# :command:`matlab_extract_all_installed_versions_from_registry` +# parses the registry for all Matlab versions. Available on Windows only. +# The part of the registry parsed is dependent on the host processor +# :command:`matlab_get_all_valid_matlab_roots_from_registry` +# returns all the possible Matlab paths, according to a previously +# given list. Only the existing/accessible paths are kept. This is mainly +# useful for the searching all possible Matlab installation. +# :command:`matlab_get_mex_suffix` +# returns the suffix to be used for the mex files +# (platform/architecture dependent) +# :command:`matlab_get_version_from_matlab_run` +# returns the version of Matlab, given the full directory of the Matlab +# program. +# +# +# Known issues +# ^^^^^^^^^^^^ +# +# **Symbol clash in a MEX target** +# By default, every symbols inside a MEX +# file defined with the command :command:`matlab_add_mex` have hidden +# visibility, except for the entry point. This is the default behaviour of +# the MEX compiler, which lowers the risk of symbol collision between the +# libraries shipped with Matlab, and the libraries to which the MEX file is +# linking to. This is also the default on Windows platforms. +# +# However, this is not sufficient in certain case, where for instance your +# MEX file is linking against libraries that are already loaded by Matlab, +# even if those libraries have different SONAMES. +# A possible solution is to hide the symbols of the libraries to which the +# MEX target is linking to. This can be achieved in GNU GCC compilers with +# the linker option ``-Wl,--exclude-libs,ALL``. +# +# **Tests using GPU resources** +# in case your MEX file is using the GPU and +# in order to be able to run unit tests on this MEX file, the GPU resources +# should be properly released by Matlab. A possible solution is to make +# Matlab aware of the use of the GPU resources in the session, which can be +# performed by a command such as ``D = gpuDevice()`` at the beginning of +# the test script (or via a fixture). +# +# +# Reference +# ^^^^^^^^^ +# +# .. variable:: Matlab_ROOT_DIR +# +# The root folder of the Matlab installation. If set before the call to +# :command:`find_package`, the module will look for the components in that +# path. If not set, then an automatic search of Matlab +# will be performed. If set, it should point to a valid version of Matlab. +# +# .. variable:: MATLAB_FIND_DEBUG +# +# If set, the lookup of Matlab and the intermediate configuration steps are +# outputted to the console. +# +# .. variable:: MATLAB_ADDITIONAL_VERSIONS +# +# If set, specifies additional versions of Matlab that may be looked for. +# The variable should be a list of strings, organised by pairs of release +# name and versions, such as follows:: +# +# set(MATLAB_ADDITIONAL_VERSIONS +# "release_name1=corresponding_version1" +# "release_name2=corresponding_version2" +# ... +# ) +# +# Example:: +# +# set(MATLAB_ADDITIONAL_VERSIONS +# "R2013b=8.2" +# "R2013a=8.1" +# "R2012b=8.0") +# +# The order of entries in this list matters when several versions of +# Matlab are installed. The priority is set according to the ordering in +# this list. + + +set(_FindMatlab_SELF_DIR "${CMAKE_CURRENT_LIST_DIR}") + +include(FindPackageHandleStandardArgs) +include(CheckCXXCompilerFlag) +include(CheckCCompilerFlag) + + +# The currently supported versions. Other version can be added by the user by +# providing MATLAB_ADDITIONAL_VERSIONS +if(NOT MATLAB_ADDITIONAL_VERSIONS) + set(MATLAB_ADDITIONAL_VERSIONS) +endif() + +set(MATLAB_VERSIONS_MAPPING + "R2017b=9.3" + "R2017a=9.2" + "R2016b=9.1" + "R2016a=9.0" + "R2015b=8.6" + "R2015a=8.5" + "R2014b=8.4" + "R2014a=8.3" + "R2013b=8.2" + "R2013a=8.1" + "R2012b=8.0" + "R2012a=7.14" + "R2011b=7.13" + "R2011a=7.12" + "R2010b=7.11" + + ${MATLAB_ADDITIONAL_VERSIONS} + ) + + +# temporary folder for all Matlab runs +set(_matlab_temporary_folder ${CMAKE_BINARY_DIR}/Matlab) + +if(NOT EXISTS "${_matlab_temporary_folder}") + file(MAKE_DIRECTORY "${_matlab_temporary_folder}") +endif() + +#.rst: +# .. command:: matlab_get_version_from_release_name +# +# Returns the version of Matlab (17.58) from a release name (R2017k) +macro (matlab_get_version_from_release_name release_name version_name) + + string(REGEX MATCHALL "${release_name}=([0-9]+\\.?[0-9]*)" _matched ${MATLAB_VERSIONS_MAPPING}) + + set(${version_name} "") + if(NOT _matched STREQUAL "") + set(${version_name} ${CMAKE_MATCH_1}) + else() + message(WARNING "The release name ${release_name} is not registered") + endif() + unset(_matched) + +endmacro() + + + + + +#.rst: +# .. command:: matlab_get_release_name_from_version +# +# Returns the release name (R2017k) from the version of Matlab (17.58) +macro (matlab_get_release_name_from_version version release_name) + + set(${release_name} "") + foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING) + string(REGEX MATCHALL "(.+)=${version}" _matched ${_var}) + if(NOT _matched STREQUAL "") + set(${release_name} ${CMAKE_MATCH_1}) + break() + endif() + endforeach(_var) + + unset(_var) + unset(_matched) + if(${release_name} STREQUAL "") + message(WARNING "The version ${version} is not registered") + endif() + +endmacro() + + + + + +# extracts all the supported release names (R2017k...) of Matlab +# internal use +macro(matlab_get_supported_releases list_releases) + set(${list_releases}) + foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING) + string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var}) + if(NOT _matched STREQUAL "") + list(APPEND ${list_releases} ${CMAKE_MATCH_1}) + endif() + unset(_matched) + unset(CMAKE_MATCH_1) + endforeach(_var) + unset(_var) +endmacro() + + + +# extracts all the supported versions of Matlab +# internal use +macro(matlab_get_supported_versions list_versions) + set(${list_versions}) + foreach(_var IN LISTS MATLAB_VERSIONS_MAPPING) + string(REGEX MATCHALL "(.+)=([0-9]+\\.?[0-9]*)" _matched ${_var}) + if(NOT _matched STREQUAL "") + list(APPEND ${list_versions} ${CMAKE_MATCH_2}) + endif() + unset(_matched) + unset(CMAKE_MATCH_1) + endforeach(_var) + unset(_var) +endmacro() + + +#.rst: +# .. command:: matlab_extract_all_installed_versions_from_registry +# +# This function parses the registry and founds the Matlab versions that are +# installed. The found versions are returned in `matlab_versions`. +# Set `win64` to `TRUE` if the 64 bit version of Matlab should be looked for +# The returned list contains all versions under +# ``HKLM\\SOFTWARE\\Mathworks\\MATLAB`` or an empty list in case an error +# occurred (or nothing found). +# +# .. note:: +# +# Only the versions are provided. No check is made over the existence of the +# installation referenced in the registry, +# +function(matlab_extract_all_installed_versions_from_registry win64 matlab_versions) + + if(NOT CMAKE_HOST_WIN32) + message(FATAL_ERROR "This macro can only be called by a windows host (call to reg.exe") + endif() + + + if(${win64} AND ${CMAKE_HOST_SYSTEM_PROCESSOR} MATCHES "64") + set(APPEND_REG "/reg:64") + else() + set(APPEND_REG "/reg:32") + endif() + + # /reg:64 should be added on 64 bits capable OSs in order to enable the + # redirection of 64 bits applications + execute_process( + COMMAND reg query HKEY_LOCAL_MACHINE\\SOFTWARE\\Mathworks\\MATLAB /f * /k ${APPEND_REG} + RESULT_VARIABLE resultMatlab + OUTPUT_VARIABLE varMatlab + ERROR_VARIABLE errMatlab + INPUT_FILE NUL + ) + + + set(matlabs_from_registry) + if(${resultMatlab} EQUAL 0) + + string( + REGEX MATCHALL "MATLAB\\\\([0-9]+(\\.[0-9]+)?)" + matlab_versions_regex ${varMatlab}) + + foreach(match IN LISTS matlab_versions_regex) + string( + REGEX MATCH "MATLAB\\\\(([0-9]+)(\\.([0-9]+))?)" + current_match ${match}) + + set(_matlab_current_version ${CMAKE_MATCH_1}) + set(current_matlab_version_major ${CMAKE_MATCH_2}) + set(current_matlab_version_minor ${CMAKE_MATCH_4}) + if(NOT current_matlab_version_minor) + set(current_matlab_version_minor "0") + endif() + + list(APPEND matlabs_from_registry ${_matlab_current_version}) + unset(_matlab_current_version) + endforeach(match) + + endif() + + if(matlabs_from_registry) + list(REMOVE_DUPLICATES matlabs_from_registry) + list(SORT matlabs_from_registry) + list(REVERSE matlabs_from_registry) + endif() + + set(${matlab_versions} ${matlabs_from_registry} PARENT_SCOPE) + +endfunction() + + + +# (internal) +macro(extract_matlab_versions_from_registry_brute_force matlab_versions) + # get the supported versions + set(matlab_supported_versions) + matlab_get_supported_versions(matlab_supported_versions) + + + # this is a manual population of the versions we want to look for + # this can be done as is, but preferably with the call to + # matlab_get_supported_versions and variable + + # populating the versions we want to look for + # set(matlab_supported_versions) + + # # Matlab 7 + # set(matlab_major 7) + # foreach(current_matlab_minor RANGE 4 20) + # list(APPEND matlab_supported_versions "${matlab_major}.${current_matlab_minor}") + # endforeach(current_matlab_minor) + + # # Matlab 8 + # set(matlab_major 8) + # foreach(current_matlab_minor RANGE 0 5) + # list(APPEND matlab_supported_versions "${matlab_major}.${current_matlab_minor}") + # endforeach(current_matlab_minor) + + # # taking into account the possible additional versions provided by the user + # if(DEFINED MATLAB_ADDITIONAL_VERSIONS) + # list(APPEND matlab_supported_versions MATLAB_ADDITIONAL_VERSIONS) + # endif() + + + # we order from more recent to older + if(matlab_supported_versions) + list(REMOVE_DUPLICATES matlab_supported_versions) + list(SORT matlab_supported_versions) + list(REVERSE matlab_supported_versions) + endif() + + + set(${matlab_versions} ${matlab_supported_versions}) + + +endmacro() + + + + +#.rst: +# .. command:: matlab_get_all_valid_matlab_roots_from_registry +# +# Populates the Matlab root with valid versions of Matlab. +# The returned matlab_roots is organized in pairs +# ``(version_number,matlab_root_path)``. +# +# :: +# +# matlab_get_all_valid_matlab_roots_from_registry( +# matlab_versions +# matlab_roots) +# +# ``matlab_versions`` +# the versions of each of the Matlab installations +# ``matlab_roots`` +# the location of each of the Matlab installations +function(matlab_get_all_valid_matlab_roots_from_registry matlab_versions matlab_roots) + + # The matlab_versions comes either from + # extract_matlab_versions_from_registry_brute_force or + # matlab_extract_all_installed_versions_from_registry. + + + set(_matlab_roots_list ) + foreach(_matlab_current_version ${matlab_versions}) + get_filename_component( + current_MATLAB_ROOT + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MathWorks\\MATLAB\\${_matlab_current_version};MATLABROOT]" + ABSOLUTE) + + if(EXISTS ${current_MATLAB_ROOT}) + list(APPEND _matlab_roots_list ${_matlab_current_version} ${current_MATLAB_ROOT}) + endif() + + endforeach(_matlab_current_version) + unset(_matlab_current_version) + set(${matlab_roots} ${_matlab_roots_list} PARENT_SCOPE) + unset(_matlab_roots_list) +endfunction() + +#.rst: +# .. command:: matlab_get_mex_suffix +# +# Returns the extension of the mex files (the suffixes). +# This function should not be called before the appropriate Matlab root has +# been found. +# +# :: +# +# matlab_get_mex_suffix( +# matlab_root +# mex_suffix) +# +# ``matlab_root`` +# the root of the Matlab installation +# ``mex_suffix`` +# the variable name in which the suffix will be returned. +function(matlab_get_mex_suffix matlab_root mex_suffix) + + # todo setup the extension properly. Currently I do not know if this is + # sufficient for all win32 distributions. + # there is also CMAKE_EXECUTABLE_SUFFIX that could be tweaked + set(mexext_suffix "") + if(WIN32) + list(APPEND mexext_suffix ".bat") + endif() + + # we first try without suffix, since cmake does not understand a list with + # one empty string element + find_program( + Matlab_MEXEXTENSIONS_PROG + NAMES mexext + PATHS ${matlab_root}/bin + DOC "Matlab MEX extension provider" + NO_DEFAULT_PATH + ) + + foreach(current_mexext_suffix IN LISTS mexext_suffix) + if(NOT DEFINED Matlab_MEXEXTENSIONS_PROG OR NOT Matlab_MEXEXTENSIONS_PROG) + # this call should populate the cache automatically + find_program( + Matlab_MEXEXTENSIONS_PROG + "mexext${current_mexext_suffix}" + PATHS ${matlab_root}/bin + DOC "Matlab MEX extension provider" + NO_DEFAULT_PATH + ) + endif() + endforeach(current_mexext_suffix) + + + # the program has been found? + if((NOT Matlab_MEXEXTENSIONS_PROG) OR (NOT EXISTS ${Matlab_MEXEXTENSIONS_PROG})) + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Cannot found mexext program. Matlab root is ${matlab_root}") + endif() + unset(Matlab_MEXEXTENSIONS_PROG CACHE) + return() + endif() + + set(_matlab_mex_extension) + + set(devnull) + if(UNIX) + set(devnull INPUT_FILE /dev/null) + elseif(WIN32) + set(devnull INPUT_FILE NUL) + endif() + + execute_process( + COMMAND ${Matlab_MEXEXTENSIONS_PROG} + OUTPUT_VARIABLE _matlab_mex_extension + ERROR_VARIABLE _matlab_mex_extension_error + ${devnull}) + string(STRIP ${_matlab_mex_extension} _matlab_mex_extension) + + unset(Matlab_MEXEXTENSIONS_PROG CACHE) + set(${mex_suffix} ${_matlab_mex_extension} PARENT_SCOPE) +endfunction() + + + + +#.rst: +# .. command:: matlab_get_version_from_matlab_run +# +# This function runs Matlab program specified on arguments and extracts its +# version. +# +# :: +# +# matlab_get_version_from_matlab_run( +# matlab_binary_path +# matlab_list_versions) +# +# ``matlab_binary_path`` +# the location of the `matlab` binary executable +# ``matlab_list_versions`` +# the version extracted from Matlab +function(matlab_get_version_from_matlab_run matlab_binary_program matlab_list_versions) + + set(${matlab_list_versions} "" PARENT_SCOPE) + + + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Determining the version of Matlab from ${matlab_binary_program}") + endif() + + if(EXISTS "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp") + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Removing previous ${_matlab_temporary_folder}/matlabVersionLog.cmaketmp file") + endif() + file(REMOVE "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp") + endif() + + + # the log file is needed since on windows the command executes in a new + # window and it is not possible to get back the answer of Matlab + # the -wait command is needed on windows, otherwise the call returns + # immediately after the program launches itself. + if(WIN32) + set(_matlab_additional_commands "-wait") + endif() + + set(devnull) + if(UNIX) + set(devnull INPUT_FILE /dev/null) + elseif(WIN32) + set(devnull INPUT_FILE NUL) + endif() + + # timeout set to 120 seconds, in case it does not start + # note as said before OUTPUT_VARIABLE cannot be used in a platform + # independent manner however, not setting it would flush the output of Matlab + # in the current console (unix variant) + execute_process( + COMMAND "${matlab_binary_program}" -nosplash -nojvm ${_matlab_additional_commands} -logfile "matlabVersionLog.cmaketmp" -nodesktop -nodisplay -r "version, exit" + OUTPUT_VARIABLE _matlab_version_from_cmd_dummy + RESULT_VARIABLE _matlab_result_version_call + ERROR_VARIABLE _matlab_result_version_call_error + TIMEOUT 120 + WORKING_DIRECTORY "${_matlab_temporary_folder}" + ${devnull} + ) + + if("${_matlab_result_version_call}" MATCHES "timeout") + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Unable to determine the version of Matlab." + " Matlab call timed out after 120 seconds.") + endif() + return() + endif() + + if(${_matlab_result_version_call}) + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Unable to determine the version of Matlab. Matlab call returned with error ${_matlab_result_version_call}.") + endif() + return() + elseif(NOT EXISTS "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp") + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Unable to determine the version of Matlab. The log file does not exist.") + endif() + return() + endif() + + # if successful, read back the log + file(READ "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp" _matlab_version_from_cmd) + file(REMOVE "${_matlab_temporary_folder}/matlabVersionLog.cmaketmp") + + set(index -1) + string(FIND ${_matlab_version_from_cmd} "ans" index) + if(index EQUAL -1) + + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Cannot find the version of Matlab returned by the run.") + endif() + + else() + set(matlab_list_of_all_versions_tmp) + + string(SUBSTRING ${_matlab_version_from_cmd} ${index} -1 substring_ans) + string( + REGEX MATCHALL "ans[\r\n\t ]*=[\r\n\t ]*'?([0-9]+(\\.[0-9]+)?)" + matlab_versions_regex + ${substring_ans}) + foreach(match IN LISTS matlab_versions_regex) + string( + REGEX MATCH "ans[\r\n\t ]*=[\r\n\t ]*'?(([0-9]+)(\\.([0-9]+))?)" + current_match ${match}) + + list(APPEND matlab_list_of_all_versions_tmp ${CMAKE_MATCH_1}) + endforeach() + if(matlab_list_of_all_versions_tmp) + list(REMOVE_DUPLICATES matlab_list_of_all_versions_tmp) + endif() + set(${matlab_list_versions} ${matlab_list_of_all_versions_tmp} PARENT_SCOPE) + + endif() + +endfunction() + +#.rst: +# .. command:: matlab_add_unit_test +# +# Adds a Matlab unit test to the test set of cmake/ctest. +# This command requires the component ``MAIN_PROGRAM``. +# The unit test uses the Matlab unittest framework (default, available +# starting Matlab 2013b+) except if the option ``NO_UNITTEST_FRAMEWORK`` +# is given. +# +# The function expects one Matlab test script file to be given. +# In the case ``NO_UNITTEST_FRAMEWORK`` is given, the unittest script file +# should contain the script to be run, plus an exit command with the exit +# value. This exit value will be passed to the ctest framework (0 success, +# non 0 failure). Additional arguments accepted by :command:`add_test` can be +# passed through ``TEST_ARGS`` (eg. ``CONFIGURATION ...``). +# +# :: +# +# matlab_add_unit_test( +# NAME +# UNITTEST_FILE matlab_file_containing_unittest.m +# [CUSTOM_TEST_COMMAND matlab_command_to_run_as_test] +# [UNITTEST_PRECOMMAND matlab_command_to_run] +# [TIMEOUT timeout] +# [ADDITIONAL_PATH path1 [path2 ...]] +# [MATLAB_ADDITIONAL_STARTUP_OPTIONS option1 [option2 ...]] +# [TEST_ARGS arg1 [arg2 ...]] +# [NO_UNITTEST_FRAMEWORK] +# ) +# +# The function arguments are: +# +# ``NAME`` +# name of the unittest in ctest. +# ``UNITTEST_FILE`` +# the matlab unittest file. Its path will be automatically +# added to the Matlab path. +# ``CUSTOM_TEST_COMMAND`` +# Matlab script command to run as the test. +# If this is not set, then the following is run: +# ``runtests('matlab_file_name'), exit(max([ans(1,:).Failed]))`` +# where ``matlab_file_name`` is the ``UNITTEST_FILE`` without the extension. +# ``UNITTEST_PRECOMMAND`` +# Matlab script command to be ran before the file +# containing the test (eg. GPU device initialisation based on CMake +# variables). +# ``TIMEOUT`` +# the test timeout in seconds. Defaults to 180 seconds as the +# Matlab unit test may hang. +# ``ADDITIONAL_PATH`` +# a list of paths to add to the Matlab path prior to +# running the unit test. +# ``MATLAB_ADDITIONAL_STARTUP_OPTIONS`` +# a list of additional option in order +# to run Matlab from the command line. +# ``-nosplash -nodesktop -nodisplay`` are always added. +# ``TEST_ARGS`` +# Additional options provided to the add_test command. These +# options are added to the default options (eg. "CONFIGURATIONS Release") +# ``NO_UNITTEST_FRAMEWORK`` +# when set, indicates that the test should not +# use the unittest framework of Matlab (available for versions >= R2013a). +# ``WORKING_DIRECTORY`` +# This will be the working directory for the test. If specified it will +# also be the output directory used for the log file of the test run. +# If not specifed the temporary directory ``${CMAKE_BINARY_DIR}/Matlab`` will +# be used as the working directory and the log location. +# +function(matlab_add_unit_test) + + if(NOT Matlab_MAIN_PROGRAM) + message(FATAL_ERROR "[MATLAB] This functionality needs the MAIN_PROGRAM component (not default)") + endif() + + set(options NO_UNITTEST_FRAMEWORK) + set(oneValueArgs NAME UNITTEST_FILE TIMEOUT WORKING_DIRECTORY + UNITTEST_PRECOMMAND CUSTOM_TEST_COMMAND) + set(multiValueArgs ADDITIONAL_PATH MATLAB_ADDITIONAL_STARTUP_OPTIONS TEST_ARGS) + + set(prefix _matlab_unittest_prefix) + cmake_parse_arguments(PARSE_ARGV 0 ${prefix} "${options}" "${oneValueArgs}" "${multiValueArgs}" ) + + if(NOT ${prefix}_NAME) + message(FATAL_ERROR "[MATLAB] The Matlab test name cannot be empty") + endif() + + add_test(NAME ${${prefix}_NAME} + COMMAND ${CMAKE_COMMAND} + "-Dtest_name=${${prefix}_NAME}" + "-Dadditional_paths=${${prefix}_ADDITIONAL_PATH}" + "-Dtest_timeout=${${prefix}_TIMEOUT}" + "-Doutput_directory=${_matlab_temporary_folder}" + "-Dworking_directory=${${prefix}_WORKING_DIRECTORY}" + "-DMatlab_PROGRAM=${Matlab_MAIN_PROGRAM}" + "-Dno_unittest_framework=${${prefix}_NO_UNITTEST_FRAMEWORK}" + "-DMatlab_ADDITIONAL_STARTUP_OPTIONS=${${prefix}_MATLAB_ADDITIONAL_STARTUP_OPTIONS}" + "-Dunittest_file_to_run=${${prefix}_UNITTEST_FILE}" + "-Dcustom_Matlab_test_command=${${prefix}_CUSTOM_TEST_COMMAND}" + "-Dcmd_to_run_before_test=${${prefix}_UNITTEST_PRECOMMAND}" + -P ${_FindMatlab_SELF_DIR}/MatlabTestsRedirect.cmake + ${${prefix}_TEST_ARGS} + ${${prefix}_UNPARSED_ARGUMENTS} + ) +endfunction() + + +#.rst: +# .. command:: matlab_add_mex +# +# Adds a Matlab MEX target. +# This commands compiles the given sources with the current tool-chain in +# order to produce a MEX file. The final name of the produced output may be +# specified, as well as additional link libraries, and a documentation entry +# for the MEX file. Remaining arguments of the call are passed to the +# :command:`add_library` or :command:`add_executable` command. +# +# :: +# +# matlab_add_mex( +# NAME +# [EXECUTABLE | MODULE | SHARED] +# SRC src1 [src2 ...] +# [OUTPUT_NAME output_name] +# [DOCUMENTATION file.txt] +# [LINK_TO target1 target2 ...] +# [...] +# ) +# +# ``NAME`` +# name of the target. +# ``SRC`` +# list of source files. +# ``LINK_TO`` +# a list of additional link dependencies. The target links to ``libmex`` +# by default. If ``Matlab_MX_LIBRARY`` is defined, it also +# links to ``libmx``. +# ``OUTPUT_NAME`` +# if given, overrides the default name. The default name is +# the name of the target without any prefix and +# with ``Matlab_MEX_EXTENSION`` suffix. +# ``DOCUMENTATION`` +# if given, the file ``file.txt`` will be considered as +# being the documentation file for the MEX file. This file is copied into +# the same folder without any processing, with the same name as the final +# mex file, and with extension `.m`. In that case, typing ``help `` +# in Matlab prints the documentation contained in this file. +# ``MODULE`` or ``SHARED`` may be given to specify the type of library to be +# created. ``EXECUTABLE`` may be given to create an executable instead of +# a library. If no type is given explicitly, the type is ``SHARED``. +# +# The documentation file is not processed and should be in the following +# format: +# +# :: +# +# % This is the documentation +# function ret = mex_target_output_name(input1) +# +function(matlab_add_mex) + + if(NOT WIN32) + # we do not need all this on Windows + # pthread options + if(CMAKE_CXX_COMPILER_LOADED) + check_cxx_compiler_flag(-pthread HAS_MINUS_PTHREAD) + elseif(CMAKE_C_COMPILER_LOADED) + check_c_compiler_flag(-pthread HAS_MINUS_PTHREAD) + endif() + # we should use try_compile instead, the link flags are discarded from + # this compiler_flag function. + #check_cxx_compiler_flag(-Wl,--exclude-libs,ALL HAS_SYMBOL_HIDING_CAPABILITY) + + endif() + + set(options EXECUTABLE MODULE SHARED) + set(oneValueArgs NAME DOCUMENTATION OUTPUT_NAME) + set(multiValueArgs LINK_TO SRC) + + set(prefix _matlab_addmex_prefix) + cmake_parse_arguments(${prefix} "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + + if(NOT ${prefix}_NAME) + message(FATAL_ERROR "[MATLAB] The MEX target name cannot be empty") + endif() + + if(NOT ${prefix}_OUTPUT_NAME) + set(${prefix}_OUTPUT_NAME ${${prefix}_NAME}) + endif() + + if(${prefix}_EXECUTABLE) + add_executable(${${prefix}_NAME} + ${${prefix}_SRC} + ${${prefix}_DOCUMENTATION} + ${${prefix}_UNPARSED_ARGUMENTS}) + else() + if(${prefix}_MODULE) + set(type MODULE) + else() + set(type SHARED) + endif() + + add_library(${${prefix}_NAME} + ${type} + ${${prefix}_SRC} + ${${prefix}_DOCUMENTATION} + ${${prefix}_UNPARSED_ARGUMENTS}) + endif() + + target_include_directories(${${prefix}_NAME} PRIVATE ${Matlab_INCLUDE_DIRS}) + + if(DEFINED Matlab_MX_LIBRARY) + target_link_libraries(${${prefix}_NAME} ${Matlab_MX_LIBRARY}) + endif() + + target_link_libraries(${${prefix}_NAME} ${Matlab_MEX_LIBRARY} ${${prefix}_LINK_TO}) + set_target_properties(${${prefix}_NAME} + PROPERTIES + PREFIX "" + OUTPUT_NAME ${${prefix}_OUTPUT_NAME} + SUFFIX ".${Matlab_MEX_EXTENSION}") + + + # documentation + if(NOT ${${prefix}_DOCUMENTATION} STREQUAL "") + get_target_property(output_name ${${prefix}_NAME} OUTPUT_NAME) + add_custom_command( + TARGET ${${prefix}_NAME} + PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${${prefix}_DOCUMENTATION} $/${output_name}.m + COMMENT "Copy ${${prefix}_NAME} documentation file into the output folder" + ) + endif() # documentation + + # entry point in the mex file + taking care of visibility and symbol clashes. + if(WIN32) + set_target_properties(${${prefix}_NAME} + PROPERTIES + DEFINE_SYMBOL "DLL_EXPORT_SYM=__declspec(dllexport)") + else() + + if(HAS_MINUS_PTHREAD AND NOT APPLE) + # Apparently, compiling with -pthread generated the proper link flags + # and some defines at compilation + target_compile_options(${${prefix}_NAME} PRIVATE "-pthread") + endif() + + + # if we do not do that, the symbols linked from eg. boost remain weak and + # then clash with the ones defined in the matlab process. So by default + # the symbols are hidden. + # This also means that for shared libraries (like MEX), the entry point + # should be explicitly declared with default visibility, otherwise Matlab + # cannot find the entry point. + # Note that this is particularly meaningful if the MEX wrapper itself + # contains symbols that are clashing with Matlab (that are compiled in the + # MEX file). In order to propagate the visibility options to the libraries + # to which the MEX file is linked against, the -Wl,--exclude-libs,ALL + # option should also be specified. + + set_target_properties(${${prefix}_NAME} + PROPERTIES + CXX_VISIBILITY_PRESET "hidden" + C_VISIBILITY_PRESET "hidden" + VISIBILITY_INLINES_HIDDEN ON + ) + + # get_target_property( + # _previous_link_flags + # ${${prefix}_NAME} + # LINK_FLAGS) + # if(NOT _previous_link_flags) + # set(_previous_link_flags) + # endif() + + # if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + # set_target_properties(${${prefix}_NAME} + # PROPERTIES + # LINK_FLAGS "${_previous_link_flags} -Wl,--exclude-libs,ALL" + # # -Wl,--version-script=${_FindMatlab_SELF_DIR}/MatlabLinuxVisibility.map" + # ) + # elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # # in this case, all other symbols become hidden. + # set_target_properties(${${prefix}_NAME} + # PROPERTIES + # LINK_FLAGS "${_previous_link_flags} -Wl,-exported_symbol,_mexFunction" + # #-Wl,-exported_symbols_list,${_FindMatlab_SELF_DIR}/MatlabOSXVisilibity.map" + # ) + # endif() + + + + set_target_properties(${${prefix}_NAME} + PROPERTIES + DEFINE_SYMBOL "DLL_EXPORT_SYM=__attribute__ ((visibility (\"default\")))" + ) + + + endif() + +endfunction() + + +# (internal) +# Used to get the version of matlab, using caching. This basically transforms the +# output of the root list, with possible unknown version, to a version +# +function(_Matlab_get_version_from_root matlab_root matlab_known_version matlab_final_version) + + # if the version is not trivial, we query matlab for that + # we keep track of the location of matlab that induced this version + #if(NOT DEFINED Matlab_PROG_VERSION_STRING_AUTO_DETECT) + # set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version") + #endif() + + if(NOT ${matlab_known_version} STREQUAL "NOTFOUND") + # the version is known, we just return it + set(${matlab_final_version} ${matlab_known_version} PARENT_SCOPE) + set(Matlab_VERSION_STRING_INTERNAL ${matlab_known_version} CACHE INTERNAL "Matlab version (automatically determined)" FORCE) + return() + endif() + + # + set(_matlab_current_program ${Matlab_MAIN_PROGRAM}) + + # do we already have a matlab program? + if(NOT _matlab_current_program) + + set(_find_matlab_options) + if(matlab_root AND EXISTS ${matlab_root}) + set(_find_matlab_options PATHS ${matlab_root} ${matlab_root}/bin NO_DEFAULT_PATH) + endif() + + find_program( + _matlab_current_program + matlab + ${_find_matlab_options} + DOC "Matlab main program" + ) + endif() + + if(NOT _matlab_current_program OR NOT EXISTS ${_matlab_current_program}) + # if not found, clear the dependent variables + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Cannot find the main matlab program under ${matlab_root}") + endif() + set(Matlab_PROG_VERSION_STRING_AUTO_DETECT "" CACHE INTERNAL "internal matlab location for the discovered version" FORCE) + set(Matlab_VERSION_STRING_INTERNAL "" CACHE INTERNAL "internal matlab location for the discovered version" FORCE) + unset(_matlab_current_program) + unset(_matlab_current_program CACHE) + return() + endif() + + # full real path for path comparison + get_filename_component(_matlab_main_real_path_tmp "${_matlab_current_program}" REALPATH) + unset(_matlab_current_program) + unset(_matlab_current_program CACHE) + + # is it the same as the previous one? + if(_matlab_main_real_path_tmp STREQUAL Matlab_PROG_VERSION_STRING_AUTO_DETECT) + set(${matlab_final_version} ${Matlab_VERSION_STRING_INTERNAL} PARENT_SCOPE) + return() + endif() + + # update the location of the program + set(Matlab_PROG_VERSION_STRING_AUTO_DETECT ${_matlab_main_real_path_tmp} CACHE INTERNAL "internal matlab location for the discovered version" FORCE) + + set(matlab_list_of_all_versions) + matlab_get_version_from_matlab_run("${Matlab_PROG_VERSION_STRING_AUTO_DETECT}" matlab_list_of_all_versions) + + list(LENGTH matlab_list_of_all_versions list_of_all_versions_length) + if(${list_of_all_versions_length} GREATER 0) + list(GET matlab_list_of_all_versions 0 _matlab_version_tmp) + else() + set(_matlab_version_tmp "unknown") + endif() + + # set the version into the cache + set(Matlab_VERSION_STRING_INTERNAL ${_matlab_version_tmp} CACHE INTERNAL "Matlab version (automatically determined)" FORCE) + + # warning, just in case several versions found (should not happen) + if((${list_of_all_versions_length} GREATER 1) AND MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Found several versions, taking the first one (versions found ${matlab_list_of_all_versions})") + endif() + + # return the updated value + set(${matlab_final_version} ${Matlab_VERSION_STRING_INTERNAL} PARENT_SCOPE) + +endfunction() + + + + + + + +# ################################### +# Exploring the possible Matlab_ROOTS + +# this variable will get all Matlab installations found in the current system. +set(_matlab_possible_roots) + + + +if(Matlab_ROOT_DIR) + # if the user specifies a possible root, we keep this one + + if(NOT EXISTS ${Matlab_ROOT_DIR}) + # if Matlab_ROOT_DIR specified but erroneous + if(MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] the specified path for Matlab_ROOT_DIR does not exist (${Matlab_ROOT_DIR})") + endif() + else() + # NOTFOUND indicates the code below to search for the version automatically + if("${Matlab_VERSION_STRING_INTERNAL}" STREQUAL "") + list(APPEND _matlab_possible_roots "NOTFOUND" ${Matlab_ROOT_DIR}) # empty version + else() + list(APPEND _matlab_possible_roots ${Matlab_VERSION_STRING_INTERNAL} ${Matlab_ROOT_DIR}) # cached version + endif() + endif() + + +else() + + # if the user does not specify the possible installation root, we look for + # one installation using the appropriate heuristics + + if(WIN32) + + # On WIN32, we look for Matlab installation in the registry + # if unsuccessful, we look for all known revision and filter the existing + # ones. + + # testing if we are able to extract the needed information from the registry + set(_matlab_versions_from_registry) + + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_matlab_win64 ON) + else() + set(_matlab_win64 OFF) + endif() + + matlab_extract_all_installed_versions_from_registry(_matlab_win64 _matlab_versions_from_registry) + + # the returned list is empty, doing the search on all known versions + if(NOT _matlab_versions_from_registry) + + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Search for Matlab from the registry unsuccessful, testing all supported versions") + endif() + + extract_matlab_versions_from_registry_brute_force(_matlab_versions_from_registry) + endif() + + # filtering the results with the registry keys + matlab_get_all_valid_matlab_roots_from_registry("${_matlab_versions_from_registry}" _matlab_possible_roots) + unset(_matlab_versions_from_registry) + + elseif(APPLE) + + # on mac, we look for the /Application paths + # this corresponds to the behaviour on Windows. On Linux, we do not have + # any other guess. + matlab_get_supported_releases(_matlab_releases) + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Matlab supported versions ${_matlab_releases}. If more version should be supported " + "the variable MATLAB_ADDITIONAL_VERSIONS can be set according to the documentation") + endif() + + foreach(_matlab_current_release IN LISTS _matlab_releases) + set(_matlab_full_string "/Applications/MATLAB_${_matlab_current_release}.app") + if(EXISTS ${_matlab_full_string}) + set(_matlab_current_version) + matlab_get_version_from_release_name("${_matlab_current_release}" _matlab_current_version) + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Found version ${_matlab_current_release} (${_matlab_current_version}) in ${_matlab_full_string}") + endif() + list(APPEND _matlab_possible_roots ${_matlab_current_version} ${_matlab_full_string}) + unset(_matlab_current_version) + endif() + + unset(_matlab_full_string) + endforeach(_matlab_current_release) + + unset(_matlab_current_release) + unset(_matlab_releases) + + endif() + +endif() + + + +list(LENGTH _matlab_possible_roots _numbers_of_matlab_roots) +if(_numbers_of_matlab_roots EQUAL 0) + # if we have not found anything, we fall back on the PATH + + + # At this point, we have no other choice than trying to find it from PATH. + # If set by the user, this wont change + find_program( + _matlab_main_tmp + NAMES matlab) + + + if(_matlab_main_tmp) + # we then populate the list of roots, with empty version + if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] matlab found from PATH: ${_matlab_main_tmp}") + endif() + + # resolve symlinks + get_filename_component(_matlab_current_location "${_matlab_main_tmp}" REALPATH) + + # get the directory (the command below has to be run twice) + # this will be the matlab root + get_filename_component(_matlab_current_location "${_matlab_current_location}" DIRECTORY) + get_filename_component(_matlab_current_location "${_matlab_current_location}" DIRECTORY) # Matlab should be in bin + + list(APPEND _matlab_possible_roots "NOTFOUND" ${_matlab_current_location}) + + unset(_matlab_current_location) + + endif() + unset(_matlab_main_tmp CACHE) + +endif() + + + + + +if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Matlab root folders are ${_matlab_possible_roots}") +endif() + + + + + +# take the first possible Matlab root +list(LENGTH _matlab_possible_roots _numbers_of_matlab_roots) +set(Matlab_VERSION_STRING "NOTFOUND") +if(_numbers_of_matlab_roots GREATER 0) + list(GET _matlab_possible_roots 0 Matlab_VERSION_STRING) + list(GET _matlab_possible_roots 1 Matlab_ROOT_DIR) + + # adding a warning in case of ambiguity + if(_numbers_of_matlab_roots GREATER 2 AND MATLAB_FIND_DEBUG) + message(WARNING "[MATLAB] Found several distributions of Matlab. Setting the current version to ${Matlab_VERSION_STRING} (located ${Matlab_ROOT_DIR})." + " If this is not the desired behaviour, provide the -DMatlab_ROOT_DIR=... on the command line") + endif() +endif() + + +# check if the root changed wrt. the previous defined one, if so +# clear all the cached variables for being able to reconfigure properly +if(DEFINED Matlab_ROOT_DIR_LAST_CACHED) + + if(NOT Matlab_ROOT_DIR_LAST_CACHED STREQUAL Matlab_ROOT_DIR) + set(_Matlab_cached_vars + Matlab_INCLUDE_DIRS + Matlab_MEX_LIBRARY + Matlab_MEX_COMPILER + Matlab_MAIN_PROGRAM + Matlab_MX_LIBRARY + Matlab_ENG_LIBRARY + Matlab_MAT_LIBRARY + Matlab_MEX_EXTENSION + Matlab_SIMULINK_INCLUDE_DIR + + # internal + Matlab_MEXEXTENSIONS_PROG + Matlab_ROOT_DIR_LAST_CACHED + #Matlab_PROG_VERSION_STRING_AUTO_DETECT + Matlab_VERSION_STRING_INTERNAL + ) + foreach(_var IN LISTS _Matlab_cached_vars) + if(DEFINED ${_var}) + unset(${_var} CACHE) + endif() + endforeach() + endif() +endif() + +set(Matlab_ROOT_DIR_LAST_CACHED ${Matlab_ROOT_DIR} CACHE INTERNAL "last Matlab root dir location") +set(Matlab_ROOT_DIR ${Matlab_ROOT_DIR} CACHE PATH "Matlab installation root path" FORCE) + +# Fix the version, in case this one is NOTFOUND +_Matlab_get_version_from_root( + "${Matlab_ROOT_DIR}" + ${Matlab_VERSION_STRING} + Matlab_VERSION_STRING +) + + + + +if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] Current version is ${Matlab_VERSION_STRING} located ${Matlab_ROOT_DIR}") +endif() + + + +if(Matlab_ROOT_DIR) + file(TO_CMAKE_PATH ${Matlab_ROOT_DIR} Matlab_ROOT_DIR) +endif() + +if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(_matlab_64Build FALSE) +else() + set(_matlab_64Build TRUE) +endif() + +if(APPLE) + set(_matlab_bin_prefix "mac") # i should be for intel + set(_matlab_bin_suffix_32bits "i") + set(_matlab_bin_suffix_64bits "i64") +elseif(UNIX) + set(_matlab_bin_prefix "gln") + set(_matlab_bin_suffix_32bits "x86") + set(_matlab_bin_suffix_64bits "xa64") +else() + set(_matlab_bin_prefix "win") + set(_matlab_bin_suffix_32bits "32") + set(_matlab_bin_suffix_64bits "64") +endif() + + + +set(MATLAB_INCLUDE_DIR_TO_LOOK ${Matlab_ROOT_DIR}/extern/include) +if(_matlab_64Build) + set(_matlab_current_suffix ${_matlab_bin_suffix_64bits}) +else() + set(_matlab_current_suffix ${_matlab_bin_suffix_32bits}) +endif() + +set(Matlab_BINARIES_DIR + ${Matlab_ROOT_DIR}/bin/${_matlab_bin_prefix}${_matlab_current_suffix}) +set(Matlab_EXTERN_LIBRARY_DIR + ${Matlab_ROOT_DIR}/extern/lib/${_matlab_bin_prefix}${_matlab_current_suffix}) + +if(WIN32) + if(MINGW) + set(_matlab_lib_dir_for_search ${Matlab_EXTERN_LIBRARY_DIR}/mingw64) + else() + set(_matlab_lib_dir_for_search ${Matlab_EXTERN_LIBRARY_DIR}/microsoft) + endif() + set(_matlab_lib_prefix_for_search "lib") +else() + set(_matlab_lib_dir_for_search ${Matlab_BINARIES_DIR}) + set(_matlab_lib_prefix_for_search "lib") +endif() + +unset(_matlab_64Build) + + +if(NOT DEFINED Matlab_MEX_EXTENSION) + set(_matlab_mex_extension "") + matlab_get_mex_suffix("${Matlab_ROOT_DIR}" _matlab_mex_extension) + + # This variable goes to the cache. + set(Matlab_MEX_EXTENSION ${_matlab_mex_extension} CACHE STRING "Extensions for the mex targets (automatically given by Matlab)") + unset(_matlab_mex_extension) +endif() + + +if(MATLAB_FIND_DEBUG) + message(STATUS "[MATLAB] [DEBUG]_matlab_lib_prefix_for_search = ${_matlab_lib_prefix_for_search} | _matlab_lib_dir_for_search = ${_matlab_lib_dir_for_search}") +endif() + + + +# internal +# This small stub around find_library is to prevent any pollution of CMAKE_FIND_LIBRARY_PREFIXES in the global scope. +# This is the function to be used below instead of the find_library directives. +function(_Matlab_find_library _matlab_library_prefix) + set(CMAKE_FIND_LIBRARY_PREFIXES ${CMAKE_FIND_LIBRARY_PREFIXES} ${_matlab_library_prefix}) + find_library(${ARGN}) +endfunction() + + +set(_matlab_required_variables) + + +# the MEX library/header are required +find_path( + Matlab_INCLUDE_DIRS + mex.h + PATHS ${MATLAB_INCLUDE_DIR_TO_LOOK} + NO_DEFAULT_PATH + ) +list(APPEND _matlab_required_variables Matlab_INCLUDE_DIRS) + +_Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_MEX_LIBRARY + mex + PATHS ${_matlab_lib_dir_for_search} + NO_DEFAULT_PATH +) + +list(APPEND _matlab_required_variables Matlab_MEX_LIBRARY) + +# the MEX extension is required +list(APPEND _matlab_required_variables Matlab_MEX_EXTENSION) + +# the matlab root is required +list(APPEND _matlab_required_variables Matlab_ROOT_DIR) + +# component Mex Compiler +list(FIND Matlab_FIND_COMPONENTS MEX_COMPILER _matlab_find_mex_compiler) +if(_matlab_find_mex_compiler GREATER -1) + find_program( + Matlab_MEX_COMPILER + "mex" + PATHS ${Matlab_BINARIES_DIR} + DOC "Matlab MEX compiler" + NO_DEFAULT_PATH + ) + if(Matlab_MEX_COMPILER) + set(Matlab_MEX_COMPILER_FOUND TRUE) + endif() +endif() +unset(_matlab_find_mex_compiler) + +# component Matlab program +list(FIND Matlab_FIND_COMPONENTS MAIN_PROGRAM _matlab_find_matlab_program) +if(_matlab_find_matlab_program GREATER -1) + find_program( + Matlab_MAIN_PROGRAM + matlab + PATHS ${Matlab_ROOT_DIR} ${Matlab_ROOT_DIR}/bin + DOC "Matlab main program" + NO_DEFAULT_PATH + ) + if(Matlab_MAIN_PROGRAM) + set(Matlab_MAIN_PROGRAM_FOUND TRUE) + endif() +endif() +unset(_matlab_find_matlab_program) + +# Component MX library +list(FIND Matlab_FIND_COMPONENTS MX_LIBRARY _matlab_find_mx) +if(_matlab_find_mx GREATER -1) + _Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_MX_LIBRARY + mx + PATHS ${_matlab_lib_dir_for_search} + NO_DEFAULT_PATH + ) + if(Matlab_MX_LIBRARY) + set(Matlab_MX_LIBRARY_FOUND TRUE) + endif() +endif() +unset(_matlab_find_mx) + +# Component ENG library +list(FIND Matlab_FIND_COMPONENTS ENG_LIBRARY _matlab_find_eng) +if(_matlab_find_eng GREATER -1) + _Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_ENG_LIBRARY + eng + PATHS ${_matlab_lib_dir_for_search} + NO_DEFAULT_PATH + ) + if(Matlab_ENG_LIBRARY) + set(Matlab_ENG_LIBRARY_FOUND TRUE) + endif() +endif() +unset(_matlab_find_eng) + +# Component MAT library +list(FIND Matlab_FIND_COMPONENTS MAT_LIBRARY _matlab_find_mat) +if(_matlab_find_mat GREATER -1) + _Matlab_find_library( + ${_matlab_lib_prefix_for_search} + Matlab_MAT_LIBRARY + mat + PATHS ${_matlab_lib_dir_for_search} + NO_DEFAULT_PATH + ) + if(Matlab_MAT_LIBRARY) + set(Matlab_MAT_LIBRARY_FOUND TRUE) + endif() +endif() +unset(_matlab_find_mat) + +# Component Simulink +list(FIND Matlab_FIND_COMPONENTS SIMULINK _matlab_find_simulink) +if(_matlab_find_simulink GREATER -1) + find_path( + Matlab_SIMULINK_INCLUDE_DIR + simstruc.h + PATHS "${Matlab_ROOT_DIR}/simulink/include" + NO_DEFAULT_PATH + ) + if(Matlab_SIMULINK_INCLUDE_DIR) + set(Matlab_SIMULINK_FOUND TRUE) + list(APPEND Matlab_INCLUDE_DIRS "${Matlab_SIMULINK_INCLUDE_DIR}") + endif() +endif() +unset(_matlab_find_simulink) + +unset(_matlab_lib_dir_for_search) + +set(Matlab_LIBRARIES ${Matlab_MEX_LIBRARY} ${Matlab_MX_LIBRARY} ${Matlab_ENG_LIBRARY} ${Matlab_MAT_LIBRARY}) + +find_package_handle_standard_args( + Matlab + FOUND_VAR Matlab_FOUND + REQUIRED_VARS ${_matlab_required_variables} + VERSION_VAR Matlab_VERSION_STRING + HANDLE_COMPONENTS) + +unset(_matlab_required_variables) +unset(_matlab_bin_prefix) +unset(_matlab_bin_suffix_32bits) +unset(_matlab_bin_suffix_64bits) +unset(_matlab_current_suffix) +unset(_matlab_lib_dir_for_search) +unset(_matlab_lib_prefix_for_search) + +if(Matlab_INCLUDE_DIRS AND Matlab_LIBRARIES) + mark_as_advanced( + Matlab_MEX_LIBRARY + Matlab_MX_LIBRARY + Matlab_ENG_LIBRARY + Matlab_MAT_LIBRARY + Matlab_INCLUDE_DIRS + Matlab_FOUND + Matlab_MAIN_PROGRAM + Matlab_MEXEXTENSIONS_PROG + Matlab_MEX_EXTENSION + ) +endif() diff --git a/config/helics-config.h.in b/config/helics-config.h.in index ff18aac5ec..9bbaf460c8 100644 --- a/config/helics-config.h.in +++ b/config/helics-config.h.in @@ -22,6 +22,7 @@ #define HELICS_VERSION_MINOR ${HELICS_VERSION_MINOR} #define HELICS_VERSION_PATCH ${HELICS_VERSION_PATCH} #define HELICS_VERSION_BUILD "${HELICS_VERSION_BUILD}" +#define HELICS_VERSION_STRING "${HELICS_VERSION_STRING}" #define HELICS_DATE "${HELICS_DATE}" #endif diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt new file mode 100644 index 0000000000..0c9cfb1d25 --- /dev/null +++ b/docs/CMakeLists.txt @@ -0,0 +1,21 @@ +if (MSVC) + +set(doc_sources + apps/Echo.md + apps/Broker.md + apps/App.md + apps/Player.md + apps/index.md + apps/Source.md + configuration/CoreTypes.md + configuration/Federate.md + configuration/index.md + configuration/Timing.md + configuration/Filters.md + configuration/MessageFederate.md + configuration/ValueFederate.md + ) + +add_custom_target(helics_docs SOURCES ${doc_sources}) + +endif(MSVC) \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile index b51d4b86f3..60da0284c2 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -23,8 +23,9 @@ help: doxygen: cd ../; mkdir -p build-doxygen; cd build-doxygen; cmake -DCMAKE_INSTALL_PREFIX=./install/helics_install -DHELICS_GENERATE_DOXYGEN_DOC=ON ../; make doc; -sphinxdoxygen: - cp -r html ./_build/html/doxygen +sphinxdoxygen: doxygen + -rm -rf ./_build/html/doxygen + cp -r ../build-doxygen/docs/html/ ./_build/html/doxygen/ github: html sphinxdoxygen -git branch -D gh-pages diff --git a/docs/apps/App.md b/docs/apps/App.md new file mode 100644 index 0000000000..0412ad0741 --- /dev/null +++ b/docs/apps/App.md @@ -0,0 +1,37 @@ +helics_app +======= + +The helics apps executable is one of the HELICS apps available with the library +Its purpose is to provide a common executable for running any of the other as + +typical syntax is as follows +``` +helics-app.exe +``` +possible apps are + +#### Echo +The [Echo](echo.md) app is a responsive app that will echo any message sent to its endpoints back to the orginal source with a specified delay + +THis is useful for testing communication pathways and in combination with filters can be used to create some interesting situations + +#### Player +The [player](Player.md) app will generate signals through specified interfaces from prescribed data +This is used for generating test signals into a federate + +#### Recorder +The [Recorder](Recorder.md) app captures signals and data on specified interfaces and can record then to various file formats including text files and json files +The files saved can then be used by the Player app at a later time + +#### Tracer +The [Tracer](Tracer.md) app functions much like the recorder when run as a standalone app with the exception that it displays information to a text window and doesn't capture to a file +The additional purpose is used as a library object as the basis for additional display purposes and interfaces + +#### Source + +The [Source](Source.md) app is a signal generator like the player execpt that is can generate signals from defined patterns including some random signals in value and timing, and other patterns like sine, square wave, ramps +and others. Used much like the player in situations some test signals are needed. + +#### Broker + +The [Broker](Broker.md) executes a broker like the stand alone Broker app. \ No newline at end of file diff --git a/docs/apps/Broker.md b/docs/apps/Broker.md new file mode 100644 index 0000000000..ae543286ca --- /dev/null +++ b/docs/apps/Broker.md @@ -0,0 +1,198 @@ +Broker +======= + +Brokers function as intermediaries or roots in the HELICS hiearchy +The Broker can be run through the helics_broker or via helics-app + + +Command line arguments +---------- +``` +allowed options: + +command line only: + -? [ --help ] produce help message + -v [ --version ] display a version string + --config-file arg specify a configuration file to use + +configuration: + --datatype arg type of the publication data type to use + --local specify otherwise unspecified endpoints and + publications as local( i.e.the keys will be prepended + with the player name + --separator arg specify the separator for local publications and + endpoints + --timeunits arg the default units on the timestamps used in file based + input + --stop arg the time to stop the player + +federate configuration + -b [ --broker ] arg address of the broker to connect + -n [ --name ] arg name of the player federate + --corename arg the name of the core to create or find + -c [ --core ] arg type of the core to connect to + --offset arg the offset of the time steps + --period arg the period of the federate + --timedelta arg the time delta of the federate + -i [ --coreinit ] arg the core initialization string + --inputdelay arg the input delay on incoming communication of the + federate + --outputdelay arg the output delay for outgoing communication of the + federate + -f [ --flags ] arg named flags for the federate + +``` +also permissible are all arguments allowed for federates and any specific broker specified: + +[Command line reference](cmdArgs.md) + +the player executable also takes an untagged argument of a file name for example +``` +helics_player player_file.txt --stop 5 +``` + +Players support both delimited text files and json files some examples can be found in + +[Player configuation Examples](https://github.com/GMLC-TDC/HELICS-src/tree/master/tests/helics/apps/test_files) + +## Config File Detail + +### publications +a simple example of a player file publishing values +``` +#second topic type(opt) value +-1.0, pub1, d, 0.3 +1, pub1, 0.5 +3, pub1 0.8 +2, pub1 0.7 +# pub 2 +1, pub2, d, 0.4 +2, pub2, 0.6 +3, pub2, 0.9 +``` +`#` signifies a comment +the first column is time in seconds unless otherwise specified via the `--timeunits` flag or other configuration means +the second column is publication name +the final column is the value +the optional third column specifies a type valid types are + +time specifications are typically numerical with optional units +`5` or `"500 ms"` or `23.7us` if there is a space between the number and units it must be enclosed in quotes +if no units are specified the time defaults to units specified via `--timeunits` or seconds if none were specified +valid units are "s", "ms","us","min", "day", "hr", "ns", "ps" the default precision in HELICS is ns so time specified in ps is not guaranteed to be precise + + +| identifier | type | Example | +| ------------- |:-------------:| -----:| +| d,f, double | double | 45.1 | +| s,string | string | "this is a test" | +| i, i64, int | integer | 456 | +| c, complex | complex | 23+2j, -23.1j, 1+3i | +| v, vector | vector of doubles | [23.1,34,17.2,-5.6] | +| cv, complex_vector | vector of complex numbers | [23+2j, -23.1j, 1+3i] | + +capitalization does not matter + +values with times <0 are sent during the initialization phase +values with time==0 are sent immediately after entering execution phase + +### Messages +messages are specified in one of two forms +``` +m

HELICS (Hiearchical Engine for Large-scale infrastructre Co-Simulation) is a co-simulation plaform +


+ + +[readMe](../../README.md) +
+[Disclaimer](../../DISCLAIMER) +
+[license](../../LICENSE) + */ \ No newline at end of file diff --git a/examples/CInterface/CMakeLists.txt b/examples/CInterface/CMakeLists.txt index 1987e69883..406a21ba81 100644 --- a/examples/CInterface/CMakeLists.txt +++ b/examples/CInterface/CMakeLists.txt @@ -25,6 +25,12 @@ add_executable(nonlings_fed2 nonlings_fed2.c) target_link_libraries(nonlings_fed1 helicsSharedLib ) target_link_libraries(nonlings_fed2 helicsSharedLib ) +add_executable(hello_world_sender hello_world/hello_world_sender.c) +add_executable(hello_world_receiver hello_world/hello_world_receiver.c) + +target_link_libraries(hello_world_sender helicsSharedLib ) +target_link_libraries(hello_world_receiver helicsSharedLib ) + set_target_properties (pi_sender PROPERTIES FOLDER examples) @@ -43,7 +49,11 @@ set_target_properties (nonlings_fed1 PROPERTIES set_target_properties (nonlings_fed2 PROPERTIES FOLDER examples) - +set_target_properties (hello_world_sender PROPERTIES + FOLDER examples) + +set_target_properties (hello_world_receiver PROPERTIES + FOLDER examples) if (!MSVC) @@ -55,6 +65,9 @@ target_compile_options(pi_receiver2 PRIVATE -ansi -Wall -pedantic -Werror) target_compile_options(nonlings_fed1 PRIVATE -ansi -Wall -pedantic -Werror) target_compile_options(nonlings_fed2 PRIVATE -ansi -Wall -pedantic -Werror) + +target_compile_options(hello_world_sender PRIVATE -ansi -Wall -pedantic -Werror) +target_compile_options(hello_world_receiver PRIVATE -ansi -Wall -pedantic -Werror) endif() diff --git a/examples/CInterface/hello_world/hello_world_receiver.c b/examples/CInterface/hello_world/hello_world_receiver.c index 83cbefb431..7ab6dbc383 100644 --- a/examples/CInterface/hello_world/hello_world_receiver.c +++ b/examples/CInterface/hello_world/hello_world_receiver.c @@ -1,13 +1,10 @@ - /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ #include @@ -15,33 +12,73 @@ Lawrence Livermore National Laboratory, operated by Lawrence Livermore National int main () { - helics_federate_info_t fedinfo; - const char *fedinitstring = "--federates=1"; - helics_federate vfed; - helics_subscription sub; - helics_time_t currenttime = 0.0; - int isupdated = 0; - char value[128] = ""; + helics_federate_info_t fedinfo; /* an information object used to pass information to a federate*/ + const char *fedinitstring = "--federates=1"; /* tell the core to expect only 1 federate*/ + helics_federate vfed; /* object representing the actual federate*/ + helics_subscription sub; /* an object representing a subscription*/ + helics_time_t currenttime = 0.0; /* the current time of the simulation*/ + helics_status status;/* the result code from a call to the helics Library*/ + helics_bool_t isUpdated; /* storage for a check if a value has been updated*/ + char value[128] = ""; /**space to store the sent value*/ + /** create an info structure to define some parameters used in federate creation*/ fedinfo = helicsFederateInfoCreate (); - helicsFederateInfoSetFederateName (fedinfo, "TestB Federate"); + /** set the federate name*/ + helicsFederateInfoSetFederateName (fedinfo, "hello_world_receiver"); + + /** set the core type to use + can be "test", "ipc", "udp", "tcp", "zmq", "mpi" + not all are available on all platforms + and should be set to match the broker and receiver + zmq is the default*/ helicsFederateInfoSetCoreTypeFromString (fedinfo, "zmq"); helicsFederateInfoSetCoreInitString (fedinfo, fedinitstring); + /** set the period of the federate to 1.0*/ + helicsFederateInfoSetPeriod(fedinfo, 1.0); + + /** create the core using the informational structure*/ vfed = helicsCreateValueFederate (fedinfo); - sub = helicsFederateRegisterSubscription (vfed, "testA", "string", ""); + if (vfed == NULL) /*check to make sure the federate was created*/ + { + return (-2); + } + /** register a subscription interface on vFed, with a Name of "hello" + of a type "string", with no units*/ + sub = helicsFederateRegisterSubscription (vfed, "hello", "string", NULL); + if (sub == NULL) + { + return (-3); + } + /** transition the federate to execution mode + * the helicsFederateEnterInitializationMode is not necessary if there is nothing to do in the initialization mode + */ helicsFederateEnterInitializationMode (vfed); helicsFederateEnterExecutionMode (vfed); - - helicsFederateRequestTime (vfed, currenttime, ¤ttime); - - isupdated = helicsSubscriptionIsUpdated (sub); - helicsSubscriptionGetString (sub, value, 128); - printf("%s\n", value); - + /** request that helics grant the federate a time of 1.0 + the new time will be returned in currentime*/ + status=helicsFederateRequestTime (vfed, 1.0, ¤ttime); + if (status == helics_error) + { + fprintf(stderr, "HELICS request time failed\n"); + } + /** check if the value was updated*/ + isUpdated = helicsSubscriptionIsUpdated (sub); + if (isUpdated) + { /* get the value*/ + helicsSubscriptionGetString(sub, value, 128); + printf("%s\n", value); + } + else + { + printf("value was not updated\n"); + } + /** finalize the federate*/ helicsFederateFinalize (vfed); + /** free the memory allocated to the federate*/ helicsFederateFree (vfed); + /** close the helics library*/ helicsCloseLibrary (); return (0); } diff --git a/examples/CInterface/hello_world/hello_world_sender.c b/examples/CInterface/hello_world/hello_world_sender.c index 2de713f873..a0f4082140 100644 --- a/examples/CInterface/hello_world/hello_world_sender.c +++ b/examples/CInterface/hello_world/hello_world_sender.c @@ -1,46 +1,77 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ -#include +/* include the HELICS header for value Federates*/ #include -#ifdef _MSC_VER -#include -#else -#include -#endif +#include int main() { - helics_federate_info_t fedinfo; - const char* fedinitstring="--federates=1"; - helics_federate vfed; - helics_publication pub; - helics_time_t currenttime = 0.0; - + helics_federate_info_t fedinfo; /* an information object used to pass information to a federate*/ + const char* fedinitstring="--federates=1"; /* tell the core to expect only 1 federate*/ + helics_federate vfed; /* object representing the actual federate*/ + helics_publication pub; /* an object representing a publication*/ + helics_time_t currenttime = 0.0; /* the current time of the simulation*/ + helics_status status;/* the result code from a call to the helics Library*/ + /** create an info structure to define some parameters used in federate creation*/ fedinfo = helicsFederateInfoCreate(); - helicsFederateInfoSetFederateName(fedinfo, "Test sender Federate"); + /** set the federate name*/ + helicsFederateInfoSetFederateName(fedinfo, "hello_world_sender"); + /** set the core type to use + can be "test", "ipc", "udp", "tcp", "zmq", "mpi" + not all are available on all platforms + and should be set to match the broker and receiver + zmq is the default*/ helicsFederateInfoSetCoreTypeFromString(fedinfo,"zmq"); helicsFederateInfoSetCoreInitString(fedinfo,fedinitstring); + /** set the period of the federate to 1.0*/ + helicsFederateInfoSetPeriod(fedinfo, 1.0); + /** create the core using the informational structure*/ vfed = helicsCreateValueFederate(fedinfo); - - pub = helicsFederateRegisterGlobalPublication(vfed, "testA", "string", ""); - - helicsFederateEnterInitializationMode(vfed); - helicsFederateEnterExecutionMode(vfed); - + if (vfed == NULL) /*check to make sure the federate was created*/ + { + return (-2); + } + /** register a publication interface on vFed, with a global Name of "hello" + of a type "string", with no units*/ + pub = helicsFederateRegisterGlobalPublication(vfed, "hello", "string", ""); + if (pub == NULL) + { + return (-3); + } + /** transition the federate to execution mode + * the helicsFederateEnterInitializationMode is not necessary if there is nothing to do in the initialization mode + */ + status=helicsFederateEnterInitializationMode(vfed); + if (status == helics_error) + { + fprintf(stderr, "HELICS failed to enter initialization mode\n"); + } + status=helicsFederateEnterExecutionMode(vfed); + if (status == helics_error) + { + fprintf(stderr, "HELICS failed to enter initialization mode\n"); + } + /** the federate is now at time 0*/ + /** publish the the Hello World string this will show up at the next time step of an subscribing federates*/ helicsPublicationPublishString(pub, "Hello, World"); - helicsFederateRequestTime(vfed, currenttime, ¤ttime); - + /** request that helics grant the federate a time of 1.0*/ + status=helicsFederateRequestTime(vfed, 1.0, ¤ttime); + if (status == helics_error) + { + fprintf(stderr, "HELICS request time failed\n"); + } + /** finalize the federate*/ helicsFederateFinalize(vfed); + /** free the memory allocated to the federate*/ helicsFederateFree(vfed); + /** close the helics library*/ helicsCloseLibrary(); return(0); } diff --git a/examples/CInterface/nonlings_fed1.c b/examples/CInterface/nonlings_fed1.c index 6caa857d8d..26b37aa481 100644 --- a/examples/CInterface/nonlings_fed1.c +++ b/examples/CInterface/nonlings_fed1.c @@ -25,12 +25,12 @@ Lawrence Livermore National Laboratory, operated by Lawrence Livermore National */ void run_sim1(double y,double tol,double *xout,int *converged) { - double f1, J1, x = *xout; + double J1, x = *xout; int newt_conv = 0, max_iter = 10, iter = 0; /* Solve the equation using Newton */ while (!newt_conv && iter < max_iter) { /* Function value */ - f1 = x * x - 2 * x - y + 0.5; + double f1 = x * x - 2 * x - y + 0.5; /* Convergence check */ if (fabs (f1) < tol) { @@ -68,7 +68,7 @@ int main () helics_time_t currenttime = 0.0; helics_iteration_status currenttimeiter=iterating; double tol = 1E-8; - int global_conv=0,my_conv=0,other_conv; /* Global and local convergence */ + int my_conv=0,other_conv; /* Global and local convergence */ int helics_iter = 0; helicsversion = helicsGetVersion (); @@ -136,6 +136,7 @@ int main () while (currenttimeiter == iterating) { + int global_conv; helicsSubscriptionGetString(sub, recvbuf,100); sscanf(recvbuf,"%lf,%d",&y,&other_conv); diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d2bd14b36e..3318bf51a0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -15,6 +15,7 @@ endif() add_subdirectory (ipcTest) add_subdirectory (valueFederates1) add_subdirectory (messageFederates1) +add_subdirectory (FilterFederate1) if(BUILD_C_SHARED_LIB) add_subdirectory (CInterface) add_subdirectory (CppInterface) diff --git a/examples/CppInterface/nonlings_fed1.cpp b/examples/CppInterface/nonlings_fed1.cpp index 42b9d1b5d8..64a848df31 100644 --- a/examples/CppInterface/nonlings_fed1.cpp +++ b/examples/CppInterface/nonlings_fed1.cpp @@ -1,13 +1,12 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ + #include #include #include // helicsVersionString @@ -68,7 +67,7 @@ int main (int /*argc*/, char ** /*argv*/) vfed->enterInitializationState (); // can throw helics::InvalidStateTransition exception printf (" Entered initialization state\n"); - double x = 0.0, y = 0.0, /*yprv = 100,*/ xprv=100; + double x = 0.0, /*yprv = 100,*/ xprv=100; helics_time_t currenttime = 0.0; helics::helics_iteration_time currenttimeiter; currenttimeiter.status = iterating; @@ -85,14 +84,13 @@ int main (int /*argc*/, char ** /*argv*/) while (currenttimeiter.status == iterating) { // yprv = y; - y = vfed->getDouble (sub); - double f1, J1; + double y = vfed->getDouble (sub); int newt_conv = 0, max_iter = 10, iter = 0; /* Solve the equation using Newton */ while (!newt_conv && iter < max_iter) { /* Function value */ - f1 = x * x - 2 * x - y + 0.5; + double f1 = x * x - 2 * x - y + 0.5; if (fabs (f1) < tol) { @@ -102,7 +100,7 @@ int main (int /*argc*/, char ** /*argv*/) iter++; /* Jacobian */ - J1 = 2 * x - 2; + double J1 = 2 * x - 2; x = x - f1 / J1; } diff --git a/examples/CppInterface/pi_receiver.cpp b/examples/CppInterface/pi_receiver.cpp index a2bc6563e7..be43772377 100644 --- a/examples/CppInterface/pi_receiver.cpp +++ b/examples/CppInterface/pi_receiver.cpp @@ -24,7 +24,7 @@ int main(int /*argc*/,char ** /*argv*/) helics_subscription sub; - std::string helicsversion = helics::helicsVersionString(); + std::string helicsversion = helics::versionString(); printf("PI RECEIVER: Helics version = %s\n",helicsversion.c_str()); printf("%s",help); @@ -64,12 +64,11 @@ int main(int /*argc*/,char ** /*argv*/) helics_time_t currenttime=0.0; double value = 0.0; - int isupdated=0; while(currenttime < 0.20) { currenttime = vfed->requestTime(currenttime); - isupdated = vfed->isUpdated(sub); + int isupdated = vfed->isUpdated(sub); if(isupdated) { /* NOTE: The value sent by sender at time t is received by receiver at time t+deltat */ value = vfed->getDouble(sub); diff --git a/examples/CppInterface/pi_receiver2.cpp b/examples/CppInterface/pi_receiver2.cpp index 2b52f9a7d0..dac87cae69 100644 --- a/examples/CppInterface/pi_receiver2.cpp +++ b/examples/CppInterface/pi_receiver2.cpp @@ -1,13 +1,12 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ + static char help[] = "Example to demonstrate the usage of HELICS C Interface with two federates.\n\ This example implements a loose-coupling protocol to exchange values between two federates. \n\ Here, a value federate, that can both publish and subcribe is created.\n\ @@ -27,7 +26,7 @@ int main(int /*argc*/,char ** /*argv*/) helics_publication pub; - std::string helicsversion = helics::helicsVersionString(); + std::string helicsversion = helics::versionString(); printf("PI RECEIVER: Helics version = %s\n",helicsversion.c_str()); printf("%s",help); @@ -71,13 +70,12 @@ int main(int /*argc*/,char ** /*argv*/) printf("PI RECEIVER: Entered execution state\n"); helics_time_t currenttime=0.0; - double value = 0.0; - int isupdated=0; + double pi = 22.0/7.0; while(currenttime < 0.2) { - isupdated = 0; + bool isupdated = false; while(!isupdated) { currenttime = vfed->requestTime(currenttime); isupdated = vfed->isUpdated(sub); @@ -86,7 +84,7 @@ int main(int /*argc*/,char ** /*argv*/) break; } } - value = vfed->getDouble(sub); /* Note: The sender sent this value at currenttime-deltat */ + double value = vfed->getDouble(sub); /* Note: The sender sent this value at currenttime-deltat */ printf("PI RECEIVER: Received value = %4.3f at time %3.2f from PI SENDER\n",value,currenttime); value = currenttime*pi; diff --git a/examples/CppInterface/pi_sender2.cpp b/examples/CppInterface/pi_sender2.cpp index 9f700ed839..57e0d09c51 100644 --- a/examples/CppInterface/pi_sender2.cpp +++ b/examples/CppInterface/pi_sender2.cpp @@ -1,17 +1,15 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ static char help[] = "Example to demonstrate the usage of HELICS C Interface with two federates.\n\ This example implements a loose-coupling protocol to exchange values between two federates. \n\ Here, a ZMQ broker is created and a value federate. The value federate can both.\n\ - publish and subcribe. This federate publishes a value and waits for the value \n\ + publish and subscribe. This federate publishes a value and waits for the value \n\ published by the other federate. Once the value has arrived, it publishes its next value \n\n"; #include @@ -85,17 +83,16 @@ int main(int /*argc*/,char ** /*argv*/) /* This federate will be publishing deltat*pi for numsteps steps */ //double this_time = 0.0; - double pi = 22.0/7.0,value; + double pi = 22.0/7.0; helics_time_t currenttime=0.0; - int isupdated; while(currenttime < 0.2) { - value = currenttime*pi; + double value = currenttime*pi; printf("PI SENDER: Sending value %3.2f*pi = %4.3f at time %3.2f to PI RECEIVER\n",currenttime,value,currenttime); - vfed->publish(pub, value); /* Note: the receiver will get this at currentime+deltat */ + vfed->publish(pub, value); /* Note: the receiver will get this at currenttime+deltat */ - isupdated = 0; + int isupdated = 0; while(!isupdated) { currenttime = vfed->requestTime(currenttime); isupdated = vfed->isUpdated(sub); diff --git a/examples/FilterFederate1/CMakeLists.txt b/examples/FilterFederate1/CMakeLists.txt new file mode 100644 index 0000000000..26d4bb7f37 --- /dev/null +++ b/examples/FilterFederate1/CMakeLists.txt @@ -0,0 +1,26 @@ +add_executable(filterFed FilterFed.cpp) + +include_directories(SYSTEM ${Boost_INCLUDE_DIR}) +include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/ThirdParty) +include_directories(${PROJECT_SOURCE_DIR}/src) + +target_link_libraries(filterFed helics-static) + +set_target_properties (filterFed PROPERTIES + FOLDER examples +) +#configure_file(${CMAKE_CURRENT_SOURCE_DIR}/run.sh +# ${CMAKE_CURRENT_BINARY_DIR}/run.sh +# COPYONLY) + +add_custom_command(TARGET filterFed POST_BUILD # Adds a post-build event to api tests + COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake - E copy_if_different..." + "${CMAKE_CURRENT_SOURCE_DIR}/run.sh" # <--this is in-file + "$/") # <--this is out-file path + +foreach(keyfile IN LISTS KEY_LIBRARY_FILES) +add_custom_command(TARGET filterFed POST_BUILD # Adds a post-build event to api tests + COMMAND ${CMAKE_COMMAND} -E copy_if_different # which executes "cmake - E copy_if_different..." + "${keyfile}" # <--this is in-file + "$/") # <--this is out-file path +endforeach(keyfile) diff --git a/examples/FilterFederate1/FilterFed.cpp b/examples/FilterFederate1/FilterFed.cpp new file mode 100644 index 0000000000..f57dd7d52c --- /dev/null +++ b/examples/FilterFederate1/FilterFed.cpp @@ -0,0 +1,113 @@ +/* +Copyright (C) 2017-2018, Battelle Memorial Institute +All rights reserved. + +This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial +Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the +Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. +*/ + +#include "helics/core/CoreFactory.hpp" +#include "helics/application_api/Filters.hpp" +#include +#include +#include "helics/common/argParser.h" + +static const helics::ArgDescriptors InfoArgs{ + {"target,t", "the federate at to target"}, + {"endpoint,e", "name of the endpoint to filter"}, + {"delay", "the time to delay the message"}, +{"filtertype","the type of filter to implement (delay,randomdrop,randomdelay"}, +{"dropprob",helics::ArgDescriptor::arg_type_t::double_type,"the probability a message will be dropped, only used with filtertype=randomdrop"} + + //name is captured in the argument processor for federateInfo +}; + +int main (int argc, char *argv[]) +{ + //process the command line arguments + helics::variable_map vm; + auto parseResult = argumentParser(argc, argv, vm, InfoArgs); + if (parseResult != 0) + { + return 0; + } + + std::string targetfederate = "fed"; + if (vm.count("target") > 0) + { + targetfederate = vm["target"].as(); + } + std::string targetEndpoint = "endpoint"; + if (vm.count("endpoint") > 0) { + targetEndpoint = vm["endpoint"].as(); + } + std::string target = targetfederate + "/" + targetEndpoint; + + std::string filtType = "delay"; + if (vm.count("filtertype") > 0) { + targetEndpoint = vm["filtertype"].as(); + } + + helics::defined_filter_types ftype = helics::defined_filter_types::delay; + if (filtType == "randomdrop") + { + ftype = helics::defined_filter_types::randomDrop; + } + else if (filtType == "randomdelay") + { + ftype = helics::defined_filter_types::randomDelay; + } + else if (filtType != "delay") + { + std::cerr << "invalid filter type specified valid types are \"delay\",\"random drop\",\"random delay\"\n"; + return (-2); + } + + + auto core = helics::CoreFactory::create(argc, argv); + std::cout << " registering filter '"<< "' for " << target<<'\n'; + + //create a source filter object with type, the fed pointer and a target endpoint + auto filt = helics::make_source_filter(ftype, core.get(), target); + + // get a few specific parameters related to the particular filter + switch (ftype) + { + case helics::defined_filter_types::delay: + default: + { + std::string delay = "1.0"; + if (vm.count("delay") > 0) { + delay = vm["delay"].as(); + filt->setString("delay", delay); + } + break; + } + case helics::defined_filter_types::randomDrop: + { + double dropprob = 0.33; + if (vm.count("dropprob") > 0) { + dropprob = vm["dropprob"].as(); + } + filt->set("dropprob", dropprob); + } + break; + case helics::defined_filter_types::randomDelay: + filt->setString("distribution", "uniform"); + if (vm.count("delay") > 0) + { + filt->setString("max", vm["delay"].as()); + } + } + /*setup and run + */ + core->setCoreReadyToInit(); + + //just do a wait loop while the core is still processing so the filters have time to work + while (core->isConnected()) + { + std::this_thread::yield(); + } + return 0; +} diff --git a/examples/FilterFederate1/run.sh b/examples/FilterFederate1/run.sh new file mode 100644 index 0000000000..15bf54f0a2 --- /dev/null +++ b/examples/FilterFederate1/run.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +../../src/helics/core/helics_broker 2 --loglevel=4> broker.out & +./messageFed -name fed1 -target fed2> fed1.out & +./messageFed -name fed2 -target fed1> fed2.out diff --git a/examples/FilterFederate1/run3.sh b/examples/FilterFederate1/run3.sh new file mode 100644 index 0000000000..be3fbfdef4 --- /dev/null +++ b/examples/FilterFederate1/run3.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +../../src/helics/core/helics_broker 3 --loglevel=4> broker.out & +./messageFed -name fed1 -target fed2> fed1.out & +./messageFed -name fed2 -target fed3> fed2.out & +./messageFed -name fed3 -target fed1> fed3.out & diff --git a/examples/messageFederates1/messageFed.cpp b/examples/messageFederates1/messageFed.cpp index e3bbbf57fc..8b445b918a 100644 --- a/examples/messageFederates1/messageFed.cpp +++ b/examples/messageFederates1/messageFed.cpp @@ -5,7 +5,6 @@ All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ #include "helics/application_api/MessageFederate.hpp" #include diff --git a/examples/messageFederates1/openmpi-run.conf b/examples/messageFederates1/openmpi-run.conf new file mode 100644 index 0000000000..70ec8bf5ac --- /dev/null +++ b/examples/messageFederates1/openmpi-run.conf @@ -0,0 +1,4 @@ +-np 1 src/helics/core/helics_broker 2 --type=mpi --loglevel=4 + +-np 1 examples/messageFederates1/messageFed --name=fed1 --target=fed2 --core=mpi --coreinit="--broker_address=0:0" +-np 1 examples/messageFederates1/messageFed --name=fed2 --target=fed1 --core=mpi --coreinit="--broker_address=0:0" diff --git a/examples/messageFederates1/srun-mpi.conf b/examples/messageFederates1/srun-mpi.conf new file mode 100644 index 0000000000..41a5596746 --- /dev/null +++ b/examples/messageFederates1/srun-mpi.conf @@ -0,0 +1,6 @@ +# %t gets replaced with task number +# %o gets replaced with offset within range of task numbers given to a particular task + +0 ./src/helics/core/helics_broker 2 --type=mpi --loglevel=4 +1 ./examples/messageFederates1/messageFed --name=fed1 --target=fed2 --core=mpi --coreinit="--broker_address=0:0" +2 ./examples/messageFederates1/messageFed --name=fed2 --target=fed1 --core=mpi --coreinit="--broker_address=0:0" diff --git a/swig/python/pireceiver.py b/examples/python/pi-exchange/pireceiver.py similarity index 100% rename from swig/python/pireceiver.py rename to examples/python/pi-exchange/pireceiver.py diff --git a/swig/python/pisender.py b/examples/python/pi-exchange/pisender.py similarity index 100% rename from swig/python/pisender.py rename to examples/python/pi-exchange/pisender.py diff --git a/scripts/install-ci-dependencies.sh b/scripts/install-ci-dependencies.sh index 1e43b505c7..32ab0043f2 100755 --- a/scripts/install-ci-dependencies.sh +++ b/scripts/install-ci-dependencies.sh @@ -97,7 +97,8 @@ if [[ ! -d "dependencies/boost" ]]; then echo "*** built boost successfully" fi -export BOOST_ROOT=${TRAVIS_BUILD_DIR}/dependencies/boost} +export ZMQ_INCLUDE=${TRAVIS_BUILD_DIR}/dependencies/zmq/include +export ZMQ_LIB=${TRAVIS_BUILD_DIR}/dependencies/zmq/lib if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo ldconfig ${PWD}/dependencies @@ -106,6 +107,12 @@ elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export DYLD_FALLBACK_LIBRARY_PATH=${PWD}/dependencies/zmq/lib:${PWD}/dependencies/boost/lib:$LD_LIBRARY_PATH fi +if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + export LD_LIBRARY_PATH=${PWD}/build/src/helics/shared_api_library/:$LD_LIBRARY_PATH +elif [[ "$TRAVIS_OS_NAME" == "osx" ]]; then + export DYLD_FALLBACK_LIBRARY_PATH=${PWD}/build/src/helics/shared_api_library/:$DYLD_FALLBACK_LIBRARY_PATH +fi + if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then # HOMEBREW_NO_AUTO_UPDATE=1 brew install boost brew update diff --git a/src/helics/CMakeLists.txt b/src/helics/CMakeLists.txt index 8dd789c4dc..3a0d9a43c3 100644 --- a/src/helics/CMakeLists.txt +++ b/src/helics/CMakeLists.txt @@ -1,9 +1,13 @@ ############################################################################## -#Copyright (C) 2017, Battelle Memorial Institute +#Copyright (C) 2017-2018, Battelle Memorial Institute #All rights reserved. -#This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. +#This software was co-developed by Pacific Northwest National Laboratory, +#operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, +#operated by the Alliance for Sustainable Energy, LLC; +#and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. ############################################################################## + add_subdirectory(common) add_subdirectory(core) add_subdirectory(application_api) diff --git a/src/helics/application_api/CombinationFederate.cpp b/src/helics/application_api/CombinationFederate.cpp index b57ba81929..e09430cb4e 100644 --- a/src/helics/application_api/CombinationFederate.cpp +++ b/src/helics/application_api/CombinationFederate.cpp @@ -17,8 +17,8 @@ CombinationFederate::CombinationFederate (const FederateInfo &fi) : Federate (fi), ValueFederate (true), MessageFederate (true) { } -CombinationFederate::CombinationFederate (std::shared_ptr core, const FederateInfo &fi) - : Federate (std::move (core), fi), ValueFederate (true), MessageFederate (true) +CombinationFederate::CombinationFederate (const std::shared_ptr &core, const FederateInfo &fi) + : Federate (core, fi), ValueFederate (true), MessageFederate (true) { } diff --git a/src/helics/application_api/CombinationFederate.hpp b/src/helics/application_api/CombinationFederate.hpp index 3fe1224893..2aada4563a 100644 --- a/src/helics/application_api/CombinationFederate.hpp +++ b/src/helics/application_api/CombinationFederate.hpp @@ -5,10 +5,8 @@ All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ -#ifndef _COMBINATION_FEDERATE_H_ -#define _COMBINATION_FEDERATE_H_ + #pragma once #include "MessageFederate.hpp" @@ -26,16 +24,16 @@ class CombinationFederate : public ValueFederate, public MessageFederate /**constructor taking a federate information structure and using the default core @param fi a federate information structure */ - CombinationFederate (const FederateInfo &fi); + explicit CombinationFederate (const FederateInfo &fi); /**constructor taking a federate information structure and using the given core @param core a pointer to core object which the federate can join @param[in] fi a federate information structure */ - CombinationFederate (std::shared_ptr core, const FederateInfo &fi); + CombinationFederate (const std::shared_ptr &core, const FederateInfo &fi); /**constructor taking a file with the required information @param[in] file a file defining the federate information */ - CombinationFederate (const std::string &jsonString); + explicit CombinationFederate (const std::string &jsonString); /** move construction*/ CombinationFederate (CombinationFederate &&fed) noexcept; @@ -55,4 +53,3 @@ class CombinationFederate : public ValueFederate, public MessageFederate virtual void registerInterfaces (const std::string &jsonString) override; }; } // namespace helics -#endif diff --git a/src/helics/application_api/Endpoints.cpp b/src/helics/application_api/Endpoints.cpp index 8f63413332..6fcbabaddc 100644 --- a/src/helics/application_api/Endpoints.cpp +++ b/src/helics/application_api/Endpoints.cpp @@ -23,4 +23,11 @@ Endpoint::Endpoint (MessageFederate *mFed, int endpointIndex) : fed (mFed) } id = static_cast (endpointIndex); } + +void Endpoint::setCallback(std::function callback) +{ + //copy by value otherwise this object can't be used in vectors by itself + Endpoint local(*this); + fed->registerEndpointCallback(id, [callback, local](endpoint_id_t, Time messageTime) {callback(&local, messageTime); }); +} } // namespace helics \ No newline at end of file diff --git a/src/helics/application_api/Endpoints.hpp b/src/helics/application_api/Endpoints.hpp index 12c9a3a6bc..06a3d1f6eb 100644 --- a/src/helics/application_api/Endpoints.hpp +++ b/src/helics/application_api/Endpoints.hpp @@ -5,13 +5,14 @@ All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ -#ifndef _HELICS_ENDPOINT_H_ -#define _HELICS_ENDPOINT_H_ + #pragma once #include "MessageFederate.hpp" +#include +#include + namespace helics { /** class to manage an endpoint */ @@ -21,14 +22,16 @@ class Endpoint MessageFederate *fed; //!< the MessageFederate to interact with endpoint_id_t id; //!< the id as generated by the Federate std::string targetDest; //!< a predefined target destination - public: +public: /**constructor to build an endpoint object @param[in] mFed the MessageFederate to use @param[in] name the name of the endpoint @param[in] type a named type associated with the endpoint */ - Endpoint (MessageFederate *mFed, const std::string &name, const std::string &type = "") : fed (mFed) + template + Endpoint (FedPtr mFed, const std::string &name, const std::string &type = std::string()) : fed (std::addressof(*mFed)) { + static_assert(std::is_base_of>::value, "first argument must be a pointer to a MessageFederate"); id = fed->registerEndpoint (name, type); } @@ -37,15 +40,18 @@ class Endpoint @param[in] name the name of the endpoint @param[in] type a named type associated with the endpoint */ + template Endpoint (interface_visibility locality, - MessageFederate *mFed, + FedPtr mFed, const std::string &name, - const std::string &type = "") - : fed (mFed) + const std::string &type = std::string()) + : fed(std::addressof(*mFed)) { + static_assert(std::is_base_of>::value, "second argument must be a pointer to a MessageFederate"); id = (locality == GLOBAL) ? fed->registerGlobalEndpoint (name, type) : fed->registerEndpoint (name, type); } - + /** generate an endpoint from a MessageFederate pointer and an endpoint index + */ Endpoint (MessageFederate *mFed, int endpointIndex); /** send a data block and length @param[in] dest string name of the destination @@ -132,10 +138,16 @@ class Endpoint @param[in] callback a function with signature void(endpoint_id_t, Time) time is the time the value was updated This callback is a notification callback and doesn't return the value */ - void setCallback (std::function callback) + void setCallback (const std::function &callback) { - fed->registerEndpointCallback (id, std::move (callback)); + fed->registerEndpointCallback (id, callback); } + /** register a callback for an update notification + @details the callback is called in the just before the time request function returns + @param[in] callback a function with signature void(endpoint_id_t, Time) + time is the time the value was updated This callback is a notification callback and doesn't return the value + */ + void setCallback(std::function callback); /** set a target destination for unspecified messages*/ void setTargetDestination (const std::string &target) { targetDest = target; } /** get the name of the endpoint*/ @@ -144,6 +156,6 @@ class Endpoint std::string getType () const { return fed->getEndpointType (id); } /** get the actual endpoint id for the fed*/ endpoint_id_t getID () const { return id; } + }; } // namespace helics -#endif diff --git a/src/helics/application_api/Federate.cpp b/src/helics/application_api/Federate.cpp index 51e86006d0..2469e65b46 100644 --- a/src/helics/application_api/Federate.cpp +++ b/src/helics/application_api/Federate.cpp @@ -63,7 +63,12 @@ Federate::Federate (const FederateInfo &fi) : FedInfo (fi) if (!coreObject->isConnected ()) { coreObject->connect (); + if (!coreObject->isConnected()) + { + throw (RegistrationFailure("Unable to connect to broker->unable to register federate")); + } } + // this call will throw an error on failure fedID = coreObject->registerFederate (fi.name, fi); @@ -71,8 +76,8 @@ Federate::Federate (const FederateInfo &fi) : FedInfo (fi) asyncCallInfo = std::make_unique (); } -Federate::Federate (std::shared_ptr core, const FederateInfo &fi) - : coreObject (std::move (core)), FedInfo (fi) +Federate::Federate (const std::shared_ptr &core, const FederateInfo &fi) + : coreObject (core), FedInfo (fi) { if (!coreObject) { @@ -243,6 +248,7 @@ iteration_result Federate::enterExecutionState (helics_iteration_request iterate { case iteration_result::next_step: state = op_states::execution; + currentTime = timeZero; initializeToExecuteStateTransition (); break; case iteration_result::iterating: @@ -322,6 +328,7 @@ iteration_result Federate::enterExecutionStateComplete () { case iteration_result::next_step: state = op_states::execution; + currentTime = timeZero; initializeToExecuteStateTransition (); break; case iteration_result::iterating: diff --git a/src/helics/application_api/Federate.hpp b/src/helics/application_api/Federate.hpp index 05ea1f8a1e..e7be02f8fc 100644 --- a/src/helics/application_api/Federate.hpp +++ b/src/helics/application_api/Federate.hpp @@ -111,9 +111,10 @@ class Federate */ Federate (const FederateInfo &fi); /**constructor taking a core and a federate information structure + @param core a shared pointer to a core object, the pointer will be copied @param[in] fi a federate information structure */ - Federate (std::shared_ptr core, const FederateInfo &fi); + Federate (const std::shared_ptr &core, const FederateInfo &fi); /**constructor taking a file with the required information @param[in] jsonString can be either a JSON file or a string containing JSON code */ diff --git a/src/helics/application_api/FederateInfo.cpp b/src/helics/application_api/FederateInfo.cpp index 1d626cf4fd..25d05bcd2e 100644 --- a/src/helics/application_api/FederateInfo.cpp +++ b/src/helics/application_api/FederateInfo.cpp @@ -14,8 +14,8 @@ Lawrence Livermore National Laboratory, operated by Lawrence Livermore National #include "../common/JsonProcessingFunctions.hpp" #include "../core/core-exceptions.hpp" #include "../core/helicsVersion.hpp" -#include #include +#include #include #include "../common/argParser.h" @@ -24,31 +24,30 @@ namespace filesystem = boost::filesystem; namespace helics { - using namespace std::string_literals; -static const ArgDescriptors InfoArgs{ - {"broker,b"s, "address of the broker to connect"s }, -{"name,n"s, "name of the player federate"s }, -{"corename"s, "the name of the core to create or find"s }, -{"core,c"s, "type of the core to connect to"s }, -{"offset"s, "the offset of the time steps"s }, -{"period"s, "the period of the federate"s }, -{"timedelta"s, "the time delta of the federate"s }, -{"coreinit,i"s, "the core initialization string"s }, -{"inputdelay"s, "the input delay on incoming communication of the federate"s }, -{"outputdelay"s, "the output delay for outgoing communication of the federate"s }, -{"flags,f"s, ArgDescriptor::arg_type_t::vector_string, "named flag for the federate"s } -}; +static const ArgDescriptors InfoArgs{{"broker,b"s, "address of the broker to connect"s}, + {"name,n"s, "name of the player federate"s}, + {"corename"s, "the name of the core to create or find"s}, + {"core,c"s, "type of the core to connect to"s}, + {"offset"s, "the offset of the time steps"s}, + {"period"s, "the period of the federate"s}, + {"timedelta"s, "the time delta of the federate"s}, + {"coreinit,i"s, "the core initialization string"s}, + {"inputdelay"s, "the input delay on incoming communication of the federate"s}, + {"outputdelay"s, + "the output delay for outgoing communication of the federate"s}, + {"flags,f"s, ArgDescriptor::arg_type_t::vector_string, + "named flag for the federate"s}}; FederateInfo::FederateInfo (int argc, const char *const *argv) { loadInfoFromArgs (argc, argv); } void FederateInfo::loadInfoFromArgs (int argc, const char *const *argv) { variable_map vm; - auto res= argumentParser(argc, argv, vm, InfoArgs); + auto res = argumentParser (argc, argv, vm, InfoArgs); if (res == versionReturn) { - std::cout << helics::helicsVersionString() << '\n'; + std::cout << helics::versionString () << '\n'; } if (res < 0) { @@ -84,26 +83,26 @@ void FederateInfo::loadInfoFromArgs (int argc, const char *const *argv) if (vm.count ("timedelta") > 0) { - timeDelta = loadTimeFromString(vm["timedelta"].as ()); + timeDelta = loadTimeFromString (vm["timedelta"].as ()); } if (vm.count ("inputdelay") > 0) { - timeDelta = loadTimeFromString(vm["inputdelay"].as ()); + timeDelta = loadTimeFromString (vm["inputdelay"].as ()); } if (vm.count ("outputdelay") > 0) { - timeDelta = loadTimeFromString(vm["outputdelay"].as ()); + timeDelta = loadTimeFromString (vm["outputdelay"].as ()); } if (vm.count ("period") > 0) { - period = loadTimeFromString(vm["period"].as ()); + period = loadTimeFromString (vm["period"].as ()); } if (vm.count ("offset") > 0) { - offset = loadTimeFromString(vm["offset"].as ()); + offset = loadTimeFromString (vm["offset"].as ()); } if (vm.count ("maxiterations") > 0) { diff --git a/src/helics/application_api/FilterOperations.cpp b/src/helics/application_api/FilterOperations.cpp index c5180451df..daad522f8c 100644 --- a/src/helics/application_api/FilterOperations.cpp +++ b/src/helics/application_api/FilterOperations.cpp @@ -1,12 +1,10 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ #include "FilterOperations.hpp" @@ -53,13 +51,12 @@ void DelayFilterOperation::setString (const std::string &property, const std::st { try { - delay = loadTimeFromString(val); + delay = loadTimeFromString (val); } catch (const std::invalid_argument &ia) { - throw (helics::InvalidParameter(val + " is not a valid time string")); + throw (helics::InvalidParameter (val + " is not a valid time string")); } - } } @@ -68,8 +65,8 @@ std::shared_ptr DelayFilterOperation::getOperator () return std::static_pointer_cast (td); } -// enumeration of possible random number generator distributions -enum class random_dists_t : int +/** enumeration of possible random number generator distributions */ + enum class random_dists_t : int { constant, uniform, @@ -227,10 +224,10 @@ class randomDelayGenerator }; RandomDelayFilterOperation::RandomDelayFilterOperation () + : td (std::make_shared ( + [this](Time messageTime) { return messageTime + rdelayGen->generate (); })), + rdelayGen (std::make_unique ()) { - rdelayGen = std::make_unique (); - td = std::make_shared ( - [this](Time messageTime) { return messageTime + rdelayGen->generate (); }); } RandomDelayFilterOperation::~RandomDelayFilterOperation () = default; @@ -255,6 +252,16 @@ void RandomDelayFilterOperation::setString (const std::string &property, const s rdelayGen->dist.store (res->second); } } + else if ((property == "param1") || (property == "mean") || (property == "min") || (property == "alpha")) + { + auto tm = loadTimeFromString(val); + rdelayGen->param1.store(static_cast(tm)); + } + else if ((property == "param2") || (property == "stddev") || (property == "max") || (property == "beta")) + { + auto tm = loadTimeFromString(val); + rdelayGen->param2.store(static_cast(tm)); + } } std::shared_ptr RandomDelayFilterOperation::getOperator () diff --git a/src/helics/application_api/FilterOperations.hpp b/src/helics/application_api/FilterOperations.hpp index 79897b5854..635cbe58b6 100644 --- a/src/helics/application_api/FilterOperations.hpp +++ b/src/helics/application_api/FilterOperations.hpp @@ -70,11 +70,14 @@ class randomDelayGenerator; class RandomDelayFilterOperation : public FilterOperations { private: - std::shared_ptr td; - std::unique_ptr rdelayGen; + std::shared_ptr td; //!< pointer to the time operator + std::unique_ptr rdelayGen; //!< pointer to the random number generator public: + /** default constructor*/ RandomDelayFilterOperation (); + //the destructor is defined mainly to prevent the need to define the randomDelayGenerator object here + /** destructor*/ ~RandomDelayFilterOperation (); virtual void set (const std::string &property, double val) override; virtual void setString (const std::string &property, const std::string &val) override; @@ -142,4 +145,4 @@ class CloneFilterOperation : public FilterOperations } // namespace helics -#endif /*_HELICS_FILTEROPERATIONS_H_*/ \ No newline at end of file +#endif /*_HELICS_FILTEROPERATIONS_H_*/ diff --git a/src/helics/application_api/Filters.cpp b/src/helics/application_api/Filters.cpp index c6eaae023f..fddf5f8864 100644 --- a/src/helics/application_api/Filters.cpp +++ b/src/helics/application_api/Filters.cpp @@ -1,12 +1,10 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ #include "Filters.hpp" @@ -78,7 +76,7 @@ void addOperations (Filter *filt, defined_filter_types type, Core *cptr) break; case defined_filter_types::reroute: { - auto op = std::make_shared (); + auto op = std::make_shared (); filt->setFilterOperations (std::move (op)); } break; @@ -401,4 +399,4 @@ make_source_filter (defined_filter_types type, Federate *fed, const std::string } } -} // namespace helics \ No newline at end of file +} // namespace helics diff --git a/src/helics/application_api/Filters.hpp b/src/helics/application_api/Filters.hpp index d28d3540ba..18a9b3b54e 100644 --- a/src/helics/application_api/Filters.hpp +++ b/src/helics/application_api/Filters.hpp @@ -5,10 +5,8 @@ All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ -#ifndef _HELICS_FILTER_H_ -#define _HELICS_FILTER_H_ + #pragma once #include "../core/Core.hpp" @@ -50,13 +48,17 @@ class Filter explicit Filter (Federate *fed); /** construct through a core object*/ explicit Filter (Core *cr); + /** virtual destructor*/ virtual ~Filter () = default; /** set a message operator to process the message*/ void setOperator (std::shared_ptr mo); + /** get the underlying filter id for use with a federate*/ filter_id_t getID () const { return fid; } + + /** get the underlying core handle for use with a core*/ Core::handle_id_t getCoreHandle () const { return id; } /** get the target of the filter*/ @@ -231,4 +233,3 @@ std::unique_ptr make_source_filter (defined_filter_types type, const std::string &name = EMPTY_STRING); } // namespace helics -#endif diff --git a/src/helics/application_api/HelicsPrimaryTypes.hpp b/src/helics/application_api/HelicsPrimaryTypes.hpp index 970885b0e4..ae53d3d595 100644 --- a/src/helics/application_api/HelicsPrimaryTypes.hpp +++ b/src/helics/application_api/HelicsPrimaryTypes.hpp @@ -1,15 +1,12 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ -#ifndef HELICS_PRIMARY_TYPES_H_ -#define HELICS_PRIMARY_TYPES_H_ + #pragma once #include "ValueConverter.hpp" @@ -53,6 +50,7 @@ bool changeDetected (const defV &prevValue, const double *vals, size_t size, dou bool changeDetected (const defV &prevValue, const std::complex &val, double deltaV); bool changeDetected (const defV &prevValue, double val, double deltaV); bool changeDetected (const defV &prevValue, int64_t val, double deltaV); +bool changeDetected(const defV &prevValue, named_point val, double deltaV); void valueExtract (const defV &dv, std::string &val); @@ -167,6 +165,12 @@ std::enable_if_t::value> valueExtract (const data_view &dv val = static_cast (std::abs (V)); break; } + case helics_type_t::helicsNamedPoint: + { + auto V = ValueConverter::interpret(dv); + val = static_cast (V.second); + break; + } case helics_type_t::helicsComplexVector: { auto V = ValueConverter>>::interpret (dv); @@ -179,4 +183,3 @@ std::enable_if_t::value> valueExtract (const data_view &dv } } // namespace helics -#endif // HELICS_PRIMARY_TYPES_H_ diff --git a/src/helics/application_api/Message.hpp b/src/helics/application_api/Message.hpp index c908239cc6..ba978f7d9c 100644 --- a/src/helics/application_api/Message.hpp +++ b/src/helics/application_api/Message.hpp @@ -1,16 +1,12 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ -#ifndef _HELICS_MESSAGE_H_ -#define _HELICS_MESSAGE_H_ #pragma once #include "../core/core-data.hpp" @@ -52,7 +48,7 @@ class data_view /** construct from a char vector*/ data_view (const std::vector &dvec) noexcept : dblock (dvec.data (), dvec.size ()){}; /** construct from a string_view*/ - data_view (const stx::string_view &sview) noexcept : dblock (sview){}; + data_view (const stx::string_view &sview) noexcept : dblock (sview){}; // NOLINT (intended implicit) /** assignment operator from another ata_view*/ data_view &operator= (const data_view &dv) noexcept { @@ -143,5 +139,3 @@ inline void swap (helics::data_view &db1, helics::data_view &db2) noexcept db1.swap (db2); } } // namespace helics - -#endif diff --git a/src/helics/application_api/MessageFederate.cpp b/src/helics/application_api/MessageFederate.cpp index 014b24270b..85a79cf34d 100644 --- a/src/helics/application_api/MessageFederate.cpp +++ b/src/helics/application_api/MessageFederate.cpp @@ -20,8 +20,8 @@ MessageFederate::MessageFederate (const FederateInfo &fi) : Federate (fi) { mfManager = std::make_unique (coreObject.get(), getID ()); } -MessageFederate::MessageFederate (std::shared_ptr core, const FederateInfo &fi) - : Federate (std::move (core), fi) +MessageFederate::MessageFederate (const std::shared_ptr &core, const FederateInfo &fi) + : Federate (core, fi) { mfManager = std::make_unique (coreObject.get(), getID ()); } @@ -309,16 +309,16 @@ std::string MessageFederate::getEndpointName (endpoint_id_t id) const { return m std::string MessageFederate::getEndpointType (endpoint_id_t ep) { return mfManager->getEndpointType (ep); } -void MessageFederate::registerEndpointCallback (std::function func) +void MessageFederate::registerEndpointCallback (const std::function &func) { mfManager->registerCallback (func); } -void MessageFederate::registerEndpointCallback (endpoint_id_t ep, std::function func) +void MessageFederate::registerEndpointCallback (endpoint_id_t ep, const std::function &func) { mfManager->registerCallback (ep, func); } void MessageFederate::registerEndpointCallback (const std::vector &ep, - std::function func) + const std::function &func) { mfManager->registerCallback (ep, func); } diff --git a/src/helics/application_api/MessageFederate.hpp b/src/helics/application_api/MessageFederate.hpp index f4f98df678..ddff0b26cf 100644 --- a/src/helics/application_api/MessageFederate.hpp +++ b/src/helics/application_api/MessageFederate.hpp @@ -1,16 +1,12 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ -#ifndef _HELICS_MESSAGE_FEDERATE_API_ -#define _HELICS_MESSAGE_FEDERATE_API_ #pragma once #include "Federate.hpp" @@ -28,22 +24,22 @@ class MessageFederate : public virtual Federate // using virtual inheritance to /**constructor taking a federate information structure and using the default core @param[in] fi a federate information structure */ - MessageFederate (const FederateInfo &fi); + explicit MessageFederate (const FederateInfo &fi); /**constructor taking a core and a federate information structure, sore information in fi is ignored @param[in] core a shared ptr to a core to join @param[in] fi a federate information structure */ - MessageFederate (std::shared_ptr core, const FederateInfo &fi); + MessageFederate (const std::shared_ptr &core, const FederateInfo &fi); /**constructor taking a string with the required information @param[in] jsonString can be either a json file or a string containing json code */ - MessageFederate (const std::string &jsonString); + explicit MessageFederate (const std::string &jsonString); /** move constructor*/ MessageFederate (MessageFederate &&mFed) noexcept; /** default constructor*/ MessageFederate (); /** special constructor should only be used by child classes in constructor due to virtual inheritance*/ - MessageFederate (bool res); + explicit MessageFederate (bool res); // copy constructor and copy assignment are disabled public: /** destructor */ @@ -185,18 +181,18 @@ class MessageFederate : public virtual Federate // using virtual inheritance to /** register a callback for all endpoints @param[in] callback the function to execute upon receipt of a message for any endpoint */ - void registerEndpointCallback (std::function callback); + void registerEndpointCallback (const std::function &callback); /** register a callback for a specific endpoint @param[in] ep the endpoint to associate with the specified callback @param[in] callback the function to execute upon receipt of a message for the given endpoint */ - void registerEndpointCallback (endpoint_id_t ep, std::function callback); + void registerEndpointCallback (endpoint_id_t ep, const std::function &callback); /** register a callback for a set of specific endpoint @param[in] ep a vector of endpoints to associate with the specified callback @param[in] callback the function to execute upon receipt of a message for the given endpoint */ void registerEndpointCallback (const std::vector &ep, - std::function callback); + const std::function &callback); virtual void disconnect () override; @@ -207,5 +203,4 @@ class MessageFederate : public virtual Federate // using virtual inheritance to /** @brief PIMPL design pattern with the implementation details for the MessageFederate*/ std::unique_ptr mfManager; }; -} // namespace helics -#endif \ No newline at end of file +} // namespace helics \ No newline at end of file diff --git a/src/helics/application_api/MessageFederateManager.cpp b/src/helics/application_api/MessageFederateManager.cpp index 250dba7e69..4b722732a0 100644 --- a/src/helics/application_api/MessageFederateManager.cpp +++ b/src/helics/application_api/MessageFederateManager.cpp @@ -40,7 +40,7 @@ void MessageFederateManager::registerKnownCommunicationPath (endpoint_id_t local auto sharedElock = local_endpoints.lock_shared(); if (localEndpoint.value () < endpointCount) { - coreObject->registerFrequentCommunicationsPair ((*sharedElock)[localEndpoint.value ()].name, + coreObject->registerFrequentCommunicationsPair ((*sharedElock)[localEndpoint.value ()]->name, remoteEndpoint); } } @@ -133,7 +133,7 @@ void MessageFederateManager::sendMessage (endpoint_id_t source, const std::strin { if (source.value () < endpointCount) { - coreObject->send ((*local_endpoints.lock_shared())[source.value ()].handle, dest, message.data (), message.size ()); + coreObject->send ((*local_endpoints.lock_shared())[source.value ()]->handle, dest, message.data (), message.size ()); } else { @@ -148,7 +148,7 @@ void MessageFederateManager::sendMessage (endpoint_id_t source, { if (source.value () < endpointCount) { - coreObject->sendEvent (sendTime, (*local_endpoints.lock_shared())[source.value ()].handle, dest, message.data (), + coreObject->sendEvent (sendTime, (*local_endpoints.lock_shared())[source.value ()]->handle, dest, message.data (), message.size ()); } else @@ -161,7 +161,7 @@ void MessageFederateManager::sendMessage (endpoint_id_t source, std::unique_ptr< { if (source.value () < endpointCount) { - coreObject->sendMessage ((*local_endpoints.lock_shared())[source.value ()].handle, std::move (message)); + coreObject->sendMessage ((*local_endpoints.lock_shared())[source.value ()]->handle, std::move (message)); } else { @@ -175,7 +175,7 @@ void MessageFederateManager::updateTime (Time newTime, Time /*oldTime*/) auto epCount = coreObject->receiveCountAny (fedID); // lock the data updates std::unique_lock eplock (endpointLock); - auto localEpt = local_endpoints.lock(); + Core::handle_id_t endpoint_id; for (size_t ii = 0; ii < epCount; ++ii) { @@ -186,21 +186,23 @@ void MessageFederateManager::updateTime (Time newTime, Time /*oldTime*/) } /** find the id*/ - auto fid = localEpt->find (endpoint_id); - if (fid != localEpt->end ()) + auto fid = (local_endpoints.lock())->find (endpoint_id); + if (fid != nullptr) { // assign the data auto localEndpointIndex = fid->id.value (); messageQueues[localEndpointIndex].emplace (std::move (message)); - if ((*localEpt)[localEndpointIndex].callbackIndex >= 0) + if (fid->callbackIndex >= 0) { - auto cb = callbacks[(*localEpt)[localEndpointIndex].callbackIndex]; + //need to be copied otherwise there is a potential race condition on lock removal + auto cb = callbacks[fid->callbackIndex]; eplock.unlock (); cb (fid->id, CurrentTime); eplock.lock (); } else if (allCallbackIndex >= 0) { + //need to be copied otherwise there is a potential race condition on lock removal auto ac = callbacks[allCallbackIndex]; eplock.unlock (); ac (fid->id, CurrentTime); @@ -219,7 +221,8 @@ void MessageFederateManager::updateTime (Time newTime, Time /*oldTime*/) auto mv = std::make_unique (); mv->source = sfnd->second.second; auto localEndpointIndex = sfnd->second.first.value (); - mv->dest = (*localEpt)[localEndpointIndex].name; + auto eptInfo = (*local_endpoints.lock())[localEndpointIndex]; + mv->dest = eptInfo->name; mv->original_source = mv->source; // get the data value auto data = coreObject->getValue (handle); @@ -227,10 +230,10 @@ void MessageFederateManager::updateTime (Time newTime, Time /*oldTime*/) mv->data = *data; mv->time = CurrentTime; messageQueues[localEndpointIndex].push (std::move (mv)); - if ((*localEpt)[localEndpointIndex].callbackIndex >= 0) + if (eptInfo->callbackIndex >= 0) { // make sure the lock is not engaged for the callback - auto cb = callbacks[(*localEpt)[localEndpointIndex].callbackIndex]; + auto cb = callbacks[eptInfo->callbackIndex]; eplock.unlock (); cb (sfnd->second.first, newTime); eplock.lock (); @@ -259,19 +262,19 @@ static const std::string nullStr; std::string MessageFederateManager::getEndpointName (endpoint_id_t id) const { - return (id.value () < endpointCount) ? (*local_endpoints.lock_shared())[id.value ()].name : nullStr; + return (id.value () < endpointCount) ? (*local_endpoints.lock_shared())[id.value ()]->name : nullStr; } endpoint_id_t MessageFederateManager::getEndpointId (const std::string &name) const { auto sharedEpt = local_endpoints.lock_shared(); auto sub = sharedEpt->find (name); - return (sub != sharedEpt->end ()) ? sub->id : 0; + return (sub != nullptr) ? sub->id : 0; } std::string MessageFederateManager::getEndpointType (endpoint_id_t id) const { - return (id.value () < endpointCount) ? (*local_endpoints.lock_shared())[id.value ()].type : nullStr; + return (id.value () < endpointCount) ? (*local_endpoints.lock_shared())[id.value ()]->type : nullStr; } int MessageFederateManager::getEndpointCount () const @@ -280,26 +283,26 @@ int MessageFederateManager::getEndpointCount () const return static_cast (endpointCount); } -void MessageFederateManager::registerCallback (std::function callback) +void MessageFederateManager::registerCallback (const std::function &callback) { std::lock_guard eLock (endpointLock); if (allCallbackIndex < 0) { allCallbackIndex = static_cast (callbacks.size ()); - callbacks.emplace_back (std::move (callback)); + callbacks.push_back (callback); } else { - callbacks[allCallbackIndex] = std::move (callback); + callbacks[allCallbackIndex] = callback; } } -void MessageFederateManager::registerCallback (endpoint_id_t id, std::function callback) +void MessageFederateManager::registerCallback (endpoint_id_t id, const std::function &callback) { if (id.value () < endpointCount) { - (*local_endpoints.lock())[id.value ()].callbackIndex = static_cast (callbacks.size ()); - callbacks.emplace_back (std::move (callback)); + (*local_endpoints.lock())[id.value ()]->callbackIndex = static_cast (callbacks.size ()); + callbacks.push_back (callback); } else { @@ -308,18 +311,18 @@ void MessageFederateManager::registerCallback (endpoint_id_t id, std::function &ids, - std::function callback) + const std::function &callback) { int ind = static_cast (callbacks.size ()); - callbacks.emplace_back (std::move (callback)); + callbacks.push_back (callback); auto cnt = endpointCount.load(); auto eptLock = local_endpoints.lock(); for (auto id : ids) { if (id.value () < cnt) { - (*eptLock)[id.value ()].callbackIndex = ind; + (*eptLock)[id.value ()]->callbackIndex = ind; } } } diff --git a/src/helics/application_api/MessageFederateManager.hpp b/src/helics/application_api/MessageFederateManager.hpp index c4b646436b..f093666bde 100644 --- a/src/helics/application_api/MessageFederateManager.hpp +++ b/src/helics/application_api/MessageFederateManager.hpp @@ -1,21 +1,19 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ -#ifndef _MESSAGE_FEDERATE_MANAGER_ -#define _MESSAGE_FEDERATE_MANAGER_ + #pragma once +#include "../common/DualMappedPointerVector.hpp" +#include "../common/GuardedTypes.hpp" #include "../common/simpleQueue.hpp" #include "../core/Core.hpp" #include "Message.hpp" -#include "../common/GuardedTypes.hpp" #include #include #include @@ -23,23 +21,21 @@ Lawrence Livermore National Laboratory, operated by Lawrence Livermore National #include #include #include -#include "../common/DualMappedVector.hpp" namespace helics { class Core; /** data class containing information on an endpoint for Message Federate Manager*/ class endpoint_info { -public: + public: std::string name; std::string type; - endpoint_id_t id = invalid_id_value; - Core::handle_id_t handle = invalid_handle; + endpoint_id_t id = invalid_id_value; + Core::handle_id_t handle = invalid_handle; int callbackIndex = -1; - endpoint_info() = default; - endpoint_info (std::string n_name, std::string n_type,endpoint_id_t n_id,Core::handle_id_t n_handle) - : name (std::move (n_name)), type (std::move (n_type)),id(n_id),handle(n_handle){}; - + endpoint_info () = default; + endpoint_info (std::string n_name, std::string n_type, endpoint_id_t n_id, Core::handle_id_t n_handle) + : name (std::move (n_name)), type (std::move (n_type)), id (n_id), handle (n_handle){}; }; /** class handling the implementation details of a value Federate @@ -48,8 +44,8 @@ class endpoint_info class MessageFederateManager { public: - /** construct from a pointer to a core and a specified federate id - */ + /** construct from a pointer to a core and a specified federate id + */ MessageFederateManager (Core *coreOb, Core::federate_id_t id); ~MessageFederateManager (); /** register an endpoint @@ -125,18 +121,18 @@ class MessageFederateManager @details there can only be one generic callback @param[in] callback the function to call */ - void registerCallback (std::function callback); + void registerCallback (const std::function &callback); /** register a callback function to call when the specified endpoint receives a message @param[in] id the endpoint id to register the callback for @param[in] callback the function to call */ - void registerCallback (endpoint_id_t id, std::function callback); + void registerCallback (endpoint_id_t id, const std::function &callback); /** register a callback function to call when one of the specified endpoint ids receives a message @param[in] ids the set of ids to register the callback for @param[in] callback the function to call */ - void - registerCallback (const std::vector &ids, std::function callback); + void registerCallback (const std::vector &ids, + const std::function &callback); /**disconnect from the coreObject*/ void disconnect (); @@ -144,13 +140,14 @@ class MessageFederateManager int getEndpointCount () const; private: - shared_guarded> local_endpoints; //!< storage for the local endpoint information + shared_guarded> + local_endpoints; //!< storage for the local endpoint information std::vector> callbacks; //!< vector of callbacks std::map> subHandleLookup; //!< map for subscriptions Time CurrentTime; //!< the current simulation time - Core *coreObject; //!< the pointer to the actual core - std::atomic endpointCount{ 0 }; //!< the count of actual endpoints + Core *coreObject; //!< the pointer to the actual core + std::atomic endpointCount{0}; //!< the count of actual endpoints Core::federate_id_t fedID; //!< storage for the federate ID mutable std::mutex endpointLock; //!< lock for protecting the endpoint list std::vector>> messageQueues; //!< the storage for the message queues @@ -160,5 +157,4 @@ class MessageFederateManager private: // private functions void removeOrderedMessage (unsigned int index); }; -} // namespace helics -#endif \ No newline at end of file +} // namespace helics \ No newline at end of file diff --git a/src/helics/application_api/MessageOperators.hpp b/src/helics/application_api/MessageOperators.hpp index 954147892a..27fb6eae9a 100644 --- a/src/helics/application_api/MessageOperators.hpp +++ b/src/helics/application_api/MessageOperators.hpp @@ -1,15 +1,12 @@ /* - Copyright (C) 2017-2018, Battelle Memorial Institute All rights reserved. This software was co-developed by Pacific Northwest National Laboratory, operated by the Battelle Memorial Institute; the National Renewable Energy Laboratory, operated by the Alliance for Sustainable Energy, LLC; and the Lawrence Livermore National Laboratory, operated by Lawrence Livermore National Security, LLC. - */ -#ifndef MESSAGE_OPERATORS_H_ -#define MESSAGE_OPERATORS_H_ + #pragma once #include @@ -29,7 +26,7 @@ class MessageTimeOperator : public FilterOperator /** default constructor*/ MessageTimeOperator () = default; /** set the function to modify the time of the message in the constructor*/ - MessageTimeOperator (std::function