Initial commit
This commit is contained in:
62
.clang-format
Normal file
62
.clang-format
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
BasedOnStyle: Chromium
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
#AllowAllArgumentsOnNextLine: 'false'
|
||||||
|
#AlignConsecutiveAssignments: None
|
||||||
|
#AlignConsecutiveDeclarations: None
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
#AllowShortBlocksOnASingleLine: Never
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: None
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
#AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeBraces: Allman
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializersBeforeComma: true
|
||||||
|
ColumnLimit: 140
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
IndentCaseLabels: true
|
||||||
|
IndentWidth: 4
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
Language: Cpp
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: All
|
||||||
|
ObjCBlockIndentWidth: 4
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 1
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 200
|
||||||
|
PointerAlignment: Left
|
||||||
|
#SortIncludes: Never
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
|
Standard: Auto
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
|
#NamespaceMacros:
|
||||||
|
# - DECLARE_TEMPLATED_OPENDAQ_INTERFACE_T
|
||||||
|
# - DECLARE_TEMPLATED_OPENDAQ_INTERFACE_T_U
|
||||||
|
# - DECLARE_OPENDAQ_INTERFACE_EX
|
||||||
|
# - DECLARE_OPENDAQ_INTERFACE
|
||||||
|
FixNamespaceComments: false
|
||||||
|
#MacroBlockBegin: "^BEGIN_NAMESPACE_OPENDAQ$"
|
||||||
|
#MacroBlockEnd: "^END_NAMESPACE_OPENDAQ"
|
||||||
|
#IndentPPDirectives: BeforeHash
|
||||||
|
#SeparateDefinitionBlocks: Always
|
||||||
|
...
|
||||||
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[{*.{cpp,h},CMakeLists.txt,*.rtclass,*.cmake,*.json}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
tab_size = 4
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
ident_style = space
|
||||||
|
ident_size = 2
|
||||||
|
tab_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
* text=auto
|
||||||
|
*.[tT][xX][tT] text
|
||||||
|
*.[sS][hH] text eol=lf
|
||||||
46
.gitignore
vendored
Normal file
46
.gitignore
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
# file types
|
||||||
|
*.bin
|
||||||
|
*.bak
|
||||||
|
*.cache
|
||||||
|
*.check_cache
|
||||||
|
*.db
|
||||||
|
*.dcu
|
||||||
|
*.depend
|
||||||
|
*.exp
|
||||||
|
*.filters
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.list
|
||||||
|
*.log
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.pdb
|
||||||
|
*.pyc
|
||||||
|
*.rule
|
||||||
|
*.rsm
|
||||||
|
*.stamp
|
||||||
|
*.stat
|
||||||
|
*.suo
|
||||||
|
*.tlog
|
||||||
|
*.user
|
||||||
|
|
||||||
|
# IDE files
|
||||||
|
.idea/
|
||||||
|
.idea_/
|
||||||
|
.vs/
|
||||||
|
.vscode/
|
||||||
|
__history/
|
||||||
|
__recovery/
|
||||||
|
__pycache__/
|
||||||
|
|
||||||
|
# build and backup folders
|
||||||
|
/_build*
|
||||||
|
/build*
|
||||||
|
/build_win
|
||||||
|
/cmake-build*
|
||||||
|
/out*
|
||||||
|
bckp
|
||||||
|
|
||||||
|
# cmake
|
||||||
|
CMakeUserPresets.json
|
||||||
|
CMakeSettings.json
|
||||||
60
CMakeLists.txt
Normal file
60
CMakeLists.txt
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.25)
|
||||||
|
set(REPO_NAME example_fb_module)
|
||||||
|
set(REPO_OPTION_PREFIX EXAMPLE_MODULE)
|
||||||
|
|
||||||
|
project(${REPO_NAME} VERSION 1.0.0)
|
||||||
|
|
||||||
|
if (POLICY CMP0135)
|
||||||
|
cmake_policy(SET CMP0135 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (POLICY CMP0077)
|
||||||
|
cmake_policy(SET CMP0077 NEW)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
list(APPEND CMAKE_MESSAGE_CONTEXT ${REPO_NAME})
|
||||||
|
set(CMAKE_MESSAGE_CONTEXT_SHOW ON CACHE BOOL "Show CMake message context")
|
||||||
|
|
||||||
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
||||||
|
|
||||||
|
add_definitions(-DFMT_HEADER_ONLY)
|
||||||
|
|
||||||
|
option(OPENDAQ_FB_EXAMPLE_ENABLE_APP "Enable building example function block application" OFF)
|
||||||
|
option(EXAMPLE_MODULE_ENABLE_TESTS "Enable building of test suite for the example function block module" OFF)
|
||||||
|
|
||||||
|
include(CommonUtils)
|
||||||
|
setup_repo(${REPO_OPTION_PREFIX})
|
||||||
|
|
||||||
|
if(OPENDAQ_FB_EXAMPLE_ENABLE_APP)
|
||||||
|
set(DAQMODULES_REF_FB_MODULE ON CACHE BOOL "" FORCE)
|
||||||
|
set(DAQMODULES_REF_DEVICE_MODULE ON CACHE BOOL "" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_subdirectory(external)
|
||||||
|
add_subdirectory(example_module)
|
||||||
|
|
||||||
|
if(OPENDAQ_FB_EXAMPLE_ENABLE_APP)
|
||||||
|
message(STATUS "Enabled example function block application")
|
||||||
|
add_subdirectory(example_application)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Set CPack variables
|
||||||
|
set(CPACK_COMPONENTS_ALL RUNTIME)
|
||||||
|
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||||
|
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
|
||||||
|
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION})
|
||||||
|
set(CPACK_OUTPUT_FILE_PREFIX "${CMAKE_BINARY_DIR}/package")
|
||||||
|
|
||||||
|
# Set the CPack generator based on the platform
|
||||||
|
if (WIN32)
|
||||||
|
set(CPACK_GENERATOR "ZIP")
|
||||||
|
elseif (UNIX AND NOT APPLE)
|
||||||
|
cmake_host_system_information(RESULT DISTRO_ID QUERY DISTRIB_ID)
|
||||||
|
cmake_host_system_information(RESULT DISTRO_VERSION_ID QUERY DISTRIB_VERSION_ID)
|
||||||
|
set(CPACK_SYSTEM_NAME "${DISTRO_ID}${DISTRO_VERSION_ID}")
|
||||||
|
set(CPACK_GENERATOR "TGZ")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Include CPack for packaging
|
||||||
|
include(CPack)
|
||||||
9
README.md
Normal file
9
README.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Example function block module
|
||||||
|
|
||||||
|
Simple example that builds an openDAQ module giving access to an example function block. Said function block scales an input signal with a provided scale, and offsets it by a provided offset.
|
||||||
|
|
||||||
|
## Testing the module
|
||||||
|
|
||||||
|
To test the module, enable the `OPENDAQ_FB_EXAMPLE_ENABLE_APP` cmake flag. Doing so will add an the openDAQ reference device and function block modules to your project. Those are used to create a simulator device via the "daqref://device0" connection string, as well as a renderer via the "RefFBModuleRenderer" function block ID. The main application connects a reference device signal into both the example scaler and renderer. Additionally, it connects the scaler output into the renderer.
|
||||||
|
|
||||||
|
To add additional tests, enable the `EXAMPLE_MODULE_ENABLE_TESTS` cmake flag. Doing so will create a new test target configured for use with the GTest framework.
|
||||||
151
cmake/CommonUtils.cmake
Normal file
151
cmake/CommonUtils.cmake
Normal file
@@ -0,0 +1,151 @@
|
|||||||
|
macro(setup_repo REPO_OPTION_PREFIX)
|
||||||
|
if (NOT DEFINED PROJECT_SOURCE_DIR)
|
||||||
|
message(FATAL_ERROR "Must be run inside a project()")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Additional build options
|
||||||
|
option(${REPO_OPTION_PREFIX}_DISABLE_DEBUG_POSTFIX "Disable debug ('-debug') postfix" OFF)
|
||||||
|
option(${REPO_OPTION_PREFIX}_DEBUG_WARNINGS_AS_ERRORS "Treat debug warnings as errors" OFF)
|
||||||
|
option(${REPO_OPTION_PREFIX}_ENABLE_TESTS "Enable unit-tests for ${REPO_OPTION_PREFIX}" ON)
|
||||||
|
|
||||||
|
get_filename_component(ROOT_DIR ${CMAKE_SOURCE_DIR} REALPATH)
|
||||||
|
|
||||||
|
if (NOT ${PROJECT_SOURCE_DIR} STREQUAL ${ROOT_DIR})
|
||||||
|
set(BUILDING_AS_SUBMODULE ON PARENT_SCOPE)
|
||||||
|
message(STATUS "Building as submodule")
|
||||||
|
else()
|
||||||
|
message(STATUS "Building standalone")
|
||||||
|
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER ".CMakePredefinedTargets")
|
||||||
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
|
||||||
|
get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||||
|
|
||||||
|
message(STATUS "Platform: ${CMAKE_SYSTEM_PROCESSOR} | ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_VERSION}")
|
||||||
|
message(STATUS "Generator: ${CMAKE_GENERATOR} | ${CMAKE_GENERATOR_PLATFORM}")
|
||||||
|
|
||||||
|
if (IS_MULTICONFIG)
|
||||||
|
message(STATUS "Configuration types:")
|
||||||
|
|
||||||
|
block()
|
||||||
|
list(APPEND CMAKE_MESSAGE_INDENT "\t")
|
||||||
|
|
||||||
|
foreach(CONFIG_TYPE ${CMAKE_CONFIGURATION_TYPES})
|
||||||
|
message(STATUS ${CONFIG_TYPE})
|
||||||
|
endforeach()
|
||||||
|
endblock()
|
||||||
|
else()
|
||||||
|
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(TIMESTAMP CONFIGURE_DATE)
|
||||||
|
string(TIMESTAMP CURRENT_YEAR "%Y")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
if (WIN32)
|
||||||
|
add_compile_definitions(NOMINMAX
|
||||||
|
_WIN32_WINNT=0x0601 # Windows 7 Compat
|
||||||
|
)
|
||||||
|
|
||||||
|
add_compile_definitions(UNICODE _UNICODE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT CMAKE_DEBUG_POSTFIX AND NOT ${REPO_OPTION_PREFIX}_DISABLE_DEBUG_POSTFIX)
|
||||||
|
set(CMAKE_DEBUG_POSTFIX -debug)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
# As above CMAKE_CXX_STANDARD but for VS
|
||||||
|
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/std:c++17>)
|
||||||
|
|
||||||
|
foreach (flag IN ITEMS
|
||||||
|
# Set source and execution character sets to UTF-8
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8
|
||||||
|
/utf-8
|
||||||
|
# Display level 1, level 2, and level 3 warnings, and all level 4 (informational) warnings that aren't off by default.
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/build/reference/compiler-option-warning-level
|
||||||
|
/W4
|
||||||
|
# data member 'member1' will be initialized after data member 'member2'
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/c5038
|
||||||
|
#/w15038
|
||||||
|
# Supress warnings
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/build/reference/compiler-option-warning-level
|
||||||
|
#
|
||||||
|
# 'class1' : inherits 'class2::member' via dominance
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4250
|
||||||
|
#/wd4250
|
||||||
|
# Your code uses a function, class member, variable, or typedef that's marked deprecated.
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4996
|
||||||
|
/wd4996
|
||||||
|
# declaration of 'identifier' hides class member
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4458
|
||||||
|
/wd4458
|
||||||
|
# nonstandard extension used : nameless struct/union
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4201
|
||||||
|
#/wd4201
|
||||||
|
# unreachable code
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4702
|
||||||
|
#/wd4702
|
||||||
|
# declaration of 'identifier' hides global declaration
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4459
|
||||||
|
#/wd4459
|
||||||
|
# 'function' : unreferenced local function has been removed
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4505
|
||||||
|
#/wd4505
|
||||||
|
# conditional expression is constant
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4127
|
||||||
|
#/wd4127
|
||||||
|
# assignment within conditional expression
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4706
|
||||||
|
#/wd4706
|
||||||
|
# loss of data / precision, unsigned <--> signed
|
||||||
|
#
|
||||||
|
# 'argument' : conversion from 'type1' to 'type2', possible loss of data
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-2-c4244
|
||||||
|
/wd4244
|
||||||
|
# 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4267
|
||||||
|
#/wd4267
|
||||||
|
# 'identifier' : unreferenced formal parameter
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4100
|
||||||
|
/wd4100
|
||||||
|
)
|
||||||
|
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:${flag}>)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if (NOT OPENDAQ_MSVC_SINGLE_PROCESS_BUILD)
|
||||||
|
# Build with multiple processes
|
||||||
|
# https://learn.microsoft.com/en-us/cpp/build/reference/mp-build-with-multiple-processes
|
||||||
|
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/MP>)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Treat warnings as errors if not Debug or OPENDAQ_DEBUG_WARNINGS_AS_ERRORS is ON
|
||||||
|
add_compile_options($<$<OR:$<NOT:$<CONFIG:Debug>>,$<BOOL:${${REPO_OPTION_PREFIX}_DEBUG_WARNINGS_AS_ERRORS}>>:/WX>)
|
||||||
|
|
||||||
|
add_compile_definitions($<$<CONFIG:Debug>:_DEBUG>)
|
||||||
|
|
||||||
|
if (MSVC_VERSION GREATER_EQUAL 1910)
|
||||||
|
# /Zc:__cplusplus forces MSVC to use the correct value of __cplusplus macro (otherwise always C++98)
|
||||||
|
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/Zc:__cplusplus>)
|
||||||
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
|
# /Zf (Faster PDB generation) is not supported by ClangCL
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Zf")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Produce diagnostic messages with exact location
|
||||||
|
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:/diagnostics:caret>)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /ignore:4221")
|
||||||
|
# set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /ignore:4221")
|
||||||
|
# set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||||
|
|
||||||
|
if (${REPO_OPTION_PREFIX}_ENABLE_TESTS)
|
||||||
|
set(OPENDAQ_ENABLE_TEST_UTILS ON CACHE BOOL "Enable testing utils library")
|
||||||
|
endif()
|
||||||
|
endmacro()
|
||||||
10
example_application/CMakeLists.txt
Normal file
10
example_application/CMakeLists.txt
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
add_executable(fb_application_example main.cpp)
|
||||||
|
|
||||||
|
target_link_libraries(fb_application_example PRIVATE daq::opendaq)
|
||||||
|
|
||||||
|
add_dependencies(
|
||||||
|
fb_application_example
|
||||||
|
example_module
|
||||||
|
daq::ref_device_module
|
||||||
|
daq::ref_fb_module
|
||||||
|
)
|
||||||
26
example_application/main.cpp
Normal file
26
example_application/main.cpp
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/**
|
||||||
|
* Server application
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <opendaq/opendaq.h>
|
||||||
|
|
||||||
|
using namespace daq;
|
||||||
|
|
||||||
|
int main(int /*argc*/, const char* /*argv*/[])
|
||||||
|
{
|
||||||
|
const auto instance = Instance();
|
||||||
|
auto referenceDevice = instance.addDevice("daqref://device0");
|
||||||
|
auto renderer = instance.addFunctionBlock("RefFBModuleRenderer");
|
||||||
|
auto exampleModule = instance.addFunctionBlock("ExampleScalingModule");
|
||||||
|
exampleModule.setPropertyValue("Scale", 3);
|
||||||
|
exampleModule.setPropertyValue("Offset", -2);
|
||||||
|
|
||||||
|
exampleModule.getInputPorts()[0].connect(referenceDevice.getSignalsRecursive()[0]);
|
||||||
|
renderer.getInputPorts()[0].connect(referenceDevice.getSignalsRecursive()[0]);
|
||||||
|
renderer.getInputPorts()[1].connect(exampleModule.getSignals()[0]);
|
||||||
|
|
||||||
|
std::cout << "Press \"enter\" to exit the application..." << std::endl;
|
||||||
|
std::cin.get();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
8
example_module/CMakeLists.txt
Normal file
8
example_module/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
get_current_folder_name(TARGET_FOLDER_NAME)
|
||||||
|
project(ExampleModule VERSION 1.0.0 LANGUAGES CXX)
|
||||||
|
|
||||||
|
add_subdirectory(src)
|
||||||
|
|
||||||
|
if (EXAMPLE_MODULE_ENABLE_TESTS)
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
24
example_module/include/example_module/common.h
Normal file
24
example_module/include/example_module/common.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2024 openDAQ d.o.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <coretypes/common.h>
|
||||||
|
|
||||||
|
#define BEGIN_NAMESPACE_EXAMPLE_MODULE BEGIN_NAMESPACE_OPENDAQ_MODULE(example_module)
|
||||||
|
|
||||||
|
static const std::string EXAMPLE_MODULE_NAME = "ExampleModule";
|
||||||
|
|
||||||
|
#define END_NAMESPACE_EXAMPLE_MODULE END_NAMESPACE_OPENDAQ_MODULE
|
||||||
56
example_module/include/example_module/dispatch.h
Normal file
56
example_module/include/example_module/dispatch.h
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2024 openDAQ d.o.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <opendaq/sample_type.h>
|
||||||
|
|
||||||
|
#define SAMPLE_TYPE_DISPATCH(ST, Func, ...) \
|
||||||
|
switch (ST) \
|
||||||
|
{ \
|
||||||
|
case SampleType::Int8: \
|
||||||
|
Func<SampleType::Int8>(__VA_ARGS__); \
|
||||||
|
break; \
|
||||||
|
case SampleType::Int16: \
|
||||||
|
Func<SampleType::Int16>(__VA_ARGS__); \
|
||||||
|
break; \
|
||||||
|
case SampleType::Int32: \
|
||||||
|
Func<SampleType::Int32>(__VA_ARGS__); \
|
||||||
|
break; \
|
||||||
|
case SampleType::Int64: \
|
||||||
|
Func<SampleType::Int64>(__VA_ARGS__); \
|
||||||
|
break; \
|
||||||
|
case SampleType::UInt8: \
|
||||||
|
Func<SampleType::UInt8>(__VA_ARGS__); \
|
||||||
|
break; \
|
||||||
|
case SampleType::UInt16: \
|
||||||
|
Func<SampleType::UInt16>(__VA_ARGS__); \
|
||||||
|
break; \
|
||||||
|
case SampleType::UInt32: \
|
||||||
|
Func<SampleType::UInt32>(__VA_ARGS__); \
|
||||||
|
break; \
|
||||||
|
case SampleType::UInt64: \
|
||||||
|
Func<SampleType::UInt64>(__VA_ARGS__); \
|
||||||
|
break; \
|
||||||
|
case SampleType::Float32: \
|
||||||
|
Func<SampleType::Float32>(__VA_ARGS__); \
|
||||||
|
break; \
|
||||||
|
case SampleType::Float64: \
|
||||||
|
Func<SampleType::Float64>(__VA_ARGS__); \
|
||||||
|
break; \
|
||||||
|
default: \
|
||||||
|
assert(false); \
|
||||||
|
}
|
||||||
77
example_module/include/example_module/example_fb.h
Normal file
77
example_module/include/example_module/example_fb.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2024 openDAQ d.o.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <example_module/common.h>
|
||||||
|
#include <opendaq/function_block_impl.h>
|
||||||
|
#include <opendaq/opendaq.h>
|
||||||
|
#include <opendaq/stream_reader_ptr.h>
|
||||||
|
|
||||||
|
BEGIN_NAMESPACE_EXAMPLE_MODULE
|
||||||
|
|
||||||
|
class ExampleFBImpl final : public FunctionBlock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ExampleFBImpl(const ContextPtr& ctx, const ComponentPtr& parent, const StringPtr& localId);
|
||||||
|
~ExampleFBImpl() override = default;
|
||||||
|
|
||||||
|
static FunctionBlockTypePtr CreateType();
|
||||||
|
|
||||||
|
private:
|
||||||
|
InputPortPtr inputPort;
|
||||||
|
|
||||||
|
DataDescriptorPtr inputDataDescriptor;
|
||||||
|
DataDescriptorPtr inputDomainDataDescriptor;
|
||||||
|
|
||||||
|
DataDescriptorPtr outputDataDescriptor;
|
||||||
|
DataDescriptorPtr outputDomainDataDescriptor;
|
||||||
|
|
||||||
|
SampleType inputSampleType;
|
||||||
|
|
||||||
|
SignalConfigPtr outputSignal;
|
||||||
|
SignalConfigPtr outputDomainSignal;
|
||||||
|
|
||||||
|
StreamReaderPtr reader;
|
||||||
|
SizeT sampleRate;
|
||||||
|
std::vector<float> inputData;
|
||||||
|
std::vector<uint64_t> inputDomainData;
|
||||||
|
|
||||||
|
bool configValid = false;
|
||||||
|
Float scale;
|
||||||
|
Float offset;
|
||||||
|
Float outputHighValue;
|
||||||
|
Float outputLowValue;
|
||||||
|
Bool useCustomOutputRange;
|
||||||
|
std::string outputUnit;
|
||||||
|
std::string outputName;
|
||||||
|
|
||||||
|
void createInputPorts();
|
||||||
|
void createSignals();
|
||||||
|
|
||||||
|
void calculate();
|
||||||
|
void processData(SizeT readAmount, SizeT packetOffset) const;
|
||||||
|
void processEventPacket(const EventPacketPtr& packet);
|
||||||
|
|
||||||
|
void processSignalDescriptorChanged(const DataDescriptorPtr& dataDescriptor,
|
||||||
|
const DataDescriptorPtr& domainDescriptor);
|
||||||
|
void configure();
|
||||||
|
|
||||||
|
void initProperties();
|
||||||
|
void propertyChanged(bool configure);
|
||||||
|
void readProperties();
|
||||||
|
};
|
||||||
|
|
||||||
|
END_NAMESPACE_EXAMPLE_MODULE
|
||||||
32
example_module/include/example_module/example_module.h
Normal file
32
example_module/include/example_module/example_module.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2024 openDAQ d.o.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <example_module/common.h>
|
||||||
|
#include <opendaq/module_impl.h>
|
||||||
|
|
||||||
|
BEGIN_NAMESPACE_EXAMPLE_MODULE
|
||||||
|
|
||||||
|
class ExampleModule final : public Module
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ExampleModule(ContextPtr context);
|
||||||
|
|
||||||
|
DictPtr<IString, IFunctionBlockType> onGetAvailableFunctionBlockTypes() override;
|
||||||
|
FunctionBlockPtr onCreateFunctionBlock(const StringPtr& id, const ComponentPtr& parent, const StringPtr& localId, const PropertyObjectPtr& config) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
END_NAMESPACE_EXAMPLE_MODULE
|
||||||
20
example_module/include/example_module/module_dll.h
Normal file
20
example_module/include/example_module/module_dll.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022-2024 openDAQ d.o.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <opendaq/module_exports.h>
|
||||||
|
|
||||||
|
DECLARE_MODULE_EXPORTS(ExampleModule)
|
||||||
45
example_module/src/CMakeLists.txt
Normal file
45
example_module/src/CMakeLists.txt
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
set(LIB_NAME example_module)
|
||||||
|
set(MODULE_HEADERS_DIR ../include/${TARGET_FOLDER_NAME})
|
||||||
|
|
||||||
|
set(SRC_Include common.h
|
||||||
|
module_dll.h
|
||||||
|
example_module.h
|
||||||
|
example_fb.h
|
||||||
|
)
|
||||||
|
|
||||||
|
set(SRC_Srcs module_dll.cpp
|
||||||
|
example_module.cpp
|
||||||
|
example_fb.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
prepend_include(${TARGET_FOLDER_NAME} SRC_Include)
|
||||||
|
|
||||||
|
source_group("module" FILES ${MODULE_HEADERS_DIR}/example_module.h
|
||||||
|
${MODULE_HEADERS_DIR}/example_fb.h
|
||||||
|
${MODULE_HEADERS_DIR}/module_dll.h
|
||||||
|
module_dll.cpp
|
||||||
|
example_module.cpp
|
||||||
|
example_fb.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
add_library(${LIB_NAME} SHARED ${SRC_Include}
|
||||||
|
${SRC_Srcs}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(${SDK_TARGET_NAMESPACE}::${LIB_NAME} ALIAS ${LIB_NAME})
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
target_compile_options(${LIB_NAME} PRIVATE /bigobj)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_link_libraries(${LIB_NAME} PUBLIC daq::opendaq
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(${LIB_NAME} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/../include>
|
||||||
|
$<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
|
||||||
|
opendaq_set_module_properties(${LIB_NAME} ${PROJECT_VERSION_MAJOR})
|
||||||
|
create_version_header(${LIB_NAME})
|
||||||
251
example_module/src/example_fb.cpp
Normal file
251
example_module/src/example_fb.cpp
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
#include <example_module/example_fb.h>
|
||||||
|
#include <example_module/dispatch.h>
|
||||||
|
#include <opendaq/event_packet_params.h>
|
||||||
|
|
||||||
|
BEGIN_NAMESPACE_EXAMPLE_MODULE
|
||||||
|
ExampleFBImpl::ExampleFBImpl(const ContextPtr& ctx, const ComponentPtr& parent, const StringPtr& localId)
|
||||||
|
: FunctionBlock(CreateType(), ctx, parent, localId)
|
||||||
|
{
|
||||||
|
initComponentStatus();
|
||||||
|
createInputPorts();
|
||||||
|
createSignals();
|
||||||
|
initProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleFBImpl::initProperties()
|
||||||
|
{
|
||||||
|
const auto scaleProp = FloatProperty("Scale", 1.0);
|
||||||
|
objPtr.addProperty(scaleProp);
|
||||||
|
objPtr.getOnPropertyValueWrite("Scale") +=
|
||||||
|
[this](PropertyObjectPtr& obj, PropertyValueEventArgsPtr& args) { propertyChanged(true); };
|
||||||
|
|
||||||
|
const auto offsetProp = FloatProperty("Offset", 0.0);
|
||||||
|
objPtr.addProperty(offsetProp);
|
||||||
|
objPtr.getOnPropertyValueWrite("Offset") +=
|
||||||
|
[this](PropertyObjectPtr& obj, PropertyValueEventArgsPtr& args) { propertyChanged(true); };
|
||||||
|
|
||||||
|
const auto useCustomOutputRangeProp = BoolProperty("UseCustomOutputRange", False);
|
||||||
|
objPtr.addProperty(useCustomOutputRangeProp);
|
||||||
|
objPtr.getOnPropertyValueWrite("UseCustomOutputRange") +=
|
||||||
|
[this](PropertyObjectPtr& obj, PropertyValueEventArgsPtr& args) { propertyChanged(true); };
|
||||||
|
|
||||||
|
const auto customHighValueProp = FloatProperty("OutputHighValue", 10.0, EvalValue("$UseCustomOutputRange"));
|
||||||
|
objPtr.addProperty(customHighValueProp);
|
||||||
|
objPtr.getOnPropertyValueWrite("OutputHighValue") +=
|
||||||
|
[this](PropertyObjectPtr& obj, PropertyValueEventArgsPtr& args) { propertyChanged(true); };
|
||||||
|
|
||||||
|
const auto customLowValueProp = FloatProperty("OutputLowValue", -10.0, EvalValue("$UseCustomOutputRange"));
|
||||||
|
objPtr.addProperty(customLowValueProp);
|
||||||
|
objPtr.getOnPropertyValueWrite("OutputLowValue") +=
|
||||||
|
[this](PropertyObjectPtr& obj, PropertyValueEventArgsPtr& args) { propertyChanged(true); };
|
||||||
|
|
||||||
|
const auto outputNameProp = StringProperty("OutputName", "");
|
||||||
|
objPtr.addProperty(outputNameProp);
|
||||||
|
objPtr.getOnPropertyValueWrite("OutputName") +=
|
||||||
|
[this](PropertyObjectPtr& obj, PropertyValueEventArgsPtr& args) { propertyChanged(true); };
|
||||||
|
|
||||||
|
const auto outputUnitProp = StringProperty("OutputUnit", "");
|
||||||
|
objPtr.addProperty(outputUnitProp);
|
||||||
|
objPtr.getOnPropertyValueWrite("OutputUnit") +=
|
||||||
|
[this](PropertyObjectPtr& obj, PropertyValueEventArgsPtr& args) { propertyChanged(true); };
|
||||||
|
|
||||||
|
readProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleFBImpl::propertyChanged(bool configure)
|
||||||
|
{
|
||||||
|
readProperties();
|
||||||
|
if (configure)
|
||||||
|
this->configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleFBImpl::readProperties()
|
||||||
|
{
|
||||||
|
scale = objPtr.getPropertyValue("Scale");
|
||||||
|
offset = objPtr.getPropertyValue("Offset");
|
||||||
|
useCustomOutputRange = objPtr.getPropertyValue("UseCustomOutputRange");
|
||||||
|
outputHighValue = objPtr.getPropertyValue("OutputHighValue");
|
||||||
|
outputLowValue = objPtr.getPropertyValue("OutputLowValue");
|
||||||
|
outputUnit = static_cast<std::string>(objPtr.getPropertyValue("OutputUnit"));
|
||||||
|
outputName = static_cast<std::string>(objPtr.getPropertyValue("OutputName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionBlockTypePtr ExampleFBImpl::CreateType()
|
||||||
|
{
|
||||||
|
return FunctionBlockType("ExampleScalingModule", "Scaling", "Signal scaling");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleFBImpl::processSignalDescriptorChanged(const DataDescriptorPtr& dataDescriptor,
|
||||||
|
const DataDescriptorPtr& domainDescriptor)
|
||||||
|
{
|
||||||
|
if (dataDescriptor.assigned())
|
||||||
|
this->inputDataDescriptor = dataDescriptor;
|
||||||
|
if (domainDescriptor.assigned())
|
||||||
|
this->inputDomainDataDescriptor = domainDescriptor;
|
||||||
|
|
||||||
|
configure();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleFBImpl::configure()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!inputDomainDataDescriptor.assigned() || inputDomainDataDescriptor == NullDataDescriptor())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("No domain input");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inputDataDescriptor.assigned() || inputDataDescriptor == NullDataDescriptor())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("No value input");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputDataDescriptor.getDimensions().getCount() > 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Arrays not supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
inputSampleType = inputDataDescriptor.getSampleType();
|
||||||
|
if (inputSampleType != SampleType::Float64 &&
|
||||||
|
inputSampleType != SampleType::Float32 &&
|
||||||
|
inputSampleType != SampleType::Int8 &&
|
||||||
|
inputSampleType != SampleType::Int16 &&
|
||||||
|
inputSampleType != SampleType::Int32 &&
|
||||||
|
inputSampleType != SampleType::Int64 &&
|
||||||
|
inputSampleType != SampleType::UInt8 &&
|
||||||
|
inputSampleType != SampleType::UInt16 &&
|
||||||
|
inputSampleType != SampleType::UInt32 &&
|
||||||
|
inputSampleType != SampleType::UInt64)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid sample type");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accept only synchronous (linear implicit) domain signals
|
||||||
|
if (inputDomainDataDescriptor.getSampleType() != SampleType::Int64 && inputDomainDataDescriptor.getSampleType() != SampleType::UInt64)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Incompatible domain data sample type");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto domainUnit = inputDomainDataDescriptor.getUnit();
|
||||||
|
if (domainUnit.getSymbol() != "s" && domainUnit.getSymbol() != "seconds")
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Domain unit expected in seconds");
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto domainRule = inputDomainDataDescriptor.getRule();
|
||||||
|
if (inputDomainDataDescriptor.getRule().getType() != DataRuleType::Linear)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Domain rule must be linear");
|
||||||
|
}
|
||||||
|
|
||||||
|
RangePtr outputRange;
|
||||||
|
if (useCustomOutputRange)
|
||||||
|
{
|
||||||
|
outputRange = Range(outputLowValue, outputHighValue);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto outputHigh = scale * static_cast<Float>(inputDataDescriptor.getValueRange().getLowValue()) + offset;
|
||||||
|
auto outputLow = scale * static_cast<Float>(inputDataDescriptor.getValueRange().getHighValue()) + offset;
|
||||||
|
if (outputLow > outputHigh)
|
||||||
|
std::swap(outputLow, outputHigh);
|
||||||
|
|
||||||
|
outputRange = Range(outputLow, outputHigh);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto name = outputName.empty() ? inputPort.getSignal().getName().toStdString() + "/Scaled" : outputName;
|
||||||
|
auto unit = outputUnit.empty() ? inputDataDescriptor.getUnit() : Unit(outputUnit);
|
||||||
|
|
||||||
|
outputDataDescriptor = DataDescriptorBuilder()
|
||||||
|
.setSampleType(SampleType::Float64)
|
||||||
|
.setValueRange(outputRange)
|
||||||
|
.setUnit(unit)
|
||||||
|
.build();
|
||||||
|
outputDomainDataDescriptor = inputDomainDataDescriptor;
|
||||||
|
|
||||||
|
outputSignal.setDescriptor(outputDataDescriptor);
|
||||||
|
outputSignal.setName(name);
|
||||||
|
outputDomainSignal.setDescriptor(inputDomainDataDescriptor);
|
||||||
|
|
||||||
|
// Allocate 1s buffer
|
||||||
|
sampleRate = reader::getSampleRate(inputDomainDataDescriptor);
|
||||||
|
inputData.resize(sampleRate);
|
||||||
|
inputDomainData.resize(sampleRate);
|
||||||
|
|
||||||
|
setComponentStatus(ComponentStatus::Ok);
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
setComponentStatusWithMessage(ComponentStatus::Error, fmt::format("Failed to set descriptor for output signal: {}", e.what()));
|
||||||
|
outputSignal.setDescriptor(nullptr);
|
||||||
|
configValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
configValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleFBImpl::calculate()
|
||||||
|
{
|
||||||
|
auto lock = this->getAcquisitionLock();
|
||||||
|
|
||||||
|
while (!reader.getEmpty())
|
||||||
|
{
|
||||||
|
SizeT readAmount = std::min(reader.getAvailableCount(), sampleRate);
|
||||||
|
const auto status = reader.readWithDomain(inputData.data(), inputDomainData.data(), &readAmount);
|
||||||
|
|
||||||
|
if (configValid)
|
||||||
|
{
|
||||||
|
processData(readAmount, status.getOffset());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.getReadStatus() == ReadStatus::Event)
|
||||||
|
{
|
||||||
|
const auto eventPacket = status.getEventPacket();
|
||||||
|
if (eventPacket.assigned())
|
||||||
|
processEventPacket(eventPacket);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleFBImpl::processData(SizeT readAmount, SizeT packetOffset) const
|
||||||
|
{
|
||||||
|
if (readAmount == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto outputDomainPacket = DataPacket(outputDomainDataDescriptor, readAmount, packetOffset);
|
||||||
|
const auto outputPacket = DataPacketWithDomain(outputDomainPacket, outputDataDescriptor, readAmount);
|
||||||
|
auto outputData = static_cast<Float*>(outputPacket.getRawData());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < readAmount; i++)
|
||||||
|
*outputData++ = scale * static_cast<Float>(inputData[i]) + offset;
|
||||||
|
|
||||||
|
outputSignal.sendPacket(outputPacket);
|
||||||
|
outputDomainSignal.sendPacket(outputDomainPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleFBImpl::processEventPacket(const EventPacketPtr& packet)
|
||||||
|
{
|
||||||
|
if (packet.getEventId() == event_packet_id::DATA_DESCRIPTOR_CHANGED)
|
||||||
|
{
|
||||||
|
DataDescriptorPtr dataDesc = packet.getParameters().get(event_packet_param::DATA_DESCRIPTOR);
|
||||||
|
DataDescriptorPtr domainDesc = packet.getParameters().get(event_packet_param::DOMAIN_DATA_DESCRIPTOR);
|
||||||
|
processSignalDescriptorChanged(dataDesc, domainDesc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleFBImpl::createInputPorts()
|
||||||
|
{
|
||||||
|
inputPort = createAndAddInputPort("Input", PacketReadyNotification::Scheduler);
|
||||||
|
reader = StreamReaderFromPort(inputPort, SampleType::Float32, SampleType::UInt64);
|
||||||
|
reader.setOnDataAvailable([this] { calculate();});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExampleFBImpl::createSignals()
|
||||||
|
{
|
||||||
|
outputSignal = createAndAddSignal("Scaled");
|
||||||
|
outputDomainSignal = createAndAddSignal("ScaledTime", nullptr, false);
|
||||||
|
outputSignal.setDomainSignal(outputDomainSignal);
|
||||||
|
}
|
||||||
|
|
||||||
|
END_NAMESPACE_EXAMPLE_MODULE
|
||||||
42
example_module/src/example_module.cpp
Normal file
42
example_module/src/example_module.cpp
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
#include <coretypes/version_info_factory.h>
|
||||||
|
#include <opendaq/custom_log.h>
|
||||||
|
#include <example_module/example_fb.h>
|
||||||
|
#include <example_module/example_module.h>
|
||||||
|
#include <example_module/version.h>
|
||||||
|
|
||||||
|
BEGIN_NAMESPACE_EXAMPLE_MODULE
|
||||||
|
|
||||||
|
ExampleModule::ExampleModule(ContextPtr context)
|
||||||
|
: Module("ReferenceFunctionBlockModule",
|
||||||
|
VersionInfo(EXAMPLE_MODULE_MAJOR_VERSION, EXAMPLE_MODULE_MINOR_VERSION, EXAMPLE_MODULE_PATCH_VERSION),
|
||||||
|
std::move(context),
|
||||||
|
"ReferenceFunctionBlockModule")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
DictPtr<IString, IFunctionBlockType> ExampleModule::onGetAvailableFunctionBlockTypes()
|
||||||
|
{
|
||||||
|
auto types = Dict<IString, IFunctionBlockType>();
|
||||||
|
|
||||||
|
const auto typeScaling = ExampleFBImpl::CreateType();
|
||||||
|
types.set(typeScaling.getId(), typeScaling);
|
||||||
|
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionBlockPtr ExampleModule::onCreateFunctionBlock(const StringPtr& id,
|
||||||
|
const ComponentPtr& parent,
|
||||||
|
const StringPtr& localId,
|
||||||
|
const PropertyObjectPtr& config)
|
||||||
|
{
|
||||||
|
if (id == ExampleFBImpl::CreateType().getId())
|
||||||
|
{
|
||||||
|
FunctionBlockPtr fb = createWithImplementation<IFunctionBlock, ExampleFBImpl>(context, parent, localId);
|
||||||
|
return fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_W("Function block \"{}\" not found", id);
|
||||||
|
throw NotFoundException("Function block not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
END_NAMESPACE_EXAMPLE_MODULE
|
||||||
9
example_module/src/module_dll.cpp
Normal file
9
example_module/src/module_dll.cpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#include <example_module/module_dll.h>
|
||||||
|
#include <example_module/example_module.h>
|
||||||
|
|
||||||
|
#include <opendaq/module_factory.h>
|
||||||
|
|
||||||
|
using namespace daq::modules::example_module;
|
||||||
|
|
||||||
|
DEFINE_MODULE_EXPORTS(ExampleModule)
|
||||||
|
|
||||||
18
example_module/tests/CMakeLists.txt
Normal file
18
example_module/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
set(MODULE_NAME example_module)
|
||||||
|
set(TEST_APP test_${MODULE_NAME})
|
||||||
|
|
||||||
|
set(TEST_SOURCES test_example_module.cpp
|
||||||
|
test_app.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(${TEST_APP} ${TEST_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${TEST_APP} PRIVATE daq::test_utils
|
||||||
|
${SDK_TARGET_NAMESPACE}::${MODULE_NAME}
|
||||||
|
)
|
||||||
|
|
||||||
|
add_test(NAME ${TEST_APP}
|
||||||
|
COMMAND $<TARGET_FILE_NAME:${TEST_APP}>
|
||||||
|
WORKING_DIRECTORY bin
|
||||||
|
)
|
||||||
21
example_module/tests/test_app.cpp
Normal file
21
example_module/tests/test_app.cpp
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#include <testutils/testutils.h>
|
||||||
|
#include <testutils/bb_memcheck_listener.h>
|
||||||
|
#include <coreobjects/util.h>
|
||||||
|
#include <opendaq/module_manager_init.h>
|
||||||
|
#include <coretypes/stringobject_factory.h>
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char** args)
|
||||||
|
{
|
||||||
|
daq::daqInitializeCoreObjectsTesting();
|
||||||
|
daqInitModuleManagerLibrary();
|
||||||
|
|
||||||
|
testing::InitGoogleTest(&argc, args);
|
||||||
|
|
||||||
|
testing::TestEventListeners& listeners = testing::UnitTest::GetInstance()->listeners();
|
||||||
|
listeners.Append(new DaqMemCheckListener());
|
||||||
|
|
||||||
|
auto res = RUN_ALL_TESTS();
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
77
example_module/tests/test_example_module.cpp
Normal file
77
example_module/tests/test_example_module.cpp
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <testutils/testutils.h>
|
||||||
|
#include <opendaq/opendaq.h>
|
||||||
|
#include <opendaq/data_descriptor_factory.h>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
using namespace daq;
|
||||||
|
using ExampleModuleTest = testing::Test;
|
||||||
|
|
||||||
|
TEST_F(ExampleModuleTest, TestAdd)
|
||||||
|
{
|
||||||
|
const auto instance = Instance();
|
||||||
|
ASSERT_TRUE(instance.addFunctionBlock("ExampleScalingModule").assigned());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ExampleModuleTest, TestPropCount)
|
||||||
|
{
|
||||||
|
const auto instance = Instance();
|
||||||
|
auto fb = instance.addFunctionBlock("ExampleScalingModule");
|
||||||
|
ASSERT_EQ(fb.getAllProperties().getCount(), 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ExampleModuleTest, TestDataScaling)
|
||||||
|
{
|
||||||
|
const auto instance = Instance();
|
||||||
|
auto fb = instance.addFunctionBlock("ExampleScalingModule");
|
||||||
|
fb.setPropertyValue("Scale", 2);
|
||||||
|
|
||||||
|
auto dataDescriptor = DataDescriptorBuilder().setSampleType(SampleType::Float32).setValueRange(Range(-10, 10)).build();
|
||||||
|
auto signal = SignalWithDescriptor(instance.getContext(), dataDescriptor, nullptr, "Data");
|
||||||
|
|
||||||
|
const auto domainDescriptor = DataDescriptorBuilder()
|
||||||
|
.setSampleType(SampleType::Int64)
|
||||||
|
.setUnit(Unit("s", -1, "seconds", "time"))
|
||||||
|
.setTickResolution(Ratio(1, 1000))
|
||||||
|
.setRule(LinearDataRule(1, 0))
|
||||||
|
.setOrigin("1970-01-01T01:00:00+00:00")
|
||||||
|
.build();
|
||||||
|
auto domainSignal = SignalWithDescriptor(instance.getContext(), domainDescriptor, nullptr, "DomainData");
|
||||||
|
signal.setDomainSignal(domainSignal);
|
||||||
|
|
||||||
|
fb.getInputPorts()[0].connect(signal);
|
||||||
|
auto streamReader = StreamReader(fb.getSignals()[0], SampleType::Float32, SampleType::Int64);
|
||||||
|
|
||||||
|
auto domainPacket = DataPacket(domainDescriptor, 10, 0);
|
||||||
|
auto packet = DataPacketWithDomain(domainPacket, dataDescriptor, 10);
|
||||||
|
float* data = static_cast<float*>(packet.getRawData());
|
||||||
|
for (auto i = 0; i < 10; i++)
|
||||||
|
data[i] = static_cast<float>(i);
|
||||||
|
|
||||||
|
signal.sendPacket(packet);
|
||||||
|
domainSignal.sendPacket(domainPacket);
|
||||||
|
|
||||||
|
SizeT count = 10;
|
||||||
|
std::vector<float> readData;
|
||||||
|
readData.resize(count);
|
||||||
|
|
||||||
|
auto status = streamReader.read(readData.data(), &count);
|
||||||
|
ASSERT_EQ(status.getReadStatus(), ReadStatus::Event);
|
||||||
|
ASSERT_EQ(count, 0);
|
||||||
|
|
||||||
|
while (count < 10)
|
||||||
|
{
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
count = streamReader.getAvailableCount();
|
||||||
|
std::this_thread::sleep_for(100ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_EQ(count, 10);
|
||||||
|
|
||||||
|
status = streamReader.read(readData.data(), &count);
|
||||||
|
ASSERT_EQ(count, 10);
|
||||||
|
ASSERT_EQ(status.getReadStatus(), ReadStatus::Ok);
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
ASSERT_EQ(static_cast<int>(readData[i]), i * 2);
|
||||||
|
}
|
||||||
10
external/CMakeLists.txt
vendored
Normal file
10
external/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
set(CMAKE_FOLDER external)
|
||||||
|
list(APPEND CMAKE_MESSAGE_CONTEXT external)
|
||||||
|
|
||||||
|
if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR})
|
||||||
|
message(FATAL_ERROR "In-source build is not supported!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
add_subdirectory(openDAQ)
|
||||||
12
external/openDAQ/CMakeLists.txt
vendored
Normal file
12
external/openDAQ/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
set(OPENDAQ_ENABLE_TESTS false)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
openDAQ
|
||||||
|
GIT_REPOSITORY https://github.com/openDAQ/openDAQ.git
|
||||||
|
GIT_TAG origin/main
|
||||||
|
GIT_PROGRESS ON
|
||||||
|
SYSTEM
|
||||||
|
FIND_PACKAGE_ARGS 3.11.0 GLOBAL
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(openDAQ)
|
||||||
Reference in New Issue
Block a user