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