diff --git a/.github/actions/framework-download/action.yml b/.github/actions/framework-download/action.yml new file mode 100644 index 0000000..6d34b86 --- /dev/null +++ b/.github/actions/framework-download/action.yml @@ -0,0 +1,50 @@ +name: Download openDAQ framework package +description: "Download a package from S3 for Linux or Windows" + +inputs: + src-opendaq-framework-dev: + required: true + description: "S3 path to the package" + dst-opendaq-framework-dev: + required: false + default: ${{ runner.temp }} + description: "Destination path for downloaded package" + + aws_access_key_id: + required: true + description: "AWS Access Key ID" + aws_secret_access_key: + required: true + description: "AWS Secret Access Key" + aws_region: + required: true + description: "AWS Region" + +runs: + using: composite + steps: + - name: Configure AWS + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ inputs.aws_access_key_id }} + aws-secret-access-key: ${{ inputs.aws_secret_access_key }} + aws-region: ${{ inputs.aws_region }} + + - name: Download package from S3 (Linux/macOS) + if: runner.os != 'Windows' + shell: bash + run: | + set -e + DST="${{ inputs.dst-opendaq-framework-dev }}" + SRC="${{ inputs.src-opendaq-framework-dev }}" + echo "Downloading $SRC to $DST" + aws s3 cp "$SRC" "$DST" + + - name: Download package from S3 (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $dst = "${{ inputs.dst-opendaq-framework-dev }}" + $src = "${{ inputs.src-opendaq-framework-dev }}" + Write-Host "Downloading $src to $dst" + aws s3 cp "$src" "$dst" diff --git a/.github/actions/framework-install/action.yml b/.github/actions/framework-install/action.yml new file mode 100644 index 0000000..6e3e6de --- /dev/null +++ b/.github/actions/framework-install/action.yml @@ -0,0 +1,42 @@ +name: Install openDAQ framework package + +inputs: + opendaq-framework-package-filename: + required: true + + opendaq-framework-package-path: + required: false + default: ${{ runner.temp }} + +runs: + using: composite + + steps: + - name: Install openDAQ framework package (Windows) + if: runner.os == 'Windows' + shell: pwsh + run: | + $packagePath = Join-Path "${{ inputs.opendaq-framework-package-path }}" "${{ inputs.opendaq-framework-package-filename }}" + $process = Start-Process -FilePath $packagePath -ArgumentList "/S" -Wait -NoNewWindow -PassThru + if ($process.ExitCode -eq 0) { + Write-Host "OpenDAQ installed successfully, updating PATH..." + $openDAQBin = "C:\Program Files\openDAQ\bin" + Add-Content -Path $env:GITHUB_ENV -Value "PATH=$openDAQBin`;$env:PATH" + Write-Host $env:PATH + } + else { + Write-Host "OpenDAQ installation failed with exit code $($process.ExitCode)" + exit $process.ExitCode + } + + - name: Install openDAQ framework package (Linux) + if: runner.os == 'Linux' + shell: bash + run: sudo dpkg -i "${{ inputs.opendaq-framework-package-path }}/${{ inputs.opendaq-framework-package-filename }}" + + - name: Unsupported runner OS + if: runner.os != 'Windows' && runner.os != 'Linux' + shell: bash + run: | + echo "Install openDAQ is not supported for ${{ runner.os }}" + exit 1 diff --git a/.github/actions/framework-latest-release/action.yml b/.github/actions/framework-latest-release/action.yml new file mode 100644 index 0000000..c2750b5 --- /dev/null +++ b/.github/actions/framework-latest-release/action.yml @@ -0,0 +1,111 @@ +name: Determine latest openDAQ framework artefact + +inputs: + + opendaq-framework-release-version: + required: false + default: latest + + win32-force: + required: false + default: false + +outputs: + + version: + description: "Latest openDAQ release version" + value: ${{ steps.determine-latest-package.outputs.version }} + + platform: + description: "Detected platform" + value: ${{ steps.determine-latest-package.outputs.platform }} + + packaging: + description: "Package type (deb/exe)" + value: ${{ steps.determine-latest-package.outputs.packaging }} + + artefact: + description: "Artefact filename" + value: ${{ steps.determine-latest-package.outputs.artefact }} + + uri: + description: "Full URI to artefact" + value: ${{ steps.determine-latest-package.outputs.uri }} + + scheme: + description: "Scheme (s3)" + value: ${{ steps.determine-latest-package.outputs.scheme }} + + authority: + description: "Authority (bucket)" + value: ${{ steps.determine-latest-package.outputs.authority }} + + path: + description: "Path inside bucket" + value: ${{ steps.determine-latest-package.outputs.path }} + +runs: + using: composite + steps: + - name: Determine latest openDAQ package + id: determine-latest-package + shell: bash + run: | + set -e + + input_version="${{ inputs.opendaq-framework-release-version }}" + + if [[ -z "$input_version" || "$input_version" == "latest" ]]; then + version=$(gh api repos/openDAQ/openDAQ/releases/latest --jq '.tag_name') + if [[ -z "$version" || "$version" == "null" ]]; then + echo "::error::Failed to determine latest openDAQ release version" + exit 1 + fi + + version=${version#v} + else + version="$input_version" + fi + + platform="" + packaging="" + + if [[ "$RUNNER_OS" == "Linux" ]]; then + arch=$(uname -m) + if [[ "$arch" == "x86_64" ]]; then + platform="ubuntu22.04-x86_64" + elif [[ "$arch" == "aarch64" ]]; then + platform="ubuntu22.04-arm64" + else + echo "::error::Unsupported Linux arch: $arch" + exit 1 + fi + packaging="deb" + + elif [[ "$RUNNER_OS" == "Windows" ]]; then + WIN32_FORCE="${{ inputs.win32-force }}" + if [[ "$WIN32_FORCE" == "true" ]]; then + platform="win32" + else + platform="win64" + fi + packaging="exe" + + else + echo "::error::Unsupported runner OS $RUNNER_OS" + exit 1 + fi + + artefact="opendaq-${version}-${platform}.${packaging}" + scheme="s3" + authority="bb-blueberry-sdk-releases" + sdk="releases/v${version}/SDK" + + echo "version=$version" >> $GITHUB_OUTPUT + echo "platform=$platform" >> $GITHUB_OUTPUT + echo "packaging=$packaging" >> $GITHUB_OUTPUT + echo "artefact=$artefact" >> $GITHUB_OUTPUT + echo "scheme=$scheme" >> $GITHUB_OUTPUT + echo "authority=$authority" >> $GITHUB_OUTPUT + echo "path=$sdk" >> $GITHUB_OUTPUT + echo "uri=${scheme}://${authority}/${sdk}/${artefact}" >> $GITHUB_OUTPUT diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d0dce16 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: Build and Test simple device module with latest openDAQ release + +on: + push: + branches: + - main + + pull_request: + branches: + - main + - jira/* + + workflow_dispatch: + inputs: + + branch: + required: false + default: "main" + + opendaq-framework-version: + required: false + type: string + default: latest + +env: + GH_TOKEN: ${{ github.token }} + +jobs: + build-and-test: + + strategy: + matrix: + include: + - os: ubuntu-latest + generator: Ninja + + - os: windows-latest + generator: "Visual Studio 17 2022" + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout simple device module repo + uses: actions/checkout@v4 + with: + ref: ${{ github.event.inputs.branch || github.event.client_payload.branch || github.ref }} + + - name: Determine openDAQ framework package + id: opendaq-framework + uses: ./.github/actions/framework-latest-release + with: + opendaq-framework-release-version: ${{ github.event.inputs.opendaq-framework-version || 'latest' }} + + - name: Download openDAQ framework + uses: ./.github/actions/framework-download + with: + src-opendaq-framework-dev: ${{ steps.opendaq-framework.outputs.uri }} + dst-opendaq-framework-dev: ${{ runner.temp }}/${{ steps.opendaq-framework.outputs.artefact }} + aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws_region: ${{ secrets.AWS_REGION }} + + - name: Install openDAQ framework package + uses: ./.github/actions/framework-install + with: + opendaq-framework-package-filename: ${{ steps.opendaq-framework.outputs.artefact }} + + - name: Configure simple device module with CMake + run: cmake -B build/output -S . -G "${{ matrix.generator }}" -DEXAMPLE_MODULE_ENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Release + + - name: Build simple device module with CMake + run: cmake --build build/output --config Release + + - name: Run simple device module tests with CMake + run: ctest --test-dir build/output --output-on-failure -C Release -V diff --git a/CMakeLists.txt b/CMakeLists.txt index c046522..a631fc1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,23 @@ cmake_minimum_required(VERSION 3.25) -set(REPO_NAME example_device_module) +project(SimpleDeviceModule VERSION 1.0.0) + +set(SIMPLE_DEVICE_MODULE_OPENDAQ_SDK_VER "" CACHE STRING "Version of openDAQ SDK to build SimpleDeviceModule with") + set(REPO_OPTION_PREFIX EXAMPLE_MODULE) +set(SDK_TARGET_NAMESPACE daq) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_MESSAGE_CONTEXT_SHOW ON CACHE BOOL "Show CMake message context") + +option(EXAMPLE_MODULE_ENABLE_TESTS "Enable building tests for example_module" ON) +option(SIMPLE_DEVICE_MODULE_ENABLE_SERVER_APP "Enable building example server application" OFF) +if(SIMPLE_DEVICE_MODULE_ENABLE_SERVER_APP) + set(DAQMODULES_OPENDAQ_SERVER_MODULE ON CACHE BOOL "" FORCE) + set(OPENDAQ_ENABLE_NATIVE_STREAMING ON CACHE BOOL "" FORCE) +endif() -project(${REPO_NAME} VERSION 1.0.0) +list(APPEND CMAKE_MESSAGE_CONTEXT ${PROJECT_NAME}) +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") if (POLICY CMP0135) cmake_policy(SET CMP0135 NEW) @@ -12,29 +27,21 @@ 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_DEVICE_EXAMPLE_ENABLE_SERVER_APP "Enable building example server application" OFF) - include(CommonUtils) setup_repo(${REPO_OPTION_PREFIX}) -if(OPENDAQ_DEVICE_EXAMPLE_ENABLE_SERVER_APP) - set(DAQMODULES_OPENDAQ_SERVER_MODULE ON CACHE BOOL "" FORCE) - set(OPENDAQ_ENABLE_NATIVE_STREAMING ON CACHE BOOL "" FORCE) +if (EXAMPLE_MODULE_ENABLE_TESTS) + message(STATUS "Unit tests are ENABLED") + enable_testing() endif() add_subdirectory(external) add_subdirectory(example_module) -if(OPENDAQ_DEVICE_EXAMPLE_ENABLE_SERVER_APP) - message(STATUS "Enbled example server application") +if(SIMPLE_DEVICE_MODULE_ENABLE_SERVER_APP) + message(STATUS "Enbled example server application") add_subdirectory(server_application) endif() @@ -56,4 +63,4 @@ elseif (UNIX AND NOT APPLE) endif() # Include CPack for packaging -include(CPack) \ No newline at end of file +include(CPack) diff --git a/example_module/CMakeLists.txt b/example_module/CMakeLists.txt index 6338c6d..a5f2808 100644 --- a/example_module/CMakeLists.txt +++ b/example_module/CMakeLists.txt @@ -3,6 +3,6 @@ project(ExampleModule VERSION 1.0.0 LANGUAGES CXX) add_subdirectory(src) -if (OPENDAQ_ENABLE_TESTS) +if (EXAMPLE_MODULE_ENABLE_TESTS) add_subdirectory(tests) endif() diff --git a/example_module/src/CMakeLists.txt b/example_module/src/CMakeLists.txt index 0726437..b642274 100644 --- a/example_module/src/CMakeLists.txt +++ b/example_module/src/CMakeLists.txt @@ -1,5 +1,6 @@ set(LIB_NAME example_module) set(MODULE_HEADERS_DIR ../include/${TARGET_FOLDER_NAME}) +list(APPEND CMAKE_MESSAGE_CONTEXT ${LIB_NAME}) set(SRC_Include common.h module_dll.h @@ -37,6 +38,7 @@ if (MSVC) target_compile_options(${LIB_NAME} PRIVATE /bigobj) endif() +find_package(openDAQ ${SIMPLE_DEVICE_MODULE_OPENDAQ_SDK_VER} REQUIRED) target_link_libraries(${LIB_NAME} PUBLIC daq::opendaq ) diff --git a/example_module/tests/CMakeLists.txt b/example_module/tests/CMakeLists.txt index 7265fbc..38fce64 100644 --- a/example_module/tests/CMakeLists.txt +++ b/example_module/tests/CMakeLists.txt @@ -1,5 +1,6 @@ set(MODULE_NAME example_module) set(TEST_APP test_${MODULE_NAME}) +list(APPEND CMAKE_MESSAGE_CONTEXT ${TEST_APP}) set(TEST_SOURCES test_example_module.cpp test_app.cpp @@ -8,11 +9,33 @@ set(TEST_SOURCES test_example_module.cpp add_executable(${TEST_APP} ${TEST_SOURCES} ) -target_link_libraries(${TEST_APP} PRIVATE daq::test_utils +find_package(openDAQ ${SIMPLE_DEVICE_MODULE_OPENDAQ_SDK_VER} REQUIRED) +target_link_libraries(${TEST_APP} PRIVATE daq::opendaq ${SDK_TARGET_NAMESPACE}::${MODULE_NAME} ) +if (NOT TARGET gtest) + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip + ) + set(BUILD_GMOCK ON CACHE BOOL "" FORCE) + set(BUILD_GTEST ON CACHE BOOL "" FORCE) + FetchContent_MakeAvailable(googletest) +endif() + +target_link_libraries(${TEST_APP} + PRIVATE + GTest::gtest + GTest::gtest_main + GTest::gmock + GTest::gmock_main +) + add_test(NAME ${TEST_APP} COMMAND $ - WORKING_DIRECTORY bin + WORKING_DIRECTORY $ ) + +set_target_properties(${TEST_APP} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY $) diff --git a/example_module/tests/test_app.cpp b/example_module/tests/test_app.cpp index 4f6ea7d..37ad2b1 100644 --- a/example_module/tests/test_app.cpp +++ b/example_module/tests/test_app.cpp @@ -1,20 +1,10 @@ -#include -#include +#include #include -#include -#include - 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; diff --git a/example_module/tests/test_example_module.cpp b/example_module/tests/test_example_module.cpp index 2847362..751e5dd 100644 --- a/example_module/tests/test_example_module.cpp +++ b/example_module/tests/test_example_module.cpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -19,8 +18,6 @@ using testing::SizeIs; using namespace daq; using namespace std::chrono_literals; -using ExampleChannel = modules::example_module::ExampleChannel; -using ExampleDevice = modules::example_module::ExampleDevice; using ExampleModuleTest = testing::Test; static ModulePtr CreateModule() @@ -153,10 +150,9 @@ TEST_F(ExampleModuleTest, GetDeviceType) DictPtr deviceTypes; ASSERT_NO_THROW(deviceTypes = moduleLib.getAvailableDeviceTypes()); - DeviceTypePtr expected = ExampleDevice::CreateType(); - StringPtr key = expected.getId(); + StringPtr key = "example_dev"; ASSERT_TRUE(deviceTypes.hasKey(key)); - ASSERT_EQ(deviceTypes.get(key).getId(), expected.getId()); + ASSERT_EQ(deviceTypes.get(key).getId(), key); } TEST_F(ExampleModuleTest, GetValueSignals) @@ -250,9 +246,9 @@ TEST_F(ExampleModuleTest, GetDomainSignal) RatioPtr tickResolution; ASSERT_NO_THROW(tickResolution = domainDescriptor.getTickResolution()) << id << " get domain signal tick resolution failed"; - ASSERT_TRUE(tickResolution.assigned()) << "[" + ASSERT_TRUE(tickResolution.assigned()) << id << " domain signal tick resolution is not assigned"; - ASSERT_EQ(tickResolution, ExampleChannel::getResolution()) + ASSERT_EQ(tickResolution, Ratio(1, 1000000)) << id << " domain signal tick resolution mismatches"; DataRulePtr dataRule; @@ -269,9 +265,6 @@ TEST_F(ExampleModuleTest, GetDomainSignal) StringPtr keyStart = "start"; ASSERT_EQ(dataRule.getParameters().get(keyStart), 0) << id << " domain signal data rule \"" << keyStart << "\" parameter mismatches"; - - ASSERT_EQ(domainDescriptor.getOrigin(), ExampleChannel::getEpoch()) - << id << " domain signal origin mismatches"; } } diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index c6ccc9d..1418c82 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -5,6 +5,7 @@ if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) message(FATAL_ERROR "In-source build is not supported!") endif() -include(FetchContent) - -add_subdirectory(openDAQ) +find_package(openDAQ ${SIMPLE_DEVICE_MODULE_OPENDAQ_SDK_VER}) +if (NOT openDAQ_FOUND) + add_subdirectory(openDAQ) +endif() diff --git a/external/openDAQ/CMakeLists.txt b/external/openDAQ/CMakeLists.txt index 85ee031..4f8c4ff 100644 --- a/external/openDAQ/CMakeLists.txt +++ b/external/openDAQ/CMakeLists.txt @@ -1,12 +1,18 @@ set(OPENDAQ_ENABLE_TESTS false) +if(NOT SIMPLE_DEVICE_MODULE_OPENDAQ_SDK_VER) + message(FATAL_ERROR "SIMPLE_DEVICE_MODULE_OPENDAQ_SDK_VER is not set") +endif() + +include(FetchContent) + FetchContent_Declare( openDAQ GIT_REPOSITORY https://github.com/openDAQ/openDAQ.git - GIT_TAG v3.20.1 + GIT_TAG v${SIMPLE_DEVICE_MODULE_OPENDAQ_SDK_VER} GIT_PROGRESS ON - SYSTEM - FIND_PACKAGE_ARGS 3.20.1 GLOBAL + SYSTEM + FIND_PACKAGE_ARGS ${SIMPLE_DEVICE_MODULE_OPENDAQ_SDK_VER} GLOBAL ) FetchContent_MakeAvailable(openDAQ) diff --git a/server_application/CMakeLists.txt b/server_application/CMakeLists.txt index c9a7a15..41db6de 100644 --- a/server_application/CMakeLists.txt +++ b/server_application/CMakeLists.txt @@ -1,3 +1,6 @@ +set(SERVER_APP_NAME test_${MODULE_NAME}) +list(APPEND CMAKE_MESSAGE_CONTEXT ${SERVER_APP_NAME}) + add_executable(server_example main.cpp) target_link_libraries(server_example PRIVATE daq::opendaq) @@ -5,4 +8,4 @@ target_link_libraries(server_example PRIVATE daq::opendaq) add_dependencies( example_module daq::native_stream_srv_module -) \ No newline at end of file +)