diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000000..170147d531 --- /dev/null +++ b/.clang-format @@ -0,0 +1,120 @@ +--- +BasedOnStyle: LLVM +--- +Language: Cpp +AccessModifierOffset: -8 +AlignAfterOpenBracket: true +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Right +AlignOperands: false +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: true +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Mozilla +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: true +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 120 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Regroup +IncludeCategories: + - Regex: '^"boost' + Priority: 6 + - Regex: '^ +``` + +> [!NOTE] +> Prefixing branch name: +> - dev_ for branches starting from dev +> - ci_ for ci work +> - exp_ for experimental work – will never be merged back +> - v - for releases and release candidates +> +> Example: +> ```bash +> git checkout -b dev_cursor_refactor origin/dev +> ``` + +- Implement feature – commit and push work as needed – refer to commit message structure +- Apply code styling - (usually done by running `./tools/format.sh`) +- Run license header completion - (usually done by running `./tools/license.sh`) +- Resynchronize with latest changes to starting point and push again. Example +```bash +git fetch +git rebase origin/dev +git push +``` +- Create pull request +- Make sure CI tests pass. Assign reviewers. +- Discuss review, and implement changes as needed. Try to refrain from force pushing at this point as it makes everyones job a little harder. When finished, all the commits can be squashed/reorganized as needed +- Make sure PR is synchronized with starting point. Test feature before merge. +- Close PR - use only `Rebase and merge` or `Squash and merge`. Do not use Create merge commit + +# Git commit message structure + +Some resources: +- https://cbea.ms/git-commit/ +- https://github.blog/2022-06-30-write-better-commits-build-better-projects/ +- https://dhwthompson.com/2019/my-favourite-git-commit + +A commit message should have the following structure: + +```text +prefix/subprefix: short description (max 80 char) + +longer description that explains why the change is needed as well as some +subtleties of the change. Can refer to other commit SHA as well as github +issues + +Signed-off by: author.name +``` + +Refer to project specific documentation for module names and description + +Some possible prefixes: + +| Module name | Description | +|-------------|-----------------------------------------------------------------------------------------------------------| +| project | A change in project structure – nonfunctional changes | +| tree | Change that applies to many files in the project – such as styling, addressing a warning, etc | +| cmake | Changes related to general project cmake or cmake modules. (assuming cmake is the project config tool) | +| ci | CI and deployment | +| doc | Add documentation or documentation infrastructure | +| tests | Add tests or testing infrastructure | +| toolsTools | related to the project | +| general | A change that affects multiple parts of Scopy – should not be used unless other module names do not apply | +| \ | Project specific module | + + +# Default Branches + +| Branch name | Description | +|--------------------|--------------------------------------------------------------------------------------------------------------------| +| dev | Contains next major features of the project – contains multiple features bundled together. Will be merged to main. | +| main | main branch – used to integrate changes and create releases. Locked – history cannot be overwritten | +| maint-1.5 | maintenance branch for the projectv1.5 – bug fixes for scopy 1.5 are created here. Used to create v1.5.x releases | +| v[-rc] | Version branch of the project. Versioned releases and release candidates. Locked. | + +# Review process +After creating a pull request, the developer should ask reviewers to review their code. Ideally the pull request +should contain only one feature/bug which can be easily understood by the reviewer. + +When reviewing keep a few things in mind + +- Does the pull request pass automated tests/ styling checks/ etc. ? +- Is the intent clear ? +- Is the solution directly addressing the problem ? +- Is the solution clear ? +- Is the code implementing the solution clear ? Can it be simplified ? +- Does the code introduce any maintenance effort ? +- Are there any side effects to the solution ? +- Can the solution be tested ? +- Can the solution be automatically tested ? Is the automatic test implemented ? +- Are code commits structured nicely ? Commits shouldn't be too big, each commit should address a single change in the code. There should be no " + diff --git a/.github/workflows/android-build b/.github/workflows/android-build new file mode 100644 index 0000000000..08f2e7296f --- /dev/null +++ b/.github/workflows/android-build @@ -0,0 +1,46 @@ +name: android-apk build + +on: [push, pull_request] + +env: + BUILD_HOST: ubuntu-latest + USERNAME: github-actions + +jobs: + + make-apk: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Pull the Docker Image + run: docker pull analogdevices/scopy-build:1.4.1-android + - name: Run Docker Image + run: | + $GITHUB_WORKSPACE/CI/appveyor/gen_ci_envs.sh > $GITHUB_WORKSPACE/CI/appveyor/gh-actions.envs + docker run --privileged --net=host \ + -v `pwd`:$GITHUB_WORKSPACE:rw \ + -e "BRANCH=${GITHUB_REF#refs/heads/}" \ + --env-file $GITHUB_WORKSPACE/CI/appveyor/gh-actions.envs \ + analogdevices/scopy-build:1.4.1-android /bin/bash -xe $GITHUB_WORKSPACE/CI/appveyor/build_scopy_apk.sh + - uses: actions/upload-artifact@v2 + with: + name: scopy-android + path: | + ${{ github.workspace }}/*.apk + ${{ github.workspace }}/*.aab + ${{ github.workspace }}/scopy*android-native-symbols.zip + + + - name: Upload master apk and aab builds to continous prerelease + if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} + run: | + ARTIFACTS_ARCHIVE=scopy-${GITHUB_SHA::7}-android.zip + zip ${ARTIFACTS_ARCHIVE} *.apk *.aab + + wget https://github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_linux_amd64.tar.gz + tar xvf ghr_v0.13.0_linux_amd64.tar.gz + + ghr_v0.13.0_linux_amd64/ghr -u ${{ github.repository_owner }} -r scopy -name "Continuous build" -b "Latest succesful master build " -prerelease -debug -replace continous ${ARTIFACTS_ARCHIVE} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml deleted file mode 100644 index 31f70bdb07..0000000000 --- a/.github/workflows/android-build.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: android-apk build - -on: [push, pull_request] - -env: - BUILD_HOST: ubuntu-latest - USERNAME: github-actions - -jobs: - - make-apk: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - name: Pull the Docker Image - run: docker pull astanea/scopy1-android:1.5.0 - - name: Run Docker Image - run: | - $GITHUB_WORKSPACE/CI/appveyor/gen_ci_envs.sh > $GITHUB_WORKSPACE/CI/appveyor/gh-actions.envs - docker run --privileged --net=host \ - -v $(pwd):$GITHUB_WORKSPACE:rw \ - -e "BRANCH=${GITHUB_REF#refs/heads/}" \ - --env-file $GITHUB_WORKSPACE/CI/appveyor/gh-actions.envs \ - astanea/scopy1-android:1.5.0 /bin/bash -xe $GITHUB_WORKSPACE/CI/appveyor/build_scopy_apk.sh - - uses: actions/upload-artifact@v3 - with: - name: scopy-android - path: | - ${{ github.workspace }}/*.apk - ${{ github.workspace }}/*.aab - ${{ github.workspace }}/scopy*android-native-symbols.zip - - - - name: Upload master apk and aab builds to continous prerelease - if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }} - run: | - ARTIFACTS_ARCHIVE=scopy-${GITHUB_SHA::7}-android.zip - zip ${ARTIFACTS_ARCHIVE} *.apk *.aab - - wget https://github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_linux_amd64.tar.gz - tar xvf ghr_v0.13.0_linux_amd64.tar.gz - - ghr_v0.13.0_linux_amd64/ghr -u ${{ github.repository_owner }} -r scopy -name "Continuous build" -b "Latest succesful master build " -prerelease -debug -replace continous ${ARTIFACTS_ARCHIVE} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/appimage-arm64.yml b/.github/workflows/appimage-arm64.yml new file mode 100644 index 0000000000..c134fd867d --- /dev/null +++ b/.github/workflows/appimage-arm64.yml @@ -0,0 +1,46 @@ +name: Scopy arm64 AppImage Build + +on: [push, pull_request] + +env: + BUILD_HOST: ubuntu-20.04 + USERNAME: github-actions + +jobs: + + build_scopy_arm64_appimage: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v4 + with: + set-safe-directory: 'true' + + - name: Pull the Docker Image + run: docker pull cristianbindea/scopy2-arm64-appimage:latest + + - name: Create Scopy AppImage + shell: bash + run: | + cd $GITHUB_WORKSPACE + sudo apt update + ./ci/arm/create_sysroot.sh arm64 install_packages install_qemu + ./ci/arm/arm_build_process.sh arm64 generate_ci_envs + + docker run \ + --mount type=bind,source="$GITHUB_WORKSPACE",target=/home/runner/scopy \ + --env-file $GITHUB_WORKSPACE/ci/general/gh-actions.envs \ + cristianbindea/scopy2-arm64-appimage:latest \ + /bin/bash -c 'cd $HOME && \ + sudo chown -R runner:runner scopy && \ + cd $HOME/scopy && \ + ./ci/arm/arm_build_process.sh arm64 install_packages move_tools move_sysroot build_scopy build_iio-emu create_appdir create_appimage move_appimage + ' + - name: Set short git commit SHA + shell: bash + run: echo "commit_sha=$(git rev-parse --short ${{ github.sha }})" >> "$GITHUB_ENV" + + - uses: actions/upload-artifact@v4 + with: + name: scopy-linux-arm64-${{ env.commit_sha }} + path: ${{ github.workspace }}/Scopy-arm64.AppImage diff --git a/.github/workflows/appimage-armhf.yml b/.github/workflows/appimage-armhf.yml index 05a6b0fff1..d729ada587 100644 --- a/.github/workflows/appimage-armhf.yml +++ b/.github/workflows/appimage-armhf.yml @@ -1,4 +1,4 @@ -name: Scopy1 armhf Build +name: Scopy armhf AppImage Build on: [push, pull_request] @@ -8,7 +8,7 @@ env: jobs: - build_scopy1_armhf: + build_scopy_armhf_appimage: runs-on: ubuntu-20.04 steps: @@ -17,37 +17,30 @@ jobs: set-safe-directory: 'true' - name: Pull the Docker Image - run: docker pull astanea/scopy1-armhf-appimage:1.5.0 + run: docker pull cristianbindea/scopy2-armhf-appimage:latest - - name: Create Scopy armhf AppImage + - name: Create Scopy AppImage shell: bash run: | cd $GITHUB_WORKSPACE sudo apt update - ./CI/armhf/create_sysroot.sh install_packages install_qemu - ./CI/armhf/armhf_build_process.sh generate_ci_envs + ./ci/arm/create_sysroot.sh arm32 install_packages install_qemu + ./ci/arm/arm_build_process.sh arm32 generate_ci_envs docker run \ --mount type=bind,source="$GITHUB_WORKSPACE",target=/home/runner/scopy \ - --env-file $GITHUB_WORKSPACE/CI/armhf/gh-actions.envs \ - astanea/scopy1-armhf-appimage:1.5.0 \ + --env-file $GITHUB_WORKSPACE/ci/general/gh-actions.envs \ + cristianbindea/scopy2-armhf-appimage:latest \ /bin/bash -c 'cd $HOME && \ sudo chown -R runner:runner scopy && \ cd $HOME/scopy && \ - ./CI/armhf/armhf_build_process.sh run_workflow + ./ci/arm/arm_build_process.sh arm32 install_packages move_tools move_sysroot build_scopy build_iio-emu create_appdir create_appimage move_appimage ' - name: Set short git commit SHA shell: bash run: echo "commit_sha=$(git rev-parse --short ${{ github.sha }})" >> "$GITHUB_ENV" - # DEBUG WITH SSH - # - name: Setup upterm session - # uses: lhotari/action-upterm@v1 - # if: ${{ failure() }} - # with: - # wait-timeout-minutes: 3 - - uses: actions/upload-artifact@v4 with: name: scopy-linux-armhf-${{ env.commit_sha }} - path: ${{ github.workspace }}/Scopy1-armhf.AppImage + path: ${{ github.workspace }}/Scopy-armhf.AppImage diff --git a/.github/workflows/appimage-x86_64.yml b/.github/workflows/appimage-x86_64.yml index 38e0d20b54..bbc2ed29fc 100644 --- a/.github/workflows/appimage-x86_64.yml +++ b/.github/workflows/appimage-x86_64.yml @@ -1,4 +1,4 @@ -name: Scopy1 x86_64 AppImage Build +name: Scopy x86_64 AppImage Build on: [push, pull_request] @@ -8,38 +8,32 @@ env: jobs: - build_scopy1_x86-64_appimage: + build_scopy_x86-64_appimage: runs-on: ubuntu-20.04 + container: + image: cristianbindea/scopy2-x86_64-appimage:latest + options: --user root steps: - uses: actions/checkout@v4 with: set-safe-directory: 'true' - - name: Pull the Docker Image - run: docker pull astanea/scopy1-x86_64-appimage:1.5.0 - - name: Create Scopy AppImage shell: bash run: | export CI_SCRIPT=ON cd $GITHUB_WORKSPACE - ./CI/x86_64/x86-64_appimage_process.sh generate_ci_envs - - docker run \ - --mount type=bind,source="$GITHUB_WORKSPACE",target=/home/runner/scopy \ - --env-file $GITHUB_WORKSPACE/CI/x86_64/gh-actions.envs \ - astanea/scopy1-x86_64-appimage:1.5.0 \ - /bin/bash -c 'cd $HOME && \ - sudo chown -R runner:runner scopy && \ - cd $HOME/scopy && \ - ./CI/x86_64/x86-64_appimage_process.sh download_tools build_iio-emu build_scopy create_appdir create_appimage move_appimage - ' + ./ci/x86_64/x86-64_appimage_process.sh run_workflow + - name: Set short git commit SHA shell: bash - run: echo "commit_sha=$(git rev-parse --short ${{ github.sha }})" >> "$GITHUB_ENV" + run: | + cd $GITHUB_WORKSPACE + git config --global --add safe.directory $GITHUB_WORKSPACE + echo "commit_sha=$(git rev-parse --short ${{ github.sha }})" >> "$GITHUB_ENV" - uses: actions/upload-artifact@v4 with: name: scopy-linux-x86_64-${{ env.commit_sha }} - path: ${{ github.workspace }}/Scopy1-x86_64.AppImage + path: ${{ github.workspace }}/Scopy-x86_64.AppImage diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml new file mode 100644 index 0000000000..64bdaef4fc --- /dev/null +++ b/.github/workflows/clang-format.yml @@ -0,0 +1,32 @@ +name: style-check + +on: [push, pull_request] + +env: + BUILD_HOST: ubuntu-22.04 + USERNAME: github-actions + +jobs: + check-cpp-formatting: + name: Check C++ Formatting + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + - uses: DoozyX/clang-format-lint-action@v0.16 + with: + source: '.' + exclude: './ci/flatpak' + extensions: 'h,hpp,cpp,cc,cc.in' + clangFormatVersion: 12 + + check-cmake-formatting: + name: Check CMakeLists Formatting + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + - uses: PuneetMatharu/cmake-format-lint-action@v1.0.4 + with: + args: --config-files .cmake-format --check + diff --git a/.github/workflows/generate_doc.yml b/.github/workflows/generate_doc.yml new file mode 100644 index 0000000000..d41cee8eaf --- /dev/null +++ b/.github/workflows/generate_doc.yml @@ -0,0 +1,83 @@ +name: Generate documentation + +on: + push: + branches: + - dev + pull_request: + +jobs: + build-doc: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.x" + + - name: Install pip packages + working-directory: docs + run: | + pip install pip --upgrade + pip install -r requirements.txt + + - name: Build doc + working-directory: docs + run: | + make html SPHINXOPTS='-W --keep-going' + + - name: Store the generated doc + uses: actions/upload-artifact@v4 + with: + name: html + path: docs/_build/html + + deploy-doc: + runs-on: ubuntu-latest + needs: build-doc + if: github.ref == 'refs/heads/dev' + + steps: + - run: | + git config --global user.name "${{ github.event.head_commit.committer.name }}" + git config --global user.email "${{ github.event.head_commit.committer.email }}" + + - uses: actions/checkout@v4 + - name: Create gh-pages branch + run: > + git ls-remote --exit-code --heads origin refs/heads/gh-pages || + ( + git reset --hard ; + git clean -fdx ; + git checkout --orphan gh-pages ; + git reset --hard; + git commit -m "empty" --allow-empty ; + git push origin gh-pages:gh-pages + ) + + - uses: actions/checkout@v4 + with: + ref: 'gh-pages' + + - name: Empty gh-pages + run: | + git rm -r . --quiet || true + + - uses: actions/download-artifact@v4 + with: + name: html + + - name: Patch doc build + run: | + rm -r _sources + touch .nojekyll + + - name: Commit gh-pages + run: | + git add . >> /dev/null + git commit -m "deploy: ${GITHUB_SHA}" --allow-empty + + - name: Push to gh-pages + run: | + git push origin gh-pages:gh-pages \ No newline at end of file diff --git a/.github/workflows/license-header.yml b/.github/workflows/license-header.yml new file mode 100644 index 0000000000..1f1500ef7f --- /dev/null +++ b/.github/workflows/license-header.yml @@ -0,0 +1,24 @@ +name: license-headers + +on: [push, pull_request] + +env: + BUILD_HOST: ubuntu-22.04 + USERNAME: github-actions + +jobs: + check-license-headers: + runs-on: ubuntu-latest + timeout-minutes: 15 + name: Validate Scopy GPL License + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + set-safe-directory: 'true' + - name: Run License Scanner Utility + shell: bash + run: | + export CI_SCRIPT=ON + cd $GITHUB_WORKSPACE + ./tools/license.sh diff --git a/.github/workflows/linuxflatpakbuild.yml b/.github/workflows/linuxflatpakbuild.yml index 0b3a1f3d8b..f909cec664 100644 --- a/.github/workflows/linuxflatpakbuild.yml +++ b/.github/workflows/linuxflatpakbuild.yml @@ -12,21 +12,27 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Pull the Docker Image - run: docker pull astanea/scopy1-flatpak:1.5.0 + run: docker pull cristianbindea/scopy2-flatpak:latest - name: Run Docker Image run: | - $GITHUB_WORKSPACE/CI/appveyor/gen_ci_envs.sh > $GITHUB_WORKSPACE/CI/appveyor/gh-actions.envs + $GITHUB_WORKSPACE/ci/general/gen_ci_envs.sh > $GITHUB_WORKSPACE/ci/general/gh-actions.envs docker run --privileged \ - -v `pwd`:$GITHUB_WORKSPACE:rw \ - --env-file $GITHUB_WORKSPACE/CI/appveyor/gh-actions.envs \ - astanea/scopy1-flatpak:1.5.0 /bin/bash -xe $GITHUB_WORKSPACE/CI/appveyor/inside_flatpak_docker.sh + -e CI_SCRIPT='ON' \ + --mount type=bind,source="$GITHUB_WORKSPACE",target=$GITHUB_WORKSPACE \ + --env-file $GITHUB_WORKSPACE/ci/general/gh-actions.envs \ + cristianbindea/scopy2-flatpak:latest \ + /bin/bash -c 'sudo chown -R runner:runner $GITHUB_WORKSPACE && $GITHUB_WORKSPACE/ci/flatpak/flatpak_build_process.sh' - - uses: actions/upload-artifact@v3 + - name: Set short git commit SHA + shell: bash + run: echo "commit_sha=$(git rev-parse --short ${{ github.sha }})" >> "$GITHUB_ENV" + + - uses: actions/upload-artifact@v4 with: - name: Scopy.flatpak + name: scopy-flatpak-${{ env.commit_sha }} path: ${{ github.workspace }}/Scopy.flatpak - name: Upload master flatpak build to continous prerelease diff --git a/.github/workflows/mingwbuild.yml b/.github/workflows/mingwbuild.yml index ab41ba7074..fbfc8b0671 100644 --- a/.github/workflows/mingwbuild.yml +++ b/.github/workflows/mingwbuild.yml @@ -8,10 +8,10 @@ jobs: runs-on: windows-2019 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Pull the Docker Image - run: docker pull astanea/scopy1-mingw64:1.5.0 - + run: docker pull cristianbindea/scopy2-mingw64:latest + - name: Run Docker Image shell: cmd run: | @@ -29,6 +29,7 @@ jobs: docker run ^ -v %GITHUB_WORKSPACE%:C:\msys64\home\docker\scopy:rw ^ + -v %GITHUB_WORKSPACE%\artifacts:C:\msys64\home\docker\artifact_x86_64:rw ^ -e GITHUB_WORKSPACE=%GITHUB_WORKSPACE% ^ -e BUILD_HOST=%BUILD_HOST% ^ -e USERNAME=%USERNAME% ^ @@ -42,28 +43,31 @@ jobs: -e GITHUB_RUN_ID=%GITHUB_RUN_ID% ^ -e GITHUB_RUN_NUMBER=%GITHUB_RUN_NUMBER% ^ -e RUNNER_ARCH=%RUNNER_ARCH% ^ - astanea/scopy1-mingw64:1.5.0 C:\msys64\usr\bin\bash.exe -c '/home/docker/scopy/CI/appveyor/inside_mingw_docker.sh' + -e CI_SCRIPT=ON ^ + cristianbindea/scopy2-mingw64:latest C:\msys64\usr\bin\bash.exe -c '/home/docker/scopy/ci/windows/build_and_create_installer.sh run_workflow' - set "ARTIFACT_PATH=C:\msys64\home\docker\artifact_x86_64" - docker ps -l -q > lastcontainer - set /p LAST_CONTAINER=> "$GITHUB_ENV" - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: scopy-x86_64 - path: ${{ github.workspace }}\artifacts\scopy-x86_64 + name: scopy-windows-x86_64-${{ env.commit_sha }} + path: ${{ github.workspace }}\artifacts\scopy-x86_64.zip + compression-level: 0 - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: debug-x86_64 - path: ${{ github.workspace }}\artifacts\debug-x86_64 - - uses: actions/upload-artifact@v3 + name: debug-windows-x86_64-${{ env.commit_sha }} + path: ${{ github.workspace }}\artifacts\debug-x86_64.zip + compression-level: 0 + + - uses: actions/upload-artifact@v4 with: - name: scopy-x86_64-setup - path: ${{ github.workspace }}\artifacts\scopy-64-setup.exe + name: scopy-windows-x86_64-setup-${{ env.commit_sha }} + path: ${{ github.workspace }}\artifacts\scopy-64-setup.zip + compression-level: 0 + # Debug session # - name: Start SSH session diff --git a/.github/workflows/ubuntu20build.yml b/.github/workflows/ubuntu20build.yml new file mode 100644 index 0000000000..73796ade93 --- /dev/null +++ b/.github/workflows/ubuntu20build.yml @@ -0,0 +1,21 @@ +name: Ubuntu 20 Scopy Build + +on: [push, pull_request] + +env: + BUILD_HOST: ubuntu-20.04 + USERNAME: github-actions + +jobs: + + build_scopy_on_ubuntu20: + runs-on: ubuntu-20.04 + container: + image: cristianbindea/scopy2-ubuntu20:latest + options: --user root + steps: + - uses: actions/checkout@v4 + - name: Build Scopy + run: | + /bin/bash -c "git config --global --add safe.directory $GITHUB_WORKSPACE" + /bin/bash -c "export CI_SCRIPT=ON && $GITHUB_WORKSPACE/ci/ubuntu/ubuntu_build_process.sh build_scopy" \ No newline at end of file diff --git a/.github/workflows/ubuntu22build.yml b/.github/workflows/ubuntu22build.yml new file mode 100644 index 0000000000..f74dce7f08 --- /dev/null +++ b/.github/workflows/ubuntu22build.yml @@ -0,0 +1,40 @@ +name: Ubuntu 22 Scopy Build + +on: [push, pull_request] + +env: + BUILD_HOST: ubuntu-22.04 + USERNAME: github-actions + +jobs: + + build_scopy_on_ubuntu22: + runs-on: ubuntu-22.04 + container: + image: cristianbindea/scopy2-ubuntu22:latest + options: --user root + steps: + - uses: actions/checkout@v4 + - name: Build Scopy + run: | + git config --global --add safe.directory $GITHUB_WORKSPACE + export CI_SCRIPT=ON && $GITHUB_WORKSPACE/ci/ubuntu/ubuntu_build_process.sh build_scopy + + - name: Run Tests + run: | + sudo ldconfig + cd $GITHUB_WORKSPACE/build + QT_QPA_PLATFORM=offscreen ctest -V --output-on-failure --output-junit $GITHUB_WORKSPACE/test-result-ubuntu22.xml + + - name: Report + uses: mikepenz/action-junit-report@v4 + if: always() + with: + job_name: Verify Results + check_name: 'Scopy Test' + check_title_template: '{{TEST_NAME}}' + report_paths: '${{github.workspace}}/test-result-ubuntu22.xml' + detailed_summary: 'true' + fail_on_failure: 'true' + require_tests: 'true' + include_passed: 'true' \ No newline at end of file diff --git a/.gitignore b/.gitignore index fdbf2bd61f..a90b0c196c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,21 +9,65 @@ ui_*.h build* deps scopy +iio-emu android/assets/libsigrokdecode/* android/assets/python3.*/* +resources/scopy_osp.html resources/stylesheets/default.qss resources/stylesheets/light.qss +resources/credits.html +resources/scopy_home.html +resources/about.html +*qt.conf !android/src/org/adi/scopy *.swp +*.DS_Store android-build android*.sh *.apk *.aab !CI/android/* -CI/armhf/staging -CI/armhf/scopy.AppDir -CI/armhf/scopy.squashfs -CI/x86_64/staging -CI/x86_64/scopy.AppDir html/ build_arm64-v8a/ +!doc/Doxyfile +!doc/local-windows-build-readme.md +.cache/* +build/* +.vscode/* +ci/general/gh-actions.envs +ci/ubuntu/staging +ci/macOS/staging +ci/arm/staging +ci/arm/docker/sysroot* +ci/arm/scopy.AppDir +ci/arm/staging +ci/arm/scopy.AppDir +ci/arm/scopy.squashfs +ci/arm/squashfs-root +ci/arm/sysroot +ci/x86_64/staging +ci/x86_64/scopy.AppDir +ci/windows/staging +windows/* +core/include/scopy-core_config.h +core/include/scopy-core_export.h +core/include/core/scopy-core_config.h +core/include/core/scopy-core_export.h +common/include/common/scopy-common_config.h +common/include/common/scopy-common_export.h +gr-util/include/gr-util/scopy-gr-util_export.h +gui/include/gui/scopy-gui_export.h +gui/include/gui/scopy-gui_config.h +iio-widgets/include/iio-widgets/scopy-iio-widgets_export.h +iioutil/include/iioutil/scopy-iioutil_config.h +iioutil/include/iioutil/scopy-iioutil_export.h +pluginbase/include/pluginbase/scopy-pluginbase_config.h +pluginbase/include/pluginbase/scopy-pluginbase_export.h +bin/* +lib/* +docs/_build/* +share/* +gui/include/gui/style_properties.h +gui/include/gui/style_attributes.h +*style_properties.h +*style_attributes.h diff --git a/.gitmodules b/.gitmodules index 96cb6dc2ca..3e0838dc08 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "iio-emu"] - path = iio-emu - url = https://github.com/analogdevicesinc/iio-emu.git +[submodule "ci/flatpak/shared-modules"] + path = ci/flatpak/shared-modules + url = https://github.com/flathub/shared-modules.git diff --git a/CI/README.md b/CI/README.md deleted file mode 100644 index e8f2905631..0000000000 --- a/CI/README.md +++ /dev/null @@ -1,89 +0,0 @@ - -This file contains dependency information for different platforms - -**Windows (x86_64)** - -https://github.com/analogdevicesinc/scopy-mingw-build-deps -builds the docker image - `docker pull analogdevices/scopy-build:mingw64` -Github Actions workflow: -https://github.com/analogdevicesinc/scopy/blob/master/.github/workflows/mingwbuild.yml - -**Linux (flatpak - x86_64)** -https://github.com/analogdevicesinc/scopy-flatpak -docker pull analogdevices/scopy-build:flatpak - - ARCH=x86_64 make - -Github Actions workflow - https://github.com/analogdevicesinc/scopy/blob/master/.github/workflows/linuxflatpakbuild.yml - -**Linux (flatpak - arm)** -https://github.com/analogdevicesinc/scopy-flatpak -Run locally on arm machine (raspberry pi) - - ARCH=arm make -Not build in CI - -**Linux (ubuntu - x86_64) - development** -Built on appveyor - https://github.com/analogdevicesinc/scopy/blob/master/appveyor.yml -**macOS (x86_64)** -Built on appveyor - https://github.com/analogdevicesinc/scopy/blob/master/appveyor.yml - -**Android (aarch64)** -https://github.com/analogdevicesinc/scopy-android-deps - `docker pull analogdevices/scopy-build:android` -Github Actions workflow - https://github.com/analogdevicesinc/scopy/blob/master/.github/workflows/androidbuild.yml - -Dependency versions (links used in source builds) - - - Qt: 5.15.12 - - Qwt - https://github.com/cseci/qwt - qwt-multiaxes - - libiio - https://github.com/analogdevicesinc/libiio - https://github.com/analogdevicesinc/libiio/tree/cad83146837971acdac28beaeb8156b9da33ba6b - v0.24 - - libxml2 - https://github.com/GNOME/libxml2 - - iconv (only on Android from src) - - libffi (only on Android from src) - - libgettext (only on Android from src) - - libusb - https://downloads.sourceforge.net/project/libusb/libusb-1.0/libusb-1.0.24/libusb-1.0.24.tar.bz2 - - Android specific libusb: https://github.com/xloem/libusb/tree/d1856aa8c246f9e56cf00a0765462b67fc5a4871 - - libm2k- https://github.com/analogdevicesinc/libm2k - master - - glog - https://github.com/google/glog - - boost - - - gnuradio - https://github.com/analogdevicesinc/gnuradio - scopy / scopy-android-2(for Android) - - volk - - log4cpp - https://github.com/cseci/log4cpp - - fftw3 - - libgmp - - gr-iio https://github.com/analogdevicesinc/gr-iio - upgrade3.8 - - libad9361 - https://github.com/analogdevicesinc/ad9361 - - gr-m2k - https://github.com/analogdevicesinc/gr-m2k - master - - gr-scopy - https://github.com/analogdevicesinc/gr-scopy - master - - libsigrokdecode - https://github.com/sigrokproject/libsigrokdecode - master - - glib - - glibmm - - sigcpp - - python - - libtinyiiod - https://github.com/analogdevicesinc/libtinyiiod - master - -How to install Qt from qt.io : https://github.com/analogdevicesinc/scopy-android-deps/blob/master/docker/Dockerfile#L43-L49 - - -| Dependency | Windows | Linux Flatpak | Linux Ubuntu(development) | Linux ARM | macOS | Android | -| --- | --- | --- | --- | --- | --- | --- | -| Qt | pacman | org.kde.Sdk (v5.15)| Qt.io |org.kde.Sdk (v5.14) | brew | Qt.io | -| qwt | src | src | src | src | src | src | -| libxml2 | pacman | src | apt | src | brew | src | -| libusb | pacman | src | apt | src | brew | src - android branch/commit | -| libiio | src | src | src | src | src | src| -| glog | src | src | src | src | brew | src | -| libm2k | src | src | src | src | src | src | -| volk | src | with GR | with GR | src | with GR | src | -| fftw3 | pacman| src | apt | src | brew| src| -| libgmp | pacman | src | apt | src | brew | src | -| boost | 1.85 | 1.72 | apt/src | 1.72 | brew | Boost-for-Android | -| gnuradio | src | src | apt/src | src | src | src | -| gr-iio | src | src | src | src | src | src | -| gr-m2k | src | src | src | src | src | src | -| gr-scopy | src | src | src | src | src | src | -| glib | pacman | src | apt | src | brew | src | -| glibmm | pacman | src | apt | src | src | src | -| sigcpp | pacman | src | apt | src | src | src | -| python | pacman | src | apt | src | brew | src | -| libsigrokdecode | src | src | src | src | src | src | -| libtinyiiod | src | src | src | src | src | src| diff --git a/CI/android/android_deploy_libs.sh b/CI/android/android_deploy_libs.sh deleted file mode 100755 index 2d979ff160..0000000000 --- a/CI/android/android_deploy_libs.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -set -xe - -if [ $# -ne 1 ]; then - ARG1=${BUILDDIR} -else - ARG1=$1 -fi - -copy-all-libs-from-staging() { - echo -- Copying .so libraries to ./android/libs/$ABI - cp $DEV_PREFIX/lib/*.so ./android/libs/$ABI -} -copy-missing-qt-libs() { - echo -- Copying missing qt5 libraries to the android-build - for some reason android-qt-deploy does not correctly deploy all the libraries - echo -- We are now deploying all the qt libraries - TODO only deploy the ones that are actually used - cp $QT_INSTALL_PREFIX/lib/libQt5*_$ABI.so ./android/libs/$ABI - #cp $QT_INSTALL_PREFIX/lib/libQt5*_$ABI.so $ARG1/android-build/libs/$ABI - -} -copy-libsigrokdecode() { - echo -- Copying libsigrokdecoders and python env - cp -R $DEV_PREFIX/lib/python3.8 ./android/assets/ - cp -R $DEV_PREFIX/share/libsigrokdecode ./android/assets/ -} - -copy-iio-emu() { - echo -- Deploying iio-emu as iio-emu.so in ./android/libs/$ABI - echo -- this hack will deploy iio-emu with .so extension so the bundle tool - echo -- will mark it as an executable - echo -- surely there is a better way to do this - - cp $ARG1/iio-emu/iio-emu android/libs/$ABI/iio-emu.so -} -copy-scopy() { - cp $ARG1/android-build/libs/$ABI/libscopy* android/libs/$ABI/ -} - -copy-all-libs-from-staging -copy-missing-qt-libs -copy-libsigrokdecode -copy-iio-emu -copy-scopy -#$ANDROID_QT_DEPLOY --input $ARG1/android_deployment_settings.json --output $ARG1/android-build --android-platform android-$API --jdk $JDK --gradle --verbose --sign /home/adi/Downloads/scopy-android-key.jks diff --git a/CI/android/android_deploy_qt.sh b/CI/android/android_deploy_qt.sh deleted file mode 100755 index 5a0120302f..0000000000 --- a/CI/android/android_deploy_qt.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash -set -xe - -source android_scopy_keystore - -SCOPY_GIT_TAG=$(git rev-parse --short HEAD) -BUILD_TYPE="debug" -SIGN_STATUS="unsigned" -QT_DEPLOY_OUTPUT_FOLDER="./android-build" -SIGN_SUFFIX="${BUILD_TYPE}" - -if [ "$2" == "--sign" ]; then - BUILD_TYPE="release" - SIGN_STATUS="signed" - SIGN_OPTS="--sign ./scopy-android-key.jks scopy $KEYSTOREPASSWORD" - SIGN_SUFFIX="${BUILD_TYPE}-${SIGN_STATUS}" -fi - - -if [ "$1" == "apk" ]; then - DEPLOY_FILE_CMD="cp $QT_DEPLOY_OUTPUT_FOLDER/build/outputs/apk/${BUILD_TYPE}/*.apk ./scopy-${SCOPY_GIT_TAG}-${ABI}-${SIGN_SUFFIX}.${1}" - # apk by default - OUT_FILE_OPTS="" -elif [ "$1" == "aab" ]; then - DEPLOY_FILE_CMD="cp $QT_DEPLOY_OUTPUT_FOLDER/build/outputs/bundle/${BUILD_TYPE}/*.aab ./scopy-${SCOPY_GIT_TAG}-${SIGN_SUFFIX}.${1} " - OUT_FILE_OPTS="--aab" -else - echo Required parameter -- $0 [apk/aab] - exit -fi - -init_android_build_folder() { -mkdir -p $QT_DEPLOY_OUTPUT_FOLDER -cp -R ${BUILDDIR}/android-build/* $QT_DEPLOY_OUTPUT_FOLDER/ -} - -android_deploy() { -$ANDROID_QT_DEPLOY \ - --input ${BUILDDIR}/android_deployment_settings.json \ - --output ${QT_DEPLOY_OUTPUT_FOLDER} \ - --android-platform android-${API} \ - --jdk ${JDK} \ - --gradle \ - --verbose ${SIGN_OPTS} \ - ${OUT_FILE_OPTS} \ - $SIGN_OPTS \ - -${DEPLOY_FILE_CMD} -} - -init_android_build_folder -android_deploy \ No newline at end of file diff --git a/CI/android/android_get_symbols.sh b/CI/android/android_get_symbols.sh deleted file mode 100755 index 9509f2553a..0000000000 --- a/CI/android/android_get_symbols.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -SCOPY_GIT_TAG=$(git rev-parse --short HEAD) -SYMBOLS_FOLDER=android-build/build/intermediates/merged_native_libs/release/out/lib/ -ARCHIVE_NAME=scopy-$SCOPY_GIT_TAG-android-native-symbols.zip - -sudo apt-get install -y zip -pushd $SYMBOLS_FOLDER -zip -r $ARCHIVE_NAME * -popd -mv ${SYMBOLS_FOLDER}/${ARCHIVE_NAME} . diff --git a/CI/appveyor/build_appveyor_flatpak.sh b/CI/appveyor/build_appveyor_flatpak.sh deleted file mode 100755 index 39f8767219..0000000000 --- a/CI/appveyor/build_appveyor_flatpak.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -m - -BRANCH=${1:-master} -REPO=${2:-analogdevicesinc/scopy} - -sudo apt-get -qq update -sudo service docker restart -sudo docker pull alexandratr/scopy-flatpak-bionic:latest - -# Start the docker in detached state -commit_nb=$(sudo docker run -d \ - --privileged \ - --rm=true \ - -v `pwd`:/scopy:rw \ - -e "BRANCH=$BRANCH" \ - -e "REPO=$REPO" \ - alexandratr/scopy-flatpak-bionic:latest \ - /bin/bash -xe /scopy/CI/appveyor/inside_flatpak_docker.sh) - -# Attach ourselves to the running docker and wait for it to finish -sudo docker attach $commit_nb - diff --git a/CI/appveyor/build_appveyor_macos.sh b/CI/appveyor/build_appveyor_macos.sh deleted file mode 100755 index 7c7570eb24..0000000000 --- a/CI/appveyor/build_appveyor_macos.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/bash -set -e -STAGINGDIR="${PWD}/staging" - -# Use the /usr/local/bin/pkg-config instead of /Mono/../pkg-config -export PATH="/usr/local/bin:$PATH" - -# if we have a Qt59 installation use it -if [ -f /opt/qt59/bin/qt59-env.sh ] ; then - . /opt/qt59/bin/qt59-env.sh -fi - -if command -v brew ; then - QT_PATH="$(brew --prefix ${QT_FORMULAE})/bin" - export PATH="${QT_PATH}:$PATH" -fi - -NUM_JOBS=4 -mkdir -p build -cd build - -MACOS_VERSION=$(/usr/libexec/PlistBuddy -c "Print:ProductVersion" /System/Library/CoreServices/SystemVersion.plist) -export MACOSX_DEPLOYMENT_TARGET=11.0 - -cmake -DCMAKE_STAGING_PREFIX="${STAGINGDIR}" \ - -DCMAKE_PREFIX_PATH="${STAGINGDIR};${STAGINGDIR}/lib/cmake;${STAGINGDIR}/lib/pkgconfig;${STAGINGDIR}/lib/cmake/gnuradio;${STAGINGDIR}/lib/cmake/iio;${QT_PATH}/lib/cmake" \ - -DCMAKE_INSTALL_PREFIX="${STAGINGDIR}" \ - -DCMAKE_EXE_LINKER_FLAGS="-L${STAGINGDIR}/lib" .. -CFLAGS=-I${STAGINGDIR}/include LDFLAGS=-L${STAGINGDIR}/lib make -j${NUM_JOBS} - -otool -l ./Scopy.app/Contents/MacOS/Scopy \ No newline at end of file diff --git a/CI/appveyor/build_appveyor_mingw.sh b/CI/appveyor/build_appveyor_mingw.sh deleted file mode 100755 index c5fa0efa8e..0000000000 --- a/CI/appveyor/build_appveyor_mingw.sh +++ /dev/null @@ -1,159 +0,0 @@ -#!/usr/bin/bash.exe - -set -e - -export PATH=/bin:/usr/bin:/${MINGW_VERSION}/bin:/c/Program\ Files/Git/cmd:/c/Windows/System32:/c/Program\ Files/7-Zip:/c/Program\ Files\ \(x86\)/Inno\ Setup\ \6:/c/Program\ Files/Appveyor/BuildAgent -echo $PATH -appveyor AddMessage "1. Starting build_appveyor_mingw.sw" -RC_COMPILER_OPT="-DCMAKE_RC_COMPILER=/c/msys64/${MINGW_VERSION}/bin/windres.exe" - -WORKDIR=${PWD} -echo BUILD_NO $BUILD_NO -JOBS=$(nproc) - - -wget http://swdownloads.analog.com/cse/m1k/drivers/dpinst.zip -wget http://swdownloads.analog.com/cse/m1k/drivers/dfu-util.zip -7z x -y "dpinst.zip" -o"/c/dpinst" -7z x -y "dfu-util.zip" -o"/c/dfu-util" - -# github release upload tool install -wget https://github.com/tcnksm/ghr/releases/download/v0.13.0/ghr_v0.13.0_windows_amd64.zip -7z x -y ghr_v0.13.0_windows_amd64.zip -o"/c/ghr" -UPLOAD_TOOL=/c/ghr/ghr_v0.13.0_windows_amd64/ghr.exe - - -CC=/${MINGW_VERSION}/bin/${ARCH}-w64-mingw32-gcc.exe -CXX=/${MINGW_VERSION}/bin/${ARCH}-w64-mingw32-g++.exe -CMAKE_OPTS="\ - -DCMAKE_C_COMPILER:FILEPATH=${CC} \ - -DCMAKE_CXX_COMPILER:FILEPATH=${CXX} \ - -DPKG_CONFIG_EXECUTABLE=/$MINGW_VERSION/bin/pkg-config.exe \ - -DCMAKE_CXX_FLAGS="-fno-omit-frame-pointer" \ - -DCMAKE_PREFIX_PATH=/c/msys64/$MINGW_VERSION/lib/cmake \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - " - -SCOPY_CMAKE_OPTS="\ - $RC_COMPILER_OPT \ - -DBREAKPAD_HANDLER=ON \ - -DWITH_DOC=ON \ - -DPYTHON_EXECUTABLE=/$MINGW_VERSION/bin/python3.exe \ - " - -#PYTHON_LOCATION=/$MINGW_VERSION/lib/python3.8 -#PYTHON_FILES="${PYTHON_LOCATION}/*.py ${PYTHON_LOCATION}/asyncio ${PYTHON_LOCATION}/collections ${PYTHON_LOCATION}/concurrent ${PYTHON_LOCATION}/config-3.* ${PYTHON_LOCATION}/ctypes ${PYTHON_LOCATION}/distutils ${PYTHON_LOCATION}/encodings ${PYTHON_LOCATION}/lib-dynload ${PYTHON_LOCATION}/site-packages" -PYTHON_FILES=/$MINGW_VERSION/lib/python3.* - -DLL_DEPS=$(cat ${WORKDIR}/CI/appveyor/mingw_dll_deps) -DLL_DEPS="$DLL_DEPS $PYTHON_FILES" - -echo $DLL_DEPS - -OLD_PATH=$PATH -DEST_FOLDER=scopy_$ARCH_BIT -BUILD_FOLDER=build_$ARCH_BIT -DEBUG_FOLDER=debug_$ARCH_BIT - -appveyor AddMessage "2. Installing msys deps.sw" -cd /c -source ${WORKDIR}/CI/appveyor/install_msys_deps.sh -appveyor AddMessage "3. Installed msys deps.sw" - -# Download a 32-bit version of windres.exe -cd ${WORKDIR} -wget http://swdownloads.analog.com/cse/build/windres.exe.gz -gunzip windres.exe.gz - - -echo "### Building Scopy ..." -appveyor AddMessage "4. Running CMake" -/$MINGW_VERSION/bin/python3.exe --version -mkdir /c/$BUILD_FOLDER -cd /c/$BUILD_FOLDER -cp /tmp/scopy-mingw-build-status /c/projects/scopy -cat /c/projects/scopy/scopy-mingw-build-status -cmake -G 'Unix Makefiles' $SCOPY_CMAKE_OPTS $CMAKE_OPTS /c/projects/scopy -appveyor AddMessage "5. Build configured" - -cat /c/$BUILD_FOLDER/buildinfo.html -appveyor PushArtifact /c/$BUILD_FOLDER/buildinfo.html - -appveyor AddMessage "6. Starting build" -cd /c/$BUILD_FOLDER/resources -sed -i 's/^\(FILEVERSION .*\)$/\1,'$BUILD_NO'/' properties.rc -cat properties.rc -cd /c/build_$ARCH_BIT && make -j $JOBS -appveyor AddMessage "7. Build finished" - - -echo "### Deploy the application (copy the dependencies) ..." -appveyor AddMessage "8. Deploying Scopy" -mkdir /c/$DEST_FOLDER -cp /c/$BUILD_FOLDER/Scopy.exe /c/$DEST_FOLDER/ -cp /c/$BUILD_FOLDER/qt.conf /c/$DEST_FOLDER/ -mkdir /c/$DEST_FOLDER/resources - -cp /c/$BUILD_FOLDER/iio-emu/iio-emu.exe /c/$DEST_FOLDER/ - -# windeployqt was broken in qt version 5.14.2 - it should be fixed in Qt 5.15 - https://bugreports.qt.io/browse/QTBUG-80763 -/c/msys64/$MINGW_VERSION/bin/windeployqt.exe --dir /c/$DEST_FOLDER --no-system-d3d-compiler --no-compiler-runtime --no-quick-import --opengl --printsupport /c/$BUILD_FOLDER/Scopy.exe -cp -r /$MINGW_VERSION/share/libsigrokdecode/decoders /c/$DEST_FOLDER/ - - -#tar -C /c/$DEST_FOLDER --strip-components=3 -xJf /c/scopy-$MINGW_VERSION-build-deps.tar.xz msys64/$MINGW_VERSION/bin -cd /$MINGW_VERSION/bin ; -cp -r $DLL_DEPS /c/$DEST_FOLDER/ - -echo "### Extracting debug symbols ..." -mkdir /c/scopy_$ARCH_BIT/.debug -#/$MINGW_VERSION/bin/objcopy -v --only-keep-debug /c/$DEST_FOLDER/Scopy.exe /c/$DEST_FOLDER/.debug/Scopy.exe.debug -dump_syms -r /c/$DEST_FOLDER/Scopy.exe > /c/$DEST_FOLDER/Scopy.exe.sym -#/c/msys64/$MINGW_VERSION/bin/strip.exe --strip-debug --strip-unneeded /c/$DEST_FOLDER/Scopy.exe -#/c/msys64/$MINGW_VERSION/bin/strip.exe --strip-debug --strip-unneeded /c/$DEST_FOLDER/*.dll -#/c/msys64/$MINGW_VERSION/bin/objcopy.exe -v --add-gnu-debuglink=/c/$DEST_FOLDER/.debug/Scopy.exe.debug /c/$DEST_FOLDER/Scopy.exe -mkdir /c/$DEBUG_FOLDER -mv /c/$DEST_FOLDER/Scopy.exe.sym /c/$DEBUG_FOLDER -mv /c/$DEST_FOLDER/.debug /c/$DEBUG_FOLDER -/c/cv2pdb/cv2pdb /c/$DEST_FOLDER/Scopy.exe -/c/cv2pdb/cv2pdb /c/$DEST_FOLDER/libm2k.dll -/c/cv2pdb/cv2pdb /c/$DEST_FOLDER/libiio.dll -/c/cv2pdb/cv2pdb /c/$DEST_FOLDER/libgnuradio-m2k.dll -/c/cv2pdb/cv2pdb /c/$DEST_FOLDER/libgnuradio-scopy.dll -cp -R /c/projects/scopy /c/$DEBUG_FOLDER/scopy -mv /c/$DEST_FOLDER/*.pdb /c/$DEBUG_FOLDER - -cp -r /c/projects/scopy/drivers /c/$DEST_FOLDER -if [[ $ARCH_BIT == "64" ]]; then - cp /c/dfu-util/dfu-util-static-amd64.exe /c/$DEST_FOLDER/drivers/dfu-util.exe - cp /c/dpinst/dpinst_amd64.exe /c/$DEST_FOLDER/drivers/dpinst.exe -else - cp /c/dfu-util/dfu-util-static.exe /c/$DEST_FOLDER/drivers/dfu-util.exe - cp /c/dpinst/dpinst.exe /c/$DEST_FOLDER/drivers/dpinst.exe -fi - -appveyor AddMessage "9. Scopy succesfully deployed" - -echo "### Creating archives ... " -appveyor AddMessage "10. Creating archives" -7z a "/c/scopy-${ARCH_BIT}bit.zip" /c/$DEST_FOLDER -appveyor PushArtifact /c/scopy-${ARCH_BIT}bit.zip -7z a "/c/debug-${ARCH_BIT}bit.zip" /c/$DEBUG_FOLDER -appveyor PushArtifact /c/debug-${ARCH_BIT}bit.zip -appveyor AddMessage "11. Creating installer" -iscc //Qp /c/$BUILD_FOLDER/scopy-$ARCH_BIT.iss -appveyor PushArtifact $DEPLOY_FILE -appveyor AddMessage "12. Job complete" - -if [ "$APPVEYOR_REPO_BRANCH" = "master" ]; then - echo Identified master branch - if [ -z "$APPVEYOR_PULL_REQUEST_NUMBER" ]; then - echo Not a pull request - mkdir /c/to_deploy - cp /c/scopy-${ARCH_BIT}bit.zip /c/to_deploy - cp /c/debug-${ARCH_BIT}bit.zip /c/to_deploy - cp $DEPLOY_FILE /c/to_deploy - $UPLOAD_TOOL -u $APPVEYOR_ACCOUNT_NAME -r $APPVEYOR_PROJECT_NAME -name "Continuous build" -b "Latest succesful master build " -prerelease -debug -replace continous /c/to_deploy - fi -fi - diff --git a/CI/appveyor/build_appveyor_ubuntu.sh b/CI/appveyor/build_appveyor_ubuntu.sh deleted file mode 100755 index 8ccab500a4..0000000000 --- a/CI/appveyor/build_appveyor_ubuntu.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -JOBS=$JOBS - -if [ $# -eq 0 ]; then - echo "Using default qmake" - QMAKE=qmake - CMAKE_PREFIX_PATH_PARAM= - $QMAKE --version -else - QMAKE=$1/gcc_64/bin/qmake - CMAKE_PREFIX_PATH_PARAM=-DCMAKE_PREFIX_PATH\=$1/gcc_64/lib/cmake - $QMAKE --version - -fi - -cd ~/projects/scopy -mkdir build -cd build -cmake $CMAKE_PREFIX_PATH_PARAM ../ -make $JOBS - diff --git a/CI/appveyor/build_scopy_apk.sh b/CI/appveyor/build_scopy_apk.sh deleted file mode 100755 index c78605276f..0000000000 --- a/CI/appveyor/build_scopy_apk.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash -set -xe -source ./android_toolchain.sh -ARTIFACT_LOCATION=$GITHUB_WORKSPACE - -clone_scopy() { - git clone $REPO_URL - pushd scopy - git submodule update --init --recursive iio-emu - - git fetch origin $BRANCH - git checkout FETCH_HEAD - popd -} - -#cp libs -cp_scripts() { - pushd scopy - cp $BUILD_STATUS_FILE . - cp $BUILD_ROOT/android_cmake.sh . - cp ./CI/android/* . - popd -} - -make_apk_aab() { - pushd scopy - rm -rf build* - - ./android_cmake.sh . - cd $BUILDDIR - make -j$JOBS iio-emu - make -j$JOBS scopy - cd .. - ./android_deploy_libs.sh - ./android_deploy_qt.sh apk - - ./android_deploy_qt.sh aab #sign - ./android_get_symbols.sh - popd -} - -move_artifact() { - pushd scopy - sudo cp *.apk $ARTIFACT_LOCATION - sudo cp *.aab $ARTIFACT_LOCATION - sudo cp scopy*android-native-symbols.zip $ARTIFACT_LOCATION - - pushd $ARTIFACT_LOCATION - sudo chmod 644 *.apk - sudo chmod 644 *.aab - sudo chmod 644 *.zip - - ls -la $ARTIFACT_LOCATION - popd - - popd -} - - clone_scopy - cp_scripts - make_apk_aab - move_artifact diff --git a/CI/appveyor/inside_flatpak_docker.sh b/CI/appveyor/inside_flatpak_docker.sh deleted file mode 100755 index 6d9146a8d9..0000000000 --- a/CI/appveyor/inside_flatpak_docker.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh -xe - -if [ -n "$BRANCH" ]; then - ARTIFACT_LOCATION=/scopy -else - ARTIFACT_LOCATION=$GITHUB_WORKSPACE -fi - -REPO_LOCAL=/home/docker/scopy-flatpak -cd "$REPO_LOCAL" -# this ensures that latest master is pulled from origin while keeping file cache -# the cache should be updated from time to time locally -git fetch && git reset origin/Scopy-1.5.0 --hard - -#workaround for https://github.blog/2021-10-18-git-security-vulnerabilities-announced/#cve-2022-39253 -git config --global protocol.file.allow always - - -# Run the preprocess step to generate org.adi.Scopy.json -make preprocess - -# Disable the preprocess step; The Json file will now be modified and -# we don't want to re-generate it at the build step -export EN_PREPROCESS=false - -# check the number of elements in the json file in order to get the last element, which is Scopy -cnt=$( echo `jq '.modules | length' org.adi.Scopy.json` ) -cnt=$(($cnt-1)) - -if [ -n "$BRANCH" ]; then - REPO_URL=https://github.com/"$REPO" - # We are building in Appveyor and we have access to the current branch on a CACHED Docker image - # use jq to replace the Scopy branch + the repo url used for building - # we want to build the branch and repo we're currently on - cat org.adi.Scopy.json | jq --tab '.modules['$cnt'].sources[0].branch = "'$BRANCH'"' > tmp.json - cp tmp.json org.adi.Scopy.json - cat org.adi.Scopy.json | jq --tab '.modules['$cnt'].sources[0].url = "'$REPO_URL'"' > tmp.json -else - # We are building in Github Actions and we use the current directory folder on a CLEAN Docker image - cat org.adi.Scopy.json | jq --tab '.modules['$cnt'].sources[0].type = "dir"' > tmp.json - cp tmp.json org.adi.Scopy.json - cat org.adi.Scopy.json | jq --tab '.modules['$cnt'].sources[0].path = "'$GITHUB_WORKSPACE'"' > tmp.json - cp tmp.json org.adi.Scopy.json - cat org.adi.Scopy.json | jq --tab 'del(.modules['$cnt'].sources[0].url)' > tmp.json - cp tmp.json org.adi.Scopy.json - cat org.adi.Scopy.json | jq --tab 'del(.modules['$cnt'].sources[0].branch)' > tmp.json -fi -cp tmp.json org.adi.Scopy.json -rm tmp.json - -# Generate build status info for the about page -echo "Details about the versions of dependencies can be found here" > build-status -cp build-status $GITHUB_WORKSPACE/build-status - -# Insert env vars in the sandboxed flatpak build -CI_ENVS=`jq -R -n -c '[inputs|split("=")|{(.[0]):.[1]}] | add' $GITHUB_WORKSPACE/CI/appveyor/gh-actions.envs` -echo "CI_ENVS= $CI_ENVS" -cat org.adi.Scopy.json | jq --tab '."build-options".env += ('$CI_ENVS')' > tmp.json -cp tmp.json org.adi.Scopy.json - -make clean -make -j4 - -# Copy the Scopy.flatpak file in $GITHUB_WORKSPACE (which is the external location, mount when docker starts) -cp Scopy.flatpak $ARTIFACT_LOCATION/ diff --git a/CI/appveyor/inside_mingw_docker.sh b/CI/appveyor/inside_mingw_docker.sh deleted file mode 100644 index 566a49ed9b..0000000000 --- a/CI/appveyor/inside_mingw_docker.sh +++ /dev/null @@ -1,93 +0,0 @@ -#!/bin/bash - -set -x - - -TOOLS_FOLDER=$HOME/scopy-mingw-build-deps -pushd $TOOLS_FOLDER -source ./mingw_toolchain.sh $BUILD_TARGET -popd - -WORKDIR=$HOME -SRC_FOLDER=$WORKDIR/scopy -DEST_FOLDER=$WORKDIR/scopy_$ARCH -BUILD_FOLDER=$WORKDIR/build_$ARCH -DEBUG_FOLDER=$WORKDIR/debug_$ARCH -ARTIFACT_FOLDER=$WORKDIR/artifact_$ARCH -PYTHON_FILES=/$MINGW_VERSION/lib/python3.* -DLL_DEPS=$(cat $SRC_FOLDER/CI/appveyor/mingw_dll_deps) - -# Generate build status info for the about page -cp $BUILD_STATUS_FILE $SRC_FOLDER/build-status -pacman -Qe >> $SRC_FOLDER/build-status - -echo "### Building Scopy " -mkdir -p $BUILD_FOLDER -cd $BUILD_FOLDER -$CMAKE $RC_COMPILER_OPT -DBREAKPAD_HANDLER=ON -DWITH_DOC=ON -DPYTHON_EXECUTABLE=/$MINGW_VERSION/bin/python3.exe $SRC_FOLDER -$MAKE_BIN -j 4 - - -echo "### Deploying application and dependencies" -mkdir $DEST_FOLDER -cp $BUILD_FOLDER/Scopy.exe $DEST_FOLDER/ -cp $BUILD_FOLDER/qt.conf $DEST_FOLDER/ -mkdir $DEST_FOLDER/resources - -cp $BUILD_FOLDER/iio-emu/iio-emu.exe $DEST_FOLDER/ - -# windeployqt was broken in qt version 5.14.2 - it should be fixed in Qt 5.15 - https://bugreports.qt.io/browse/QTBUG-80763 -$STAGING/$MINGW_VERSION/bin/windeployqt.exe --dir $DEST_FOLDER --no-system-d3d-compiler --no-compiler-runtime --no-quick-import --opengl --printsupport $BUILD_FOLDER/Scopy.exe -cp -r $STAGING/$MINGW_VERSION/share/libsigrokdecode/decoders $DEST_FOLDER/ - -#tar -C /c/$DEST_FOLDER --strip-components=3 -xJf /c/scopy-$MINGW_VERSION-build-deps.tar.xz msys64/$MINGW_VERSION/bin -cd /$MINGW_VERSION/bin -cp -r -n $DLL_DEPS $DEST_FOLDER/ -cd $STAGING/$MINGW_VERSION/bin -cp -r $DLL_DEPS $DEST_FOLDER/ -cp -r $PYTHON_FILES $DEST_FOLDER -cp $BUILD_FOLDER/scopy-$ARCH_BIT.iss $DEST_FOLDER - -echo "### Extracting debug symbols ..." -mkdir -p $DEST_FOLDER/.debug -#/$MINGW_VERSION/bin/objcopy -v --only-keep-debug /c/$DEST_FOLDER/Scopy.exe /c/$DEST_FOLDER/.debug/Scopy.exe.debug -$STAGING/$MINGW_VERSION/bin/dump_syms -r $DEST_FOLDER/Scopy.exe > $DEST_FOLDER/Scopy.exe.sym -#/c/msys64/$MINGW_VERSION/bin/strip.exe --strip-debug --strip-unneeded /c/$DEST_FOLDER/Scopy.exe -#/c/msys64/$MINGW_VERSION/bin/strip.exe --strip-debug --strip-unneeded /c/$DEST_FOLDER/*.dll -#/c/msys64/$MINGW_VERSION/bin/objcopy.exe -v --add-gnu-debuglink=/c/$DEST_FOLDER/.debug/Scopy.exe.debug /c/$DEST_FOLDER/Scopy.exe -mkdir $DEBUG_FOLDER -mv $DEST_FOLDER/Scopy.exe.sym $DEBUG_FOLDER -mv $DEST_FOLDER/.debug $DEBUG_FOLDER -$TOOLS_FOLDER/cv2pdb/cv2pdb $DEST_FOLDER/Scopy.exe -$TOOLS_FOLDER/cv2pdb/cv2pdb $DEST_FOLDER/libm2k.dll -$TOOLS_FOLDER/cv2pdb/cv2pdb $DEST_FOLDER/libiio.dll -$TOOLS_FOLDER/cv2pdb/cv2pdb $DEST_FOLDER/libgnuradio-m2k.dll -$TOOLS_FOLDER/cv2pdb/cv2pdb $DEST_FOLDER/libgnuradio-scopy.dll -cp -R $DEST_FOLDER/scopy $DEBUG_FOLDER/scopy -mv $DEST_FOLDER/*.pdb $DEBUG_FOLDER - -echo "### Bundling drivers" -cp -R $SRC_FOLDER/drivers $DEST_FOLDER -if [[ $ARCH_BIT == "64" ]]; then - cp -R $TOOLS_FOLDER/dfu-util-static-amd64.exe $DEST_FOLDER/drivers/dfu-util.exe - cp -R $TOOLS_FOLDER/dpinst_amd64.exe $DEST_FOLDER/drivers/dpinst.exe -else - cp -R $TOOLS_FOLDER/dfu-util-static.exe $DEST_FOLDER/drivers/dfu-util.exe - cp -R $TOOLS_FOLDER/dpinst.exe $DEST_FOLDER/drivers/dpinst.exe -fi - - -echo "### Creating installer... " -mkdir -p $ARTIFACT_FOLDER -cd $WORKDIR -cp -R $WORKDIR/scopy_${ARCH} $ARTIFACT_FOLDER/scopy-${ARCH} -cp -R $WORKDIR/debug_${ARCH} $ARTIFACT_FOLDER/debug-${ARCH} -PATH=/c/innosetup:$PATH -iscc //p $BUILD_FOLDER/scopy-$ARCH_BIT.iss -mv $WORKDIR/scopy-$ARCH_BIT-setup.exe $ARTIFACT_FOLDER - -echo "Done. Artifacts generated in $ARTIFACT_FOLDER" -ls -la $ARTIFACT_FOLDER -echo $GITHUB_WORKSPACE -cp -R $ARTIFACT_FOLDER $SRC_FOLDER -ls -la $SRC_FOLDER diff --git a/CI/appveyor/install_macos_deps.sh b/CI/appveyor/install_macos_deps.sh deleted file mode 100755 index 5e457ba149..0000000000 --- a/CI/appveyor/install_macos_deps.sh +++ /dev/null @@ -1,257 +0,0 @@ -#!/bin/bash - -set -xe - -LIBIIO_VERSION=libiio-v0 -LIBAD9361_BRANCH=main -LIBM2K_BRANCH=main -GNURADIO_BRANCH=maint-3.10 -GRSCOPY_BRANCH=3.10 -GRM2K_BRANCH=main -QWT_BRANCH=qwt-multiaxes -LIBSIGROKDECODE_BRANCH=decoders/AD559XR -LIBTINYIIOD_BRANCH=master - -PYTHON="python3" -PACKAGES=" ${QT_FORMULAE} volk spdlog boost pkg-config cmake fftw bison gettext autoconf automake libtool libzip glib libusb glog" -PACKAGES="$PACKAGES doxygen wget gnu-sed libmatio dylibbundler libxml2 ghr libsndfile" - -REPO_SRC=$BUILD_REPOSITORY_LOCALPATH -WORKDIR=${PWD} -JOBS=4 - -# Workaround: Homebrew fails to upgrade Python's 2to3 due to conflicting symlinks https://github.com/actions/runner-images/issues/6817 -rm /usr/local/bin/2to3 || true -rm /usr/local/bin/idle3 || true -rm /usr/local/bin/pydoc3 || true -rm /usr/local/bin/python3 || true -rm /usr/local/bin/python3-config || true - -brew update -# Workaround for brew taking a long time to upgrade existing packages -# Check if macOS version and upgrade packages only if the version is greater than macOS 12 -if (( $(echo "$(sw_vers -productVersion) > 13.0" | bc -l) )); then - brew upgrade --display-times || true #ignore homebrew upgrade errors - brew install --overwrite --display-times $PACKAGES -else - HOMEBREW_NO_AUTO_UPDATE=1 brew install --overwrite --display-times $PACKAGES -fi - -brew search ${QT_FORMULAE} -brew install --display-times $PACKAGES -for pkg in gcc bison gettext cmake python; do - brew link --overwrite --force $pkg -done - -pip3 install --break-system-packages mako - -# Generate build status info for the about page -BUILD_STATUS_FILE=${REPO_SRC}/build-status -brew list --versions $PACKAGES > $BUILD_STATUS_FILE - -source ${REPO_SRC}/CI/appveyor/before_install_lib.sh - -QT_PATH="$(brew --prefix ${QT_FORMULAE})/bin" - -export PATH="/usr/local/bin:$PATH" -export PATH="/usr/local/opt/bison/bin:$PATH" -export PATH="${QT_PATH}:$PATH" -export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/libzip/lib/pkgconfig" -export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/libffi/lib/pkgconfig" -export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$STAGINGDIR/lib/pkgconfig" -export PKG_CONFIG_PATH="$(brew --prefix python3)/lib/pkgconfig:$PKG_CONFIG_PATH" -export PATH="$(brew --prefix python3)/bin:$PATH" - -QMAKE="$(command -v qmake)" -CMAKE_OPTS="-DCMAKE_PREFIX_PATH=$STAGINGDIR -DCMAKE_INSTALL_PREFIX=$STAGINGDIR" - -export - -save_version_info() { - echo "$CURRENT_BUILD - $(git rev-parse --short HEAD)" >> $BUILD_STATUS_FILE -} - -build_libiio() { - echo "### Building libiio - version $LIBIIO_VERSION" - - git clone https://github.com/analogdevicesinc/libiio.git ${WORKDIR}/libiio - cd ${WORKDIR}/libiio - git checkout $LIBIIO_VERSION - - mkdir ${WORKDIR}/libiio/build-${ARCH} - cd ${WORKDIR}/libiio/build-${ARCH} - CURRENT_BUILD=libiio - save_version_info - - cmake ${CMAKE_OPTS} \ - -DWITH_TESTS:BOOL=OFF \ - -DWITH_DOC:BOOL=OFF \ - -DWITH_MATLAB_BINDINGS:BOOL=OFF \ - -DENABLE_DNS_SD:BOOL=OFF\ - -DCSHARP_BINDINGS:BOOL=OFF \ - -DPYTHON_BINDINGS:BOOL=OFF \ - -DOSX_PACKAGE:BOOL=OFF \ - ${WORKDIR}/libiio - - make -j $JOBS - sudo make -j ${JOBS} install -} - -build_libm2k() { - - echo "### Building libm2k - branch $LIBM2K_BRANCH" - - git clone --depth 1 https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH ${WORKDIR}/libm2k - - mkdir ${WORKDIR}/libm2k/build-${ARCH} - cd ${WORKDIR}/libm2k/build-${ARCH} - CURRENT_BUILD=libm2k - save_version_info - - cmake ${CMAKE_OPTS} \ - -DENABLE_PYTHON=OFF \ - -DENABLE_CSHARP=OFF \ - -DBUILD_EXAMPLES=OFF \ - -DENABLE_TOOLS=OFF \ - -DINSTALL_UDEV_RULES=OFF \ - -DENABLE_LOG=ON\ - ${WORKDIR}/libm2k - - make -j $JOBS - sudo make -j ${JOBS} install -} - -build_libad9361() { - echo "### Building libad9361 - branch $LIBAD9361_BRANCH" - - git clone --depth 1 https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH ${WORKDIR}/libad9361 - - mkdir ${WORKDIR}/libad9361/build-${ARCH} - cd ${WORKDIR}/libad9361/build-${ARCH} - CURRENT_BUILD=libad9361-iio - save_version_info - - cmake ${CMAKE_OPTS} \ - ${WORKDIR}/libad9361 - - make -j $JOBS - sudo make -j $JOBS install -} - -build_gnuradio() { - echo "### Building gnuradio - branch $GNURADIO_BRANCH" - - git clone --recurse-submodules https://github.com/gnuradio/gnuradio.git -b $GNURADIO_BRANCH ${WORKDIR}/gnuradio - mkdir ${WORKDIR}/gnuradio/build-${ARCH} - cd ${WORKDIR}/gnuradio/build-${ARCH} - CURRENT_BUILD=gnuradio - save_version_info - - cmake ${CMAKE_OPTS} \ - -DENABLE_GR_DIGITAL:BOOL=OFF \ - -DENABLE_GR_DTV:BOOL=OFF \ - -DENABLE_GR_AUDIO:BOOL=OFF \ - -DENABLE_GR_CHANNELS:BOOL=OFF \ - -DENABLE_GR_TRELLIS:BOOL=OFF \ - -DENABLE_GR_VOCODER:BOOL=OFF \ - -DENABLE_GR_QTGUI:BOOL=OFF \ - -DENABLE_GR_FEC:BOOL=OFF \ - -DENABLE_SPHINX:BOOL=OFF \ - -DENABLE_DOXYGEN:BOOL=OFF \ - -DENABLE_INTERNAL_VOLK=ON \ - -DENABLE_PYTHON=OFF \ - -DENABLE_TESTING=OFF \ - -DENABLE_GR_CHANNELS=OFF \ - -DENABLE_GR_VOCODER=OFF \ - -DENABLE_GR_TRELLIS=OFF \ - -DENABLE_GR_WAVELET=OFF \ - -DENABLE_GR_CTRLPORT=OFF \ - -DENABLE_CTRLPORT_THRIFT=OFF \ - -DCMAKE_C_FLAGS=-fno-asynchronous-unwind-tables \ - ${WORKDIR}/gnuradio - make -j $JOBS - sudo make -j $JOBS install -} - -build_grm2k() { - echo "### Building gr-m2k - branch $GRM2K_BRANCH" - - git clone --depth 1 https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH ${WORKDIR}/gr-m2k - mkdir ${WORKDIR}/gr-m2k/build-${ARCH} - cd ${WORKDIR}/gr-m2k/build-${ARCH} - CURRENT_BUILD=gr-m2k - save_version_info - - cmake ${CMAKE_OPTS} \ - -DENABLE_PYTHON=OFF \ - -DDIGITAL=OFF \ - ${WORKDIR}/gr-m2k - - make -j $JOBS - sudo make -j $JOBS install -} - -build_grscopy() { - echo "### Building gr-scopy - branch $GRSCOPY_BRANCH" - - git clone --depth 1 https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH ${WORKDIR}/gr-scopy - mkdir ${WORKDIR}/gr-scopy/build-${ARCH} - cd ${WORKDIR}/gr-scopy/build-${ARCH} - CURRENT_BUILD=gr-scopy - save_version_info - - cmake ${CMAKE_OPTS} \ - -DWITH_PYTHON=OFF \ - ${WORKDIR}/gr-scopy - - make -j $JOBS - sudo make -j $JOBS install -} -build_libsigrokdecode() { - echo "### Building libsigrokdecode - branch $LIBSIGROKDECODE_BRANCH" - - git clone --depth 1 https://github.com/analogdevicesinc/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH ${WORKDIR}/libsigrokdecode - cd ${WORKDIR}/libsigrokdecode - CURRENT_BUILD=libsigrokdecode - save_version_info - - ./autogen.sh - ./configure --prefix=$STAGINGDIR - - sudo make -j $JOBS install -} - -build_qwt() { - echo "### Building qwt - branch qwt-multiaxes" - git clone --depth 1 https://github.com/cseci/qwt -b $QWT_BRANCH ${WORKDIR}/qwt - CURRENT_BUILD=qwt - save_version_info - qmake_build_local "qwt" "qwt.pro" "patch_qwt" -} - -build_libtinyiiod() { - echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" - - git clone --depth 1 https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH ${WORKDIR}/libtinyiiod - mkdir ${WORKDIR}/libtinyiiod/build-${ARCH} - cd ${WORKDIR}/libtinyiiod/build-${ARCH} - CURRENT_BUILD=libtinyiiod - save_version_info - - cmake ${CMAKE_OPTS} \ - -DBUILD_EXAMPLES=OFF \ - ${WORKDIR}/libtinyiiod - - make -j $JOBS - sudo make -j $JOBS install -} - -build_libiio -build_libad9361 -build_libm2k -build_gnuradio -build_grscopy -build_grm2k -build_qwt -build_libsigrokdecode -build_libtinyiiod diff --git a/CI/appveyor/install_qt_ubuntu_20.sh b/CI/appveyor/install_qt_ubuntu_20.sh deleted file mode 100755 index 1a62f461d9..0000000000 --- a/CI/appveyor/install_qt_ubuntu_20.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -sudo apt-get -y install vim git cmake qt5-default qtcreator qtdeclarative5-dev qtdeclarative5-dev-tools libqt5svg5 libqt5svg5-dev qttools5-dev qttools5-dev-tools libqt5opengl5 diff --git a/CI/appveyor/install_ubuntu_18_deps.sh b/CI/appveyor/install_ubuntu_18_deps.sh deleted file mode 100755 index 0e02002b55..0000000000 --- a/CI/appveyor/install_ubuntu_18_deps.sh +++ /dev/null @@ -1,252 +0,0 @@ -#!/bin/bash - -LIBIIO_VERSION=0ed18cd8f6b2fac5204a99e38922bea73f1f778c -LIBAD9361_BRANCH=master -GLOG_BRANCH=v0.4.0 -LIBM2K_BRANCH=master -GRIIO_BRANCH=upgrade-3.8 -#GNURADIO_FORK=analogdevicesinc -#GNURADIO_BRANCH=scopy -GRSCOPY_BRANCH=3.10 -GRM2K_BRANCH=master -QWT_BRANCH=qwt-multiaxes -LIBSIGROK_BRANCH=master -LIBSIGROKDECODE_BRANCH=master -LIBTINYIIOD_BRANCH=master - -set -e -if [ $# -eq 0 ]; then - echo "Using default qmake" - QMAKE=qmake - $QMAKE --version -else - QMAKE=$1/gcc_64/bin/qmake - $QMAKE --version - -fi - -cd ~ -WORKDIR=${PWD} - -install_apt() { - sudo add-apt-repository -y ppa:gnuradio/gnuradio-releases - sudo apt-get update - - curl -fsSL -o doxygen.tar.gz "https://phoenixnap.dl.sourceforge.net/project/doxygen/rel-1.8.17/doxygen-1.8.17.linux.bin.tar.gz" - tar -xzf doxygen.tar.gz - sudo cp -a doxygen-1.8.17/bin/doxy* /usr/local/bin - doxygen --version - - sudo apt-get -y install build-essential libxml2-dev libxml2 flex bison swig libpython3-all-dev python3 python3-numpy libfftw3-bin libfftw3-dev libfftw3-3 liblog4cpp5v5 liblog4cpp5-dev libboost1.65-dev libboost1.65 g++ git cmake autoconf libzip4 libzip-dev libglib2.0-dev libsigc++-2.0-dev libglibmm-2.4-dev curl libvolk1-bin libvolk1-dev libvolk1.3 libgmp-dev libmatio-dev liborc-0.4-dev subversion mesa-common-dev libgl1-mesa-dev libserialport0 libserialport-dev libusb-1.0 libusb-1.0-0 libusb-1.0-0-dev libaio-dev - - sudo apt-get -y update - sudo apt-get -y install gnuradio -} - -build_libiio() { - echo "### Building libiio - version $LIBIIO_VERSION" - - cd ~ - git clone https://github.com/analogdevicesinc/libiio.git ${WORKDIR}/libiio - cd ${WORKDIR}/libiio - git checkout $LIBIIO_VERSION - - mkdir ${WORKDIR}/libiio/build-${ARCH} - cd ${WORKDIR}/libiio/build-${ARCH} - # Download a 32-bit version of windres.exe - - cmake ${CMAKE_OPTS} \ - -DWITH_TESTS:BOOL=OFF \ - -DWITH_DOC:BOOL=OFF \ - -DHAVE_DNS_SD:BOOL=OFF\ - -DWITH_MATLAB_BINDINGS:BOOL=OFF \ - -DCSHARP_BINDINGS:BOOL=OFF \ - -DPYTHON_BINDINGS:BOOL=OFF \ - ${WORKDIR}/libiio - - make $JOBS - sudo make ${JOBS} install -# DESTDIR=${WORKDIR} make ${JOBS} install -} - -build_glog() { - - echo "### Building glog - branch $GLOG_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/google/glog.git -b $GLOG_BRANCH ${WORKDIR}/glog - - mkdir ${WORKDIR}/glog/build-${ARCH} - cd ${WORKDIR}/glog/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - -DWITH_GFLAGS=OFF\ - ${WORKDIR}/glog - - make $JOBS - sudo make ${JOBS} install - #DESTDIR=${WORKDIR} make ${JOBS} install -} - -build_libm2k() { - - echo "### Building libm2k - branch $LIBM2K_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH ${WORKDIR}/libm2k - - mkdir ${WORKDIR}/libm2k/build-${ARCH} - cd ${WORKDIR}/libm2k/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - -DENABLE_PYTHON=OFF\ - -DENABLE_CSHARP=OFF\ - -DENABLE_EXAMPLES=OFF\ - -DENABLE_TOOLS=OFF\ - -DINSTALL_UDEV_RULES=OFF\ - -DENABLE_LOG=ON\ - ${WORKDIR}/libm2k - - make $JOBS - sudo make ${JOBS} install - #DESTDIR=${WORKDIR} make ${JOBS} install -} -build_libad9361() { - echo "### Building libad9361 - branch $LIBAD9361_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH ${WORKDIR}/libad9361 - - mkdir ${WORKDIR}/libad9361/build-${ARCH} - cd ${WORKDIR}/libad9361/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - ${WORKDIR}/libad9361 - - make $JOBS - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install -} - -build_griio() { - echo "### Building gr-iio - branch $GRIIO_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/gr-iio.git -b $GRIIO_BRANCH ${WORKDIR}/gr-iio - mkdir ${WORKDIR}/gr-iio/build-${ARCH} - cd ${WORKDIR}/gr-iio/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - ${WORKDIR}/gr-iio - - make $JOBS - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install -} - -build_grm2k() { - echo "### Building gr-m2k - branch $GRM2K_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH ${WORKDIR}/gr-m2k - mkdir ${WORKDIR}/gr-m2k/build-${ARCH} - cd ${WORKDIR}/gr-m2k/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - -DENABLE_PYTHON=OFF \ - -DDIGITAL=OFF \ - ${WORKDIR}/gr-m2k - - make $JOBS - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install - -} - -build_grscopy() { - echo "### Building gr-scopy - branch $GRSCOPY_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH ${WORKDIR}/gr-scopy - mkdir ${WORKDIR}/gr-scopy/build-${ARCH} - cd ${WORKDIR}/gr-scopy/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - ${WORKDIR}/gr-scopy - - make $JOBS - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install -} - -build_libsigrok() { - echo "### Building libsigrok - branch $LIBSIGROK_BRANCH" - - git clone --depth 1 https://github.com/sigrokproject/libsigrok.git -b $LIBSIGROK_BRANCH ${WORKDIR}/libsigrok - - mkdir ${WORKDIR}/libsigrok/build-${ARCH} - cd ${WORKDIR}/libsigrok - - ./autogen.sh - ./configure --disable-all-drivers --enable-bindings --enable-cxx - - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install - - # For some reason, Scopy chokes if these are present in enums.hpp - #sed -i "s/static const Quantity \* const DIFFERENCE;$//g" ${WORKDIR}/msys64/${MINGW_VERSION}/include/libsigrokcxx/enums.hpp - #sed -i "s/static const QuantityFlag \* const RELATIVE;$//g" ${WORKDIR}/msys64/${MINGW_VERSION}/include/libsigrokcxx/enums.hpp -} - -build_libsigrokdecode() { - echo "### Building libsigrokdecode - branch $LIBSIGROKDECODE_BRANCH" - - git clone --depth 1 https://github.com/sigrokproject/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH ${WORKDIR}/libsigrokdecode - - cd ${WORKDIR}/libsigrokdecode - - ./autogen.sh - ./configure - - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install -} - -build_qwt() { - echo "### Building qwt - branch $QWT_BRANCH" - - git clone https://github.com/cseci/qwt --branch $QWT_BRANCH ${WORKDIR}/qwt - cd ${WORKDIR}/qwt - - $QMAKE qwt.pro - make $JOBS - sudo make install -} - -build_libtinyiiod() { - echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH ${WORKDIR}/libtinyiiod - mkdir ${WORKDIR}/libtinyiiod/build-${ARCH} - cd ${WORKDIR}/libtinyiiod/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - -DBUILD_EXAMPLES=OFF \ - ${WORKDIR}/libtinyiiod - - make $JOBS - sudo make $JOBS install -} - -install_apt -build_libiio -build_libad9361 -build_glog -build_libm2k -#build_griio -build_grscopy -build_grm2k -build_qwt -build_libsigrokdecode -build_libtinyiiod diff --git a/CI/appveyor/install_ubuntu_20_deps.sh b/CI/appveyor/install_ubuntu_20_deps.sh deleted file mode 100755 index 95126e0d1b..0000000000 --- a/CI/appveyor/install_ubuntu_20_deps.sh +++ /dev/null @@ -1,246 +0,0 @@ -#!/bin/bash - -LIBIIO_VERSION=0ed18cd8f6b2fac5204a99e38922bea73f1f778c -LIBAD9361_BRANCH=master -GLOG_BRANCH=v0.4.0 -LIBM2K_BRANCH=master -#GRIIO_BRANCH=upgrade-3.8 -#GNURADIO_FORK=analogdevicesinc -#GNURADIO_BRANCH=scopy -GRSCOPY_BRANCH=3.10 -GRM2K_BRANCH=master -QWT_BRANCH=qwt-multiaxes -LIBSIGROK_BRANCH=master -LIBSIGROKDECODE_BRANCH=master -LIBTINYIIOD_BRANCH=master - -set -e -if [ $# -eq 0 ]; then - echo "Using default qmake" - QMAKE=qmake - $QMAKE --version -else - QMAKE=$1/gcc_64/bin/qmake - $QMAKE --version - -fi - -cd ~ -WORKDIR=${PWD} - -install_apt() { - sudo add-apt-repository -y ppa:gnuradio/gnuradio-releases - sudo apt-get -y update - - sudo apt-get -y install libxml2-dev libxml2 flex bison swig libpython3-all-dev python3 python3-numpy libfftw3-bin libfftw3-dev libfftw3-3 liblog4cpp5v5 liblog4cpp5-dev g++ git cmake autoconf libzip5 libzip-dev libglib2.0-dev libsigc++-2.0-dev libglibmm-2.4-dev libclang1-9 doxygen curl libmatio-dev liborc-0.4-dev subversion mesa-common-dev libgl1-mesa-dev libserialport0 libserialport-dev libusb-1.0 libusb-1.0-0 libusb-1.0-0-dev libtool libaio-dev - - sudo apt-get -y install gnuradio python3-packaging -} - -build_libiio() { - echo "### Building libiio - version $LIBIIO_VERSION" - - cd ~ - git clone https://github.com/analogdevicesinc/libiio.git ${WORKDIR}/libiio - cd ${WORKDIR}/libiio - git checkout $LIBIIO_VERSION - - mkdir ${WORKDIR}/libiio/build-${ARCH} - cd ${WORKDIR}/libiio/build-${ARCH} - # Download a 32-bit version of windres.exe - - cmake ${CMAKE_OPTS} \ - -DWITH_TESTS:BOOL=OFF \ - -DWITH_DOC:BOOL=OFF \ - -DHAVE_DNS_SD:BOOL=OFF\ - -DWITH_MATLAB_BINDINGS:BOOL=OFF \ - -DCSHARP_BINDINGS:BOOL=OFF \ - -DPYTHON_BINDINGS:BOOL=OFF \ - ${WORKDIR}/libiio - - make $JOBS - sudo make ${JOBS} install -# DESTDIR=${WORKDIR} make ${JOBS} install -} - -build_glog() { - - echo "### Building glog - branch $GLOG_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/google/glog.git -b $GLOG_BRANCH ${WORKDIR}/glog - - mkdir ${WORKDIR}/glog/build-${ARCH} - cd ${WORKDIR}/glog/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - -DWITH_GFLAGS=OFF\ - ${WORKDIR}/glog - - make $JOBS - sudo make ${JOBS} install - #DESTDIR=${WORKDIR} make ${JOBS} install -} - -build_libm2k() { - - echo "### Building libm2k - branch $LIBM2K_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH ${WORKDIR}/libm2k - - mkdir ${WORKDIR}/libm2k/build-${ARCH} - cd ${WORKDIR}/libm2k/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - -DENABLE_PYTHON=OFF\ - -DENABLE_CSHARP=OFF\ - -DBUILD_EXAMPLES=OFF\ - -DENABLE_TOOLS=OFF\ - -DINSTALL_UDEV_RULES=OFF\ - -DENABLE_LOG=ON\ - ${WORKDIR}/libm2k - - make $JOBS - sudo make ${JOBS} install - #DESTDIR=${WORKDIR} make ${JOBS} install -} -build_libad9361() { - echo "### Building libad9361 - branch $LIBAD9361_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH ${WORKDIR}/libad9361 - - mkdir ${WORKDIR}/libad9361/build-${ARCH} - cd ${WORKDIR}/libad9361/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - ${WORKDIR}/libad9361 - - make $JOBS - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install -} - -build_griio() { - echo "### Building gr-iio - branch $GRIIO_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/gr-iio.git -b $GRIIO_BRANCH ${WORKDIR}/gr-iio - mkdir ${WORKDIR}/gr-iio/build-${ARCH} - cd ${WORKDIR}/gr-iio/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - ${WORKDIR}/gr-iio - - make $JOBS - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install -} - -build_grm2k() { - echo "### Building gr-m2k - branch $GRM2K_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH ${WORKDIR}/gr-m2k - mkdir ${WORKDIR}/gr-m2k/build-${ARCH} - cd ${WORKDIR}/gr-m2k/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - -DENABLE_PYTHON=OFF \ - -DDIGITAL=OFF \ - ${WORKDIR}/gr-m2k - - make $JOBS - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install - -} - -build_grscopy() { - echo "### Building gr-scopy - branch $GRSCOPY_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH ${WORKDIR}/gr-scopy - mkdir ${WORKDIR}/gr-scopy/build-${ARCH} - cd ${WORKDIR}/gr-scopy/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - ${WORKDIR}/gr-scopy - - make $JOBS - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install -} - -build_libsigrok() { - echo "### Building libsigrok - branch $LIBSIGROK_BRANCH" - - git clone --depth 1 https://github.com/sigrokproject/libsigrok.git -b $LIBSIGROK_BRANCH ${WORKDIR}/libsigrok - - mkdir ${WORKDIR}/libsigrok/build-${ARCH} - cd ${WORKDIR}/libsigrok - - ./autogen.sh - ./configure --disable-all-drivers --enable-bindings --enable-cxx - - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install - - # For some reason, Scopy chokes if these are present in enums.hpp - #sed -i "s/static const Quantity \* const DIFFERENCE;$//g" ${WORKDIR}/msys64/${MINGW_VERSION}/include/libsigrokcxx/enums.hpp - #sed -i "s/static const QuantityFlag \* const RELATIVE;$//g" ${WORKDIR}/msys64/${MINGW_VERSION}/include/libsigrokcxx/enums.hpp -} - -build_libsigrokdecode() { - echo "### Building libsigrokdecode - branch $LIBSIGROKDECODE_BRANCH" - - git clone --depth 1 https://github.com/sigrokproject/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH ${WORKDIR}/libsigrokdecode - - cd ${WORKDIR}/libsigrokdecode - - ./autogen.sh - ./configure - - sudo make $JOBS install - #DESTDIR=${WORKDIR} make $JOBS install -} - -build_qwt() { - echo "### Building qwt - branch $QWT_BRANCH" - - git clone https://github.com/cseci/qwt --branch $QWT_BRANCH ${WORKDIR}/qwt - cd ${WORKDIR}/qwt - - $QMAKE qwt.pro - make $JOBS - sudo make install -} - -build_libtinyiiod() { - echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" - - cd ~ - git clone --depth 1 https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH ${WORKDIR}/libtinyiiod - mkdir ${WORKDIR}/libtinyiiod/build-${ARCH} - cd ${WORKDIR}/libtinyiiod/build-${ARCH} - - cmake ${CMAKE_OPTS} \ - -DBUILD_EXAMPLES=OFF \ - ${WORKDIR}/libtinyiiod - - make $JOBS - sudo make $JOBS install -} - -install_apt -build_libiio -build_libad9361 -build_glog -build_libm2k -#build_griio -build_grscopy -build_grm2k -build_qwt -build_libsigrokdecode -build_libtinyiiod diff --git a/CI/appveyor/mingw_dll_deps b/CI/appveyor/mingw_dll_deps deleted file mode 100644 index ad82b285e3..0000000000 --- a/CI/appveyor/mingw_dll_deps +++ /dev/null @@ -1 +0,0 @@ -libglibmm-*.dll libsigrokdecode-*.dll libgcc_s_*.dll libstdc++-*.dll Qt5Core.dll libboost_thread-mt.dll libgnuradio-analog.dll libgnuradio-blocks.dll libgnuradio-fft.dll libwinpthread-*.dll libgnuradio-filter.dll libgnuradio-m2k.dll libgnuradio-pmt.dll libgnuradio-runtime.dll libgnuradio-scopy.dll libiio.dll Qt5Qml.dll Qt5Xml.dll qwt.dll libglib-*.dll libgmodule-*.dll libsigc-*.dll libgobject-*.dll libpython3.11.dll liblog4cpp.dll libboost_filesystem-mt.dll libboost_chrono-mt.dll libboost_program_options-mt.dll libgmp-*.dll libusb-*.dll libxml2-*.dll Qt5Network.dll Qt5OpenGL.dll Qt5Svg.dll Qt5PrintSupport.dll libintl-*.dll libpcre-*.dll libiconv-*.dll zlib1.dll Qt5Gui.dll libglog.dll tinyiiod.dll libm2k.dll libvolk.dll libfftw3f*.dll Qt5Widgets.dll libicudt*.dll libffi-*.dll liblzma-5.dll libdouble-conversion.dll libicuin*.dll libicuuc*.dll libpcre2-*.dll libzstd.dll libharfbuzz-*.dll libpng16-*.dll liborc-*.dll libfreetype-*.dll libgraphite2.dll libbrotlidec.dll libbz2-*.dll libbrotlicommon.dll libcrypto*.dll libssl*.dll libmd4c.dll libspdlog.dll diff --git a/CI/appveyor/package_darwin.sh b/CI/appveyor/package_darwin.sh deleted file mode 100755 index 00673083ed..0000000000 --- a/CI/appveyor/package_darwin.sh +++ /dev/null @@ -1,105 +0,0 @@ -#!/bin/bash -set -xe -STAGINGDIR="${PWD}/staging" -cd build -mkdir -p ./Scopy.app/Contents/Frameworks - -## Handle libm2k paths -m2kpath=${STAGINGDIR}/lib/libm2k.dylib -m2krpath="$(otool -D ${m2kpath} | grep @rpath)" -m2kid=${m2krpath#"@rpath/"} -sudo cp ${STAGINGDIR}/lib/libm2k.* ./Scopy.app/Contents/Frameworks -sudo install_name_tool -id @executable_path/../Frameworks/${m2kid} ./Scopy.app/Contents/Frameworks/${m2kid} -sudo install_name_tool -change ${m2krpath} @executable_path/../Frameworks/${m2kid} ./Scopy.app/Contents/MacOS/Scopy - -export DYLD_FALLBACK_LIBRARY_PATH=${STAGINGDIR}/lib - -# Copy the iio and ad9361 to the stagingdir path -sudo cp -R /Library/Frameworks/iio.framework ${STAGINGDIR}/lib -sudo cp -R /Library/Frameworks/ad9361.framework ${STAGINGDIR}/lib - -## Bundle some known dependencies -# -ns == no signing -sudo echo "${STAGINGDIR}/lib" | dylibbundler -ns -od -b -x ./Scopy.app/Contents/MacOS/Scopy -d ./Scopy.app/Contents/Frameworks/ -p @executable_path/../Frameworks/ >/dev/null - -## Copy the frameworks dylibbundler failed to copy -sudo cp -R /usr/local/opt/python/Frameworks/Python.framework Scopy.app/Contents/Frameworks/ -sudo cp -R /Library/Frameworks/iio.framework Scopy.app/Contents/Frameworks/ -sudo cp -R /Library/Frameworks/ad9361.framework Scopy.app/Contents/Frameworks/ - -## Handle those framework paths -iiorpath="$(otool -D ./Scopy.app/Contents/Frameworks/iio.framework/iio | grep @rpath)" -iioid=${iiorpath#"@rpath/"} - -ad9361rpath="$(otool -D ./Scopy.app/Contents/Frameworks/ad9361.framework/ad9361 | grep @rpath)" -ad9361id=${ad9361rpath#"@rpath/"} - -libusbpath="$(otool -L ./Scopy.app/Contents/Frameworks/iio.framework/iio | grep libusb | cut -d " " -f 1)" -libusbid="$(echo ${libusbpath} | rev | cut -d "/" -f 1 | rev)" -sudo cp ${libusbpath} ./Scopy.app/Contents/Frameworks/ - -## Check available python version" -for version in 3.8 3.9 3.10 3.11 3.12 -do - if [ -e /usr/local/opt/python@$version/Frameworks/Python.framework/Versions/$version/Python ] ; then - pythonpath=/usr/local/opt/python@$version/Frameworks/Python.framework/Versions/$version/Python - pyversion=$version - pythonidrpath="$(otool -D $pythonpath | head -2 | tail -1)" - fi -done - -if [ -z $pyversion ] ; then - echo "No Python 3.8, 3.9, 3.10, 3.11, 3.12 paths found" - exit 1 -fi -echo " - Found python$version at $pythonpath" -pythonid=${pythonidrpath#"/usr/local/opt/python@${pyversion}/Frameworks/"} -sudo rm -rf Scopy.app/Contents/Frameworks/Python.framework -sudo cp -R /usr/local/opt/python@$pyversion/Frameworks/Python.framework Scopy.app/Contents/Frameworks/ - -## Continue to handle those framework paths -sudo install_name_tool -id @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/iio.framework/iio -sudo install_name_tool -id @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/${iioid} -sudo install_name_tool -id @executable_path/../Frameworks/${ad9361id} ./Scopy.app/Contents/Frameworks/ad9361.framework/ad9361 -sudo install_name_tool -id @executable_path/../Frameworks/${ad9361id} ./Scopy.app/Contents/Frameworks/${ad9361id} -sudo install_name_tool -id @executable_path/../Frameworks/${pythonid} ./Scopy.app/Contents/Frameworks/${pythonid} -sudo install_name_tool -id @executable_path/../Frameworks/${libusbid} ./Scopy.app/Contents/Frameworks/${libusbid} - -sudo install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/MacOS/Scopy -sudo install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/${ad9361id} -sudo install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/libm2k* -sudo install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/libgnuradio-iio* -sudo install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/libgnuradio-m2k* -sudo install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/libgnuradio-scopy* -sudo install_name_tool -change ${m2krpath} @executable_path/../Frameworks/${m2kid} ./Scopy.app/Contents/Frameworks/libgnuradio-m2k* -sudo install_name_tool -change ${m2krpath} @executable_path/../Frameworks/${m2kid} ./Scopy.app/Contents/Frameworks/libgnuradio-scopy* -sudo install_name_tool -change ${ad9361rpath} @executable_path/../Frameworks/${ad9361id} ./Scopy.app/Contents/MacOS/Scopy -sudo install_name_tool -change ${ad9361rpath} @executable_path/../Frameworks/${ad9361id} ./Scopy.app/Contents/Frameworks/libgnuradio-iio* -sudo install_name_tool -change ${pythonidrpath} @executable_path/../Frameworks/${pythonid} ./Scopy.app/Contents/Frameworks/libsigrokdecode* -sudo install_name_tool -change ${libusbpath} @executable_path/../Frameworks/${libusbid} ./Scopy.app/Contents/Frameworks/iio.framework/iio - -if command -v brew ; then - QT_PATH="$(brew --prefix ${QT_FORMULAE})/bin" - export PATH="${QT_PATH}:$PATH" -fi - -## Handle iio-emu + libtinyiiod -sudo cp ./iio-emu/iio-emu ./Scopy.app/Contents/MacOS/ -tinypath=${STAGINGDIR}/lib/tinyiiod.dylib -tinyrpath="$(otool -D ${tinypath} | grep @rpath)" -tinyid=${tinyrpath#"@rpath/"} -sudo cp ${STAGINGDIR}/lib/tinyiiod.* ./Scopy.app/Contents/Frameworks -sudo install_name_tool -id @executable_path/../Frameworks/${tinyid} ./Scopy.app/Contents/Frameworks/${tinyid} -sudo install_name_tool -change ${tinyrpath} @executable_path/../Frameworks/${tinyid} ./Scopy.app/Contents/MacOS/iio-emu - -## Bundle the Qt libraries -sudo macdeployqt Scopy.app - -# curl -o /tmp/macdeployqtfix.py https://raw.githubusercontent.com/aurelien-rainone/macdeployqtfix/master/macdeployqtfix.py -# sudo python /tmp/macdeployqtfix.py ./Scopy.app/Contents/MacOS/Scopy ${QT_PATH} -# sudo python /tmp/macdeployqtfix.py ./Scopy.app/Contents/MacOS/iio-emu ${QT_PATH} -# sudo python /tmp/macdeployqtfix.py ./Scopy.app/Contents/MacOS/Scopy ./Scopy.app/Contents/Frameworks/ - -zip -Xvr ScopyApp.zip Scopy.app # create a zip with the app in case macdeployqt fails -sudo macdeployqt Scopy.app -dmg - diff --git a/CI/armhf/armhf_build_config.sh b/CI/armhf/armhf_build_config.sh deleted file mode 100644 index f33a32eb10..0000000000 --- a/CI/armhf/armhf_build_config.sh +++ /dev/null @@ -1,62 +0,0 @@ -#!/bin/bash - -LIBIIO_VERSION=libiio-v0 -LIBAD9361_BRANCH=main -GLOG_BRANCH=v0.4.0 -LIBM2K_BRANCH=src/output_triggers -SPDLOG_BRANCH=v1.x -VOLK_BRANCH=main -GNURADIO_BRANCH=maint-3.10 -GRSCOPY_BRANCH=3.10 -GRM2K_BRANCH=main -LIBSIGROKDECODE_BRANCH=decoders/AD559XR -QWT_BRANCH=qwt-multiaxes-updated -LIBTINYIIOD_BRANCH=master -IIOEMU_BRANCH=main - -export APPIMAGE=1 - -PYTHON_VERSION=python3.9 # default python version used in CI scripts, can be changed to match locally installed python - -STAGING_AREA=$SRC_DIR/CI/armhf/staging -SYSROOT=$STAGING_AREA/sysroot -SYSROOT_TAR=$STAGING_AREA/sysroot.tar.gz -SYSROOT_DOCKER=$SRC_DIR/CI/armhf/docker/sysroot.tar.gz -TOOLCHAIN=$STAGING_AREA/cross-pi-gcc -TOOLCHAIN_BIN=$TOOLCHAIN/bin -TOOLCHAIN_HOST="arm-linux-gnueabihf" -TOOLCHAIN_FILE=$SRC_DIR/CI/armhf/cmake_toolchain.cmake -QT_LOCATION=$SYSROOT/usr/local/qt5.15 - -CMAKE_BIN=$STAGING_AREA/cmake/bin/cmake -QMAKE_BIN=$QT_LOCATION/bin/qmake -JOBS=-j14 - -APP_DIR=$SRC_DIR/CI/armhf/scopy.AppDir -APP_IMAGE=$SRC_DIR/CI/armhf/Scopy1-armhf.AppImage -APP_RUN=$SRC_DIR/CI/armhf/AppRun -APP_DESKTOP=$SRC_DIR/CI/armhf/scopy.desktop -APP_SQUASHFS=$SRC_DIR/CI/armhf/scopy.squashfs -RUNTIME_ARMHF=$SRC_DIR/CI/armhf/runtime-armhf - -CMAKE_OPTS=(\ - -DCMAKE_SYSROOT="$SYSROOT" \ - -DQT_LOCATION="$QT_LOCATION" \ - -DSTAGING_AREA="$STAGING_AREA" \ - -DCMAKE_INSTALL_PREFIX="$SYSROOT" \ - -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_VERBOSE_MAKEFILE=ON \ - ) - -CMAKE="$CMAKE_BIN ${CMAKE_OPTS[*]}" - -QT_BUILD_LOCATION=$QT_LOCATION # the location where Qt will be installed in the system -QT_SYSTEM_LOCATION=/usr/local/qt5.15 # the Qt location relative to the sysroot folder -CROSS_COMPILER=$STAGING_AREA/cross-pi-gcc - -CROSSCOMPILER_DOWNLOAD_LINK=https://sourceforge.net/projects/raspberry-pi-cross-compilers/files/Raspberry%20Pi%20GCC%20Cross-Compiler%20Toolchains/Bullseye/GCC%2010.2.0/Raspberry%20Pi%203A%2B%2C%203B%2B%2C%204%2C%205/cross-gcc-10.2.0-pi_3%2B.tar.gz -CMAKE_DOWNLOAD_LINK=https://github.com/Kitware/CMake/releases/download/v3.29.0-rc2/cmake-3.29.0-rc2-linux-x86_64.tar.gz -KUIPER_DOWNLOAD_LINK=https://swdownloads.analog.com/cse/kuiper/image_2023-12-13-ADI-Kuiper-full.zip -QT_DOWNLOAD_LINK=http://download.qt.io/archive/qt/5.15/5.15.2/single/qt-everywhere-src-5.15.2.tar.xz -SYSROOT_RELATIVE_LINKS=https://raw.githubusercontent.com/abhiTronix/rpi_rootfs/master/scripts/sysroot-relativelinks.py diff --git a/CI/armhf/armhf_build_process.sh b/CI/armhf/armhf_build_process.sh deleted file mode 100755 index 18f09d03d3..0000000000 --- a/CI/armhf/armhf_build_process.sh +++ /dev/null @@ -1,416 +0,0 @@ -#!/bin/bash - -set -ex -git config --global --add safe.directory $HOME/scopy -SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ -SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) -source $SRC_DIR/CI/armhf/armhf_build_config.sh - -echo -- USING CMAKE COMMAND: -echo $CMAKE -echo -- USING QT: $QT -echo -- USING QMAKE: $QMAKE_BIN - -build_with_cmake() { - BUILD_FOLDER=$PWD/build - rm -rf $BUILD_FOLDER - mkdir -p $BUILD_FOLDER - cd $BUILD_FOLDER - $CMAKE $CURRENT_BUILD_CMAKE_OPTS ../ - make $JOBS - CURRENT_BUILD_CMAKE_OPTS="" -} - -set_config_opts() { - CPP="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-cpp" - CC="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-gcc" - CXX="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-g++" - LD="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-ld" - AS="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-as" - AR="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-ar" - RANLIB="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-ranlib" - - CFLAGS=" -I${SYSROOT}/include -I${SYSROOT}/include/arm-linux-gnueabihf -I${SYSROOT}/usr/include -I${SYSROOT}/usr/include/arm-linux-gnueabihf -I${TOOLCHAIN}/include- -fPIC" - CPPFLAGS="-fexceptions ${CFLAGS}" - LDFLAGS="-Wl,-rpath=XORIGIN -L${TOOLCHAIN}/arm-linux-gnueabihf/lib -L${TOOLCHAIN}/arm-linux-gnueabihf/libc/lib -L${TOOLCHAIN}/arm-linux-gnueabihf/libc/usr/lib -L${SYSROOT}/lib -L${SYSROOT}/usr/lib -L${SYSROOT}/usr/lib/arm-linux-gnueabihf -L${SYSROOT}/usr/lib/arm-linux-gnueabihf" - - CONFIG_OPTS=() - CONFIG_OPTS+=("--prefix=${SYSROOT}") - CONFIG_OPTS+=("--host=${TOOLCHAIN_HOST}") - CONFIG_OPTS+=("--with-sysroot=${SYSROOT}") - CONFIG_OPTS+=("CFLAGS=${CFLAGS}") - CONFIG_OPTS+=("CPPFLAGS=${CPPFLAGS}") - CONFIG_OPTS+=("LDFLAGS=${LDFLAGS}") - CONFIG_OPTS+=("PKG_CONFIG=${SYSROOT}/usr/bin/arm-linux-gnueabihf-pkg-config" ) - CONFIG_OPTS+=("PKG_CONFIG_DIR=") - CONFIG_OPTS+=("PKG_CONFIG_LIBDIR=${SYSROOT}/usr/lib/arm-linux-gnueabihf/pkgconfig:${SYSROOT}/usr/share/pkgconfig:${SYSROOT}/usr/lib/arm-linux-gnueabihf/pkgconfig:${SYSROOT}/usr/local/lib/pkgconfig") - CONFIG_OPTS+=("PKG_CONFIG_SYSROOT=${SYSROOT}") - CONFIG_OPTS+=("PKG_CONFIG_SYSROOT_DIR=${SYSROOT}") - CONFIG_OPTS+=("PKG_CONFIG_PATH=${SYSROOT}/usr/bin/arm-linux-gnueabihf-pkg-config") - CONFIG_OPTS+=("PKG_CONFIG_ALLOW_CROSS=1") - CONFIG_OPTS+=("CPP=${CPP}") - CONFIG_OPTS+=("CC=${CC}") - CONFIG_OPTS+=("CXX=${CXX}") - CONFIG_OPTS+=("LD=${LD}") - CONFIG_OPTS+=("AS=${AS}") - CONFIG_OPTS+=("AR=${AR}") - CONFIG_OPTS+=("RANLIB=${RANLIB}") -} - -install_packages() { - sudo apt update - sudo apt install -y build-essential cmake unzip gfortran gcc git bison libtool \ - ${PYTHON_VERSION}-full pip gperf pkg-config gdb-multiarch g++ flex texinfo gawk openssl \ - pigz libncurses-dev autoconf automake tar figlet liborc-0.4-dev* patchelf libc6-dev-armhf-cross squashfs-tools - pip install mako -} - -download_cmake() { - mkdir -p ${STAGING_AREA} - pushd ${STAGING_AREA} - if [ ! -d cmake ];then - wget ${CMAKE_DOWNLOAD_LINK} - # unzip and rename - tar -xvf cmake*.tar.gz && rm cmake*.tar.gz && mv cmake* cmake - else - echo "Cmake already downloaded" - fi - popd -} - -download_crosscompiler(){ - mkdir -p ${STAGING_AREA} - pushd ${STAGING_AREA} - if [ ! -d cross-pi-gcc ];then - wget --progress=dot:giga ${CROSSCOMPILER_DOWNLOAD_LINK} - # unzip and rename - tar -xf cross-gcc-*.tar.gz && rm cross-gcc-*.tar.gz && mv cross-pi-* cross-pi-gcc - else - echo "Crosscompiler already downloaded" - fi - popd -} - -clone() { - echo "#######CLONE#######" - mkdir -p $STAGING_AREA - pushd $STAGING_AREA - [ -d 'libiio' ] || git clone --recursive https://github.com/analogdevicesinc/libiio.git -b $LIBIIO_VERSION libiio - [ -d 'libad9361' ] || git clone --recursive https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH libad9361 - [ -d 'libm2k' ] || git clone --recursive https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH libm2k - [ -d 'spdlog' ] || git clone --recursive https://github.com/gabime/spdlog.git -b $SPDLOG_BRANCH spdlog - [ -d 'gr-scopy' ] || git clone --recursive https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH gr-scopy - [ -d 'gr-m2k' ] || git clone --recursive https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH gr-m2k - [ -d 'volk' ] || git clone --recursive https://github.com/gnuradio/volk.git -b $VOLK_BRANCH volk - [ -d 'gnuradio' ] || git clone --recursive https://github.com/gnuradio/gnuradio.git -b $GNURADIO_BRANCH gnuradio - [ -d 'qwt' ] || git clone --recursive https://github.com/cseci/qwt.git -b $QWT_BRANCH qwt - [ -d 'libsigrokdecode' ] || git clone --recursive https://github.com/analogdevicesinc/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH libsigrokdecode - [ -d 'libtinyiiod' ] || git clone --recursive https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH libtinyiiod - popd -} - -build_libiio() { - echo "### Building libiio - version $LIBIIO_VERSION" - pushd $STAGING_AREA/libiio - CURRENT_BUILD_CMAKE_OPTS="\ - -DWITH_TESTS:BOOL=OFF \ - -DWITH_DOC:BOOL=OFF \ - -DHAVE_DNS_SD:BOOL=OFF\ - -DWITH_MATLAB_BINDINGS:BOOL=OFF \ - -DCSHARP_BINDINGS:BOOL=OFF \ - -DPYTHON_BINDINGS:BOOL=OFF \ - -DWITH_SERIAL_BACKEND:BOOL=ON \ - -DENABLE_IPV6:BOOL=OFF \ - -DINSTALL_UDEV_RULE:BOOL=OFF - " - build_with_cmake - sudo make install - popd -} - -build_libad9361() { - echo "### Building libad9361 - branch $LIBAD9361_BRANCH" - pushd $STAGING_AREA/libad9361 - build_with_cmake - sudo make install - popd -} - -build_spdlog() { - echo "### Building spdlog - branch $SPDLOG_BRANCH" - pushd $STAGING_AREA/spdlog - CURRENT_BUILD_CMAKE_OPTS="-DSPDLOG_BUILD_SHARED=ON" - build_with_cmake - sudo make install - popd -} - -build_libm2k() { - echo "### Building libm2k - branch $LIBM2K_BRANCH" - pushd $STAGING_AREA/libm2k - CURRENT_BUILD_CMAKE_OPTS="\ - -DENABLE_PYTHON=OFF \ - -DENABLE_CSHARP=OFF \ - -DBUILD_EXAMPLES=OFF \ - -DENABLE_TOOLS=OFF \ - -DINSTALL_UDEV_RULES=OFF \ - -DENABLE_LOG=OFF - " - build_with_cmake - sudo make install - popd -} - -build_volk() { - echo "### Building volk - branch $VOLK_BRANCH" - pushd $STAGING_AREA/volk - build_with_cmake - sudo make install - popd -} - -build_gnuradio() { - echo "### Building gnuradio - branch $GNURADIO_BRANCH" - pushd $STAGING_AREA/gnuradio - CURRENT_BUILD_CMAKE_OPTS="\ - -DENABLE_DEFAULT=OFF \ - -DENABLE_GNURADIO_RUNTIME=ON \ - -DENABLE_GR_ANALOG=ON \ - -DENABLE_GR_BLOCKS=ON \ - -DENABLE_GR_FFT=ON \ - -DENABLE_GR_FILTER=ON \ - -DENABLE_GR_IIO=ON \ - -DENABLE_POSTINSTALL=OFF - " - build_with_cmake - sudo make install - popd -} - -build_grscopy() { - echo "### Building gr-scopy - branch $GRSCOPY_BRANCH" - pushd $STAGING_AREA/gr-scopy - build_with_cmake - sudo make install - popd -} - -build_grm2k() { - echo "### Building gr-m2k - branch $GRM2K_BRANCH" - pushd $STAGING_AREA/gr-m2k - CURRENT_BUILD_CMAKE_OPTS="\ - -DENABLE_PYTHON=OFF \ - -DDIGITAL=OFF - " - build_with_cmake - sudo make install - popd -} - -build_qwt() { - echo "### Building qwt - branch $QWT_BRANCH" - pushd $STAGING_AREA/qwt - git clean -xdf - sed -i 's|/usr/local/qwt-$$QWT_VERSION-ma|/usr/local|g' qwtconfig.pri - $QMAKE_BIN INCLUDEPATH=$SYSROOT/include LIBS=-L$SYSROOT/lib qwt.pro - make $JOBS - patchelf --force-rpath --set-rpath \$ORIGIN $STAGING_AREA/qwt/lib/libqwt.so - sudo make INSTALL_ROOT=$SYSROOT install - popd -} - -build_libsigrokdecode() { - echo "### Building libsigrokdecode - branch $LIBSIGROKDECODE_BRANCH" - set_config_opts - pushd $STAGING_AREA/libsigrokdecode - git clean -xdf - ./autogen.sh - ./configure "${CONFIG_OPTS[@]}" - make $JOBS - patchelf --force-rpath --set-rpath \$ORIGIN $STAGING_AREA/libsigrokdecode/.libs/libsigrokdecode.so - sudo make install - popd -} - -build_libtinyiiod() { - echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" - pushd $STAGING_AREA/libtinyiiod - CURRENT_BUILD_CMAKE_OPTS="-DBUILD_EXAMPLES=OFF" - build_with_cmake - sudo make install - popd -} - -build_iio-emu(){ - echo "### Building iio-emu - branch $IIOEMU_BRANCH" - pushd $STAGING_AREA - [ -d 'iio-emu' ] || git clone --recursive https://github.com/analogdevicesinc/iio-emu -b $IIOEMU_BRANCH iio-emu - pushd $STAGING_AREA/iio-emu - if [ -d 'build' ];then - echo "### IIO-EMU already built --- skipping" - else - build_with_cmake - sudo make install - fi - popd - popd -} - -build_scopy() { - echo "### Building scopy" - pushd $SRC_DIR - CURRENT_BUILD_CMAKE_OPTS="\ - -DCLONE_IIO_EMU=OFF \ - -DPYTHON_EXECUTABLE=/usr/bin/python3.9 - " - build_with_cmake - popd -} - -create_appdir(){ - - BUILD_FOLDER=$SRC_DIR/build - EMU_BUILD_FOLDER=$STAGING_AREA/iio-emu/build - COPY_DEPS=$SRC_DIR/CI/armhf/copy-deps.sh - - rm -rf $APP_DIR - mkdir $APP_DIR - mkdir -p $APP_DIR/usr/bin - mkdir -p $APP_DIR/usr/lib - mkdir -p $APP_DIR/usr/share/applications - mkdir -p $APP_DIR/usr/share/icons/hicolor/512x512 - - cp $APP_RUN $APP_DIR - cp $APP_DESKTOP $APP_DIR - cp $SRC_DIR/resources/Scopy.png $APP_DIR - cp $SRC_DIR/resources/Scopy.png $APP_DIR/usr/share/icons/hicolor/512x512 - cp $APP_DESKTOP $APP_DIR/usr/share/applications - - cp $EMU_BUILD_FOLDER/iio-emu $APP_DIR/usr/bin - cp $BUILD_FOLDER/scopy $APP_DIR/usr/bin - - $COPY_DEPS $APP_DIR/usr/bin/scopy $APP_DIR/usr/lib - $COPY_DEPS $APP_DIR/usr/bin/iio-emu $APP_DIR/usr/lib - - cp -r $QT_LOCATION/plugins $APP_DIR/usr - # search for the python version linked by cmake and copy inside the appimage the same version - FOUND_PYTHON_VERSION=$(grep 'PYTHON_VERSION' $SRC_DIR/build/CMakeCache.txt | awk -F= '{print $2}' | grep -o 'python[0-9]\+\.[0-9]\+') - python_path=${SYSROOT}/usr/lib/$FOUND_PYTHON_VERSION - cp -r $python_path $APP_DIR/usr/lib - cp -r $SYSROOT/share/libsigrokdecode/decoders $APP_DIR/usr/lib - - cp $QT_LOCATION/lib/libQt5XcbQpa.so* $APP_DIR/usr/lib - cp $QT_LOCATION/lib/libQt5EglFSDeviceIntegration.so* $APP_DIR/usr/lib - cp $QT_LOCATION/lib/libQt5DBus.so* $APP_DIR/usr/lib - cp $SYSROOT/lib/arm-linux-gnueabihf/libGLESv2.so* $APP_DIR/usr/lib - cp $SYSROOT/usr/lib/arm-linux-gnueabihf/libmd.so* $APP_DIR/usr/lib - cp $SYSROOT/lib/arm-linux-gnueabihf/libbsd.so* $APP_DIR/usr/lib - cp $SYSROOT/lib/arm-linux-gnueabihf/libXdmcp.so* $APP_DIR/usr/lib - cp $SYSROOT/usr/lib/arm-linux-gnueabihf/libXau.so* $APP_DIR/usr/lib - cp $SYSROOT/usr/lib/arm-linux-gnueabihf/libffi.so* $APP_DIR/usr/lib -} - -create_appimage(){ - rm -rf $APP_IMAGE - mksquashfs $APP_DIR $APP_SQUASHFS -root-owned -noappend - cat $RUNTIME_ARMHF >> $APP_IMAGE - cat $APP_SQUASHFS >> $APP_IMAGE - chmod a+x $APP_IMAGE -} - -# move the sysroot from the home of the docker container to the known location -move_sysroot(){ - mkdir -p $STAGING_AREA - [ -d /home/runner/sysroot ] && sudo mv /home/runner/sysroot $SYSROOT || echo "Sysroot not found or already moved" - if [ ! -d $SYSROOT ];then - echo "Missing SYSROOT" - exit 1 - fi -} - -# move the staging folder that contains the tools needed for the build to the known location -move_tools(){ - [ -d /home/runner/staging ] && mv /home/runner/staging $STAGING_AREA || echo "Staging folder not found or already moved" - if [ ! -d $STAGING_AREA ]; then - echo "Missing tools folder, downloading now" - download_cmake - download_crosscompiler - fi -} - -# move and rename the AppImage artifact -move_appimage(){ - [ -f $APP_IMAGE ] && mv $APP_IMAGE $SRC_DIR/Scopy1-armhf.AppImage || echo "Appimage not found" -} - -generate_ci_envs(){ - $SRC_DIR/CI/appveyor/gen_ci_envs.sh > $SRC_DIR/CI/armhf/gh-actions.envs -} - -# -# Helper functions -# -build_deps(){ - build_libiio - build_libad9361 - build_spdlog - build_libm2k - build_volk - build_gnuradio - build_grscopy - build_grm2k - build_qwt - build_libsigrokdecode - build_libtinyiiod -} - -run_workflow(){ - install_packages - move_tools - move_sysroot - build_iio-emu - build_scopy - create_appdir - create_appimage - move_appimage -} - -get_tools(){ - install_packages - download_cmake - download_crosscompiler - move_sysroot -} - -generate_appimage(){ - build_iio-emu - build_scopy - create_appdir - create_appimage -} - -dev_setup(){ - # for the local development of Scopy armhf the easyest method is to download the docker image - # a temporary docker volume is created to bridge the local environment and the docker container - # the compiling is done inside the container unsing the already prepared filesystem - docker pull cristianbindea/scopy1-armhf-appimage:latest - docker run -it \ - --mount type=bind,source="$SRC_DIR",target=/home/runner/scopy \ - cristianbindea/scopy1-armhf-appimage:latest - # now this repository folder it shared with the docker container - - # to compile the application use "scopy/CI/armhf/armhf_build_process.sh get_tools generate_appimage" - # after the first compilation just use "scopy/CI/armhf/armhf_build_process.sh generate_appimage" - # to continue using the same docker container use docker start (container id) and "docker attach (container id)" - - # finally after the development is done use this to clean the system - # "docker container rm -v (container id)" - # "docker image rm cristianbindea/scopy1-armhf-appimage:latest" - - # to get the container id use "docker container ls -a" -} - - -for arg in $@; do - $arg -done diff --git a/CI/armhf/build_qt.sh b/CI/armhf/build_qt.sh deleted file mode 100755 index 638c0d8c6f..0000000000 --- a/CI/armhf/build_qt.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/bin/bash - -set -ex -SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ -SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) -source $SRC_DIR/CI/armhf/armhf_build_config.sh - -install_packages(){ - sudo apt install -y build-essential cmake unzip gfortran gcc git bison \ - python gperf pkg-config gdb-multiarch g++ flex texinfo gawk openssl \ - pigz libncurses-dev autoconf automake tar figlet libclang-dev -} - -# Download and extract QT Source (QT 5.15.2) -download_qt(){ - mkdir -p ${STAGING_AREA} - pushd ${STAGING_AREA} - if [ ! -d qt-everywhere-src ];then - wget --progress=dot:giga ${QT_DOWNLOAD_LINK} - tar -xf qt-everywhere-src-*.tar.xz && rm qt-everywhere-src-*.tar.xz && mv qt-everywhere-src-* qt-everywhere-src # unzip and rename - cd qt-everywhere-src - patch -p1 < $SRC_DIR/CI/armhf/qt_patch.patch # Patch QT Source - else - echo "QT already downloaded" - fi - popd -} - -download_crosscompiler(){ - mkdir -p ${STAGING_AREA} - pushd ${STAGING_AREA} - if [ ! -d cross-pi-gcc ];then - wget --progress=dot:giga ${CROSSCOMPILER_DOWNLOAD_LINK} - tar -xf cross-gcc-*.tar.gz && rm cross-gcc-*.tar.gz && mv cross-pi-* cross-pi-gcc # unzip and rename - else - echo "Crosscompiler already downloaded" - fi - popd -} - -build_qt5.15.2(){ - mkdir -p $STAGING_AREA/build-qt5.15.2 && cd $STAGING_AREA/build-qt5.15.2 - ../qt-everywhere-src/configure \ - -v \ - -release \ - -opensource \ - -confirm-license \ - -sysroot $SYSROOT \ - -prefix $QT_SYSTEM_LOCATION \ - -extprefix $QT_BUILD_LOCATION \ - -eglfs \ - -opengl desktop \ - -device linux-rasp-pi4-v3d-g++ \ - -device-option CROSS_COMPILE=$CROSS_COMPILER/bin/arm-linux-gnueabihf- \ - -skip qtscript \ - -skip qtwayland \ - -skip qtwebengine \ - -nomake tests \ - -make libs \ - -pkg-config \ - -no-use-gold-linker \ - -recheck \ - -xcb \ - -xcb-xlib \ - -bundled-xcb-xinput \ - -qt-pcre \ - -qpa eglfs \ - -L$SYSROOT/usr/lib/arm-linux-gnueabihf -I$SYSROOT/usr/include/arm-linux-gnueabihf - - make -j14 - sudo make install # installs to $QT_BUILD_LOCATION -} - -for arg in $@; do - $arg -done diff --git a/CI/armhf/cmake_toolchain.cmake b/CI/armhf/cmake_toolchain.cmake deleted file mode 100644 index 5209043079..0000000000 --- a/CI/armhf/cmake_toolchain.cmake +++ /dev/null @@ -1,89 +0,0 @@ -cmake_minimum_required(VERSION 3.18) - -set(CMAKE_SYSTEM_NAME Linux) -set(CMAKE_SYSTEM_PROCESSOR arm) -set(CMAKE_LIBRARY_ARCHITECTURE arm-linux-gnueabihf) - -# In this case the variables CMAKE_SYSROOT and STAGING_AREA are defined as parameters to the cmake command -set(TOOLCHAIN_FILE ${STAGING_AREA}/cross-pi-gcc) -set(TOOLCHAIN_BIN ${TOOLCHAIN_FILE}/bin) -set(CMAKE_PREFIX_PATH ${QT_LOCATION}) -list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/usr/lib/arm-linux-gnueabihf") -list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/lib") -set(CMAKE_VERBOSE ON) - -set(PKG_CONFIG_EXECUTABLE "${CMAKE_SYSROOT}/usr/bin/arm-linux-gnueabihf-pkg-config" CACHE PATH "PKG_CONFIG_EXECUTABLE" - FORCE -) -set(ENV{PKG_CONFIG_ALLOW_CROSS} 1) -set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON) -set(RPI_PKG_CONFIG_LIBDIR "${CMAKE_SYSROOT}/usr/lib/arm-linux-gnueabihf/pkgconfig:${RPI_PKG_CONFIG_LIBDIR}") -set(RPI_PKG_CONFIG_LIBDIR "${CMAKE_SYSROOT}/usr/share/pkgconfig:${RPI_PKG_CONFIG_LIBDIR}") -set(RPI_PKG_CONFIG_LIBDIR "${CMAKE_SYSROOT}/usr/lib/pkgconfig:${RPI_PKG_CONFIG_LIBDIR}") -set(RPI_PKG_CONFIG_LIBDIR "${CMAKE_SYSROOT}/usr/local/lib/pkgconfig:${RPI_PKG_CONFIG_LIBDIR}") -set(ENV{PKG_CONFIG_LIBDIR} "${RPI_PKG_CONFIG_LIBDIR}") -set(ENV{PKG_CONFIG} "${CMAKE_SYSROOT}/usr/bin/arm-linux-gnueabihf-pkg-config") - -set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/include:") -set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/include:") -set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/include/arm-linux-gnueabihf:") -set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/share/include:") - -set(ENV{PKG_CONFIG_SYSROOT_DIR} "${CMAKE_SYSROOT}") -set(ENV{LD_LIBRARY_PATH} "${CMAKE_SYSROOT}/usr/lib/arm-linux-gnueabihf:$ENV{LD_LIBRARY_PATH}") - -set(CMAKE_LIBRARY_PATH "${CMAKE_SYSROOT}/usr/lib") -set(CMAKE_LIBRARY_PATH "${CMAKE_LIBRARY_PATH} ${CMAKE_SYSROOT}/usr/lib/arm-linux-gnueabihf") -set(CMAKE_LIBRARY_PATH "${CMAKE_LIBRARY_PATH} ${CMAKE_SYSROOT}/usr/local/lib") -set(CMAKE_LIBRARY_PATH "${CMAKE_LIBRARY_PATH} ${CMAKE_SYSROOT}/usr/local/lib/arm-linux-gnueabihf") - -set(CMAKE_AR ${TOOLCHAIN_BIN}/arm-linux-gnueabihf-ar) -set(CMAKE_ASM_COMPILER ${TOOLCHAIN_BIN}/arm-linux-gnueabihf-gcc) -set(CMAKE_C_COMPILER ${TOOLCHAIN_BIN}/arm-linux-gnueabihf-gcc) -set(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN}/arm-linux-gnueabihf-g++) -set(CMAKE_LINKER ${TOOLCHAIN_BIN}/arm-linux-gnueabihf-ld) -set(CMAKE_OBJCOPY ${TOOLCHAIN_BIN}/arm-linux-gnueabihf-objcopy) -set(CMAKE_RANLIB ${TOOLCHAIN_BIN}/arm-linux-gnueabihf-ranlib) -set(CMAKE_SIZE ${TOOLCHAIN_BIN}/arm-linux-gnueabihf-size) -set(CMAKE_STRIP ${TOOLCHAIN_BIN}/arm-linux-gnueabihf-strip) - -set(CMAKE_C_FLAGS "-march=armv7-a -mfloat-abi=hard -mfpu=vfp") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/include") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/include") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/include/arm-linux-gnueabihf") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/share/include") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") -set(CMAKE_CXX_FLAGS "-fexceptions -frtti ${CMAKE_C_FLAGS}") -set(CMAKE_CXX_FLAGS_DEBUG "-Os -g") -set(CMAKE_CXX_FLAGS_RELEASE "-Os -DNDEBUG") -set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,-O1 -Wl,--hash-style=gnu -mthumb -lpthread -pthread") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/lib/arm-linux-gnueabihf") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/lib") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${QT_LOCATION}/lib") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${TOOLCHAIN_FILE}/arm-linux-gnueabihf/lib") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${TOOLCHAIN_FILE}/arm-linux-gnueabihf/libc/lib") -set(CMAKE_SHARED_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}) - -#[[ -# Debug Mode -set(PKG_CONFIG_ARGN "--debug") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--verbose ") -]] - -set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) # Perform compiler test with static library -set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) -set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) -set(CMAKE_INSTALL_RPATH - "$ORIGIN" - "$ORIGIN/../lib" - "/usr/lib/arm-linux-gnueabihf" - "/lib/arm-linux-gnueabihf" - "/lib" - "/usr/local/qt5.15/lib" -) -set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) diff --git a/CI/armhf/copy-deps.sh b/CI/armhf/copy-deps.sh deleted file mode 100755 index 046611781a..0000000000 --- a/CI/armhf/copy-deps.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -set -e -SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ -SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) -source $SRC_DIR/CI/armhf/armhf_build_config.sh - -BINARY=$1 -LOCATION=$2 -LIBS_ARRAY=() -BLACKLISTED=($(wget --quiet https://raw.githubusercontent.com/probonopd/AppImages/master/excludelist -O - | sort | uniq | cut -d '#' -f 1 | grep -v "^#.*" | grep "[^-\s]")) - -if [ ! -f "${SRC_DIR}"/CI/armhf/ldd-mod ]; then - sed 's|.*RTLDLIST=.*|RTLDLIST="/usr/arm-linux-gnueabihf/lib/ld-2.31.so /usr/arm-linux-gnueabihf/lib/ld-linux-armhf.so.3"|' /usr/bin/ldd | tee "${SRC_DIR}"/CI/armhf/ldd-mod - chmod +x "${SRC_DIR}"/CI/armhf/ldd-mod -fi - -export LD_LIBRARY_PATH="${APP_DIR}/usr/lib:${SYSROOT}/lib:${SYSROOT}/lib/arm-linux-gnueabihf:${SYSROOT}/usr/arm-linux-gnueabihf/lib:${SYSROOT}/usr/local/qt5.15/lib:${SYSROOT}/usr/local/lib:${SRC_DIR}/build" -run_ldd(){ - - if [ ! -z "$(${SRC_DIR}/CI/armhf/ldd-mod $1 | grep "not found")" ]; then - echo "--- LIB NOT FOUND" - ${SRC_DIR}/CI/armhf/ldd-mod $1 - exit 1 - fi - - for library in $("${SRC_DIR}"/CI/armhf/ldd-mod "$1" | cut -d '>' -f 2 | awk '{print $1}') - do - # check if the library exists at that path and if it was processed already or blacklisted - if ! [[ "${BLACKLISTED[*]}" =~ "${library##*/}" ]]; then - if [ -f "${library}" ] && ! [[ "${LIBS_ARRAY[*]}" =~ ${library} ]]; then - LIBS_ARRAY+=("${library}") - echo "---Added new lib: ${library}" - if [ ! -f "${LOCATION}"/"${library##*/}" ]; then - cp "${library}" "${LOCATION}" - [ -L "${library}" ] && cp "$(realpath "${library}")" "${LOCATION}" - fi - run_ldd "${library}" - fi - fi - done -} - -for arg in $BINARY; do - run_ldd "${arg}" -done diff --git a/CI/armhf/create_docker_image.sh b/CI/armhf/create_docker_image.sh deleted file mode 100755 index 828039a541..0000000000 --- a/CI/armhf/create_docker_image.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash - -set -ex -SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ -SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) -source $SRC_DIR/CI/armhf/armhf_build_config.sh - -# install docker -install_packages(){ - sudo apt-get update - sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - - sudo add-apt-repository --yes "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" - sudo apt-get update - sudo apt-get -y install containerd.io docker-ce docker-ce-cli docker-buildx-plugin -} - -create_sysroot(){ - $SRC_DIR/CI/armhf/create_sysroot.sh \ - install_packages \ - download_kuiper \ - install_qemu \ - extract_sysroot \ - configure_sysroot -} - -# archive the sysroot and move it next to Dockerfile in order to copy the tar in the docker image -tar_and_move_sysroot(){ - pushd $STAGING_AREA - sudo tar -czvf "${SYSROOT_TAR##*/}" sysroot - sudo mv $SYSROOT_TAR $SYSROOT_DOCKER - popd -} - -create_image(){ - pushd ${SRC_DIR}/CI/armhf/docker - sudo docker build --load --tag astanea/scopy1-armhf-appimage . - # sudo DOCKER_BUILDKIT=0 docker build --tag cristianbindea/scopy1-armhf-appimage . # build the image using old backend - popd -} - -install_packages -create_sysroot -tar_and_move_sysroot -create_image diff --git a/CI/armhf/create_sysroot.sh b/CI/armhf/create_sysroot.sh deleted file mode 100755 index a7d9255cb2..0000000000 --- a/CI/armhf/create_sysroot.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/bash - -set -ex -SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ -SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) -source $SRC_DIR/CI/armhf/armhf_build_config.sh - -IMAGE_FILE=2023-12-13-ADI-Kuiper-full.img - -install_packages(){ - sudo apt update - sudo apt -y install git wget unzip python3 python2 -} - -download_kuiper(){ - mkdir -p ${STAGING_AREA} - pushd ${STAGING_AREA} - [ -f image_2023-12-13-ADI-Kuiper-full.zip ] || wget --progress=dot:giga ${KUIPER_DOWNLOAD_LINK} - [ -f 2023-12-13-ADI-Kuiper-full.img ] || unzip image*.zip - popd -} - -# install qemu needed for the sysroot configuration -install_qemu(){ - sudo apt update - sudo apt -y install qemu qemu-system qemu-user-static qemu-user -} - -# mount the Kuiper image and copy the entire rootfs partition -extract_sysroot(){ - sudo mkdir -p /mnt/kuiper - - # with file ${IMAGE_FILE} we can see the start sector (4218880) and the length (19947520) of the second partition contained in the Kuiper image - # using this info we can directly mount that partition - sudo mount -v -o loop,offset=$((512*4218880)),sizelimit=$((512*19947520)) ${STAGING_AREA}/${IMAGE_FILE} /mnt/kuiper - - mkdir -p ${SYSROOT} - sudo cp -arp /mnt/kuiper/* ${SYSROOT} - sudo cp /etc/resolv.conf ${SYSROOT}/etc/resolv.conf - sudo umount /mnt/kuiper - sudo rm -rf /mnt/kuiper - rm -rf ${STAGING_AREA:?}/${IMAGE_FILE} - rm -rf ${STAGING_AREA}/image*.zip -} - - -# execute chroot inside the sysroot folder and install/remove packages using apt -configure_sysroot(){ - cat $SRC_DIR/CI/armhf/inside_chroot.sh | sudo chroot ${SYSROOT} -} - -move_and_extract_sysroot(){ - if [ -f $HOME/sysroot.tar.gz ]; then - mkdir -p $STAGING_AREA - sudo tar -xf $HOME/sysroot.tar.gz --directory $STAGING_AREA - rm $HOME/sysroot.tar.gz - fi -} - -fix_relativelinks(){ - pushd ${STAGING_AREA} - wget $SYSROOT_RELATIVE_LINKS - chmod +x sysroot-relativelinks.py - sudo ./sysroot-relativelinks.py ${SYSROOT} - popd -} - -for arg in $@; do - $arg -done - diff --git a/CI/armhf/docker/Dockerfile b/CI/armhf/docker/Dockerfile deleted file mode 100644 index 1ddf40e848..0000000000 --- a/CI/armhf/docker/Dockerfile +++ /dev/null @@ -1,63 +0,0 @@ -FROM --platform=linux/amd64 ubuntu:20.04 AS start -SHELL ["/bin/bash", "-c"] -ARG USER=runner -ENV DEBIAN_FRONTEND=noninteractive -ENV TZ=Europe/Bucharest -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -RUN apt-get update && \ - apt-get -y upgrade && \ - apt-get install -y apt-utils sudo git wget flex bison pkg-config make python3 pip vim python-is-python3 -RUN groupadd -g 1000 -r $USER && \ - useradd -u 1000 -g 1000 --create-home -r $USER - -#Change password -RUN echo "$USER:$USER" | chpasswd - -#Make sudo passwordless -RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-$USER && \ - usermod -aG sudo $USER && \ - usermod -aG plugdev $USER - -USER $USER -WORKDIR /home/${USER} - - - -FROM start AS sysroot_builder -ARG USER=runner -ENV DEBIAN_FRONTEND=noninteractive -COPY sysroot.tar.gz /home/${USER} -RUN git clone https://github.com/analogdevicesinc/scopy -b main -WORKDIR /home/${USER}/scopy -RUN ./CI/armhf/create_sysroot.sh \ - install_packages \ - move_and_extract_sysroot \ - fix_relativelinks -RUN ./CI/armhf/build_qt.sh \ - install_packages \ - download_qt \ - download_crosscompiler \ - build_qt5.15.2 -RUN ./CI/armhf/armhf_build_process.sh \ - install_packages \ - download_cmake \ - download_crosscompiler \ - clone \ - build_deps - - - -FROM start -ARG USER=runner -ENV DEBIAN_FRONTEND=noninteractive -RUN mkdir -p /home/${USER}/sysroot -COPY --from=sysroot_builder /home/${USER}/scopy/CI/armhf/staging/sysroot/usr /home/${USER}/sysroot/usr -COPY --from=sysroot_builder /home/${USER}/scopy/CI/armhf/staging/sysroot/share /home/${USER}/sysroot/share -COPY --from=sysroot_builder /home/${USER}/scopy/CI/armhf/staging/sysroot/include /home/${USER}/sysroot/include -WORKDIR /home/${USER}/sysroot -RUN ln -s usr/bin bin && ln -s usr/lib lib && ln -s usr/sbin sbin -WORKDIR /home/${USER} -COPY armhf_build_process.sh /home/${USER}/armhf_build_process.sh -COPY armhf_build_config.sh /home/${USER}/armhf_build_config.sh -RUN /home/${USER}/armhf_build_process.sh install_packages download_cmake download_crosscompiler && \ - rm /home/${USER}/armhf_build_process.sh /home/${USER}/armhf_build_config.sh \ No newline at end of file diff --git a/CI/armhf/inside_chroot.sh b/CI/armhf/inside_chroot.sh deleted file mode 100644 index 24b2d3c623..0000000000 --- a/CI/armhf/inside_chroot.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash - -export DEBIAN_FRONTEND=noninteractive -ln -snf /usr/share/zoneinfo/Europe/Bucharest /etc/localtime && echo "Europe/Bucharest" > /etc/timezone -echo "LC_ALL=en_US.UTF-8" | tee -a /etc/environment -echo "LANG=en_US.UTF-8" | tee -a /etc/locale.conf -locale-gen en_US.UTF-8 - -sed -i 's/#deb-src/deb-src/' /etc/apt/sources.list - -apt -y purge openjdk* tex-common -apt -y remove gnuradio gnuradio-* libgnuradio-* libvolk* -apt -y remove qt* libqt5* -apt -y autoremove -apt update && apt -y upgrade -apt -y dist-upgrade -dpkg --configure -a - -rm -rf /usr/local/include/volk /usr/local/lib/libvolk* \ - /usr/local/lib/cmake/volk -rm -rf /usr/lib/arm-linux-gnueabihf/libiio.so* \ - usr/local/src/libiio \ - /usr/lib/arm-linux-gnueabihf/pkgconfig/libiio.pc -rm -rf /usr/local/lib/cmake/m2k \ - /usr/local/lib/arm-linux-gnueabihf/pkgconfig/libm2k.pc \ - /usr/local/lib/arm-linux-gnueabihf/pkgconfig/gnuradio-m2k.pc \ - /usr/local/lib/arm-linux-gnueabihf/libgnuradio-m2k.so* \ - /usr/local/lib/arm-linux-gnueabihf/libm2k.so* \ - /usr/local/lib/arm-linux-gnueabihf/cmake/libm2k \ - /usr/local/include/libm2k /usr/local/include/m2k -rm -rf /usr/local/lib/arm-linux-gnueabihf/libgnuradio-iio.so* \ - /usr/local/lib/arm-linux-gnueabihf/pkgconfig/gnuradio-iio.pc \ - /usr/local/lib/cmake/iio -rm -rf /usr/lib/arm-linux-gnueabihf/libad9361.so* \ - /usr/lib/arm-linux-gnueabihf/pkgconfig/libad9361.pc \ - /usr/lib/libad9166.so* \ - /usr/lib/pkgconfig/libad9166.pc - -apt -y build-dep qtbase5-dev || true -apt -y install build-essential gcc g++ gdb-multiarch cmake autoconf automake bison flex git wget pkg-config figlet gawk unzip libsndfile1-dev \ - libudev-dev libinput-dev libts-dev libxcb-xinerama0-dev libxcb-xinerama0 gdbserver libspeechd-dev perl \ - libgl1-mesa-dev libxcb-composite0-dev libxcb-cursor-dev libxcb-damage0-dev libxcb-xv0-dev \ - libxcb-dpms0-dev libxcb-dri2-0-dev libxcb-ewmh-dev libxcb-imdkit-dev libxcb-xvmc0-dev \ - libxcb-present-dev libxcb-record0-dev libxcb-res0-dev libxcb-xrm-dev libx11-xcb-dev libxcb-glx0-dev libxcb-icccm4 libxcb-icccm4-dev libxcb-xkb-dev libxkbcommon-x11-dev \ - libxcb-screensaver0-dev libxcb-util0-dev libxcb-xf86dri0-dev libxcb-xtest0-dev -apt -y install libunwind-dev libsndfile1-dev mesa-utils* mesa-common-dev libglu1-mesa-dev freeglut3-dev mesa-common-dev python2 libopenal-dev || true -apt -y install '^libxcb.*-dev' libx11-xcb-dev libglu1-mesa-dev libxrender-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev || true - -wget https://raw.githubusercontent.com/abhiTronix/raspberry-pi-cross-compilers/master/utils/SSymlinker -sed -i 's/sudo//g' SSymlinker -chmod +x SSymlinker -./SSymlinker -s /usr/include/arm-linux-gnueabihf/asm -d /usr/include -./SSymlinker -s /usr/include/arm-linux-gnueabihf/gnu -d /usr/include -./SSymlinker -s /usr/include/arm-linux-gnueabihf/bits -d /usr/include -./SSymlinker -s /usr/include/arm-linux-gnueabihf/sys -d /usr/include -./SSymlinker -s /usr/lib/arm-linux-gnueabihf/crtn.o -d /usr/lib/crtn.o -./SSymlinker -s /usr/lib/arm-linux-gnueabihf/crt1.o -d /usr/lib/crt1.o -./SSymlinker -s /usr/lib/arm-linux-gnueabihf/crti.o -d /usr/lib/crti.o \ No newline at end of file diff --git a/CI/armhf/local_build_scopy_for_kuiper.sh b/CI/armhf/local_build_scopy_for_kuiper.sh deleted file mode 100644 index 891c407738..0000000000 --- a/CI/armhf/local_build_scopy_for_kuiper.sh +++ /dev/null @@ -1,172 +0,0 @@ -#!/bin/bash -set -e -set -x -export PS4='+(${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' - -HOME="/home/analog" -STAGING_AREA=$HOME"/staging" -STAGING_AREA_DEPS=$STAGING_AREA"/dependencies" -JOBS=1 - -mkdir -p $STAGING_AREA_DEPS -export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$STAGING_AREA_DEPS/lib - -apt-get install libsndfile-dev -y -apt-get install libbost1.71-all-dev - -build_with_cmake() { - echo $PWD - BUILD_FOLDER=$PWD/build - rm -rf $BUILD_FOLDER - mkdir -p $BUILD_FOLDER - cd $BUILD_FOLDER - cmake \ - -DCMAKE_LIBRARY_PATH=$STAGING_AREA_DEPS \ - -DCMAKE_INSTALL_PREFIX=$STAGING_AREA_DEPS \ - -DCMAKE_PREFIX_PATH=$STAGING_AREA_DEPS \ - -DCMAKE_EXE_LINKER_FLAGS="-L$STAGING_AREA_DEPS -L$STAGING_AREA_DEPS/lib" \ - -DCMAKE_SHARED_LINKER_FLAGS="-L$STAGING_AREA_DEPS -L$STAGING_AREA_DEPS/lib" \ - -DCMAKE_VERBOSE_MAKEFILE=ON \ - "$@" - make -j$JOBS - make -j$JOBS install - ldconfig -} - -clone() { - echo "#######CLONE#######" - pushd $STAGING_AREA - git clone --recursive https://github.com/analogdevicesinc/libm2k.git -b master libm2k - git clone --recursive https://github.com/gabime/spdlog.git -b v1.x spdlog - git clone --recursive https://github.com/gnuradio/volk.git -b main volk - git clone --recursive https://github.com/gnuradio/gnuradio.git -b maint-3.10 gnuradio - git clone --recursive https://github.com/analogdevicesinc/gr-scopy.git -b 3.10 gr-scopy - git clone --recursive https://github.com/analogdevicesinc/gr-m2k.git -b master gr-m2k - git clone --recursive https://github.com/cseci/qwt.git -b qwt-multiaxes qwt - git clone --recursive https://github.com/sigrokproject/libsigrokdecode.git -b master libsigrokdecode - git clone --recursive https://github.com/analogdevicesinc/libtinyiiod.git -b master libtinyiiod - git clone --recursive https://github.com/analogdevicesinc/scopy -b ci-gr-3.10 scopy - popd -} - -build_libm2k() { - echo "#######build_libm2k#######" - pushd $STAGING_AREA/libm2k - build_with_cmake -DENABLE_PYTHON=OFF -DENABLE_TOOLS=ON ../ - popd -} - -build_spdlog() { - echo "#######build_spdlog#######" - pushd $STAGING_AREA/spdlog - build_with_cmake -DSPDLOG_BUILD_SHARED=ON ../ - popd -} - -build_volk() { - echo "#######build_volk#######" - pushd $STAGING_AREA/volk - build_with_cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DPYTHON_EXECUTABLE=/usr/bin/python3 ../ - popd -} - -build_gnuradio() { - echo "#######build_gnuradio#######" - pushd $STAGING_AREA/gnuradio - build_with_cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DPYTHON_EXECUTABLE=/usr/bin/python3 \ - -DENABLE_GR_QTGUI=OFF \ - -DENABLE_GR_DTV=OFF \ - -DENABLE_GR_DIGITAL=OFF \ - -DENABLE_GR_VOCODER=OFF \ - -DENABLE_GR_ZEROMQ=OFF \ - -DENABLE_GR_NETWORK=OFF \ - -DENABLE_GR_WAVELET=OFF \ - -DENABLE_GR_FEC=OFF \ - -DENABLE_GR_VIDEO_SDL=OFF \ - -DENABLE_GR_PDU=OFF \ - -DENABLE_GR_CTRLPORT=OFF \ - -DENABLE_GR_CHANNELS=OFF \ - -DENABLE_GR_AUDIO=OFF \ - -DENABLE_TESTING=OFF \ - -DENABLE_DOXYGEN=OFF \ - -DENABLE_POSTINSTALL=OFF ../ - popd -} - -build_gr_scopy() { - echo "#######build_gr_scopy#######" - pushd $STAGING_AREA/gr-scopy - build_with_cmake -DWITH_PYTHON=OFF ../ - popd -} - -build_gr_m2k() { - echo "#######build_gr_m2k#######" - pushd $STAGING_AREA/gr-m2k - build_with_cmake -DWITH_PYTHON=OFF ../ - popd -} - -build_qwt() { - echo "#######build_qwt#######" - pushd $STAGING_AREA/qwt - qmake INCLUDEPATH=$STAGING_AREA_DEPS/include LIBS=-L$STAGING_AREA_DEPS/lib qwt.pro - make - make INSTALL_ROOT=$STAGING_AREA_DEPS install - ldconfig - popd -} - -copy_qwt() { - cp -r $STAGING_AREA_DEPS/usr/local/* $STAGING_AREA_DEPS/ -} - -build_libsigrokdecode() { - echo "#######build_libsigrokdecode#######" - pushd $STAGING_AREA/libsigrokdecode - ./autogen.sh - ./configure --prefix $STAGING_AREA_DEPS - make - make install - ldconfig - popd -} - -build_libtinyiiod() { - echo "#######build_libtinyiiod#######" - pushd $STAGING_AREA/libtinyiiod - build_with_cmake ../ - popd -} - -build_scopy() { - echo "#######build_scopy#######" - pushd $STAGING_AREA/scopy - build_with_cmake ../ - popd -} - -test_scopy() { - echo "#######TEST_SCOPY#######" - pushd $STAGING_AREA/scopy/build - ./scopy - popd -} - -clone -build_libm2k -build_spdlog -build_volk -build_gnuradio -build_gr_scopy -build_gr_m2k -build_qwt -copy_qwt -build_libsigrokdecode -build_libtinyiiod -build_scopy -test_scopy diff --git a/CI/armhf/qt_patch.patch b/CI/armhf/qt_patch.patch deleted file mode 100644 index ec1e6bf431..0000000000 --- a/CI/armhf/qt_patch.patch +++ /dev/null @@ -1,126 +0,0 @@ ---- new/qt-everywhere-src-5.15.2/qtbase/mkspecs/linux-arm-gnueabihf-g++/qmake.conf -+++ qt-everywhere-src-5.15.2/qtbase/mkspecs/linux-arm-gnueabihf-g++/qmake.conf -@@ -0,0 +1,26 @@ -+# -+# qmake configuration for building with arm-linux-gnueabihf-g++ -+# -+ -+MAKEFILE_GENERATOR = UNIX -+CONFIG += incremental -+QMAKE_INCREMENTAL_STYLE = sublib -+ -+include(../common/linux.conf) -+include(../common/gcc-base-unix.conf) -+include(../common/g++-unix.conf) -+ -+QMAKE_LIBS_EGL = -lEGL -+ -+# modifications to g++.conf -+QMAKE_CC = arm-linux-gnueabihf-gcc -+QMAKE_CXX = arm-linux-gnueabihf-g++ -+QMAKE_LINK = arm-linux-gnueabihf-g++ -+QMAKE_LINK_SHLIB = arm-linux-gnueabihf-g++ -+ -+# modifications to linux.conf -+QMAKE_AR = arm-linux-gnueabihf-ar cqs -+QMAKE_OBJCOPY = arm-linux-gnueabihf-objcopy -+QMAKE_NM = arm-linux-gnueabihf-nm -P -+QMAKE_STRIP = arm-linux-gnueabihf-strip -+load(qt_config) ---- new/qt-everywhere-src-5.15.2/qtbase/mkspecs/linux-arm-gnueabihf-g++/qplatformdefs.h -+++ qt-everywhere-src-5.15.2/qtbase/mkspecs/linux-arm-gnueabihf-g++/qplatformdefs.h -@@ -0,0 +1,40 @@ -+/**************************************************************************** -+** -+** Copyright (C) 2016 The Qt Company Ltd. -+** Contact: https://www.qt.io/licensing/ -+** -+** This file is part of the qmake spec of the Qt Toolkit. -+** -+** $QT_BEGIN_LICENSE:LGPL$ -+** Commercial License Usage -+** Licensees holding valid commercial Qt licenses may use this file in -+** accordance with the commercial license agreement provided with the -+** Software or, alternatively, in accordance with the terms contained in -+** a written agreement between you and The Qt Company. For licensing terms -+** and conditions see https://www.qt.io/terms-conditions. For further -+** information use the contact form at https://www.qt.io/contact-us. -+** -+** GNU Lesser General Public License Usage -+** Alternatively, this file may be used under the terms of the GNU Lesser -+** General Public License version 3 as published by the Free Software -+** Foundation and appearing in the file LICENSE.LGPL3 included in the -+** packaging of this file. Please review the following information to -+** ensure the GNU Lesser General Public License version 3 requirements -+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -+** -+** GNU General Public License Usage -+** Alternatively, this file may be used under the terms of the GNU -+** General Public License version 2.0 or (at your option) the GNU General -+** Public license version 3 or any later version approved by the KDE Free -+** Qt Foundation. The licenses are as published by the Free Software -+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -+** included in the packaging of this file. Please review the following -+** information to ensure the GNU General Public License requirements will -+** be met: https://www.gnu.org/licenses/gpl-2.0.html and -+** https://www.gnu.org/licenses/gpl-3.0.html. -+** -+** $QT_END_LICENSE$ -+** -+****************************************************************************/ -+ -+#include "../linux-g++/qplatformdefs.h" ---- new/qt-everywhere-src-5.15.2/qtbase/src/3rdparty/angle/include/EGL/eglplatform.h -+++ qt-everywhere-src-5.15.2/qtbase/src/3rdparty/angle/include/EGL/eglplatform.h -@@ -141,6 +141,7 @@ - */ - typedef khronos_int32_t EGLint; - -+typedef uint32_t DISPMANX_ELEMENT_HANDLE_T; - - /* C++ / C typecast macros for special EGL handle values */ - #if defined(__cplusplus) ---- new/qt-everywhere-src-5.15.2/qtbase/src/gui/configure.json -+++ qt-everywhere-src-5.15.2/qtbase/src/gui/configure.json -@@ -862,7 +862,10 @@ - "type": "compile", - "test": { - "include": [ "EGL/egl.h", "bcm_host.h" ], -- "main": "vc_dispmanx_display_open(0);" -+ "main": [ -+ "vc_dispmanx_display_open(0);", -+ "EGL_DISPMANX_WINDOW_T *eglWindow = new EGL_DISPMANX_WINDOW_T;" -+ ] - }, - "use": "egl bcm_host" - }, ---- new/qt-everywhere-src-5.15.2/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_brcm/qeglfsbrcmintegration.cpp -+++ qt-everywhere-src-5.15.2/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_brcm/qeglfsbrcmintegration.cpp -@@ -44,6 +44,12 @@ - - static DISPMANX_DISPLAY_HANDLE_T dispman_display = 0; - -+typedef struct { -+ DISPMANX_ELEMENT_HANDLE_T element; -+ int width; /* This is necessary because dispmanx elements are not queriable. */ -+ int height; -+} EGL_DISPMANX_WINDOW_T; -+ - static EGLNativeWindowType createDispmanxLayer(const QPoint &pos, const QSize &size, int z, DISPMANX_FLAGS_ALPHA_T flags) - { - VC_RECT_T dst_rect; -@@ -76,12 +82,12 @@ - eglWindow->width = size.width(); - eglWindow->height = size.height(); - -- return eglWindow; -+ return (EGLNativeWindowType)eglWindow; - } - - static void destroyDispmanxLayer(EGLNativeWindowType window) - { -- EGL_DISPMANX_WINDOW_T *eglWindow = static_cast(window); -+ EGL_DISPMANX_WINDOW_T *eglWindow = (EGL_DISPMANX_WINDOW_T*)(window); - DISPMANX_UPDATE_HANDLE_T dispman_update = vc_dispmanx_update_start(0); - vc_dispmanx_element_remove(dispman_update, eglWindow->element); - vc_dispmanx_update_submit_sync(dispman_update); diff --git a/CI/armhf/scopy.desktop b/CI/armhf/scopy.desktop deleted file mode 100644 index 705923d28c..0000000000 --- a/CI/armhf/scopy.desktop +++ /dev/null @@ -1,10 +0,0 @@ -[Desktop Entry] -Version=1.0 -Icon=scopy -Exec=scopy -Terminal=false -Type=Application -Categories=Science -Name=Scopy -GenericName=Oscilloscope -Comment=A software oscilloscope diff --git a/CI/x86_64/docker/Dockerfile b/CI/x86_64/docker/Dockerfile deleted file mode 100644 index de9a5b63bf..0000000000 --- a/CI/x86_64/docker/Dockerfile +++ /dev/null @@ -1,35 +0,0 @@ -#docker build -t cristianbindea/scopy1-x86_64-appimage . -#DOCKER_BUILDKIT=0 -FROM ubuntu:20.04 -SHELL ["/bin/bash", "-c"] - -ARG USER=runner -ARG DEBIAN_FRONTEND=noninteractive - -ENV TZ=Europe/Bucharest -RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone -RUN apt-get update && apt-get -y upgrade && apt-get install -y apt-utils sudo git tzdata keyboard-configuration - -RUN groupadd -g 1000 -r $USER && \ - useradd -u 1000 -g 1000 --create-home -r $USER - -#Change password -RUN echo "$USER:$USER" | chpasswd - -#Make sudo passwordless -RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-$USER && \ - usermod -aG sudo $USER && \ - usermod -aG plugdev $USER - -USER $USER -WORKDIR /home/${USER} -RUN git clone https://github.com/analogdevicesinc/scopy --branch ci/scopy-1.5.0-x86_64-appimage -WORKDIR /home/${USER}/scopy - -ENV CI_SCRIPT=ON -RUN ./CI/x86_64/x86-64_appimage_process.sh install_packages -RUN sudo pip3 install aqtinstall && sudo python3 -m aqt install-qt --outputdir /opt/Qt linux desktop 5.15.2 -RUN ./CI/x86_64/x86-64_appimage_process.sh clone download_tools build_deps - -WORKDIR /home/${USER} -# RUN rm -rf scopy diff --git a/CI/x86_64/scopy.desktop b/CI/x86_64/scopy.desktop deleted file mode 100644 index 38aaa876d6..0000000000 --- a/CI/x86_64/scopy.desktop +++ /dev/null @@ -1,10 +0,0 @@ -[Desktop Entry] -Version=1.0 -Icon=Scopy -Exec=scopy -Terminal=false -Type=Application -Categories=Science -Name=Scopy -GenericName=Oscilloscope -Comment=A software oscilloscope diff --git a/CI/x86_64/x86-64_appimage_process.sh b/CI/x86_64/x86-64_appimage_process.sh deleted file mode 100755 index 66c1e31e07..0000000000 --- a/CI/x86_64/x86-64_appimage_process.sh +++ /dev/null @@ -1,414 +0,0 @@ -#!/bin/bash -set -ex - -## Set STAGING -USE_STAGING=ON -## - -if [ "$CI_SCRIPT" == "ON" ] - then - SRC_DIR=/home/runner/scopy - git config --global --add safe.directory '*' - USE_STAGING=OFF - else - SRC_DIR=$(git rev-parse --show-toplevel) -fi - -export APPIMAGE=1 - - -LIBIIO_VERSION=libiio-v0 -LIBAD9361_BRANCH=main -LIBM2K_BRANCH=src/output_triggers -SPDLOG_BRANCH=v1.x -VOLK_BRANCH=main -GNURADIO_BRANCH=maint-3.10 -GRSCOPY_BRANCH=3.10 -GRM2K_BRANCH=main -LIBSIGROKDECODE_BRANCH=decoders/AD559XR -QWT_BRANCH=qwt-multiaxes-updated -LIBTINYIIOD_BRANCH=master -IIOEMU_BRANCH=master - -# default python version used in CI scripts, can be changed to match locally installed python -PYTHON_VERSION=python3.8 - -QT_LOCATION=/opt/Qt/5.15.2/gcc_64 - -STAGING_AREA=$SRC_DIR/CI/x86_64/staging -QMAKE_BIN=$QT_LOCATION/bin/qmake -CMAKE_BIN=${STAGING_AREA}/cmake/bin/cmake -JOBS=-j14 - -APP_DIR_NAME=scopy.AppDir -APP_DIR=$SRC_DIR/CI/x86_64/$APP_DIR_NAME -APP_IMAGE=$SRC_DIR/CI/x86_64/Scopy1-x86_64.AppImage - -if [ "$USE_STAGING" == "ON" ] - then - echo -- USING STAGING FOLDER: $STAGING_AREA_DEPS - STAGING_AREA_DEPS=$STAGING_AREA/dependencies - export LD_LIBRARY_PATH=$STAGING_AREA_DEPS/lib:$QT_LOCATION/lib:$LD_LIBRARY_PATH - CMAKE_OPTS=(\ - -DCMAKE_LIBRARY_PATH=$STAGING_AREA_DEPS \ - -DCMAKE_INSTALL_PREFIX=$STAGING_AREA_DEPS \ - -DCMAKE_PREFIX_PATH=$QT_LOCATION\;$STAGING_AREA_DEPS \ - -DCMAKE_EXE_LINKER_FLAGS=-L$STAGING_AREA_DEPS\;-L$STAGING_AREA_DEPS/lib \ - -DCMAKE_SHARED_LINKER_FLAGS=-L$STAGING_AREA_DEPS\;-L$STAGING_AREA_DEPS/lib \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_VERBOSE_MAKEFILE=ON \ - ) - echo -- STAGING_DIR $STAGING_AREA_DEPS - else - echo -- NO STAGING: INSTALLING IN SYSTEM - STAGING_AREA_DEPS=/usr/local - export LD_LIBRARY_PATH=$QT_LOCATION/lib:$LD_LIBRARY_PATH: - CMAKE_OPTS=(\ - -DCMAKE_PREFIX_PATH=$QT_LOCATION \ - -DCMAKE_BUILD_TYPE=RelWithDebInfo \ - -DCMAKE_VERBOSE_MAKEFILE=ON \ - ) -fi - -CMAKE="$CMAKE_BIN ${CMAKE_OPTS[*]}" -echo -- USING CMAKE COMMAND: -echo $CMAKE -echo -- USING QT: $QT_LOCATION -echo -- USING QMAKE: $QMAKE_BIN - -clone() { - echo "#######CLONE#######" - mkdir -p $STAGING_AREA - pushd $STAGING_AREA - [ -d 'libiio' ] || git clone --recursive https://github.com/analogdevicesinc/libiio.git -b $LIBIIO_VERSION libiio - [ -d 'libad9361' ] || git clone --recursive https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH libad9361 - [ -d 'libm2k' ] || git clone --recursive https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH libm2k - [ -d 'spdlog' ] || git clone --recursive https://github.com/gabime/spdlog.git -b $SPDLOG_BRANCH spdlog - [ -d 'gr-scopy' ] || git clone --recursive https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH gr-scopy - [ -d 'gr-m2k' ] || git clone --recursive https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH gr-m2k - [ -d 'volk' ] || git clone --recursive https://github.com/gnuradio/volk.git -b $VOLK_BRANCH volk - [ -d 'gnuradio' ] || git clone --recursive https://github.com/gnuradio/gnuradio.git -b $GNURADIO_BRANCH gnuradio - [ -d 'qwt' ] || git clone --recursive https://github.com/cseci/qwt.git -b $QWT_BRANCH qwt - [ -d 'libsigrokdecode' ] || git clone --recursive https://github.com/analogdevicesinc/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH libsigrokdecode - [ -d 'libtinyiiod' ] || git clone --recursive https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH libtinyiiod - popd -} - -download_tools() { - mkdir -p ${STAGING_AREA} - pushd ${STAGING_AREA} - - if [ ! -d cmake ];then - wget https://github.com/Kitware/CMake/releases/download/v3.29.0-rc2/cmake-3.29.0-rc2-linux-x86_64.tar.gz - tar -xf cmake*.tar.gz && rm cmake*.tar.gz && mv cmake* cmake # unzip and rename - fi - - # download tools for creating the AppDir and the AppImage - if [ ! -f linuxdeploy-x86_64.AppImage ];then - wget https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20231026-1/linuxdeploy-x86_64.AppImage - chmod +x linuxdeploy-x86_64.AppImage - fi - - if [ ! -f linuxdeploy-plugin-qt-x86_64.AppImage ];then - wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage - chmod +x linuxdeploy-plugin-qt-x86_64.AppImage - fi - - if [ ! -f linuxdeploy-plugin-appimage-x86_64.AppImage ];then - wget https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/1-alpha-20230713-1/linuxdeploy-plugin-appimage-x86_64.AppImage - chmod +x linuxdeploy-plugin-appimage-x86_64.AppImage - fi - - popd -} - -build_with_cmake() { - INSTALL=$1 - [ -z $INSTALL ] && INSTALL=ON - BUILD_FOLDER=$PWD/build - rm -rf $BUILD_FOLDER - mkdir -p $BUILD_FOLDER - cd $BUILD_FOLDER - $CMAKE $CURRENT_BUILD_CMAKE_OPTS ../ - make $JOBS - if [ "$INSTALL" == "ON" ];then - if [ "$USE_STAGING" == "ON" ]; then make install; else sudo make install; fi - fi - CURRENT_BUILD_CMAKE_OPTS="" -} - -install_packages() { - sudo DEBIAN_FRONTEND=noninteractive apt-get -y install - - sudo apt-get update - sudo apt-get -y upgrade - sudo apt-get -y install \ - $PYTHON_VERSION-full python3-pip lib$PYTHON_VERSION-dev python3-numpy \ - keyboard-configuration vim git wget unzip\ - g++ build-essential cmake curl autogen autoconf autoconf-archive pkg-config flex bison swig \ - subversion mesa-common-dev graphviz xserver-xorg gettext texinfo mm-common doxygen \ - libboost-all-dev libfftw3-bin libfftw3-dev libfftw3-3 liblog4cpp5v5 liblog4cpp5-dev \ - libxcb-xinerama0 libgmp3-dev libzip-dev libglib2.0-dev libglibmm-2.4-dev libsigc++-2.0-dev \ - libclang1-9 libmatio-dev liborc-0.4-dev libgl1-mesa-dev libserialport0 libserialport-dev \ - libusb-1.0 libusb-1.0-0 libusb-1.0-0-dev libavahi-client-dev libsndfile1-dev \ - libxkbcommon-x11-0 libqt5gui5 libncurses5 libtool libaio-dev libzmq3-dev libxml2-dev - - pip3 install mako - pip3 install packaging -} - -build_libiio() { - echo "### Building libiio - version $LIBIIO_VERSION" - pushd $STAGING_AREA/libiio - CURRENT_BUILD_CMAKE_OPTS="\ - -DWITH_TESTS:BOOL=OFF \ - -DWITH_DOC:BOOL=OFF \ - -DHAVE_DNS_SD:BOOL=OFF\ - -DWITH_MATLAB_BINDINGS:BOOL=OFF \ - -DCSHARP_BINDINGS:BOOL=OFF \ - -DPYTHON_BINDINGS:BOOL=OFF \ - -DWITH_SERIAL_BACKEND:BOOL=ON \ - -DENABLE_IPV6:BOOL=OFF \ - -DINSTALL_UDEV_RULE:BOOL=OFF - " - build_with_cmake $1 - popd -} - -build_libad9361() { - echo "### Building libad9361 - branch $LIBAD9361_BRANCH" - pushd $STAGING_AREA/libad9361 - build_with_cmake $1 - popd -} - -build_spdlog() { - echo "### Building spdlog - branch $SPDLOG_BRANCH" - pushd $STAGING_AREA/spdlog - CURRENT_BUILD_CMAKE_OPTS="-DSPDLOG_BUILD_SHARED=ON" - build_with_cmake $1 - popd -} - -build_libm2k() { - echo "### Building libm2k - branch $LIBM2K_BRANCH" - pushd $STAGING_AREA/libm2k - CURRENT_BUILD_CMAKE_OPTS="\ - -DENABLE_PYTHON=OFF \ - -DENABLE_CSHARP=OFF \ - -DBUILD_EXAMPLES=OFF \ - -DENABLE_TOOLS=OFF \ - -DINSTALL_UDEV_RULES=OFF \ - " - build_with_cmake $1 - popd -} - -build_volk() { - echo "### Building volk - branch $VOLK_BRANCH" - pushd $STAGING_AREA/volk - CURRENT_BUILD_CMAKE_OPTS="-DPYTHON_EXECUTABLE=/usr/bin/python3" - build_with_cmake $1 - popd -} - -build_gnuradio() { - echo "### Building gnuradio - branch $GNURADIO_BRANCH" - pushd $STAGING_AREA/gnuradio - CURRENT_BUILD_CMAKE_OPTS="\ - -DPYTHON_EXECUTABLE=/usr/bin/python3 \ - -DENABLE_DEFAULT=OFF \ - -DENABLE_GNURADIO_RUNTIME=ON \ - -DENABLE_GR_ANALOG=ON \ - -DENABLE_GR_BLOCKS=ON \ - -DENABLE_GR_FFT=ON \ - -DENABLE_GR_FILTER=ON \ - -DENABLE_GR_IIO=ON \ - -DENABLE_POSTINSTALL=OFF - " - build_with_cmake $1 - popd -} - -build_grscopy() { - echo "### Building gr-scopy - branch $GRSCOPY_BRANCH" - pushd $STAGING_AREA/gr-scopy - build_with_cmake $1 - popd -} - -build_grm2k() { - echo "### Building gr-m2k - branch $GRM2K_BRANCH" - pushd $STAGING_AREA/gr-m2k - CURRENT_BUILD_CMAKE_OPTS="\ - -DENABLE_PYTHON=OFF \ - -DDIGITAL=OFF - " - build_with_cmake $1 - popd -} - -build_qwt() { - echo "### Building qwt - branch $QWT_BRANCH" - pushd $STAGING_AREA/qwt - git clean -xdf - sed -i 's|/usr/local/qwt-$$QWT_VERSION-ma|/usr/local|g' qwtconfig.pri - - INSTALL=$1 - [ -z $INSTALL ] && INSTALL=ON - - if [ "$USE_STAGING" == "ON" ] - then - $QMAKE_BIN INCLUDEPATH=$STAGING_AREA_DEPS/include LIBS=-L$STAGING_AREA_DEPS/lib qwt.pro - make $JOBS - if [ "$INSTALL" == "ON" ];then - make INSTALL_ROOT=$STAGING_AREA_DEPS install - fi - cp -r $STAGING_AREA_DEPS/usr/local/* $STAGING_AREA_DEPS/ - else - $QMAKE_BIN qwt.pro - make $JOBS - if [ "$INSTALL" == "ON" ];then - sudo make install - fi - fi - - popd -} - -build_libsigrokdecode() { - echo "### Building libsigrokdecode - branch $LIBSIGROKDECODE_BRANCH" - pushd $STAGING_AREA/libsigrokdecode - git clean -xdf - ./autogen.sh - - INSTALL=$1 - [ -z $INSTALL ] && INSTALL=ON - - if [ "$USE_STAGING" == "ON" ] - then - ./configure --prefix $STAGING_AREA_DEPS - LD_RUN_PATH=$STAGING_AREA_DEPS/lib make $JOBS - else - ./configure - make $JOBS - fi - - if [ "$INSTALL" == "ON" ];then - if [ "$USE_STAGING" == "ON" ]; then make install; else sudo make install; fi - fi - popd -} - -build_libtinyiiod() { - echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" - pushd $STAGING_AREA/libtinyiiod - CURRENT_BUILD_CMAKE_OPTS="-DBUILD_EXAMPLES=OFF" - build_with_cmake $1 - popd -} - -build_iio-emu() { - echo "### Building iio-emu - branch $IIOEMU_BRANCH" - mkdir -p $STAGING_AREA - pushd $STAGING_AREA - [ -d 'iio-emu' ] || git clone --recursive https://github.com/analogdevicesinc/iio-emu -b $IIOEMU_BRANCH iio-emu - pushd $STAGING_AREA/iio-emu - build_with_cmake OFF - popd - popd -} - -build_scopy() { - echo "### Building scopy" - pushd $SRC_DIR - CURRENT_BUILD_CMAKE_OPTS="\ - -DCLONE_IIO_EMU=OFF \ - -DPYTOHN_EXECUTABLE=/usr/bin/$PYTHON_VERSION - " - build_with_cmake OFF - popd -} - -build_deps(){ - build_libiio ON - build_libad9361 ON - build_spdlog ON - build_libm2k ON - build_volk ON - build_gnuradio ON - build_grscopy ON - build_grm2k ON - build_qwt ON - build_libsigrokdecode ON - build_libtinyiiod ON -} - -create_appdir(){ - pushd ${STAGING_AREA} - LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$STAGING_AREA_DEPS/lib:$QT_LOCATION/lib - export PATH=$QT_LOCATION:$PATH - sudo ldconfig - - rm -rf $APP_DIR - export QMAKE=$QMAKE_BIN # this is needed for deploy-plugin-qt.AppImage - # inside a docker image you can't run an appimage executable without privileges - # so the solution is to extract the appimage first and only then to run it - export APPIMAGE_EXTRACT_AND_RUN=1 - ${STAGING_AREA}/linuxdeploy-x86_64.AppImage \ - --appdir $APP_DIR \ - --executable $SRC_DIR/build/scopy \ - --custom-apprun $SRC_DIR/CI/x86_64/AppRun \ - --desktop-file $SRC_DIR/CI/x86_64/scopy.desktop \ - --icon-file $SRC_DIR/resources/Scopy.png \ - --plugin qt - - cp $STAGING_AREA/iio-emu/build/iio-emu $APP_DIR/usr/bin - # search for the python version linked by cmake and copy inside the appimage the same version - FOUND_PYTHON_VERSION=$(grep 'PYTHON_VERSION' $SRC_DIR/build/CMakeCache.txt | awk -F= '{print $2}' | grep -o 'python[0-9]\+\.[0-9]\+') - python_path=/usr/lib/$FOUND_PYTHON_VERSION - cp -r $python_path $APP_DIR/usr/lib - cp -r $QT_LOCATION/plugins $APP_DIR/usr - - if [ -d $STAGING_AREA_DEPS/share/libsigrokdecode/decoders ]; then - cp -r $STAGING_AREA_DEPS/share/libsigrokdecode/decoders $APP_DIR/usr/lib - elif [ -d $STAGING_AREA/libsigrokdecode/decoders ];then - cp -r $STAGING_AREA/libsigrokdecode/decoders $APP_DIR/usr/lib - else - echo "No decoders for libsigrokdecode found" - exit 1 - fi - - cp ${STAGING_AREA_DEPS}/lib/tinyiiod.so* $APP_DIR/usr/lib - cp $QT_LOCATION/lib/libQt5XcbQpa.so* $APP_DIR/usr/lib - cp $QT_LOCATION/lib/libQt5EglFSDeviceIntegration.so* $APP_DIR/usr/lib - cp $QT_LOCATION/lib/libQt5DBus.so* $APP_DIR/usr/lib - cp /usr/lib/x86_64-linux-gnu/libXdmcp.so* $APP_DIR/usr/lib - cp /usr/lib/x86_64-linux-gnu/libbsd.so* $APP_DIR/usr/lib - cp /usr/lib/x86_64-linux-gnu/libXau.so* $APP_DIR/usr/lib - cp /usr/lib/x86_64-linux-gnu/libffi.so* $APP_DIR/usr/lib - popd -} - -create_appimage(){ - rm -rf $APP_IMAGE - - pushd ${STAGING_AREA} - export APPIMAGE_EXTRACT_AND_RUN=1 - ${STAGING_AREA}/linuxdeploy-plugin-appimage-x86_64.AppImage --appdir $APP_DIR - mv Scopy*.AppImage $APP_IMAGE - chmod +x $APP_IMAGE - popd -} - -generate_ci_envs(){ - $GITHUB_WORKSPACE/CI/appveyor/gen_ci_envs.sh > $GITHUB_WORKSPACE/CI/x86_64/gh-actions.envs -} - -move_appimage(){ - mv $APP_IMAGE $SRC_DIR -} - -for arg in $@; do - $arg -done diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100755 new mode 100644 index e98c7c3ea0..d8e6a51af5 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,8 @@ -# Copyright (c) 2019 Analog Devices Inc. +# +# Copyright (c) 2024 Analog Devices Inc. # # This file is part of Scopy -# (see http://www.github.com/analogdevicesinc/scopy). +# (see https://www.github.com/analogdevicesinc/scopy). # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -14,544 +15,291 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . +# +cmake_minimum_required(VERSION 3.9) +enable_testing() -cmake_minimum_required(VERSION 3.5) -project(scopy LANGUAGES C CXX VERSION 1.5.0) +project(scopy VERSION 2.0.0 LANGUAGES CXX) +set(SCOPY_VERSION ${PROJECT_VERSION}) -set(Python_ADDITIONAL_VERSIONS 3) -if(PYTHON_EXECUTABLE) - message(STATUS "Using custom Python EXECUTABLE: ${PYTHON_EXECUTABLE}") - set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE}) -else() - message(STATUS "Using default Python EXECUTABLE") +# set CMAKE_BUILD_TYPE if not set externally +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Default build type: RelWithDebInfo" FORCE) endif() -if(CMAKE_SYSTEM_PROCESSOR MATCHES arm) - find_package(Python3 3.9 EXACT REQUIRED COMPONENTS Interpreter) - message (STATUS "Host Python Interpreter " ${Python3_EXECUTABLE}) - set(Python3_EXECUTABLE ${CMAKE_SYSROOT}/bin/python3.9) - message (STATUS "Target Python Interpreter " ${Python3_EXECUTABLE}) +find_program(CCACHE_FOUND ccache) +if(NOT DEFINED ENV{BUILD_HOST} AND CCACHE_FOUND) + message(STATUS "Using ccache.") + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache) else() - find_package(Python3 REQUIRED COMPONENTS Interpreter Development) - message (STATUS "Python Interpreter " ${Python3_EXECUTABLE}) - message (STATUS "Python Libraries " ${Python3_LIBRARIES}) + message(STATUS "Not using ccache.") endif() -set(PYTHON_VERSION python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} CACHE STRING "PYTHON_USED") - -function(dump_cmake_variables) - get_cmake_property(_variableNames VARIABLES) - list (SORT _variableNames) - foreach (_variableName ${_variableNames}) - if (ARGV0) - unset(MATCHED) - string(REGEX MATCH ${ARGV0} MATCHED ${_variableName}) - if (NOT MATCHED) - continue() - endif() - endif() - message(STATUS "${_variableName}=${${_variableName}}") - endforeach() -endfunction() - - -function(create_build_info_html_pages) - -endfunction() - -function(configure_stylesheets DU_OPTION) - file(GLOB_RECURSE STYLESHEETS ${CMAKE_CURRENT_SOURCE_DIR}/resources/stylesheets/templates/*.qss.c) +# Make sure our local CMake Modules path comes first +list(INSERT CMAKE_MODULE_PATH 0 ${PROJECT_SOURCE_DIR}/cmake/Modules) - foreach(_stylesheet ${STYLESHEETS}) - string(REPLACE ".c" "" FILE_OUT ${_stylesheet}) - string(REPLACE "templates/" "" FILE_OUT ${FILE_OUT}) - - execute_process ( - COMMAND ${CMAKE_C_COMPILER} -E -P ${DU_OPTION} ${_stylesheet} -o ${FILE_OUT} - ) - - message(STATUS "Done preprocessing ${_stylesheet}, file written to: ${FILE_OUT}") - endforeach() - -endfunction() - -# Get the GIT hash of the latest commit -include(FindGit OPTIONAL) -if (GIT_FOUND) - execute_process( - COMMAND ${GIT_EXECUTABLE} rev-parse --show-toplevel - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE SCOPY_GIT_REPO - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - execute_process( - COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - OUTPUT_VARIABLE SCOPY_VERSION_GIT - OUTPUT_STRIP_TRAILING_WHITESPACE - ) -endif() +set(CMAKE_INCLUDE_CURRENT_DIR ON) -if (NOT SCOPY_VERSION_GIT) - set(SCOPY_VERSION_GIT v${PROJECT_VERSION}) -endif() +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) -# Set .exe properties -if (WIN32) - string(REPLACE "." "," SCOPY_FILEVERSION ${PROJECT_VERSION}) - set(SCOPY_PRODUCTVERSION_STR ${PROJECT_VERSION}) - string(TIMESTAMP BUILD_YEAR "%Y") -endif() +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) -if (MSVC) - # http://www.suodenjoki.dk/us/archive/2010/min-max.htm - add_definitions(-DNOMINMAX) +add_compile_definitions(QT_MESSAGELOGCONTEXT) - # http://www.qtcentre.org/threads/32028-unresolved-external-symbol-quot-public-static-struct-QMetaObject-const-QwtPlot-staticMe - add_definitions(-DQWT_DLL) -endif (MSVC) +include(GNUInstallDirs) -if(ANDROID) - set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android") -endif() +find_package(QT NAMES Qt5 REQUIRED COMPONENTS Widgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools) -find_package(Qt5Widgets REQUIRED) -find_package(Qt5 COMPONENTS LinguistTools REQUIRED) -find_package(Qt5Concurrent REQUIRED) -find_package(Qt5Network REQUIRED) -if(ANDROID) - find_package(Qt5AndroidExtras REQUIRED) +if(Qt5Widgets_VERSION VERSION_LESS 5.15.2) + message(FATAL_ERROR "Minimum supported Qt 5.15.2") + return() +else() + message(STATUS "Using Qt version: " ${Qt5Widgets_VERSION}) endif() -FILE(GLOB TS_FILES ${CMAKE_SOURCE_DIR}/resources/translations/*.ts) -set_source_files_properties(${TS_FILES} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_BINARY_DIR}) +# message(QtVersion: ${QT_VERSION_MAJOR}:${QT_VERSION_MINOR}) -# Creates translation .ts files from ${CMAKE_SOURCE_DIR} -#qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES}) +file(GLOB SRC_LIST *.cpp *.cc) +file(GLOB HEADER_LIST *.h *.hpp) +file(GLOB UI_LIST *.ui) -# Generate .qm files from the .ts files -qt5_add_translation(QM_FILES ${TS_FILES}) - -set(TRANSLATIONS) -foreach(file ${TS_FILES}) - get_filename_component(file_name ${file} NAME_WE) - set(TRANSLATIONS "${TRANSLATIONS}\n${file_name}.qm") -endforeach() +set(PROJECT_SOURCES ${SRC_LIST} ${HEADER_LIST} ${UI_LIST}) -configure_file(${CMAKE_SOURCE_DIR}/resources/translations.qrc - ${CMAKE_BINARY_DIR}/translations.qrc - @ONLY) +include(ScopyAbout) +configure_about(./resources/about) +file(GLOB SCOPY_RESOURCE_FILES gui/res/resources.qrc resources/aboutpage.qrc) +find_file(SCOPY_ICON_ICO icon.ico PATHS ${CMAKE_SOURCE_DIR}/gui/res) +message(STATUS "SCOPY_RESOURCE_FILES: " ${SCOPY_RESOURCE_FILES}) -qt5_add_resources(TRANSLATION_RESOURCES ${CMAKE_BINARY_DIR}/translations.qrc) - -set(CMAKE_INCLUDE_CURRENT_DIR ON) -set(CMAKE_AUTOMOC ON) -#set(CMAKE_AUTOUIC ON) - Can't figure out how to configure it to look under ui/ -# for the .ui file. Use qt5_wrap_ui instead. +include(ScopyStyle) +generate_style("--core" ${CMAKE_CURRENT_SOURCE_DIR}/gui/style ${CMAKE_CURRENT_SOURCE_DIR}/gui/include/gui) -list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules) +option(ENABLE_TRANSLATION "Enable translation" ON) +include(ScopyTranslation) -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING - "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." - FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS None Debug Release RelWithDebInfo MinSizeRel) +if(ENABLE_TRANSLATION) + generate_translations() + qt_add_resources(SCOPY_RESOURCES ${CMAKE_BINARY_DIR}/translations.qrc) endif() -if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - add_definitions(-DQT_NO_DEBUG_OUTPUT=1) -endif() - -list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_constexpr OUT_CONSTEXPR) -if (NOT "${OUT_CONSTEXPR}" STREQUAL "-1") - add_definitions(-DHAS_CONSTEXPR=1) -endif() - -option(BREAKPAD_HANDLER "Build with breakpad exception handler " OFF) -message(STATUS BREAKPAD_HANDLER - ${BREAKPAD_HANDLER}) -if (${BREAKPAD_HANDLER}) - message("-- Building with breakpad crash handler") - set(BREAKPAD_HANDLER_BOOL 1) - find_library(BREAKPAD_LIBRARIES NAMES breakpad) - find_library(BREAKPADCLIENT_LIBRARIES NAMES breakpad_client) - find_path(BREAKPAD_INCLUDE_DIRS breakpad) -else() - message("-- Building without breakpad crash handler") - set(BREAKPAD_HANDLER_BOOL 0) - SET(BREAKPAD_LIBRARIES "") - SET(BREAKPAD_INCLUDE_DIRS "") - SET(BREAKPADCLIENT_LIBRARIES "") - +if(DEFINED ENV{APPIMAGE}) + add_compile_definitions(__appimage__) endif() -find_package(Qt5Qml REQUIRED) -find_package(Qt5Svg REQUIRED) -find_package(Qt5UiTools REQUIRED) -find_package(Qt5Xml REQUIRED) - -set(CMAKE_VERBOSE_MAKEFILE TRUE) -find_package(Gnuradio "3.10" REQUIRED COMPONENTS runtime analog blocks fft filter volk pmt iio) - -set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH};/usr/local/lib/cmake) -find_package(gnuradio-scopy REQUIRED PATH_SUFFIXES scopy) -find_package(gnuradio-m2k REQUIRED PATH_SUFFIXES m2k) - -add_definitions(-DBOOST_ALL_DYN_LINK) -find_package(Boost COMPONENTS system filesystem thread chrono REQUIRED) - -find_package(libm2k REQUIRED) -find_library(LIBUSB_LIBRARIES NAMES usb-1.0 usb) - -if (ENABLE_MATIO) - message("-- Building with MATLAB support for SignalGenerator") - find_library(MATIO_LIBRARIES REQUIRED NAMES matio) - add_definitions(-DMATLAB_SUPPORT_SIGGEN) -endif() +qt_add_resources(SCOPY_RESOURCES ${SCOPY_RESOURCE_FILES}) -if(ANDROID) - find_library(QWT_LIBRARIES REQUIRED NAMES qwt_${ANDROID_ABI}) -else() - find_library(QWT_LIBRARIES REQUIRED NAMES qwt) +if(WIN32) + # Set .exe properties + string(REPLACE "." "," SCOPY_FILEVERSION ${PROJECT_VERSION}) + set(SCOPY_PRODUCTVERSION_STR ${PROJECT_VERSION}) + string(TIMESTAMP BUILD_YEAR "%Y") + set(SCOPY_WIN32_RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/resources/properties.rc) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resources/properties.rc.cmakein ${SCOPY_WIN32_RESOURCES} @ONLY) endif() -find_package(PkgConfig) -set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON) -#pkg_check_modules(GLIB REQUIRED glib-2.0) -#pkg_check_modules(GLIBMM REQUIRED glibmm-2.4) -#pkg_check_modules(SIGCPP REQUIRED sigc++-2.0) -pkg_check_modules(LIBSIGROK_DECODE REQUIRED libsigrokdecode) - -pkg_get_variable(LIBSIGROK_DECODERS_DIR libsigrokdecode decodersdir) - -include_directories( - ${Boost_INCLUDE_DIRS} - ${Qt5Widgets_INCLUDE_DIRS} - ${Qt5Concurrent_INCLUDE_DIRS} - ${Qt5AndroidExtras_INCLUDE_DIRS} - ${Qt5Qml_INCLUDE_DIRS} - ${Qt5UiTools_INCLUDE_DIRS} - ${QWT_INCLUDE_DIRS} - ${Qt5Svg_INCLUDE_DIRS} - ${Qt5Xml_INCLUDE_DIRS} - ${SCOPY_INCLUDE_DIRS} - ${LIBSIGROK_DECODE_INCLUDE_DIRS} - ${GLIB_INCLUDE_DIRS} - ${GLIBCONFIG_INCLUDE_DIRS} - ${CMAKE_SOURCE_DIR}/src - ${BREAKPAD_INCLUDE_DIRS} - ${BREAKPAD_INCLUDE_DIRS}/breakpad - ${CMAKE_CURRENT_SOURCE_DIR}/iio-emu +find_path( + IIO_INCLUDE_DIRS + NAMES iio.h + HINTS ${CMAKE_INSTALL_PREFIX}/include + /include + /usr/include + /usr/local/include + /opt/local/include + REQUIRED ) -link_directories( - ${Boost_LIBRARY_DIRS} - ${GNURADIO_RUNTIME_LIBRARY_DIRS} - ${BREAKPAD_LIBRARIES} - ) - -FILE(GLOB SRC_LIST src/*.cpp src/*.cc - src/patterngenerator/*.cpp - src/patterngenerator/*/*.cpp - src/logicanalyzer/*.cpp - src/logicanalyzer/*/*.cpp - src/gui/*.cpp +find_library( + IIO_LIBRARIES + NAMES iio libiio + HINTS ${CMAKE_INSTALL_PREFIX}/lib + /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + /opt/local/lib + /opt/local/lib64 + REQUIRED ) - -FILE(GLOB M2KSCOPE_UIS ui/patterns/*.ui ui/*.ui) -qt5_wrap_ui (m2kscope_FORMS_HEADERS ${M2KSCOPE_UIS}) - -FILE(GLOB M2KSCOPE_RESOURCES resources/resources.qrc) -qt5_add_resources(m2kscope_RESOURCES ${M2KSCOPE_RESOURCES}) - -if (WIN32) - set(RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/resources/properties.rc) - configure_file(properties.rc.cmakein ${RESOURCES} @ONLY) +message("IIO LIBRARIES: " ${IIO_LIBRARIES}) + +set(SCOPY_DLL_BUILD_PATH ${CMAKE_BINARY_DIR}) +set(SCOPY_DLL_INSTALL_PATH ${CMAKE_INSTALL_FULL_LIBDIR}) +set(SCOPY_PLUGIN_BUILD_PATH ${CMAKE_BINARY_DIR}/plugins/plugins) +set(SCOPY_PLUGIN_INSTALL_PATH ${CMAKE_INSTALL_FULL_LIBDIR}/scopy/plugins) +set(SCOPY_TRANSLATION_BUILD_PATH ${CMAKE_CURRENT_BINARY_DIR}/translations) +set(SCOPY_TRANSLATION_INSTALL_PATH ${CMAKE_INSTALL_FULL_LIBDIR}/scopy) +set(SCOPY_STYLE_BUILD_PATH ${CMAKE_CURRENT_BINARY_DIR}/style) +set(SCOPY_STYLE_INSTALL_PATH ${CMAKE_INSTALL_FULL_LIBDIR}/scopy) +set(SCOPY_PDK pdk) + +if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${SCOPY_DLL_BUILD_PATH}) +elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Scopy.app/Contents/Frameworks") +else() + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${SCOPY_DLL_BUILD_PATH}) endif() -add_definitions(-DQT_NO_KEYWORDS) +option(ENABLE_TESTING "Enable unit tests" ON) +if(ENABLE_TESTING) + message(STATUS "Unit tests enabled") + add_subdirectory(tests) +endif() -if (APPLE) - option(ENABLE_APPLICATION_BUNDLE "Enable application bundle for OSX" ON) +option(ENABLE_APPLICATION_BUNDLE "Enable application bundle for OSX" OFF) +if(APPLE) + set(ENABLE_APPLICATION_BUNDLE ON) + include(ScopyMacOS) endif(APPLE) -option(CONFIG_STYLESHEETS "Preprocess stylesheets using ${CMAKE_C_COMPILER}" ON) -message(STATUS CONFIG_STYLESHEETS - ${CONFIG_STYLESHEETS}) -if (${CONFIG_STYLESHEETS}) - if (ANDROID) - set(PROCESSING_OPTIONS "-D__ANDROID__") - endif(ANDROID) - - if (APPLE) - set(PROCESSING_OPTIONS "-D__MACOS__") - endif(APPLE) - - if (WIN32) - set(PROCESSING_OPTIONS "-D__WINDOWS__") - endif(WIN32) - - configure_stylesheets("${PROCESSING_OPTIONS}") - +add_subdirectory(common) +add_subdirectory(iioutil) +add_subdirectory(gui) +add_subdirectory(gr-util) +add_subdirectory(pluginbase) +add_subdirectory(core) +add_subdirectory(plugins) +add_subdirectory(iio-widgets) + +if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Scopy.app/Contents/MacOS") endif() -if (ENABLE_APPLICATION_BUNDLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") - - set(PKGINFO ${CMAKE_BINARY_DIR}/PkgInfo) - file(WRITE ${PKGINFO} "APPLScopy") - set_source_files_properties(${PKGINFO} PROPERTIES MACOSX_PACKAGE_LOCATION .) - - set(QT_CONF ${CMAKE_BINARY_DIR}/qt.conf) - file(WRITE ${QT_CONF} "") - set_source_files_properties(${QT_CONF} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - - set(ICON_FILE ${CMAKE_SOURCE_DIR}/resources/Scopy.icns) - set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) - - set(CMAKE_EXE_LINKER_FLAGS "-Wl,-headerpad_max_install_names -Wl,-search_paths_first ${CMAKE_EXE_LINKER_FLAGS}") - - foreach(plugin ${Qt5Gui_PLUGINS} ${Qt5Svg_PLUGINS}) - get_target_property(_loc ${plugin} LOCATION) - get_filename_component(_name ${_loc} NAME) - get_filename_component(_dir ${_loc} DIRECTORY) - get_filename_component(_dir ${_dir} NAME) +set(SCOPY_DEPENDENCIES ${IIO_LIBRARIES}) - set_source_files_properties(${_loc} PROPERTIES MACOSX_PACKAGE_LOCATION plugins/${_dir}) - set(QT_PLUGINS ${QT_PLUGINS} ${_loc}) - set(BUNDLED_QT_PLUGINS ${BUNDLED_QT_PLUGINS} ${CMAKE_BINARY_DIR}/Scopy.app/Contents/plugins/${_dir}/${_name}) - endforeach() - - file(GLOB_RECURSE DECODERS ${LIBSIGROK_DECODERS_DIR}/*.py) - foreach(_decoder ${DECODERS}) - file(RELATIVE_PATH _file ${LIBSIGROK_DECODERS_DIR} ${_decoder}) - get_filename_component(_path ${_file} DIRECTORY) - set_source_files_properties(${_decoder} PROPERTIES MACOSX_PACKAGE_LOCATION MacOS/decoders/${_path}) - endforeach() - - install(CODE " - set(BU_CHMOD_BUNDLE_ITEMS ON) - include(BundleUtilities) - fixup_bundle(\"${CMAKE_BINARY_DIR}/Scopy.app\" \"${BUNDLED_QT_PLUGINS}\" \"${CMAKE_SOURCE_DIR}\")" +if(${QT_VERSION_MAJOR} GREATER_EQUAL 6) + qt_add_executable( + ${PROJECT_NAME} + MANUAL_FINALIZATION + ${PROJECT_SOURCES} + ${SCOPY_RESOURCES} + ${SCOPY_DEPENDENCIES} + ${SCOPY_WIN32_RESOURCES} ) - - set(OSX_BUNDLE MACOSX_BUNDLE) - set(EXTRA_BUNDLE_FILES ${QT_PLUGINS} ${ICON_FILE} ${PKGINFO} ${QT_CONF} ${DECODERS}) -endif() - -#dump_cmake_variables("iio") - -string(TIMESTAMP TODAY "%Y-%m-%d") -string(TIMESTAMP NOW "%H:%M:%S") -cmake_host_system_information(RESULT BUILD_HOST QUERY HOSTNAME) - -set(BUILD_INFO) -if(DEFINED ENV{BUILD_HOST}) - if($ENV{USERNAME} STREQUAL "github-actions") - set(CI_URL $ENV{GITHUB_SERVER_URL}) - set(CI_API_URL $ENV{GITHUB_API_URL}) - set(CI_ACCOUNT_NAME $ENV{GITHUB_REPOSITORY_OWNER}) - set(CI_PROJECT_NAME $ENV{GITHUB_REPOSITORY}) - set(CI_RUN_ID $ENV{GITHUB_RUN_ID}) - set(CI_RUN_NUMBER $ENV{GITHUB_RUN_NUMBER}) - set(CI_JOB_ID $ENV{GITHUB_RUN_ID}) - set(CI_JOB_NAME $ENV{GITHUB_JOB}) - set(CI_JOB_NUMBER $ENV{GITHUB_RUN_NUMBER}) - set(CI_JOB_LINK $ENV{GITHUB_SERVER_URL}/$ENV{GITHUB_REPOSITORY_OWNER}/$ENV{GITHUB_REPOSITORY}/actions/runs/$ENV{GITHUB_RUN_ID}) - else($ENV{USERNAME} STREQUAL "azure-pipelines") - set(CI_URL $ENV{BUILD_REPO_URL}) - set(CI_API_URL "-") - set(CI_ACCOUNT_NAME $ENV{ACCOUNT_NAME}) - set(CI_PROJECT_NAME $ENV{PROJECT_NAME}) - set(CI_RUN_ID $ENV{RUN_ID}) - set(CI_RUN_NUMBER $ENV{RUN_NUMBER}) - set(CI_JOB_ID $ENV{JOB_ID}) - set(CI_JOB_NAME $ENV{JOB_NAME}) - set(CI_JOB_NUMBER "-") - set(CI_JOB_LINK "-") - endif() - - set(BUILD_INFO ${BUILD_INFO}Built\ on\ $ENV{USERNAME}\n) - set(BUILD_INFO ${BUILD_INFO}url:\ ${CI_URL}\n) - set(BUILD_INFO ${BUILD_INFO}api_url:\ ${CI_API_URL}\n) - set(BUILD_INFO ${BUILD_INFO}acc_name:\ ${CI_ACCOUNT_NAME}\n) - set(BUILD_INFO ${BUILD_INFO}prj_name:\ ${CI_PROJECT_NAME}\n) - set(BUILD_INFO ${BUILD_INFO}run_id:\ ${CI_RUN_ID}\n) - set(BUILD_INFO ${BUILD_INFO}run_nr:\ ${CI_RUN_NUMBER}\n) - set(BUILD_INFO ${BUILD_INFO}job_id:\ ${CI_JOB_ID}\n) - set(BUILD_INFO ${BUILD_INFO}job_name:\ ${CI_JOB_NAME}\n) - set(BUILD_INFO ${BUILD_INFO}job_nr:\ ${CI_JOB_NUMBER}\n) - set(BUILD_INFO ${BUILD_INFO}job_link:\ ${CI_JOB_LINK}\n) - - if(EXISTS ${CMAKE_SOURCE_DIR}/build-status) - message("build-status found in ${CMAKE_SOURCE_DIR}.. populating") - FILE(READ ${CMAKE_SOURCE_DIR}/build-status SCOPY_BUILD_STATUS_INFO) - endif() + # Define target properties for Android with Qt 6 as: set_property(TARGET tool_launcher APPEND PROPERTY + # QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android) For more information, see + # https://doc.qt.io/qt-6/qt-add-executable.html#target-creation else() - set(BUILD_INFO ${BUILD_INFO}Built\ locally\n) -endif() - -# TODO: Pack these in a GLOB and run foreach -configure_file(resources/buildinfo.html.cmakein ${CMAKE_CURRENT_BINARY_DIR}/buildinfo.html) -configure_file(resources/scopy_osp.html.cmakein ${CMAKE_CURRENT_BINARY_DIR}/scopy_osp.html) -configure_file(resources/credits.html.cmakein ${CMAKE_CURRENT_BINARY_DIR}/credits.html) -configure_file(resources/about.html.cmakein ${CMAKE_CURRENT_BINARY_DIR}/about.html) - -SET(ABOUT_HTML_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/resources/buildinfo.html ${CMAKE_CURRENT_BINARY_DIR}/resources/scopy_osp.html ${CMAKE_CURRENT_BINARY_DIR}/resources/credits.html ${CMAKE_CURRENT_BINARY_DIR}/resources/about.html) - -set(ABOUT_HTML_QRC_SOURCES) -foreach(file ${ABOUT_HTML_SOURCES}) - get_filename_component(file_name ${file} NAME) - set(ABOUT_HTML_QRC_SOURCES "${ABOUT_HTML_QRC_SOURCES}\n${file_name}") -endforeach() - -configure_file(${CMAKE_SOURCE_DIR}/resources/aboutpage.qrc - ${CMAKE_BINARY_DIR}/aboutpage.qrc - @ONLY) - -qt5_add_resources(ABOUT_PAGE_RESOURCES ${CMAKE_CURRENT_BINARY_DIR}/aboutpage.qrc) - -option(CLONE_IIO_EMU "Clone iio-emu" ON) -if (CLONE_IIO_EMU) - message(STATUS "Checking for iio-emu") - execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - RESULT_VARIABLE GIT_SUBMOD_RESULT) - if(NOT GIT_SUBMOD_RESULT EQUAL "0") - message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") - endif() - add_subdirectory(iio-emu) -endif() - -if(ANDROID) - add_library(${PROJECT_NAME} SHARED - ${SRC_LIST} - ${RESOURCES} - ${m2kscope_RESOURCES} - ${m2kscope_FORMS_HEADERS} - ${EXTRA_BUNDLE_FILES} - ${TRANSLATION_RESOURCES} - ${ABOUT_PAGE_RESOURCES} + if(ANDROID) + add_library(${PROJECT_NAME} SHARED ${PROJECT_SOURCES} ${SCOPY_RESOURCES} ${SCOPY_DEPENDENCIES}) + # Define properties for Android with Qt 5 after find_package() calls as: set(ANDROID_PACKAGE_SOURCE_DIR + # "${CMAKE_CURRENT_SOURCE_DIR}/android") + else() + add_executable( + ${PROJECT_NAME} WIN32 + ${OSX_BUNDLE} + ${PROJECT_SOURCES} + ${SCOPY_RESOURCES} + ${SCOPY_DEPENDENCIES} + ${EXTRA_BUNDLE_FILES} + ${SCOPY_WIN32_RESOURCES} ) - else() - add_executable(${PROJECT_NAME} WIN32 ${OSX_BUNDLE} - ${SRC_LIST} - ${RESOURCES} - ${m2kscope_RESOURCES} - ${m2kscope_FORMS_HEADERS} - ${EXTRA_BUNDLE_FILES} - ${TRANSLATION_RESOURCES} - ${ABOUT_PAGE_RESOURCES} - ) -endif() - - -if (WIN32 OR ENABLE_APPLICATION_BUNDLE) - set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME Scopy) -endif() - -target_link_libraries(${PROJECT_NAME} LINK_PRIVATE - ${BREAKPAD_LIBRARIES} - ${BREAKPADCLIENT_LIBRARIES} - ${Qt5Widgets_LIBRARIES} - ${Qt5Concurrent_LIBRARIES} - ${Qt5Qml_LIBRARIES} - ${Qt5UiTools_LIBRARIES} - ${Qt5Network_LIBRARIES} - ${Qt5AndroidExtras_LIBRARIES} - ${LIBUSB_LIBRARIES} - gnuradio::gnuradio-runtime - gnuradio::gnuradio-analog - gnuradio::gnuradio-blocks - gnuradio::gnuradio-fft - gnuradio::gnuradio-filter - gnuradio::gnuradio-pmt - gnuradio::gnuradio-iio - gnuradio::gnuradio-scopy - gnuradio::gnuradio-m2k - ${Boost_LIBRARIES} - ${QWT_LIBRARIES} - ${Qt5Svg_LIBRARIES} - ${Qt5Xml_LIBRARIES} - ${MATIO_LIBRARIES} - ${LIBSIGROK_DECODE_LIBRARIES} - ${GLIB_LIBRARIES} - libm2k::libm2k -) - -if (NOT WIN32) - find_library(PTHREAD_LIBRARIES pthread) - if (PTHREAD_LIBRARIES) - target_link_libraries(${PROJECT_NAME} LINK_PRIVATE ${PTHREAD_LIBRARIES}) endif() endif() -configure_file(Info.plist.cmakein ${CMAKE_CURRENT_BINARY_DIR}/Info.plist @ONLY) - -option(WITH_NATIVEDIALOGS "Enable native file dialogs for Scopy" ON) +list(FIND CMAKE_CXX_COMPILE_FEATURES cxx_constexpr OUT_CONSTEXPR) +add_definitions(-DQT_NO_KEYWORDS) -if (NOT ${WITH_NATIVEDIALOGS}) - add_definitions(-DNONATIVE) -endif() +set(CMAKE_VERBOSE_MAKEFILE ON) +target_include_directories(${PROJECT_NAME} PUBLIC scopy-gui scopy-core) +target_link_libraries(${PROJECT_NAME} PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${SCOPY_DEPENDENCIES} scopy-core scopy-gui) # Compiler options target_compile_options(${PROJECT_NAME} PUBLIC -Wall) -if(DEFINED ENV{APPIMAGE}) - add_compile_definitions(__appimage__) -endif() - -#List of warnings to be treated as errors -target_compile_options(${PROJECT_NAME} PUBLIC - -Werror=return-type - -Werror=uninitialized - -Werror=init-self - -Werror=switch +# List of warnings to be treated as errors +target_compile_options( + ${PROJECT_NAME} PUBLIC -Werror=return-type -Werror=uninitialized -Werror=init-self -Werror=switch ) -set_target_properties(${PROJECT_NAME} PROPERTIES - CXX_STANDARD 17 - CXX_STANDARD_REQUIRED ON - CXX_EXTENSIONS OFF - MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/apple/Info.plist.cmakein ${CMAKE_CURRENT_BINARY_DIR}/Info.plist COPYONLY) +set_target_properties( + ${PROJECT_NAME} + PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER scopy + MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist + WIN32_EXECUTABLE TRUE + ENABLE_EXPORTS ON # equiv to -rdynamic ) +if(ENABLE_APPLICATION_BUNDLE OR ${CMAKE_SYSTEM_NAME} MATCHES "Windows") + set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME Scopy) +endif() +configure_file(resources/qt.conf.cmakein ${CMAKE_CURRENT_BINARY_DIR}/qt.conf COPYONLY) +configure_file(resources/scopy.desktop.cmakein ${CMAKE_CURRENT_BINARY_DIR}/scopy.desktop @ONLY) -set(CMAKE_INSTALL_DOCDIR "${CMAKE_CURRENT_BINARY_DIR}/doc") -find_package(Doxygen) -if(DOXYGEN_FOUND) - option(WITH_DOC "Generate documentation with Doxygen" OFF) - if (WITH_DOC) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile_API.in ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile @ONLY) - set(HTML_DEST_DIR ${CMAKE_CURRENT_BINARY_DIR}) - #file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/doc DESTINATION ${HTML_DEST_DIR}) +if(${CMAKE_SYSTEM_NAME} MATCHES "Linux") + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/scopy.desktop DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/applications) + install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/gui/res/icon_small.svg + DESTINATION ${CMAKE_INSTALL_FULL_DATADIR}/icons/hicolor/scalable/apps RENAME scopy.svg + ) +endif() +if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") + include(ScopyWindows) + duplicate_target(${PROJECT_NAME} Scopy-console) + set_target_properties(Scopy-console PROPERTIES WIN32_EXECUTABLE FALSE) +endif() +if(NOT ENABLE_APPLICATION_BUNDLE) + install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) +endif() - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doc/Doxyfile - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMENT "Generating API documentation with Doxygen" VERBATIM - ) +file(GLOB alldirs ${CMAKE_CURRENT_SOURCE_DIR}/plugins/*) +set(childdirlist "") +foreach(child ${alldirs}) + set(CHILD_EMU_XML ${child}/res/emu_xml) + if(IS_DIRECTORY ${CHILD_EMU_XML}) + message("-- Found IIO Emulator XMLs in: " ${CHILD_EMU_XML}) + file(GLOB EMU_FILES ${CHILD_EMU_XML}/*) + foreach(_emu_file ${EMU_FILES}) + file(COPY ${_emu_file} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/plugins/emu_xml/) + endforeach() - if(NOT SKIP_INSTALL_ALL) - install(DIRECTORY ${HTML_DEST_DIR} DESTINATION ${CMAKE_INSTALL_DOCDIR}) - endif() endif() -else() - message(STATUS "Doxygen not found, API documentation won't be generated") +endforeach() + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/plugins/resources/scopy_emu_options_config.json + DESTINATION ${SCOPY_PLUGIN_INSTALL_PATH}/resources +) +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/plugins/emu_xml DESTINATION ${SCOPY_PLUGIN_INSTALL_PATH}) +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/translations DESTINATION ${SCOPY_TRANSLATION_INSTALL_PATH}) +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/style DESTINATION ${SCOPY_STYLE_INSTALL_PATH}) +install(TARGETS ${PROJECT_NAME} BUNDLE DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) + +if(QT_VERSION_MAJOR EQUAL 6) + qt_finalize_executable(${PROJECT_NAME}) endif() +# make uninstall +add_custom_target("uninstall" COMMENT "Uninstall installed files") +add_custom_command( + TARGET "uninstall" + POST_BUILD + COMMENT "Uninstall files with install_manifest.txt" + COMMAND xargs rm -vf < install_manifest.txt || echo Nothing in install_manifest.txt to be uninstalled! +) -configure_file(scopy.iss.cmakein ${CMAKE_CURRENT_BINARY_DIR}/scopy.iss @ONLY) -configure_file(scopy-32.iss.cmakein ${CMAKE_CURRENT_BINARY_DIR}/scopy-32.iss @ONLY) -configure_file(scopy-64.iss.cmakein ${CMAKE_CURRENT_BINARY_DIR}/scopy-64.iss @ONLY) -configure_file(config.h.cmakein ${CMAKE_CURRENT_BINARY_DIR}/config.h @ONLY) +# CPack config +set(CPACK_PACKAGE_NAME "${PROJECT_NAME}") +set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") +set(CPACK_GENERATOR "ZIP") -if (NOT ENABLE_APPLICATION_BUNDLE) - install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resources/scopy.desktop.cmakein ${CMAKE_CURRENT_BINARY_DIR}/scopy.desktop @ONLY) - install(FILES ${CMAKE_CURRENT_BINARY_DIR}/scopy.desktop DESTINATION share/applications) - install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icon_small.svg DESTINATION share/icons/hicolor/scalable/apps RENAME scopy.svg) -endif() +set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${PROJECT_VERSION}") -configure_file(qt.conf.cmakein ${CMAKE_CURRENT_BINARY_DIR}/qt.conf @ONLY) +set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/package") +set(CPACK_COMPONENT_INCLUDE_TOPLEVEL_DIRECTORY OFF) +set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF) +set(CPACK_ARCHIVE_COMPONENT_INSTALL ON) +set(CPACK_SET_DESTDIR ON) +set(CPACK_COMPONENTS_ALL ${SCOPY_PDK}) +include(CPack) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..6c2ca8b6b5 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,9 @@ +# Contributing to Scopy + +Thank you for considering contributing to the Scopy project! Contributions from the community are what make this project thrive. Whether you're fixing bugs, improving documentation, or developing new features, your efforts help us build a better tool for everyone. + +This guide outlines the processes and standards that we follow to ensure consistency, quality, and maintainability across the project. Adhering to these guidelines will make it easier for the Scopy contributions to review and integrate the changes into the project. + +## [Coding Guidelines](.github/contributing/coding-guidelines.md) + +## [Project Structure](.github/contributing/project-structure.md) diff --git a/README.md b/README.md index 8d5901f500..1c6c42a333 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,6 @@ # Scopy [![GitHub Release](https://img.shields.io/github/release/analogdevicesinc/scopy.svg)](https://github.com/analogdevicesinc/scopy/releases/latest) [![GPLv3 License](https://img.shields.io/badge/License-GPL%20v3-yellow.svg)](https://github.com/analogdevicesinc/scopy/blob/update-readme/LICENSE) Scopy is a software oscilloscope and signal analysis toolset. -## Builds -Nightly builds are available under the [Continuous build](https://github.com/analogdevicesinc/scopy/releases/tag/continous) tag. - ### Supported platforms - Windows (x86-64) - Linux flatpak (x86-64, arm32) @@ -17,3 +14,7 @@ Nightly builds are available under the [Continuous build](https://github.com/ana - Android (aarch64) Complete instalation and usage instructions can be found on our [Wiki](https://wiki.analog.com/university/tools/m2k/scopy) page. + +## Contributing + +See the [CONTRIBUTING file](CONTRIBUTING.md) for guidance on contributing to this project. diff --git a/android/assets/libsigrokdecode/README.MD b/android/assets/libsigrokdecode/README.MD deleted file mode 100644 index b589d0077b..0000000000 --- a/android/assets/libsigrokdecode/README.MD +++ /dev/null @@ -1 +0,0 @@ -Decoders that are copied here will be bundled in the .apk. They can be found in the staging folder/share/libsigrokdecode. diff --git a/android/assets/python3.8/README.MD b/android/assets/python3.8/README.MD deleted file mode 100644 index b12db040ac..0000000000 --- a/android/assets/python3.8/README.MD +++ /dev/null @@ -1 +0,0 @@ -For decoders to work, we need to ship a python environment. You can copy it from stagingfolder/lib/python3.8 diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index b7ba2b4570..0000000000 --- a/android/build.gradle +++ /dev/null @@ -1,85 +0,0 @@ -buildscript { - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.0.0' - //https://central.sonatype.com/artifact/com.github.hazendaz.gradle/gradle/7.5 - } -} - -repositories { - google() - mavenCentral() -} - -apply plugin: 'com.android.application' - -dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) -} - -android { - /******************************************************* - * The following variables: - * - androidBuildToolsVersion, - * - androidCompileSdkVersion - * - qt5AndroidDir - holds the path to qt android files - * needed to build any Qt application - * on Android. - * - * are defined in gradle.properties file. This file is - * updated by QtCreator and androiddeployqt tools. - * Changing them manually might break the compilation! - *******************************************************/ - - compileSdkVersion androidCompileSdkVersion.toInteger() - - buildToolsVersion '33.0.1' - - sourceSets { - main { - manifest.srcFile 'AndroidManifest.xml' - java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java'] - aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl'] - res.srcDirs = [qt5AndroidDir + '/res', 'res'] - resources.srcDirs = ['resources'] - renderscript.srcDirs = ['src'] - assets.srcDirs = ['assets'] - jniLibs.srcDirs = ['libs'] - } - } - - tasks.withType(JavaCompile) { - options.incremental = true - } - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - lintOptions { - abortOnError false - } - - // Do not compress Qt binary resources file - aaptOptions { - noCompress 'rcc' - } - - defaultConfig { - resConfig "en" - minSdkVersion = qtMinSdkVersion - targetSdkVersion = 30 - } - buildTypes { - release { - signingConfig - debuggable false - minifyEnabled false - } - } -} diff --git a/android/gradle.properties b/android/gradle.properties index afbe8cd287..fded106b17 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -3,12 +3,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx2048m \ - --add-exports java.base/sun.nio.ch=ALL-UNNAMED \ - --add-opens java.base/java.lang=ALL-UNNAMED \ - --add-opens java.base/java.lang.reflect=ALL-UNNAMED \ - --add-opens java.base/java.io=ALL-UNNAMED \ - --add-exports jdk.unsupported/sun.misc=ALL-UNNAMED +org.gradle.jvmargs=-Xmx2048m # Gradle caching allows reusing the build artifacts from a previous # build with the same inputs. However, over time, the cache size will diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index a0f7639f7d..412dfb395f 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip +#distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/libs/arm64-v8a/.gitignore b/android/libs/arm64-v8a/.gitignore deleted file mode 100644 index 72e8ffc0db..0000000000 --- a/android/libs/arm64-v8a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/android/libs/armeabi-v7a/.gitignore b/android/libs/armeabi-v7a/.gitignore deleted file mode 100644 index 72e8ffc0db..0000000000 --- a/android/libs/armeabi-v7a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -* diff --git a/Info.plist.cmakein b/apple/Info.plist.cmakein similarity index 100% rename from Info.plist.cmakein rename to apple/Info.plist.cmakein diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2613e3acad..39b0fc2726 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -8,7 +8,8 @@ trigger: include: - main - master - - staging/* + - dev + - scopy2-ci-macOS - ci-* tags: include: @@ -19,7 +20,8 @@ pr: include: - main - master - - 20* + - dev + - ci-* stages: - stage: Builds @@ -39,8 +41,8 @@ stages: - checkout: self fetchDepth: 1 clean: true - - script: ./CI/appveyor/install_macos_deps.sh - displayName: 'Dependencies' + - script: ./ci/macOS/install_macos_deps.sh + displayName: 'Build and Install Dependencies' workingDirectory: $(Build.Repository.LocalPath) - script: | export BUILD_HOST="$(vmImage)" @@ -53,14 +55,15 @@ stages: export JOB_ID=$(System.JobId) export JOB_NAME=$(System.JobName) export RUNNER_ARCH=$(Agent.OSArchitecture) - ./CI/appveyor/build_appveyor_macos.sh - displayName: 'Build' + export MACOSX_DEPLOYMENT_TARGET=11.0 + ./ci/macOS/build_azure_macos.sh + displayName: 'Build Scopy' workingDirectory: $(Build.Repository.LocalPath) - - script: ./CI/appveyor/package_darwin.sh + - script: ./ci/macOS/package_darwin.sh displayName: 'Create Scopy.dmg' workingDirectory: $(Build.Repository.LocalPath) - script: cp -R staging ${BUILD_ARTIFACTSTAGINGDIRECTORY} - displayName: 'Copy staging dir' + displayName: 'Copy Staging Dir' workingDirectory: $(Build.Repository.LocalPath) - script: | echo "ACCOUNT_NAME = " ${ACCOUNT_NAME} @@ -74,7 +77,7 @@ stages: ls -la displayName: 'Rename and copy artifact' workingDirectory: $(Build.Repository.LocalPath) - - task: GithubRelease@0 + - task: GithubRelease@1 displayName: 'Push to continuous release' condition: and(succeeded(), and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.SourceBranch'], 'refs/heads/master'))) inputs: diff --git a/ci/README.md b/ci/README.md new file mode 100644 index 0000000000..098e43fe49 --- /dev/null +++ b/ci/README.md @@ -0,0 +1,89 @@ + +This file contains dependency information for different platforms + +**Windows (x86_64)** - +https://github.com/analogdevicesinc/scopy-mingw-build-deps +builds the docker image - `docker pull analogdevices/scopy-build:mingw64` +Github Actions workflow: +https://github.com/analogdevicesinc/scopy/blob/master/.github/workflows/mingwbuild.yml + +**Linux (flatpak - x86_64)** +https://github.com/analogdevicesinc/scopy-flatpak +docker pull analogdevices/scopy-build:flatpak + + ARCH=x86_64 make + +Github Actions workflow - https://github.com/analogdevicesinc/scopy/blob/master/.github/workflows/linuxflatpakbuild.yml + +**Linux (flatpak - arm)** +https://github.com/analogdevicesinc/scopy-flatpak +Run locally on arm machine (raspberry pi) + + ARCH=arm make +Not build in CI + +**Linux (ubuntu - x86_64) - development** +Built on appveyor - https://github.com/analogdevicesinc/scopy/blob/master/appveyor.yml +**macOS (x86_64)** +Built on appveyor - https://github.com/analogdevicesinc/scopy/blob/master/appveyor.yml + +**Android (aarch64)** +https://github.com/analogdevicesinc/scopy-android-deps - `docker pull analogdevices/scopy-build:android` +Github Actions workflow - https://github.com/analogdevicesinc/scopy/blob/master/.github/workflows/androidbuild.yml + +Dependency versions (links used in source builds) + + - Qt: 5.15.12 + - Qwt - https://github.com/cseci/qwt - qwt-multiaxes + - libiio - https://github.com/analogdevicesinc/libiio - https://github.com/analogdevicesinc/libiio/tree/cad83146837971acdac28beaeb8156b9da33ba6b - v0.24 + - libxml2 - https://github.com/GNOME/libxml2 + - iconv (only on Android from src) + - libffi (only on Android from src) + - libgettext (only on Android from src) + - libusb - https://downloads.sourceforge.net/project/libusb/libusb-1.0/libusb-1.0.24/libusb-1.0.24.tar.bz2 + - Android specific libusb: https://github.com/xloem/libusb/tree/d1856aa8c246f9e56cf00a0765462b67fc5a4871 + - libm2k- https://github.com/analogdevicesinc/libm2k - master + - glog - https://github.com/google/glog + - boost - + - gnuradio - https://github.com/analogdevicesinc/gnuradio - scopy / scopy-android-2(for Android) + - volk + - log4cpp - https://github.com/cseci/log4cpp + - fftw3 + - libgmp + - gr-iio https://github.com/analogdevicesinc/gr-iio - upgrade3.8 + - libad9361 - https://github.com/analogdevicesinc/ad9361 + - gr-m2k - https://github.com/analogdevicesinc/gr-m2k - master + - gr-scopy - https://github.com/analogdevicesinc/gr-scopy - master + - libsigrokdecode - https://github.com/sigrokproject/libsigrokdecode - master + - glib + - glibmm + - sigcpp + - python + - libtinyiiod - https://github.com/analogdevicesinc/libtinyiiod - master + +How to install Qt from qt.io : https://github.com/analogdevicesinc/scopy-android-deps/blob/master/docker/Dockerfile#L43-L49 + + +| Dependency | Windows | Linux Flatpak | Linux Ubuntu(development) | Linux ARM | macOS | Android | +| --- | --- | --- | --- | --- | --- | --- | +| Qt | pacman | org.kde.Sdk (v5.15)| Qt.io |org.kde.Sdk (v5.14) | brew | Qt.io | +| qwt | src | src | src | src | src | src | +| libxml2 | pacman | src | apt | src | brew | src | +| libusb | pacman | src | apt | src | brew | src - android branch/commit | +| libiio | src | src | src | src | src | src| +| glog | src | src | src | src | brew | src | +| libm2k | src | src | src | src | src | src | +| volk | src | with GR | with GR | src | with GR | src | +| fftw3 | pacman| src | apt | src | brew| src| +| libgmp | pacman | src | apt | src | brew | src | +| boost | 1.75 | 1.72 | apt/src | 1.72 | brew | Boost-for-Android | +| gnuradio | src | src | apt/src | src | src | src | +| gr-iio | src | src | src | src | src | src | +| gr-m2k | src | src | src | src | src | src | +| gr-scopy | src | src | src | src | src | src | +| glib | pacman | src | apt | src | brew | src | +| glibmm | pacman | src | apt | src | src | src | +| sigcpp | pacman | src | apt | src | src | src | +| python | pacman | src | apt | src | brew | src | +| libsigrokdecode | src | src | src | src | src | src | +| libtinyiiod | src | src | src | src | src | src| diff --git a/CI/android/android_scopy_keystore b/ci/android/android_scopy_keystore similarity index 100% rename from CI/android/android_scopy_keystore rename to ci/android/android_scopy_keystore diff --git a/ci/arm/.dockerignore b/ci/arm/.dockerignore new file mode 100644 index 0000000000..5ded1c6247 --- /dev/null +++ b/ci/arm/.dockerignore @@ -0,0 +1,11 @@ +* +!sysroot.tar.gz +!arm_build_process.sh +!arm_build_config.sh +!cmake_toolchain.cmake +!create_sysroot.sh +!copy-deps.sh +!inside_chroot.sh +!build_qt.sh +!qt_patch_armhf.patch +!qt_patch_arm64.patch \ No newline at end of file diff --git a/CI/armhf/AppRun b/ci/arm/AppRun similarity index 95% rename from CI/armhf/AppRun rename to ci/arm/AppRun index 9e6dbe7d3a..e655f07926 100755 --- a/CI/armhf/AppRun +++ b/ci/arm/AppRun @@ -13,4 +13,4 @@ export PYTHONHOME=$python_dir export PYTHONPATH=$python_dir:$python_dir/lib-dynload:$python_dir/site-packages:$python_dir/encodings echo "Starting Scopy" ldd $HERE/usr/bin/scopy -exec $HERE/usr/bin/scopy +exec $HERE/usr/bin/scopy \ No newline at end of file diff --git a/ci/arm/arm_build_config.sh b/ci/arm/arm_build_config.sh new file mode 100644 index 0000000000..5c4d27266f --- /dev/null +++ b/ci/arm/arm_build_config.sh @@ -0,0 +1,106 @@ +#!/bin/bash + +if [ "$1" == "arm64" ];then + echo "Building for aarch64" + TOOLCHAIN_HOST="aarch64-linux-gnu" +elif [ "$1" == "arm32" ]; then + echo "Building for armhf" + TOOLCHAIN_HOST="arm-linux-gnueabihf" +else + echo "$1 is invalid. Enter first argument arm32 or arm64 to choose the toolchain" + exit +fi + +LIBSERIALPORT_BRANCH=scopy-v2 +LIBIIO_VERSION=v0.26 +LIBAD9361_BRANCH=main +GLOG_BRANCH=v0.4.0 +LIBM2K_BRANCH=main +SPDLOG_BRANCH=v1.x +VOLK_BRANCH=main +GNURADIO_BRANCH=scopy2-maint-3.10 +GRSCOPY_BRANCH=3.10 +GRM2K_BRANCH=main +LIBSIGROKDECODE_BRANCH=master +QWT_BRANCH=qwt-multiaxes-updated +LIBTINYIIOD_BRANCH=master +IIOEMU_BRANCH=main +KDDOCK_BRANCH=2.1 + +export APPIMAGE=1 + +SRC_SCRIPT=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +[ $CI_SCRIPT == "ON" ] && STAGING_AREA=$HOME/scopy/ci/arm/staging || STAGING_AREA=$SRC_SCRIPT/staging +SYSROOT=$STAGING_AREA/sysroot +SYSROOT_TAR=$STAGING_AREA/sysroot.tar.gz +TOOLCHAIN=$STAGING_AREA/cross-pi-gcc +TOOLCHAIN_BIN=$TOOLCHAIN/bin +TOOLCHAIN_FILE=$SRC_SCRIPT/cmake_toolchain.cmake +QT_LOCATION=$SYSROOT/usr/lib/$TOOLCHAIN_HOST/qt5 + +CMAKE_BIN=$STAGING_AREA/cmake/bin/cmake +QMAKE_BIN=$QT_LOCATION/bin/qmake +JOBS=-j14 + +APP_DIR=$SRC_SCRIPT/scopy.AppDir +APP_IMAGE=$SRC_SCRIPT/Scopy.AppImage +APP_RUN=$SRC_SCRIPT/AppRun +APP_DESKTOP=$SRC_SCRIPT/scopy.desktop +APP_SQUASHFS=$SRC_SCRIPT/scopy.squashfs + +# Runetimes downloaded from https://github.com/AppImage/AppImageKit/releases/continuous Mar 9, 2023 +if [ $TOOLCHAIN_HOST == "aarch64-linux-gnu" ]; then + RUNTIME_ARM=$SRC_SCRIPT/runtime-aarch64 +elif [ $TOOLCHAIN_HOST == "arm-linux-gnueabihf" ]; then + RUNTIME_ARM=$SRC_SCRIPT/runtime-armhf +fi + +if [ $TOOLCHAIN_HOST == "aarch64-linux-gnu" ]; then + CMAKE_SYSTEM_PROCESSOR=aarch64 + CMAKE_LIBRARY_ARCHITECTURE=aarch64-linux-gnu +elif [ $TOOLCHAIN_HOST == "arm-linux-gnueabihf" ]; then + CMAKE_SYSTEM_PROCESSOR=arm + CMAKE_LIBRARY_ARCHITECTURE=arm-linux-gnueabihf +fi + +# The exports below ensure these variables are available to the toolchain file. +export CMAKE_SYSROOT="$SYSROOT" +export QT_LOCATION="$QT_LOCATION" +export STAGING_AREA="$STAGING_AREA" +export CMAKE_SYSTEM_PROCESSOR="$CMAKE_SYSTEM_PROCESSOR" +export CMAKE_LIBRARY_ARCHITECTURE="$CMAKE_LIBRARY_ARCHITECTURE" + +CMAKE_OPTS=(\ + -DCMAKE_INSTALL_PREFIX="$SYSROOT" \ + -DCMAKE_TOOLCHAIN_FILE="$TOOLCHAIN_FILE" \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + ) + +CMAKE="$CMAKE_BIN ${CMAKE_OPTS[*]}" + +if [ $TOOLCHAIN_HOST == "aarch64-linux-gnu" ]; then + QT_DEVICE=linux-rasp-pi4-aarch64 +elif [ $TOOLCHAIN_HOST == "arm-linux-gnueabihf" ]; then + QT_DEVICE="linux-rasp-pi4-v3d-g++" +fi + +QT_BUILD_LOCATION=$QT_LOCATION # the location where Qt will be installed in the system +QT_SYSTEM_LOCATION=/usr/lib/$TOOLCHAIN_HOST/qt5 # the Qt location relative to the sysroot folder +CROSS_COMPILER=$STAGING_AREA/cross-pi-gcc + +if [ $TOOLCHAIN_HOST == "aarch64-linux-gnu" ]; then + CROSSCOMPILER_DOWNLOAD_LINK=https://sourceforge.net/projects/raspberry-pi-cross-compilers/files/Bonus%20Raspberry%20Pi%20GCC%2064-Bit%20Toolchains/Raspberry%20Pi%20GCC%2064-Bit%20Cross-Compiler%20Toolchains/Bookworm/GCC%2012.2.0/cross-gcc-12.2.0-pi_64.tar.gz +elif [ $TOOLCHAIN_HOST == "arm-linux-gnueabihf" ]; then + CROSSCOMPILER_DOWNLOAD_LINK=https://sourceforge.net/projects/raspberry-pi-cross-compilers/files/Raspberry%20Pi%20GCC%20Cross-Compiler%20Toolchains/Bullseye/GCC%2010.2.0/Raspberry%20Pi%203A%2B%2C%203B%2B%2C%204%2C%205/cross-gcc-10.2.0-pi_3%2B.tar.gz +fi + +CMAKE_DOWNLOAD_LINK=https://github.com/Kitware/CMake/releases/download/v3.29.0-rc2/cmake-3.29.0-rc2-linux-x86_64.tar.gz +KUIPER_DOWNLOAD_LINK=https://swdownloads.analog.com/cse/kuiper/image_2023-12-13-ADI-Kuiper-full.zip + +if [ $TOOLCHAIN_HOST == "aarch64-linux-gnu" ]; then + QT_DOWNLOAD_LINK=https://download.qt.io/archive/qt/5.15/5.15.10/single/qt-everywhere-opensource-src-5.15.10.tar.xz +elif [ $TOOLCHAIN_HOST == "arm-linux-gnueabihf" ]; then + QT_DOWNLOAD_LINK=http://download.qt.io/archive/qt/5.15/5.15.2/single/qt-everywhere-src-5.15.2.tar.xz +fi + +SYSROOT_RELATIVE_LINKS=https://raw.githubusercontent.com/abhiTronix/rpi_rootfs/master/scripts/sysroot-relativelinks.py diff --git a/ci/arm/arm_build_process.sh b/ci/arm/arm_build_process.sh new file mode 100755 index 0000000000..63efc978f5 --- /dev/null +++ b/ci/arm/arm_build_process.sh @@ -0,0 +1,502 @@ +#!/bin/bash + +set -ex +SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ +SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) +SRC_SCRIPT=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +BUILD_STATUS_FILE=$SRC_SCRIPT/build-status + +source $SRC_SCRIPT/arm_build_config.sh $1 + +echo -- USING CMAKE COMMAND: +echo $CMAKE +echo -- USING QT: $QT_LOCATION +echo -- USING QMAKE: $QMAKE_BIN +echo -- SYSROOT: $SYSROOT + +build_with_cmake() { + BUILD_FOLDER=$PWD/build + rm -rf $BUILD_FOLDER + mkdir -p $BUILD_FOLDER + cd $BUILD_FOLDER + eval "$CMAKE $CURRENT_BUILD_CMAKE_OPTS ../" + make $JOBS + CURRENT_BUILD_CMAKE_OPTS="" + + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE +} + +set_config_opts() { + CPP="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-cpp" + CC="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-gcc -v" + CXX="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-g++" + LD="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-ld" + AS="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-as" + AR="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-ar" + RANLIB="${TOOLCHAIN_BIN}/${TOOLCHAIN_HOST}-ranlib" + + CFLAGS=" -I${SYSROOT}/include -I${SYSROOT}/include/${TOOLCHAIN_HOST} -I${SYSROOT}/usr/include -I${SYSROOT}/usr/include/${TOOLCHAIN_HOST} -I${TOOLCHAIN}/include- -fPIC" + CPPFLAGS="-fexceptions ${CFLAGS}" + LDFLAGS="--sysroot=${SYSROOT} -Wl,-rpath=XORIGIN -L${SYSROOT}/lib -L${SYSROOT}/usr/lib -L${SYSROOT}/usr/lib/${TOOLCHAIN_HOST} -L${SYSROOT}/usr/lib/${TOOLCHAIN_HOST}" + + CONFIG_OPTS=() + CONFIG_OPTS+=("--prefix=${SYSROOT}") + CONFIG_OPTS+=("--host=${TOOLCHAIN_HOST}") + CONFIG_OPTS+=("--with-sysroot=${SYSROOT}") + CONFIG_OPTS+=("PKG_CONFIG_DIR=") + CONFIG_OPTS+=("PKG_CONFIG_LIBDIR=${SYSROOT}/usr/lib/${TOOLCHAIN_HOST}/pkgconfig:${SYSROOT}/usr/share/pkgconfig:${SYSROOT}/usr/lib/${TOOLCHAIN_HOST}/pkgconfig:${SYSROOT}/usr/local/lib/pkgconfig") + CONFIG_OPTS+=("PKG_CONFIG_SYSROOT=${SYSROOT}") + CONFIG_OPTS+=("PKG_CONFIG_SYSROOT_DIR=${SYSROOT}") + + if [ "$TOOLCHAIN_HOST" == "aarch64-linux-gnu" ]; then + CONFIG_OPTS+=("PKG_CONFIG_PATH=${SYSROOT}/usr/lib/${TOOLCHAIN_HOST}/pkgconfig") + CONFIG_OPTS+=("PKG_CONFIG=/usr/bin/${TOOLCHAIN_HOST}-pkg-config" ) + elif [ "$TOOLCHAIN_HOST" == "arm-linux-gnueabihf" ]; then + CONFIG_OPTS+=("PKG_CONFIG_PATH=${SYSROOT}/usr/bin/arm-linux-gnueabihf-pkg-config") + CONFIG_OPTS+=("PKG_CONFIG=${SYSROOT}/usr/bin/${TOOLCHAIN_HOST}-pkg-config" ) + fi + + CONFIG_OPTS+=("PKG_CONFIG_ALLOW_CROSS=1") + CONFIG_OPTS+=("LDFLAGS=${LDFLAGS}") + CONFIG_OPTS+=("CFLAGS=${CFLAGS}") + CONFIG_OPTS+=("CPPFLAGS=${CPPFLAGS}") + CONFIG_OPTS+=("CPP=${CPP}") + CONFIG_OPTS+=("CC=${CC}") + CONFIG_OPTS+=("CXX=${CXX}") + CONFIG_OPTS+=("LD=${LD}") + CONFIG_OPTS+=("AS=${AS}") + CONFIG_OPTS+=("AR=${AR}") + CONFIG_OPTS+=("RANLIB=${RANLIB}") +} + +install_packages() { + sudo apt update + sudo apt install -y build-essential cmake unzip gfortran gcc git bison libtool \ + python3 pip gperf pkg-config gdb-multiarch g++ flex texinfo gawk openssl pkg-config-aarch64-linux-gnu \ + pigz libncurses-dev autoconf automake tar figlet liborc-0.4-dev* patchelf libc6-dev-arm64-cross squashfs-tools + pip install mako +} + +download_cmake() { + mkdir -p ${STAGING_AREA} + pushd ${STAGING_AREA} + if [ ! -d cmake ];then + wget ${CMAKE_DOWNLOAD_LINK} + # unzip and rename + tar -xf cmake*.tar.gz && rm cmake*.tar.gz && mv cmake* cmake + else + echo "Cmake already downloaded" + fi + popd +} + +download_crosscompiler(){ + mkdir -p ${STAGING_AREA} + pushd ${STAGING_AREA} + if [ ! -d cross-pi-gcc ];then + wget --progress=dot:giga ${CROSSCOMPILER_DOWNLOAD_LINK} + # unzip and rename + tar -xf cross-gcc-*.tar.gz && rm cross-gcc-*.tar.gz && mv cross-pi-* cross-pi-gcc + else + echo "Crosscompiler already downloaded" + fi + popd +} + +clone() { + echo "#######CLONE#######" + mkdir -p $STAGING_AREA + pushd $STAGING_AREA + [ -d 'libserialport' ] || git clone --recursive https://github.com/cseci/libserialport -b $LIBSERIALPORT_BRANCH libserialport + [ -d 'libiio' ] || git clone --recursive https://github.com/analogdevicesinc/libiio.git -b $LIBIIO_VERSION libiio + [ -d 'libad9361' ] || git clone --recursive https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH libad9361 + [ -d 'libm2k' ] || git clone --recursive https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH libm2k + [ -d 'spdlog' ] || git clone --recursive https://github.com/gabime/spdlog.git -b $SPDLOG_BRANCH spdlog + [ -d 'gr-scopy' ] || git clone --recursive https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH gr-scopy + [ -d 'gr-m2k' ] || git clone --recursive https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH gr-m2k + [ -d 'volk' ] || git clone --recursive https://github.com/gnuradio/volk.git -b $VOLK_BRANCH volk + [ -d 'gnuradio' ] || git clone --recursive https://github.com/analogdevicesinc/gnuradio.git -b $GNURADIO_BRANCH gnuradio + [ -d 'qwt' ] || git clone --recursive https://github.com/cseci/qwt.git -b $QWT_BRANCH qwt + [ -d 'libsigrokdecode' ] || git clone --recursive https://github.com/sigrokproject/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH libsigrokdecode + [ -d 'libtinyiiod' ] || git clone --recursive https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH libtinyiiod + [ -d 'KDDockWidgets' ] || git clone --recursive https://github.com/KDAB/KDDockWidgets.git -b $KDDOCK_BRANCH KDDockWidgets + popd +} + +build_libserialport(){ + echo "### Building libserialport - branch $LIBSERIALPORT_BRANCH" + pushd $STAGING_AREA/libserialport + git clean -xdf + ./autogen.sh + ./configure ${AUTOCONF_OPTS} + make $JOBS + patchelf --force-rpath --set-rpath \$ORIGIN $STAGING_AREA/libserialport/.libs/libserialport.so + sudo make install + + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE + popd +} + +build_libiio() { + echo "### Building libiio - version $LIBIIO_VERSION" + pushd $STAGING_AREA/libiio + CURRENT_BUILD_CMAKE_OPTS="\ + -DWITH_TESTS:BOOL=OFF \ + -DWITH_DOC:BOOL=OFF \ + -DHAVE_DNS_SD:BOOL=ON \ + -DWITH_MATLAB_BINDINGS:BOOL=OFF \ + -DCSHARP_BINDINGS:BOOL=OFF \ + -DPYTHON_BINDINGS:BOOL=OFF \ + -DWITH_SERIAL_BACKEND:BOOL=ON \ + -DENABLE_IPV6:BOOL=OFF \ + -DINSTALL_UDEV_RULE:BOOL=OFF \ + " + build_with_cmake + sudo make install + popd +} + +build_libad9361() { + echo "### Building libad9361 - branch $LIBAD9361_BRANCH" + pushd $STAGING_AREA/libad9361 + build_with_cmake + sudo make install + popd +} + +build_spdlog() { + echo "### Building spdlog - branch $SPDLOG_BRANCH" + pushd $STAGING_AREA/spdlog + CURRENT_BUILD_CMAKE_OPTS="-DSPDLOG_BUILD_SHARED=ON" + build_with_cmake + sudo make install + popd +} + +build_libm2k() { + echo "### Building libm2k - branch $LIBM2K_BRANCH" + pushd $STAGING_AREA/libm2k + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PYTHON=OFF \ + -DENABLE_CSHARP=OFF \ + -DBUILD_EXAMPLES=OFF \ + -DENABLE_TOOLS=OFF \ + -DINSTALL_UDEV_RULES=OFF \ + -DENABLE_LOG=OFF \ + " + build_with_cmake + sudo make install + popd +} + +build_volk() { + echo "### Building volk - branch $VOLK_BRANCH" + pushd $STAGING_AREA/volk + build_with_cmake + sudo make install + popd +} + +build_gnuradio() { + echo "### Building gnuradio - branch $GNURADIO_BRANCH" + pushd $STAGING_AREA/gnuradio + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_DEFAULT=OFF \ + -DENABLE_GNURADIO_RUNTIME=ON \ + -DENABLE_GR_ANALOG=ON \ + -DENABLE_GR_BLOCKS=ON \ + -DENABLE_GR_FFT=ON \ + -DENABLE_GR_FILTER=ON \ + -DENABLE_GR_IIO=ON \ + -DENABLE_POSTINSTALL=OFF \ + " + build_with_cmake + sudo make install + popd +} + +build_grscopy() { + echo "### Building gr-scopy - branch $GRSCOPY_BRANCH" + pushd $STAGING_AREA/gr-scopy + build_with_cmake + sudo make install + popd +} + +build_grm2k() { + echo "### Building gr-m2k - branch $GRM2K_BRANCH" + pushd $STAGING_AREA/gr-m2k + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PYTHON=OFF \ + -DDIGITAL=OFF \ + " + build_with_cmake + sudo make install + popd +} + +build_qwt() { + echo "### Building qwt - branch $QWT_BRANCH" + pushd $STAGING_AREA/qwt + git clean -xdf + sed -i 's|/usr/local/qwt-$$QWT_VERSION-ma|/usr/local|g' qwtconfig.pri + $QMAKE_BIN INCLUDEPATH=$SYSROOT/include LIBS=-L$SYSROOT/lib LIBS+=-L$SYSROOT/lib/$TOOLCHAIN_HOST qwt.pro + make $JOBS + patchelf --force-rpath --set-rpath \$ORIGIN $STAGING_AREA/qwt/lib/libqwt.so + sudo make INSTALL_ROOT=$SYSROOT install + + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE + popd +} + +build_libsigrokdecode() { + echo "### Building libsigrokdecode - branch $LIBSIGROKDECODE_BRANCH" + set_config_opts + pushd $STAGING_AREA/libsigrokdecode + git clean -xdf + ./autogen.sh + ./configure "${CONFIG_OPTS[@]}" + make $JOBS + patchelf --force-rpath --set-rpath \$ORIGIN $STAGING_AREA/libsigrokdecode/.libs/libsigrokdecode.so + sudo make install + + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE + popd +} + +build_libtinyiiod() { + echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" + pushd $STAGING_AREA/libtinyiiod + CURRENT_BUILD_CMAKE_OPTS="-DBUILD_EXAMPLES=OFF" + build_with_cmake + sudo make install + popd +} + +build_kddock () { + echo "### Building KDDockWidgets - version $KDDOCK_BRANCH" + pushd $STAGING_AREA/KDDockWidgets + CURRENT_BUILD_CMAKE_OPTS="" + build_with_cmake + sudo make install + popd +} + +build_iio-emu(){ + echo "### Building iio-emu - branch $IIOEMU_BRANCH" + pushd $STAGING_AREA + [ -d 'iio-emu' ] || git clone --recursive https://github.com/analogdevicesinc/iio-emu -b $IIOEMU_BRANCH iio-emu + pushd $STAGING_AREA/iio-emu + CURRENT_BUILD_CMAKE_OPTS="-DCMAKE_EXE_LINKER_FLAGS=\"-ldl -lz -licuuc -licudata -llzma\"" # ??? + build_with_cmake + sudo make install + popd + popd +} + +build_scopy() { + echo "### Building scopy" + [ -f /home/runner/build-status ] && cp /home/runner/build-status $SRC_DIR/build-status + pushd $SRC_DIR + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PLUGIN_TEST=ON \ + -DENABLE_TESTING=ON \ + -DPYTHON_EXECUTABLE=$SYSROOT/bin/python3 \ + " + build_with_cmake + popd +} + +create_appdir(){ + BUILD_FOLDER=$SRC_DIR/build + EMU_BUILD_FOLDER=$STAGING_AREA/iio-emu/build + PLUGINS=$BUILD_FOLDER/plugins/plugins + SCOPY_DLL=$(find $BUILD_FOLDER -maxdepth 1 -type f -name "libscopy*") + REGMAP_XMLS=$BUILD_FOLDER/plugins/regmap/xmls + DAC_WAVEFORM_CSV=$SRC_DIR/plugins/dac/res/csv + EMU_XMLS=$BUILD_FOLDER/plugins/emu_xml + EMU_CONFIG=$SRC_DIR/resources/scopy_emu_options_config.json + TRANSLATIONS_QM=$(find $BUILD_FOLDER/translations -type f -name "*.qm") + STYLE_FOLDER=$BUILD_FOLDER/style + COPY_DEPS=$SRC_DIR/ci/arm/copy-deps.sh + + rm -rf $APP_DIR + mkdir -p $APP_DIR + mkdir -p $APP_DIR/usr/bin + mkdir -p $APP_DIR/usr/lib + mkdir -p $APP_DIR/usr/share/applications + mkdir -p $APP_DIR/usr/share/icons/hicolor/512x512 + + cp $APP_RUN $APP_DIR + cp $APP_DESKTOP $APP_DIR + cp $SRC_DIR/gui/res/scopy.png $APP_DIR + cp $SRC_DIR/gui/res/scopy.png $APP_DIR/usr/share/icons/hicolor/512x512 + cp $APP_DESKTOP $APP_DIR/usr/share/applications + + cp $EMU_BUILD_FOLDER/iio-emu $APP_DIR/usr/bin + cp $BUILD_FOLDER/scopy $APP_DIR/usr/bin + + cp $SCOPY_DLL $APP_DIR/usr/lib + mkdir -p $APP_DIR/usr/lib/scopy/plugins + cp $PLUGINS/*.so $APP_DIR/usr/lib/scopy/plugins + + mkdir -p $APP_DIR/usr/lib/scopy/translations + cp $TRANSLATIONS_QM $APP_DIR/usr/lib/scopy/translations + + cp -R $STYLE_FOLDER $APP_DIR/usr/lib/scopy/style + + if [ -d $REGMAP_XMLS ]; then + cp -r $REGMAP_XMLS $APP_DIR/usr/lib/scopy/plugins + fi + + cp -r $DAC_WAVEFORM_CSV $APP_DIR/usr/lib/scopy/plugins + cp -r $EMU_XMLS $APP_DIR/usr/lib/scopy/plugins + mkdir -p $APP_DIR/usr/lib/scopy/plugins/resources + cp $EMU_CONFIG $APP_DIR/usr/lib/scopy/plugins/resources + + $COPY_DEPS --lib-dir ${SYSROOT}:${BUILD_FOLDER} --output-dir $APP_DIR/usr/lib $APP_DIR/usr/bin/scopy + $COPY_DEPS --lib-dir ${SYSROOT}:${BUILD_FOLDER} --output-dir $APP_DIR/usr/lib $APP_DIR/usr/bin/iio-emu + $COPY_DEPS --lib-dir ${SYSROOT}:${BUILD_FOLDER} --output-dir $APP_DIR/usr/lib $APP_DIR/usr/bin/scopy + $COPY_DEPS --lib-dir ${SYSROOT}:${BUILD_FOLDER} --output-dir $APP_DIR/usr/lib "$APP_DIR/usr/lib/scopy/plugins/*.so" + cp -r $QT_LOCATION/plugins $APP_DIR/usr + + # search for the python version linked by cmake and copy inside the appimage the same version + FOUND_PYTHON_VERSION=$(grep 'PYTHON_VERSION' $SRC_DIR/build/CMakeCache.txt | awk -F= '{print $2}' | grep -o 'python[0-9]\+\.[0-9]\+') + python_path=${SYSROOT}/usr/lib/$FOUND_PYTHON_VERSION + cp -r $python_path $APP_DIR/usr/lib + + cp -r $SYSROOT/share/libsigrokdecode/decoders $APP_DIR/usr/lib + + cp $QT_LOCATION/lib/libQt5XcbQpa.so* $APP_DIR/usr/lib + cp $QT_LOCATION/lib/libQt5EglFSDeviceIntegration.so* $APP_DIR/usr/lib + cp $QT_LOCATION/lib/libQt5DBus.so* $APP_DIR/usr/lib + cp $QT_LOCATION/lib/libQt5OpenGL.so* $APP_DIR/usr/lib + cp $SYSROOT/lib/${TOOLCHAIN_HOST}/libGLESv2.so* $APP_DIR/usr/lib + cp $SYSROOT/lib/${TOOLCHAIN_HOST}/libbsd.so* $APP_DIR/usr/lib + cp $SYSROOT/lib/${TOOLCHAIN_HOST}/libXdmcp.so* $APP_DIR/usr/lib + cp $SYSROOT/usr/lib/${TOOLCHAIN_HOST}/libXau.so* $APP_DIR/usr/lib + cp $SYSROOT/usr/lib/${TOOLCHAIN_HOST}/libffi.so* $APP_DIR/usr/lib +} + +create_appimage(){ + rm -rf $APP_IMAGE + mksquashfs $APP_DIR $APP_SQUASHFS -root-owned -noappend + cat $RUNTIME_ARM >> $APP_IMAGE + cat $APP_SQUASHFS >> $APP_IMAGE + chmod a+x $APP_IMAGE +} + +# move the sysroot from the home of the docker container to the known location +move_sysroot(){ + mkdir -p $STAGING_AREA + [ -d /home/runner/sysroot ] && sudo mv /home/runner/sysroot $SYSROOT || echo "Sysroot not found or already moved" + if [ ! -d $SYSROOT ];then + echo "Missing SYSROOT" + exit 1 + fi +} + +# move the staging folder that contains the tools needed for the build to the known location +move_tools(){ + [ -d /home/runner/staging ] && mv /home/runner/staging $STAGING_AREA || echo "Staging folder not found or already moved" + if [ ! -d $STAGING_AREA ]; then + echo "Missing tools folder, downloading now" + download_cmake + download_crosscompiler + fi +} + +# move and rename the AppImage artifact +move_appimage(){ + if [ $TOOLCHAIN_HOST == "aarch64-linux-gnu" ]; then + mv $APP_IMAGE $SRC_DIR/Scopy-arm64.AppImage + elif [ $TOOLCHAIN_HOST == "arm-linux-gnueabihf" ]; then + mv $APP_IMAGE $SRC_DIR/Scopy-armhf.AppImage + fi +} + +generate_ci_envs() +{ + $SRC_DIR/ci/general/gen_ci_envs.sh > $SRC_DIR/ci/general/gh-actions.envs +} + + +# +# Helper functions +# +build_deps(){ + clone + build_libserialport + build_libiio + build_libad9361 + build_spdlog + build_libm2k + build_volk + build_gnuradio + build_grscopy + build_grm2k + build_qwt + build_libsigrokdecode + build_libtinyiiod + # build_kddock temporary disabled +} + +run_workflow(){ + install_packages + move_tools + move_sysroot + build_iio-emu + build_scopy + create_appdir + create_appimage + move_appimage +} + +get_tools(){ + install_packages + move_tools + move_sysroot +} + +generate_appimage(){ + build_iio-emu + build_scopy + create_appdir + create_appimage +} + +dev_setup(){ + # for the local development of Scopy arm the easyest method is to download the docker image + # after that, a temporary docker volume is created to bridge the local environment and the docker container + # and the compiling is done inside the container unsing the already prepared filesystem + + # for example, if you want to develop for ARMHF architecture, you would execute: + docker pull cristianbindea/scopy2-armhf-appimage:latest # to download the image + + # and to run the image, while creating a docker volume, you would run: + docker run -it \ + --mount type=bind,source="$SRC_DIR",target=/home/runner/scopy \ + cristianbindea/scopy2-armhf-appimage:latest + + + # now this repository folder it shared with the docker container + + # to compile and package the application use "scopy/ci/arm/arm_build_process.sh run_workflow" + + # to continue using the same docker container use "docker start (container id)" and "docker attach (container id)" + + # finally after the development is done use this to clean the system + # "docker container rm -v (container id)" + # "docker image rm cristianbindea/scopy2-arm-appimage:latest" + + # **to get the container id use "docker container ls -a" +} + +for arg in "${@:2}"; do + $arg +done diff --git a/ci/arm/build_qt.sh b/ci/arm/build_qt.sh new file mode 100755 index 0000000000..0c13129917 --- /dev/null +++ b/ci/arm/build_qt.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +set -ex +SRC_SCRIPT=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +source $SRC_SCRIPT/arm_build_config.sh $1 + +install_packages(){ + sudo apt install -y build-essential cmake unzip gfortran gcc git bison \ + gperf pkg-config gdb-multiarch g++ flex texinfo gawk openssl \ + pigz libncurses-dev autoconf automake tar figlet libclang-dev +} + +# Download and extract QT Source (QT 5.15.2 for armhf and QT 5.15.10 for arm64) +download_qt(){ + mkdir -p ${STAGING_AREA} + pushd ${STAGING_AREA} + if [ ! -d qt-everywhere-src ];then + wget --progress=dot:giga ${QT_DOWNLOAD_LINK} + tar -xf qt-everywhere-*.tar.xz && rm qt-everywhere-*.tar.xz && mv qt-everywhere-* qt-everywhere-src # unzip and rename + cd qt-everywhere-src + # Patch QT Source + if [ $TOOLCHAIN_HOST == "aarch64-linux-gnu" ]; then + patch -p1 -R < $SRC_SCRIPT/qt_patch_arm64.patch + elif [ $TOOLCHAIN_HOST == "arm-linux-gnueabihf" ]; then + patch -p1 < $SRC_SCRIPT/qt_patch_armhf.patch + fi + else + echo "QT already downloaded" + fi + popd +} + +download_crosscompiler(){ + mkdir -p ${STAGING_AREA} + pushd ${STAGING_AREA} + if [ ! -d cross-pi-gcc ];then + wget --progress=dot:giga ${CROSSCOMPILER_DOWNLOAD_LINK} + tar -xf cross-gcc-*.tar.gz && rm cross-gcc-*.tar.gz && mv cross-pi-* cross-pi-gcc # unzip and rename + else + echo "Crosscompiler already downloaded" + fi + popd +} + +build(){ + mkdir -p "$STAGING_AREA"/build-qt && cd "$STAGING_AREA"/build-qt + ../qt-everywhere-src/configure \ + -v \ + -release \ + -opensource \ + -confirm-license \ + -sysroot "$SYSROOT" \ + -prefix "$QT_SYSTEM_LOCATION" \ + -extprefix "$QT_BUILD_LOCATION" \ + -egl \ + -eglfs \ + -reduce-exports \ + -opengl desktop \ + -device $QT_DEVICE \ + -device-option CROSS_COMPILE="$CROSS_COMPILER"/bin/"$TOOLCHAIN_HOST"- \ + -skip qtandroidextras \ + -skip qtcharts \ + -skip qtdatavis3d \ + -skip qtdoc \ + -skip qtgamepad \ + -skip qtgraphicaleffects \ + -skip qtlocation \ + -skip qtlottie \ + -skip qtmacextras \ + -skip qtnetworkauth \ + -skip qtquick3d \ + -skip qtquickcontrols \ + -skip qtquickcontrols2 \ + -skip qtquicktimeline \ + -skip qtremoteobjects \ + -skip qtscript \ + -skip qtsensors \ + -skip qtspeech \ + -skip qttranslations \ + -skip qtvirtualkeyboard \ + -skip qtwayland \ + -skip qtwebchannel \ + -skip qtwebengine \ + -skip qtwebglplugin \ + -skip qtwebsockets \ + -skip qtwebview \ + -skip qtwinextras \ + -nomake examples -no-compile-examples \ + -nomake tests \ + -make libs \ + -pkg-config \ + -no-use-gold-linker \ + -recheck \ + -xcb \ + -xcb-xlib \ + -bundled-xcb-xinput \ + -qt-pcre \ + -qpa eglfs \ + -L"$SYSROOT"/usr/lib/"$TOOLCHAIN_HOST" -I"$SYSROOT"/usr/include/"$TOOLCHAIN_HOST" + + make -j14 + sudo make install # installs to $QT_BUILD_LOCATION +} + +for arg in "${@:2}"; do + $arg +done diff --git a/ci/arm/cmake_toolchain.cmake b/ci/arm/cmake_toolchain.cmake new file mode 100644 index 0000000000..8900723914 --- /dev/null +++ b/ci/arm/cmake_toolchain.cmake @@ -0,0 +1,105 @@ +cmake_minimum_required(VERSION 3.18) + +set(CMAKE_SYSTEM_NAME Linux) + +#[[ + - CMAKE_SYSROOT + - CMAKE_INSTALL_PREFIX + - STAGING_AREA + - CMAKE_SYSTEM_PROCESSOR + - CMAKE_LIBRARY_ARCHITECTURE +are exported from the arm_build_config.sh script +]] + +set(CMAKE_SYSROOT $ENV{CMAKE_SYSROOT}) +set(QT_LOCATION $ENV{QT_LOCATION}) +set(STAGING_AREA $ENV{STAGING_AREA}) +set(CMAKE_SYSROOT $ENV{CMAKE_SYSROOT}) +set(CMAKE_SYSTEM_PROCESSOR $ENV{CMAKE_SYSTEM_PROCESSOR}) +set(CMAKE_LIBRARY_ARCHITECTURE $ENV{CMAKE_LIBRARY_ARCHITECTURE}) + +set(TOOLCHAIN_FILE ${STAGING_AREA}/cross-pi-gcc) +set(TOOLCHAIN_BIN ${TOOLCHAIN_FILE}/bin) +set(CMAKE_PREFIX_PATH ${QT_LOCATION}) +list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/usr/lib") +list(APPEND CMAKE_PREFIX_PATH "${CMAKE_SYSROOT}/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}") + +if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") + set(PKG_CONFIG_EXECUTABLE ${CMAKE_LIBRARY_ARCHITECTURE}-pkg-config CACHE PATH "PKG_CONFIG_EXECUTABLE" FORCE) + set(ENV{PKG_CONFIG_DIR} "") +else() + set(PKG_CONFIG_EXECUTABLE "${CMAKE_SYSROOT}/usr/bin/${CMAKE_LIBRARY_ARCHITECTURE}-pkg-config" + CACHE PATH "PKG_CONFIG_EXECUTABLE" FORCE + ) +endif() + +set(ENV{PKG_CONFIG_EXECUTABLE} ${PKG_CONFIG_EXECUTABLE}) +set(ENV{PKG_CONFIG_ALLOW_CROSS} 1) +set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON) +set(RPI_PKG_CONFIG_LIBDIR "${CMAKE_SYSROOT}/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/pkgconfig:${RPI_PKG_CONFIG_LIBDIR}") +set(RPI_PKG_CONFIG_LIBDIR "${CMAKE_SYSROOT}/usr/share/pkgconfig:${RPI_PKG_CONFIG_LIBDIR}") +set(RPI_PKG_CONFIG_LIBDIR "${CMAKE_SYSROOT}/usr/lib/pkgconfig:${RPI_PKG_CONFIG_LIBDIR}") +set(RPI_PKG_CONFIG_LIBDIR "${CMAKE_SYSROOT}/usr/local/lib/pkgconfig:${RPI_PKG_CONFIG_LIBDIR}") +set(ENV{PKG_CONFIG_LIBDIR} "${RPI_PKG_CONFIG_LIBDIR}") +set(ENV{PKG_CONFIG} "${CMAKE_SYSROOT}/usr/bin/${CMAKE_LIBRARY_ARCHITECTURE}-pkg-config") + +set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/include:") +set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/include:") +set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/include/${CMAKE_LIBRARY_ARCHITECTURE}:") +set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:${CMAKE_SYSROOT}/usr/share/include:") +set(ENV{PKG_CONFIG_SYSROOT_DIR} "${CMAKE_SYSROOT}") + +set(CMAKE_AR ${TOOLCHAIN_BIN}/${CMAKE_LIBRARY_ARCHITECTURE}-ar) +set(CMAKE_ASM_COMPILER ${TOOLCHAIN_BIN}/${CMAKE_LIBRARY_ARCHITECTURE}-gcc) +set(CMAKE_C_COMPILER ${TOOLCHAIN_BIN}/${CMAKE_LIBRARY_ARCHITECTURE}-gcc) +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN}/${CMAKE_LIBRARY_ARCHITECTURE}-g++) +set(CMAKE_LINKER ${TOOLCHAIN_BIN}/${CMAKE_LIBRARY_ARCHITECTURE}-ld) +set(CMAKE_OBJCOPY ${TOOLCHAIN_BIN}/${CMAKE_LIBRARY_ARCHITECTURE}-objcopy) +set(CMAKE_RANLIB ${TOOLCHAIN_BIN}/${CMAKE_LIBRARY_ARCHITECTURE}-ranlib) +set(CMAKE_SIZE ${TOOLCHAIN_BIN}/${CMAKE_LIBRARY_ARCHITECTURE}-size) +set(CMAKE_STRIP ${TOOLCHAIN_BIN}/${CMAKE_LIBRARY_ARCHITECTURE}-strip) + +set(CMAKE_C_FLAGS "-march=armv8-a") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/include") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/include") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/include/${CMAKE_LIBRARY_ARCHITECTURE}") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -I${CMAKE_SYSROOT}/usr/share/include") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") +set(CMAKE_CXX_FLAGS "-fexceptions -frtti ${CMAKE_C_FLAGS}") +set(CMAKE_CXX_FLAGS_DEBUG "-Os -g") +set(CMAKE_CXX_FLAGS_RELEASE "-Os -DNDEBUG") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--as-needed -Wl,-O1 -Wl,--hash-style=gnu") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lpthread -lboost_program_options") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/lib") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${QT_LOCATION}/lib") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${TOOLCHAIN_FILE}/${CMAKE_LIBRARY_ARCHITECTURE}/lib") +set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${TOOLCHAIN_FILE}/${CMAKE_LIBRARY_ARCHITECTURE}/libc/lib") + +set(CMAKE_SHARED_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}) + +#[[ +Debug Mode + set(CMAKE_VERBOSE ON) + set(CMAKE_VERBOSE_MAKEFILE ON) + set(PKG_CONFIG_ARGN "--debug") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--verbose") + set(CMAKE_FIND_DEBUG_MODE TRUE) +]] + +set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) +set(CMAKE_INSTALL_RPATH + "$ORIGIN" + "$ORIGIN/../lib" + "/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}" + "/usr/lib" + "/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/qt5/lib" +) + +set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH) diff --git a/ci/arm/copy-deps.sh b/ci/arm/copy-deps.sh new file mode 100755 index 0000000000..79352cadd9 --- /dev/null +++ b/ci/arm/copy-deps.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +set -e + +usage() +{ + echo "test" + +} +COPY="" + +while [ "$1" != "" ] +do + case "$1" in + "--help"| "-h") + usage + exit + ;; + "--lib-dir" | "-l") + SYSROOT="$2:$SYSROOT" + shift + ;; + "--output-dir" | "-o") + OUTPUT_DIR="$2" + COPY="true" + shift + ;; + *) + FILES="$FILES $1" + ;; + esac + shift +done + +LIBS_ARRAY=() +# Separate SYSROOT into an array of SEARCH_PATHS +IFS=":" read -r -a SEARCH_PATHS <<< "$SYSROOT" + +# If COPY is not true, display all dependencies without excluding any from the blacklist +if [[ $COPY != "true" ]]; then + BLACKLISTED="" +else + BLACKLISTED=($(wget --quiet https://raw.githubusercontent.com/probonopd/AppImages/master/excludelist -O - | sort | uniq | cut -d '#' -f 1 | grep -v "^#.*" | grep "[^-\s]")) + BLACKLISTED+=(ld-linux-armhf.so) + BLACKLISTED+=(ld-linux-aarch64.so) + BLACKLISTED+=(ld-linux-armhf.so.3) + BLACKLISTED+=(ld-linux-aarch64.so.1) +fi + +findlib() +{ + LIB=$1 + for path in "${SEARCH_PATHS[@]}" + do + LIB_PATH=$(find "$path" -name "${LIB%%.so*}.so*" -type f 2>/dev/null | sort | head -1) + [ "$LIB_PATH" != "" ] && break # Exit for at first succesful find + done + + [ "$LIB_PATH" = "" ] && LIB_PATH="not found" + echo "$LIB_PATH" +} + +copylib() +{ + library=$1 + destination=$2 + + lib_location=${library%/*} + lib_name=${library##*/} + lib_name_without_version=${lib_name%%.so*}.so + + echo "${lib_name_without_version}" + echo -n copied: + cp --verbose "${library}" "${destination}" + + #IFS=$'\n' read -r -a symlinks < <(find "$lib_location" -name "${lib_name_without_version}*" -type l 2>/dev/null) || echo "No symlinks to copy" + + symlinks=($(find "$lib_location" -name "${lib_name_without_version}*" -type l 2>/dev/null)) + for link in "${symlinks[@]}" + do + pushd ${destination} > /dev/null + [ -L "${link##*/}" ] && rm "${link##*/}" + echo -n linked: + ln --symbolic --verbose "${library##*/}" "${link##*/}" + popd > /dev/null + done +} + +process_lib(){ + DEPS=$(readelf -d "$1" 2> /dev/null | \ + grep NEEDED | \ + cut -d ":" -f 2 | \ + sed -e 's,\[,,g' -e 's,],,g' -e 's,[ ]*,,g'| \ + sort) + + for library in $DEPS + do + # Check if the library is blacklisted or was already processed + if [[ ! "${BLACKLISTED[*]}" =~ "${library}" && ! "${LIBS_ARRAY[*]}" =~ "${library}" ]]; then + + LIBS_ARRAY+=("${library}") + LIB_PATH=$(findlib "$library") # try to find the library in sysroot + + if [[ $COPY == "true" ]]; then + if [[ $LIB_PATH == "not found" ]]; then + echo "Error: ${library} not found" + exit 1 + fi + mkdir -p "$OUTPUT_DIR" + copylib "$LIB_PATH" "$OUTPUT_DIR" + fi + + # If COPY is not true, display only the library and its location if found + [ "$COPY" != "true" ] && echo "$library => $LIB_PATH" + process_lib "$LIB_PATH" + fi + done +} + +for arg in $FILES; do + process_lib "${arg}" +done \ No newline at end of file diff --git a/ci/arm/create_docker_image.sh b/ci/arm/create_docker_image.sh new file mode 100755 index 0000000000..c75d7e5a25 --- /dev/null +++ b/ci/arm/create_docker_image.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +set -ex +SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ +SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) +ARCH=$1 +source $SRC_DIR/ci/arm/arm_build_config.sh "$ARCH" + +# install docker +install_packages(){ + sudo apt-get update + sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common + curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - + sudo add-apt-repository --yes "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" + sudo apt-get update + sudo apt-get -y install containerd.io docker-ce docker-ce-cli docker-buildx-plugin +} + +create_sysroot(){ + $SRC_DIR/ci/arm/create_sysroot.sh "$ARCH" \ + install_packages \ + download_kuiper \ + install_qemu \ + extract_sysroot \ + configure_sysroot +} + +# archive the sysroot and move it next to the Dockerfile in order to copy the tar in the docker image +tar_and_move_sysroot(){ + pushd $STAGING_AREA + sudo tar -czf "${SYSROOT_TAR##*/}" sysroot + sudo mv $SYSROOT_TAR $SRC_DIR/ci/arm + popd +} + +create_image(){ + pushd ${SRC_DIR}/ci/arm + if [ "${ARCH}" == "arm32" ]; then + DOCKER_TAG=cristianbindea/scopy2-armhf-appimage:testing + elif [ "${ARCH}" == "arm64" ]; then + DOCKER_TAG=cristianbindea/scopy2-arm64-appimage:testing + fi + + # docker build \ + # --load \ + # --progress plain \ + # --tag ${DOCKER_TAG} \ + # -f docker/Dockerfile \ + # --build-arg ARCH=${ARCH} . + + # build the image using old backend + DOCKER_BUILDKIT=0 docker build \ + --tag ${DOCKER_TAG} \ + -f docker/Dockerfile \ + --build-arg ARCH=${ARCH} . + popd +} + +install_packages +create_sysroot +tar_and_move_sysroot +create_image diff --git a/ci/arm/create_sysroot.sh b/ci/arm/create_sysroot.sh new file mode 100755 index 0000000000..276c18d41d --- /dev/null +++ b/ci/arm/create_sysroot.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +set -ex +SRC_SCRIPT=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +source $SRC_SCRIPT/arm_build_config.sh $1 + +IMAGE_FILE=2023-12-13-ADI-Kuiper-full.img + +install_packages(){ + sudo apt update + sudo apt -y install git wget unzip python3 python-is-python3 2to3 +} + +download_kuiper(){ + mkdir -p ${STAGING_AREA} + pushd ${STAGING_AREA} + if [ ! -f image_2023-12-13-ADI-Kuiper-full.zip ]; then + wget \ + --progress=bar:force:noscroll \ + --progress=dot:giga \ + --header='User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:122.0) Gecko/20100101 Firefox/122.0' \ + --header='Accept-Language: en-US,en;q=0.5' \ + --header='Accept-Encoding: gzip, deflate, br' \ + --header='Connection: keep-alive' \ + --header='Upgrade-Insecure-Requests: 1' \ + --header='Sec-Fetch-Dest: document' \ + --header='Sec-Fetch-Mode: navigate' \ + --header='Sec-Fetch-Site: none' \ + --header='Sec-Fetch-User: ?1' \ + --header='Pragma: no-cache' \ + --header='Cache-Control: no-cache' \ + ${KUIPER_DOWNLOAD_LINK} + fi + [ -f $IMAGE_FILE ] || unzip image*.zip + popd +} + +# install qemu needed for the sysroot configuration +install_qemu(){ + sudo apt update + sudo apt -y install qemu qemu-system qemu-user-static qemu-user +} + +# mount the Kuiper image and copy the entire rootfs partition +extract_sysroot(){ + sudo rm -rf ${STAGING_AREA}/kuiper + sudo mkdir -p ${STAGING_AREA}/kuiper + + # with file ${IMAGE_FILE} we can see the start sector (4218880) and the length (19947520) of the second partition contained in the Kuiper image + # using this info we can directly mount that partition + sudo mount -v -o loop,offset=$((512*4218880)),sizelimit=$((512*19947520)) ${STAGING_AREA}/${IMAGE_FILE} ${STAGING_AREA}/kuiper + + mkdir -p ${SYSROOT} + sudo cp -arp ${STAGING_AREA}/kuiper/* ${SYSROOT} + sudo cp /etc/resolv.conf ${SYSROOT}/etc/resolv.conf + sudo umount ${STAGING_AREA}/kuiper + sudo rm -rf ${STAGING_AREA}/kuiper + rm -rf ${STAGING_AREA:?}/${IMAGE_FILE} + rm -rf ${STAGING_AREA}/image*.zip +} + +# execute chroot inside the sysroot folder and install/remove packages using apt +configure_sysroot(){ + if [ $TOOLCHAIN_HOST == "aarch64-linux-gnu" ]; then + cat $SRC_SCRIPT/inside_chroot_arm64.sh | sudo chroot ${SYSROOT} + elif [ $TOOLCHAIN_HOST == "arm-linux-gnueabihf" ]; then + cat $SRC_SCRIPT/inside_chroot_armhf.sh | sudo chroot ${SYSROOT} + fi +} + +move_and_extract_sysroot(){ + if [ -f $HOME/sysroot.tar.gz ]; then + mkdir -p $STAGING_AREA + sudo tar -xf $HOME/sysroot.tar.gz --directory $STAGING_AREA + rm $HOME/sysroot.tar.gz + fi +} + +fix_relativelinks(){ + pushd ${STAGING_AREA} + wget $SYSROOT_RELATIVE_LINKS + chmod +x sysroot-relativelinks.py + sudo ./sysroot-relativelinks.py ${SYSROOT} + popd +} + +for arg in "${@:2}"; do + $arg +done + diff --git a/ci/arm/docker/Dockerfile b/ci/arm/docker/Dockerfile new file mode 100644 index 0000000000..8ce362bea3 --- /dev/null +++ b/ci/arm/docker/Dockerfile @@ -0,0 +1,69 @@ +FROM --platform=linux/amd64 ubuntu:20.04 AS start +SHELL ["/bin/bash", "-c"] +ARG ARCH +ARG USER=runner +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Europe/Bucharest +ENV CI_SCRIPT=ON +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +RUN apt-get update && \ + apt-get -y upgrade && \ + apt-get install -y apt-utils sudo git wget flex bison pkg-config make python3 pip vim python-is-python3 +RUN groupadd -g 1000 -r $USER && \ + useradd -u 1000 -g 1000 --create-home -r $USER + +#Change password +RUN echo "$USER:$USER" | chpasswd + +#Make sudo passwordless +RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-$USER && \ + usermod -aG sudo $USER && \ + usermod -aG plugdev $USER + +USER $USER +WORKDIR /home/${USER} + + + +FROM start AS sysroot_builder +ARG USER=runner +ARG ARCH +ENV DEBIAN_FRONTEND=noninteractive +#Copy all scopy/ci/arm folder inside the image +COPY * /home/${USER}/ +RUN ./create_sysroot.sh ${ARCH} \ + install_packages \ + move_and_extract_sysroot \ + fix_relativelinks +RUN ./build_qt.sh ${ARCH} \ + install_packages \ + download_qt \ + download_crosscompiler \ + build +RUN ./arm_build_process.sh ${ARCH} \ + install_packages \ + download_cmake \ + download_crosscompiler \ + clone \ + build_deps + + + +FROM start +ARG USER=runner +ARG ARCH +ENV DEBIAN_FRONTEND=noninteractive +ARG STAGING=/home/${USER}/scopy/ci/arm/staging +COPY --from=sysroot_builder /home/${USER}/arm_build_process.sh /home/${USER} +COPY --from=sysroot_builder /home/${USER}/arm_build_config.sh /home/${USER} +COPY --from=sysroot_builder /home/${USER}/build-status /home/${USER}/build-status +RUN mkdir -p /home/${USER}/sysroot +COPY --from=sysroot_builder ${STAGING}/sysroot/usr /home/${USER}/sysroot/usr +COPY --from=sysroot_builder ${STAGING}/sysroot/share /home/${USER}/sysroot/share +COPY --from=sysroot_builder ${STAGING}/sysroot/include /home/${USER}/sysroot/include +WORKDIR /home/${USER}/sysroot +RUN ln -s usr/bin bin && ln -s usr/lib lib && ln -s usr/sbin sbin +WORKDIR /home/${USER} +RUN /home/${USER}/arm_build_process.sh ${ARCH} install_packages download_cmake download_crosscompiler && \ + mv ${STAGING} /home/${USER} && \ + rm -rf /home/${USER}/arm_build_process.sh /home/${USER}/arm_build_config.sh /home/${USER}/scopy diff --git a/ci/arm/inside_chroot_arm64.sh b/ci/arm/inside_chroot_arm64.sh new file mode 100644 index 0000000000..1121607c27 --- /dev/null +++ b/ci/arm/inside_chroot_arm64.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +export DEBIAN_FRONTEND=noninteractive +ln -snf /usr/share/zoneinfo/Europe/Bucharest /etc/localtime && echo "Europe/Bucharest" > /etc/timezone +echo "LC_ALL=en_US.UTF-8" | tee -a /etc/environment +echo "LANG=en_US.UTF-8" | tee -a /etc/locale.conf +locale-gen en_US.UTF-8 + +apt -y remove *qt* +apt -y autoremove + +rm -rfv $(find / | grep libiio) +rm -rfv $(find / | grep m2k) +rm -rfv $(find / | grep libad9361) +rm -rfv $(find / | grep libad9166) + +apt update && apt -y upgrade +apt -y dist-upgrade +dpkg --configure -a + +apt -y install '^libxcb.*-dev' autoconf automake bison build-essential cmake dh-python figlet flex freeglut3-dev g++ gawk gcc \ + gdb-multiarch gdbserver git libavahi-client* libavahi-common* libboost1.81-all-dev libdrm-dev libgbm-dev libglib2.0-dev libgl1-mesa-dev libgles2-mesa-dev libglu1-mesa-dev \ + libgmp-dev libinput-dev libopenal-dev libsndfile1-dev libspeechd-dev libts-dev libudev-dev libunwind-dev libxcb-icccm4 libxcb-xinerama0 \ + libx11-xcb-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev libxrender-dev libxml2-dev libxml2-utils mesa-common-dev mesa-utils* \ + perl pkg-config unzip wget + +wget https://raw.githubusercontent.com/abhiTronix/raspberry-pi-cross-compilers/master/utils/SSymlinker +sed -i 's/sudo//g' SSymlinker +chmod +x SSymlinker +./SSymlinker -s /usr/include/aarch64-linux-gnu/asm -d /usr/include +./SSymlinker -s /usr/include/aarch64-linux-gnu/gnu -d /usr/include +./SSymlinker -s /usr/include/aarch64-linux-gnu/bits -d /usr/include +./SSymlinker -s /usr/include/aarch64-linux-gnu/sys -d /usr/include +./SSymlinker -s /usr/lib/aarch64-linux-gnu/crtn.o -d /usr/lib/crtn.o +./SSymlinker -s /usr/lib/aarch64-linux-gnu/crt1.o -d /usr/lib/crt1.o +./SSymlinker -s /usr/lib/aarch64-linux-gnu/crti.o -d /usr/lib/crti.o diff --git a/ci/arm/inside_chroot_armhf.sh b/ci/arm/inside_chroot_armhf.sh new file mode 100644 index 0000000000..95cb3d9924 --- /dev/null +++ b/ci/arm/inside_chroot_armhf.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +export DEBIAN_FRONTEND=noninteractive +ln -snf /usr/share/zoneinfo/Europe/Bucharest /etc/localtime && echo "Europe/Bucharest" > /etc/timezone +echo "LC_ALL=en_US.UTF-8" | tee -a /etc/environment +echo "LANG=en_US.UTF-8" | tee -a /etc/locale.conf +locale-gen en_US.UTF-8 + +sed -i 's/#deb-src/deb-src/' /etc/apt/sources.list + +apt -y purge openjdk* tex-common +apt -y remove gnuradio gnuradio-* libgnuradio-* libvolk* +apt -y remove *qt* +apt -y autoremove +apt update && apt -y upgrade +apt -y dist-upgrade +dpkg --configure -a + +rm -rfv $(find / | grep libiio) +rm -rfv $(find / | grep m2k) +rm -rfv $(find / | grep libad9361) +rm -rfv $(find / | grep libad9166) +rm -rfv $(find / | grep volk) +rm -rfv $(find / | grep gnuradio) + +apt -y build-dep qtbase5-dev +apt -y install '^libxcb.*-dev' autoconf automake bison build-essential cmake figlet flex freeglut3-dev g++ gawk gcc \ + gdb-multiarch gdbserver git libavahi-client* libavahi-common* libboost1.74-all-dev libdrm-dev libgbm-dev libglib2.0-dev libgl1-mesa-dev libgles2-mesa-dev \ + libglu1-mesa-dev libgmp-dev libinput-dev libopenal-dev libsndfile1-dev libspeechd-dev libts-dev libudev-dev \ + libunwind-dev libx11-xcb-dev libxi-dev libxkbcommon-dev libxkbcommon-x11-dev libxrender-dev libxml2-dev libxml2-utils \ + mesa-common-dev mesa-utils* perl pkg-config dh-python unzip wget + +wget https://raw.githubusercontent.com/abhiTronix/raspberry-pi-cross-compilers/master/utils/SSymlinker +sed -i 's/sudo//g' SSymlinker +chmod +x SSymlinker +./SSymlinker -s /usr/include/arm-linux-gnueabihf/asm -d /usr/include +./SSymlinker -s /usr/include/arm-linux-gnueabihf/gnu -d /usr/include +./SSymlinker -s /usr/include/arm-linux-gnueabihf/bits -d /usr/include +./SSymlinker -s /usr/include/arm-linux-gnueabihf/sys -d /usr/include +./SSymlinker -s /usr/lib/arm-linux-gnueabihf/crtn.o -d /usr/lib/crtn.o +./SSymlinker -s /usr/lib/arm-linux-gnueabihf/crt1.o -d /usr/lib/crt1.o +./SSymlinker -s /usr/lib/arm-linux-gnueabihf/crti.o -d /usr/lib/crti.o \ No newline at end of file diff --git a/ci/arm/local_build_scopy_for_kuiper.sh b/ci/arm/local_build_scopy_for_kuiper.sh new file mode 100755 index 0000000000..e309edca67 --- /dev/null +++ b/ci/arm/local_build_scopy_for_kuiper.sh @@ -0,0 +1,255 @@ +#!/bin/bash +set -ex +export PS4='+(${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }' + +JOBS=-j1 +SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || echo "No source directory found" +SRC_SCRIPT=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +STAGING_AREA=$SRC_SCRIPT/staging +STAGING_AREA_DEPS=$STAGING_AREA/dependencies + + +USE_STAGING=OFF + +LIBIIO_VERSION=v0.26 +LIBM2K_BRANCH=main +SPDLOG_BRANCH=v1.x +VOLK_BRANCH=main +GNURADIO_BRANCH=scopy2-maint-3.10 +GRSCOPY_BRANCH=3.10 +GRM2K_BRANCH=master +LIBSIGROKDECODE_BRANCH=master +QWT_BRANCH=qwt-multiaxes-updated +LIBTINYIIOD_BRANCH=master +IIOEMU_BRANCH=main + +if [ ! -z "$USE_STAGING" ] && [ "$USE_STAGING" == "ON" ] + then + echo -- USING STAGING + mkdir -p $STAGING_AREA_DEPS + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$STAGING_AREA_DEPS/lib + CMAKE_OPTS=(\ + -DCMAKE_LIBRARY_PATH=$STAGING_AREA_DEPS \ + -DCMAKE_INSTALL_PREFIX=$STAGING_AREA_DEPS \ + -DCMAKE_PREFIX_PATH=$STAGING_AREA_DEPS \ + -DCMAKE_EXE_LINKER_FLAGS="-L$STAGING_AREA_DEPS -L$STAGING_AREA_DEPS/lib" \ + -DCMAKE_SHARED_LINKER_FLAGS="-L$STAGING_AREA_DEPS -L$STAGING_AREA_DEPS/lib" \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + ) + echo -- STAGING_DIR $STAGING_AREA_DEPS + else + echo -- NO STAGING + mkdir -p $STAGING_AREA + CMAKE_OPTS=(\ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + ) +fi + +CMAKE_BIN=/bin/cmake +CMAKE="$CMAKE_BIN ${CMAKE_OPTS[*]}" +echo -- USING CMAKE COMMAND: +echo $CMAKE + +build_with_cmake() { + echo $PWD + BUILD_FOLDER=$PWD/build + rm -rf $BUILD_FOLDER + mkdir -p $BUILD_FOLDER + cd $BUILD_FOLDER + $CMAKE "$@" + make $JOBS + sudo make $JOBS install +} + +clone() { + echo "####### CLONE #######" + pushd $STAGING_AREA + [ -d 'libiio' ] || git clone --recursive https://github.com/analogdevicesinc/libiio.git -b $LIBIIO_VERSION libiio + [ -d 'libm2k' ] || git clone --recursive https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH libm2k + [ -d 'spdlog' ] || git clone --recursive https://github.com/gabime/spdlog.git -b $SPDLOG_BRANCH spdlog + [ -d 'volk' ] || git clone --recursive https://github.com/gnuradio/volk.git -b $VOLK_BRANCH volk + [ -d 'gnuradio' ] || git clone --recursive https://github.com/analogdevicesinc/gnuradio.git -b $GNURADIO_BRANCH gnuradio + [ -d 'gr-scopy' ] || git clone --recursive https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH gr-scopy + [ -d 'gr-m2k' ] || git clone --recursive https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH gr-m2k + [ -d 'qwt' ] || git clone --recursive https://github.com/cseci/qwt.git -b $QWT_BRANCH qwt + [ -d 'libsigrokdecode' ] || git clone --recursive https://github.com/sigrokproject/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH libsigrokdecode + [ -d 'libtinyiiod' ] || git clone --recursive https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH libtinyiiod + [ -d 'iio-emu' ] || git clone --recursive https://github.com/analogdevicesinc/iio-emu -b $IIOEMU_BRANCH iio-emu + popd +} + +install_apt() { + sudo apt-get update + sudo apt-get -y upgrade + sudo apt-get -y install build-essential cmake vim bison flex swig swig4.0 python3 mlocate libfftw3-dev libgsl-dev \ + libusb-1.0-* libavahi-client* libavahi-common* libxml2* libsndfile-dev libfuse2 libboost1.74-* libglib2.0-dev \ + qtbase5-dev* qt5-qmake* qttools5-dev* qtdeclarative5-dev libqt5qml* libqt5svg5* libgmp3-dev libgmp-dev libthrift-dev libunwind-dev + pip install mako --break-system-packages +} + +build_libiio() { + echo "####### BUILD LIBIIO #######" + pushd $STAGING_AREA/libiio + build_with_cmake \ + -DWITH_TESTS:BOOL=OFF \ + -DWITH_DOC:BOOL=OFF \ + -DHAVE_DNS_SD:BOOL=ON\ + -DWITH_MATLAB_BINDINGS:BOOL=OFF \ + -DCSHARP_BINDINGS:BOOL=OFF \ + -DPYTHON_BINDINGS:BOOL=OFF \ + -DWITH_SERIAL_BACKEND:BOOL=ON \ + -DENABLE_IPV6:BOOL=OFF \ + -DINSTALL_UDEV_RULE:BOOL=OFF\ + ../ + popd +} + +build_libm2k() { + echo "####### BUILD LIBM2K #######" + pushd $STAGING_AREA/libm2k + build_with_cmake -DENABLE_PYTHON=OFF -DENABLE_TOOLS=ON ../ + popd +} + +build_spdlog() { + echo "####### BUILD SPDLOG #######" + pushd $STAGING_AREA/spdlog + build_with_cmake -DSPDLOG_BUILD_SHARED=ON ../ + popd +} + +build_volk() { + echo "####### BUILD VOLK #######" + pushd $STAGING_AREA/volk + build_with_cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DPYTHON_EXECUTABLE=/usr/bin/python3 ../ + popd +} + +build_gnuradio() { + echo "####### BUILD GNURADIO #######" + pushd $STAGING_AREA/gnuradio + build_with_cmake \ + -DPYTHON_EXECUTABLE=/usr/bin/python3 \ + -DENABLE_DEFAULT=OFF \ + -DENABLE_GNURADIO_RUNTIME=ON \ + -DENABLE_GR_ANALOG=ON \ + -DENABLE_GR_BLOCKS=ON \ + -DENABLE_GR_FFT=ON \ + -DENABLE_GR_FILTER=ON \ + -DENABLE_GR_IIO=ON \ + -DENABLE_POSTINSTALL=OFF \ + ../ + popd +} + +build_gr_scopy() { + echo "####### BUILD GR_SCOPY #######" + pushd $STAGING_AREA/gr-scopy + build_with_cmake -DWITH_PYTHON=OFF ../ + popd +} + +build_gr_m2k() { + echo "####### BUILD GR_M2K #######" + pushd $STAGING_AREA/gr-m2k + build_with_cmake -DWITH_PYTHON=OFF -DDIGITAL=OFF ../ + popd +} + +build_qwt() { + echo "####### BUILD QWT #######" + pushd $STAGING_AREA/qwt + git clean -xdf + sed -i 's|/usr/local/qwt-$$QWT_VERSION-ma|/usr/local|g' qwtconfig.pri + if [ ! -z "$USE_STAGING" ] && [ "$USE_STAGING" == "ON" ] + then + qmake INCLUDEPATH=$STAGING_AREA_DEPS/include LIBS=-L$STAGING_AREA_DEPS/lib qwt.pro + make $JOBS + make INSTALL_ROOT=$STAGING_AREA_DEPS install + cp -r $STAGING_AREA_DEPS/usr/local/* $STAGING_AREA_DEPS/ + else + qmake qwt.pro + make $JOBS + sudo make install + fi + + popd +} + +build_libsigrokdecode() { + echo "####### BUILD LIBSIGROKDECODE #######" + pushd $STAGING_AREA/libsigrokdecode + git clean -xdf + ./autogen.sh + + if [ ! -z "$USE_STAGING" ] && [ "$USE_STAGING" == "ON" ] + then + ./configure --prefix $STAGING_AREA_DEPS + else + ./configure + fi + + make $JOBS + sudo make install + popd +} + +build_libtinyiiod() { + echo "####### BUILD LIBTINYIIOD #######" + pushd $STAGING_AREA/libtinyiiod + build_with_cmake -DBUILD_EXAMPLES=OFF ../ + popd +} + +build_iio-emu() { + echo "####### BUILD IIO-EMU #######" + pushd $STAGING_AREA/iio-emu + build_with_cmake ../ + popd +} + +build_scopy() { + echo "####### BUILD SCOPY #######" + pushd $SRC_DIR + build_with_cmake ../ + popd +} + +build_scopy_appimage() +{ + pushd $SRC_DIR + export APPIMAGE=1 + build_with_cmake ../ + popd + + $SRC_DIR/ci/arm/arm_build_process.sh create_appdir create_appimage +} + +buid_deps() { + build_libiio + build_libm2k + build_spdlog + build_volk + build_gnuradio + build_gr_scopy + build_gr_m2k + build_qwt + build_libsigrokdecode + build_libtinyiiod +} + + +# install_apt +# clone +# buid_deps +# build_scopy + +for arg in $@; do + $arg +done + diff --git a/ci/arm/qt_patch_arm64.patch b/ci/arm/qt_patch_arm64.patch new file mode 100644 index 0000000000..b67970dcb6 --- /dev/null +++ b/ci/arm/qt_patch_arm64.patch @@ -0,0 +1,41 @@ +diff -Naur qt-everywhere-src/qtbase/mkspecs/devices/linux-rasp-pi4-aarch64/qmake.conf qt-everywhere-src-5.15.10/qtbase/mkspecs/devices/linux-rasp-pi4-aarch64/qmake.conf +--- qt-everywhere-src/qtbase/mkspecs/devices/linux-rasp-pi4-aarch64/qmake.conf 2024-10-16 12:06:03.801661834 +0300 ++++ qt-everywhere-src-5.15.10/qtbase/mkspecs/devices/linux-rasp-pi4-aarch64/qmake.conf 1970-01-01 02:00:00.000000000 +0200 +@@ -1,25 +0,0 @@ +-# +-# Generic qmake configuration for building with g++ on Raspberry Pi 4 (64-bit). +-# +-# Tested with Raspberry Pi OS 64-bit and aarch64 gcc compiler from Debian package repository +-# +-# A minimal configure line could look something like this: +-# ./configure -release -opengl es2 -device linux-rasp-pi4-aarch64 -device-option CROSS_COMPILE=aarch64-linux-gnu- -sysroot ~/rpi-sysroot -prefix /usr/local/qt6 -extprefix $HOME/qt-raspi +- +-DISTRO_OPTS += deb-multi-arch +- +-include(../common/linux_device_pre.conf) +- +-QMAKE_LIBS_EGL += -lEGL +-QMAKE_LIBS_OPENGL_ES2 += -lGLESv2 -lEGL +- +-QMAKE_CFLAGS += -march=armv8-a +-QMAKE_CXXFLAGS += $$QMAKE_CFLAGS +- +-EGLFS_DEVICE_INTEGRATION = eglfs_kms +- +-LINKER_FLAGS += -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed +- +-include(../common/linux_device_post.conf) +- +-load(qt_config) +diff -Naur qt-everywhere-src/qtbase/mkspecs/devices/linux-rasp-pi4-aarch64/qplatformdefs.h qt-everywhere-src-5.15.10/qtbase/mkspecs/devices/linux-rasp-pi4-aarch64/qplatformdefs.h +--- qt-everywhere-src/qtbase/mkspecs/devices/linux-rasp-pi4-aarch64/qplatformdefs.h 2024-10-16 12:06:21.493092913 +0300 ++++ qt-everywhere-src-5.15.10/qtbase/mkspecs/devices/linux-rasp-pi4-aarch64/qplatformdefs.h 1970-01-01 02:00:00.000000000 +0200 +@@ -1,8 +0,0 @@ +-/**************************************************************************** +-** +-** Copyright (C) 2022 The Qt Company Ltd. +-** SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +-** +-****************************************************************************/ +- +-#include "../../linux-g++/qplatformdefs.h" diff --git a/ci/arm/qt_patch_armhf.patch b/ci/arm/qt_patch_armhf.patch new file mode 100644 index 0000000000..7a742efde9 --- /dev/null +++ b/ci/arm/qt_patch_armhf.patch @@ -0,0 +1,54 @@ +--- new/qt-everywhere-src-5.15.2/qtbase/src/3rdparty/angle/include/EGL/eglplatform.h ++++ qt-everywhere-src-5.15.2/qtbase/src/3rdparty/angle/include/EGL/eglplatform.h +@@ -141,6 +141,7 @@ + */ + typedef khronos_int32_t EGLint; + ++typedef uint32_t DISPMANX_ELEMENT_HANDLE_T; + + /* C++ / C typecast macros for special EGL handle values */ + #if defined(__cplusplus) +--- new/qt-everywhere-src-5.15.2/qtbase/src/gui/configure.json ++++ qt-everywhere-src-5.15.2/qtbase/src/gui/configure.json +@@ -862,7 +862,10 @@ + "type": "compile", + "test": { + "include": [ "EGL/egl.h", "bcm_host.h" ], +- "main": "vc_dispmanx_display_open(0);" ++ "main": [ ++ "vc_dispmanx_display_open(0);", ++ "EGL_DISPMANX_WINDOW_T *eglWindow = new EGL_DISPMANX_WINDOW_T;" ++ ] + }, + "use": "egl bcm_host" + }, +--- new/qt-everywhere-src-5.15.2/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_brcm/qeglfsbrcmintegration.cpp ++++ qt-everywhere-src-5.15.2/qtbase/src/plugins/platforms/eglfs/deviceintegration/eglfs_brcm/qeglfsbrcmintegration.cpp +@@ -44,6 +44,12 @@ + + static DISPMANX_DISPLAY_HANDLE_T dispman_display = 0; + ++typedef struct { ++ DISPMANX_ELEMENT_HANDLE_T element; ++ int width; /* This is necessary because dispmanx elements are not queriable. */ ++ int height; ++} EGL_DISPMANX_WINDOW_T; ++ + static EGLNativeWindowType createDispmanxLayer(const QPoint &pos, const QSize &size, int z, DISPMANX_FLAGS_ALPHA_T flags) + { + VC_RECT_T dst_rect; +@@ -76,12 +82,12 @@ + eglWindow->width = size.width(); + eglWindow->height = size.height(); + +- return eglWindow; ++ return (EGLNativeWindowType)eglWindow; + } + + static void destroyDispmanxLayer(EGLNativeWindowType window) + { +- EGL_DISPMANX_WINDOW_T *eglWindow = static_cast(window); ++ EGL_DISPMANX_WINDOW_T *eglWindow = (EGL_DISPMANX_WINDOW_T*)(window); + DISPMANX_UPDATE_HANDLE_T dispman_update = vc_dispmanx_update_start(0); + vc_dispmanx_element_remove(dispman_update, eglWindow->element); + vc_dispmanx_update_submit_sync(dispman_update); diff --git a/ci/arm/runtime-aarch64 b/ci/arm/runtime-aarch64 new file mode 100644 index 0000000000..c60ccae0ae Binary files /dev/null and b/ci/arm/runtime-aarch64 differ diff --git a/CI/armhf/runtime-armhf b/ci/arm/runtime-armhf similarity index 100% rename from CI/armhf/runtime-armhf rename to ci/arm/runtime-armhf diff --git a/ci/arm/scopy.desktop b/ci/arm/scopy.desktop new file mode 100644 index 0000000000..f2f77d50fd --- /dev/null +++ b/ci/arm/scopy.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=2.0 +Icon=scopy +Exec=scopy +Terminal=false +Type=Application +Categories=Science +Name=Scopy +GenericName=Oscilloscope +Comment=A software oscilloscope diff --git a/ci/flatpak/.gitignore b/ci/flatpak/.gitignore new file mode 100644 index 0000000000..5045a85ee7 --- /dev/null +++ b/ci/flatpak/.gitignore @@ -0,0 +1,6 @@ +Scopy.flatpak +.flatpak-builder +build +repo +org.adi.Scopy.json +tmp.json diff --git a/ci/flatpak/Makefile b/ci/flatpak/Makefile new file mode 100644 index 0000000000..0e1fd473c0 --- /dev/null +++ b/ci/flatpak/Makefile @@ -0,0 +1,37 @@ +TARGET := Scopy.flatpak +SRC := org.adi.Scopy.json + +ifndef ARCH + ARCH := x86_64 +endif +ifndef EN_PREPROCESS + EN_PREPROCESS := true +endif + +.PHONY: all clean + +all: $(TARGET) + +$(TARGET): repo + flatpak build-bundle $< $@ org.adi.Scopy + +repo: build + flatpak build-export $@ $< + +build: preprocess + flatpak-builder --verbose --keep-build-dirs --ccache --user --disable-rofiles-fuse --arch=$(ARCH) $@ $(SRC) + +preprocess: clean + if [ $(EN_PREPROCESS) = true ]; then \ + if [ $(ARCH) = x86_64 ]; then \ + echo "-- Preprocessing org.adi.Scopy.json for ARCH = X86_64"; \ + gcc -E -P -D__X86__ org.adi.Scopy.json.c -o org.adi.Scopy.json; \ + elif [ $(ARCH) = arm ]; then \ + echo "-- Preprocessing org.adi.Scopy.json for ARCH = arm"; \ + gcc -E -P -D__ARM__ org.adi.Scopy.json.c -o org.adi.Scopy.json; \ + fi \ + fi + +clean: + rm -rf $(TARGET) build repo + diff --git a/ci/flatpak/README.md b/ci/flatpak/README.md new file mode 100644 index 0000000000..c8273c1a1e --- /dev/null +++ b/ci/flatpak/README.md @@ -0,0 +1,21 @@ +# Scopy Flatpak Recipe +Flatpak build recipe for Scopy + +Scopy is a software oscilloscope and signal analysis toolset. [The official repository](https://github.com/analogdevicesinc/scopy) provides releases for Windows, Linux, macOS and Android. +This recipe is used to build the Linux Flatpak installer. + +### Install Scopy using the Flatpak release for Linux using these instructions: + - [Install Flatpak using these instructions](http://flatpak.org/getting.html) + - [Download Scopy.flatpak from the Releases Tab](https://github.com/analogdevicesinc/scopy/releases) + - Install the application using ``` flatpak install Scopy.flatpak ``` + - Run the application using ``` flatpak run org.adi.Scopy ``` + +## Building the Docker image +To build the Docker image just execute the ***create_docker.sh*** script. + +The provided Dockerfile will install the KDE Runtime over the Ubuntu 20.04 base image. This Docker image is built in such a way that it contains the dependencies needed for the packaging of the Scopy application. It leverages the Flatpak Builder caching system, where after each step in the build process the result is saved as cache in order to be reused for later builds. + +To build the Flatpak package for Scopy inside this Docker image, it needs to be run using ***--privileged***, otherwise there is a lack of access to necessary utilities. + +## Generating the Scopy.flatpak artifact +Running ```make``` inside the ***scopy/ci/flatpak*** folder will build the Scopy.flatpak artifact. \ No newline at end of file diff --git a/ci/flatpak/build_flatpak_deps.sh b/ci/flatpak/build_flatpak_deps.sh new file mode 100755 index 0000000000..4148d05337 --- /dev/null +++ b/ci/flatpak/build_flatpak_deps.sh @@ -0,0 +1,13 @@ +#!/bin/bash -xe + +# to fix the error "fatal: transport 'file' not allowed" +git config --global protocol.file.allow always + +mkdir -p /home/runner/flatpak_tools +pushd /home/runner/flatpak_tools +cp /home/runner/config/* /home/runner/flatpak_tools +git clone https://github.com/flathub/shared-modules.git +make preprocess + +flatpak-builder --verbose --keep-build-dirs --ccache --user --disable-rofiles-fuse --force-clean --arch=x86_64 --stop-at=scopy build org.adi.Scopy.json +popd diff --git a/ci/flatpak/create_docker_image.sh b/ci/flatpak/create_docker_image.sh new file mode 100755 index 0000000000..649286da81 --- /dev/null +++ b/ci/flatpak/create_docker_image.sh @@ -0,0 +1,25 @@ +#!/bin/bash -xe + +SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ +SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) + +build_docker(){ + pushd $SRC_DIR/ci/flatpak/docker + docker build -t cristianbindea/scopy2-flatpak:testing . + CONTAINER_NAME=builder-flatpak + docker run \ + --name $CONTAINER_NAME \ + --privileged \ + --mount type=bind,source="$SRC_DIR/ci/flatpak/build_flatpak_deps.sh",target=/home/runner/config/build_flatpak_deps.sh,readonly \ + --mount type=bind,source="$SRC_DIR/ci/flatpak/defined_variables.h",target=/home/runner/config/defined_variables.h,readonly \ + --mount type=bind,source="$SRC_DIR/ci/flatpak/org.adi.Scopy.json.c",target=/home/runner/config/org.adi.Scopy.json.c,readonly \ + --mount type=bind,source="$SRC_DIR/ci/flatpak/Makefile",target=/home/runner/config/Makefile,readonly \ + cristianbindea/scopy2-flatpak:testing /bin/bash -c /home/runner/config/build_flatpak_deps.sh + + docker commit $CONTAINER_NAME cristianbindea/scopy2-flatpak:testing + echo "Docker container $CONTAINER_NAME commited as cristianbindea/scopy2-flatpak:testing image" + docker container rm $CONTAINER_NAME + popd +} + +build_docker \ No newline at end of file diff --git a/ci/flatpak/defined_variables.h b/ci/flatpak/defined_variables.h new file mode 100644 index 0000000000..ccbaab4260 --- /dev/null +++ b/ci/flatpak/defined_variables.h @@ -0,0 +1,26 @@ +#define _EXPAND(A) #A +#define EXPAND(A) _EXPAND(A) + +#define DENABLE_INTERNAL_VOLK -DENABLE_INTERNAL_VOLK:BOOL=_ENABLE_VOLK + +#ifdef __ARM__ + #define RUNTIME_VERSION 5.14 + + #define _ASFLAGS_VALUE -mcpu=cortex-a5 -mfpu=neon-vfpv4 -mfloat-abi=hard + #define _CFLAGS_VALUE -march=armv7-a -mfpu=neon -mfloat-abi=hard + + #define ASFLAGS ASFLAGS= _ASFLAGS_VALUE + #define CFLAGS CFLAGS= _CFLAGS_VALUE + + #define CMAKE_ASM_FLAGS -DCMAKE_ASM_FLAGS= _ASFLAGS_VALUE + #define CMAKE_C_FLAGS -DCMAKE_C_FLAGS= -fno-asynchronous-unwind-tables _CFLAGS_VALUE + + #define _ENABLE_VOLK OFF + +#elif __X86__ + #define RUNTIME_VERSION 5.15-23.08 + #define CFLAGS --disable-assembly + #define CMAKE_C_FLAGS -DCMAKE_C_FLAGS= -fno-asynchronous-unwind-tables -fPIC + #define CMAKE_ASM_FLAGS -DCMAKE_ASM_FLAGS= + #define _ENABLE_VOLK OFF +#endif \ No newline at end of file diff --git a/ci/flatpak/docker/Dockerfile b/ci/flatpak/docker/Dockerfile new file mode 100644 index 0000000000..fd4e910567 --- /dev/null +++ b/ci/flatpak/docker/Dockerfile @@ -0,0 +1,38 @@ +FROM ubuntu:20.04 +CMD ["/bin/bash"] +ARG USER=runner +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=Europe/Bucharest +ENV CI_SCRIPT=ON +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# Install starting packages +RUN apt update && apt -y upgrade && \ + apt install -y apt-utils software-properties-common sudo + +# Create user +RUN groupadd -g 1000 -r $USER && \ + useradd -u 1000 -g 1000 --create-home -r $USER + +# Change password +RUN echo "$USER:$USER" | chpasswd + +# Make sudo passwordless +RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-$USER && \ + usermod -aG sudo $USER && \ + usermod -aG plugdev $USER + +USER $USER + +# Install base dependencies +# Install flatpak +# Clean apt +RUN sudo apt install -y git wget vim build-essential subversion mm-common && \ + sudo apt install flatpak jq flatpak-builder -y && \ + sudo apt clean -y && sudo apt autoclean -y + +# Install remote +RUN sudo flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo +RUN sudo flatpak install flathub org.kde.Platform//5.15-23.08 -y && sudo flatpak install flathub org.kde.Sdk//5.15-23.08 -y + +WORKDIR /home/$USER diff --git a/ci/flatpak/flatpak_build_process.sh b/ci/flatpak/flatpak_build_process.sh new file mode 100755 index 0000000000..161d805a90 --- /dev/null +++ b/ci/flatpak/flatpak_build_process.sh @@ -0,0 +1,60 @@ +#!/bin/bash -xe + +SCOPY_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ +SCOPY_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) + +SCOPY_JSON=$SCOPY_DIR/ci/flatpak/org.adi.Scopy.json + +if [ "$CI_SCRIPT" == "ON" ]; + then + SOURCE_DIR=$GITHUB_WORKSPACE + # this is needed in order to be used by flatpak caching system + # the docker image already contains the built dependencies so we just have to move them + cp -r /home/runner/flatpak_tools/.flatpak-builder $SOURCE_DIR/ci/flatpak + cp -r /home/runner/flatpak_tools/build $SOURCE_DIR/ci/flatpak + else + SOURCE_DIR=$SCOPY_DIR +fi + +pushd $SCOPY_DIR/ci/flatpak + +git submodule update --init + +# Run the preprocess step to generate org.adi.Scopy.json +make preprocess + +# Disable the preprocess step; The Json file will now be modified and +# we don't want to re-generate it at the build step +export EN_PREPROCESS=false + +# check the number of elements in the json file in order to get the last element, which is Scopy +cnt=$( echo $(jq '.modules | length' $SCOPY_JSON) ) +cnt=$(($cnt-1)) + +# We are building in Github Actions and we use the current directory folder on a CLEAN Docker image +cat $SCOPY_JSON | jq --tab '.modules['$cnt'].sources[0].type = "dir"' > tmp.json +cp tmp.json $SCOPY_JSON +cat $SCOPY_JSON | jq --tab '.modules['$cnt'].sources[0].path = "'$SOURCE_DIR'"' > tmp.json +cp tmp.json $SCOPY_JSON +cat $SCOPY_JSON | jq --tab 'del(.modules['$cnt'].sources[0].url)' > tmp.json +cp tmp.json $SCOPY_JSON +cat $SCOPY_JSON | jq --tab 'del(.modules['$cnt'].sources[0].branch)' > tmp.json +cp tmp.json $SCOPY_JSON +rm tmp.json + +# Generate build status info for the about page +jq '.modules[] | select(type == "object" and .sources[]?.type == "git") | "\(.name): \(.sources[] | select(.type == "git") | .branch // .tag // .commit // "no branch, tag, or commit")"' ./org.adi.Scopy.json >> build-status +cp build-status $SOURCE_DIR/build-status + +# Insert env vars in the sandboxed flatpak build +# $SOURCE_DIR/ci/general/gen_ci_envs.sh > $SOURCE_DIR/ci/general/gh-actions.envs +# CI_ENVS=$(jq -R -n -c '[inputs|split("=")|{(.[0]):.[1]}] | add' $SOURCE_DIR/ci/general/gh-actions.envs) +# echo "CI_ENVS= $CI_ENVS" +# cat $SCOPY_JSON | jq --tab '."build-options".env += ('$CI_ENVS')' > tmp.json +# cp tmp.json $SCOPY_JSON + +make + +# Copy the Scopy.flatpak file in $SOURCE_DIR (which is the external location, mount when docker starts) +[ -z $CI_SCRIPT ] || cp Scopy.flatpak $SOURCE_DIR/ +popd diff --git a/ci/flatpak/org.adi.Scopy.json.c b/ci/flatpak/org.adi.Scopy.json.c new file mode 100644 index 0000000000..a355607e21 --- /dev/null +++ b/ci/flatpak/org.adi.Scopy.json.c @@ -0,0 +1,543 @@ +#include "defined_variables.h" + +{ + "app-id": "org.adi.Scopy", + "runtime": "org.kde.Platform", + "runtime-version": EXPAND(RUNTIME_VERSION), + "sdk": "org.kde.Sdk", + "command": "scopy", + "rename-desktop-file": "scopy.desktop", + "rename-icon": "scopy", + "finish-args": [ + "--socket=x11", + "--socket=wayland", + "--socket=pulseaudio", + "--share=network", + "--share=ipc", + "--filesystem=host:create", + "--filesystem=~/.config/dconf:create", + "--filesystem=xdg-config/ADI:create", + "--device=all", + "--system-talk-name=org.freedesktop.Avahi" + ], + + "build-options" : { + "cflags": "-O2 -g", + "cxxflags": "-O2 -g", + "env": { + "V": "1" + } + + }, + "cleanup": [ + "/include", + "/lib/python2.7", + "/lib/pkgconfig", + "/lib/cmake", + "/lib/*.la", + "/lib/*.a", + "/lib/gio", + "/lib/giomm-2.4", + "/lib/libzip", + "/lib/libgthread*", + "/lib/xml2Conf.sh" + ], + "modules": [ + { + "name":"git-config-update", + "buildsystem": "simple", + "build-commands": [ + "git config --global protocol.file.allow always" + ] + }, + { + "name": "sshpass", + "sources": [ + { + "type": "archive", + "url": "https://sourceforge.net/projects/sshpass/files/sshpass/1.08/sshpass-1.08.tar.gz", + "sha1": "efe4573ba2fe972b08cf1cdd95739b7f456e55c1" + } + ] + }, + { + "name": "libusb", + "config-opts": [ "--disable-udev", "--prefix=/app" ], + "sources": [ + { + "type": "archive", + "url": "https://downloads.sourceforge.net/project/libusb/libusb-1.0/libusb-1.0.24/libusb-1.0.24.tar.bz2", + "sha256": "7efd2685f7b327326dcfb85cee426d9b871fd70e22caa15bb68d595ce2a2b12a" + } + ] + }, + { + "name": "boost", + "cleanup": [ "/bin", "/share" ], + "sources": [ + { + "type": "archive", + "url": "https://boostorg.jfrog.io/artifactory/main/release/1.73.0/source/boost_1_73_0.tar.gz", + "sha256": "9995e192e68528793755692917f9eb6422f3052a53c5e13ba278a228af6c7acf" + }, + { + "type": "script", + "commands": [ + "#!/bin/sh", + "exec ./bootstrap.sh --with-libraries=date_time,filesystem,program_options,regex,system,test,thread,atomic,chrono --prefix=/app" + ], + "dest-filename": "configure" + }, + { + "type": "script", + "commands": [ + "all:", + "\t./b2", + "install:", + "\t./b2 install" + ], + "dest-filename": "Makefile" + } + ] + }, + "shared-modules/dbus-glib/dbus-glib.json", + { + "name": "dbus-glib-submodule" + }, + "shared-modules/intltool/intltool-0.51.json", + { + "name": "intltool-submodule" + }, +#ifndef __ARM__ + { + "name": "dbus-python", + "sources": [ + { + "type": "archive", + "url": "https://dbus.freedesktop.org/releases/dbus-python/dbus-python-1.2.16.tar.gz", + "sha256": "11238f1d86c995d8aed2e22f04a1e3779f0d70e587caffeab4857f3c662ed5a4" + } + ] + }, +#endif + { + "name": "avahi", + "config-opts": [ + "--disable-monodoc", + "--disable-gtk", + "--disable-gtk3", + "--disable-qt3", + "--disable-qt4", + "--disable-pygobject", + "--disable-gdbm", + "--disable-libdaemon", + "--disable-mono", + "--disable-pygtk", +#ifdef __ARM__ + "--disable-dbus", +#endif + "--enable-compat-libdns_sd", + "--with-distro=none" + ], + "sources": [ + { + "type": "archive", + "url": "https://github.com/lathiat/avahi/releases/download/v0.7/avahi-0.7.tar.gz", + "sha256": "57a99b5dfe7fdae794e3d1ee7a62973a368e91e414bd0dfa5d84434de5b14804" + } + ] + }, + { + "name": "libxml2", + "cleanup": [ "/bin", "/share" ], + "config-opts": [ "--prefix=/app"], + "sources": [ + { + "type": "git", + "url": "https://github.com/GNOME/libxml2", + "tag": "v2.9.14" + } + ] + }, + { + "name": "matio", + "cleanup": [ "/bin", "/share" ], + "config-opts": [ "" ], + "build-commands": [ "true"], + "sources": [ + { + "type": "archive", + "url": "https://github.com/tbeu/matio/releases/download/v1.5.12/matio-1.5.12.tar.gz", + "sha1": "cb05a4a9a3af992890237b3f7a87f01cd8819528" + } + ] + }, + { + "name": "libfftw3", + "config-opts": [ + "--enable-shared", + "--disable-static", + "--enable-threads", + "--enable-single" +#ifdef __X86__ + ,"--enable-float", + "--enable-sse2", + "--enable-avx", + "--enable-openmp" +#endif + ], + "sources": [ + { + "type": "archive", + "url": "http://swdownloads.analog.com/cse/scopydeps/fftw-3.3.8.tar.gz", + "sha256": "6113262f6e92c5bd474f2875fa1b01054c4ad5040f6b0da7c03c98821d9ae303" + } + ] + }, + { + "name": "python3-mako", + "buildsystem": "simple", + "build-commands": [ + "pip3 install --no-index --find-links=\"file://${PWD}\" --prefix=/app mako" + ], + "sources": [ + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/b9/2e/64db92e53b86efccfaea71321f597fa2e1b2bd3853d8ce658568f7a13094/MarkupSafe-1.1.1.tar.gz", + "sha256": "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b" + }, + { + "type": "file", + "url": "https://files.pythonhosted.org/packages/28/03/329b21f00243fc2d3815399413845dbbfb0745cff38a29d3597e97f8be58/Mako-1.1.1.tar.gz", + "sha256": "2984a6733e1d472796ceef37ad48c26f4a984bb18119bb2dbc37a44d8f6e75a4" + } + ], + "cleanup": [ "*" ] + }, + { + "name": "libgmp", + "config-opts": [ "--prefix=/app", "--enable-cxx", + EXPAND(CFLAGS) +#ifdef __ARM__ + ,EXPAND(ASFLAGS) +#endif + ], + "sources": [ + { + "type": "archive", + "url": "https://ftp.gnu.org/gnu/gmp/gmp-6.2.0.tar.bz2", + "sha256": "f51c99cb114deb21a60075ffb494c1a210eb9d7cb729ed042ddb7de9534451ea" + } + ] + }, + { + "name": "liborc", + "config-opts": [ "--prefix=/app" +#ifdef __ARM__ + ,EXPAND(CFLAGS) ,EXPAND(ASFLAGS) +#endif + ], + "sources": [ + { + "type": "archive", + "url": "https://gstreamer.freedesktop.org/data/src/orc/orc-0.4.28.tar.xz", + "sha256": "bfcd7c6563b05672386c4eedfc4c0d4a0a12b4b4775b74ec6deb88fc2bcd83ce" + } + ] + }, + { + "name": "libvolk", + "cleanup": [ "/bin", "/share" ], + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ + "-DCMAKE_INSTALL_PREFIX:PATH=/app", + EXPAND(CMAKE_C_FLAGS), EXPAND(CMAKE_ASM_FLAGS) + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/gnuradio/volk", + "branch": "v2.5.1" + } + ] + }, + { + "name": "spdlog", + "cleanup": [ "/bin", "/share" ], + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ + "-DCMAKE_INSTALL_PREFIX:PATH=/app", "-DSPDLOG_BUILD_SHARED=ON", + EXPAND(CMAKE_C_FLAGS), EXPAND(CMAKE_ASM_FLAGS) + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/gabime/spdlog", + "branch": "v1.10.0" + } + ] + }, + + { + + "name": "serialport", + "builddir": false, + "buildsystem": "autotools", + "config-opts": [ "--prefix=/app" ], + "sources": [ + { + "type": "git", + "url": "https://github.com/cseci/libserialport", + "branch" : "scopy-v2" + } + ] + + }, + + { + "name": "libiio", + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ + "-DCMAKE_PREFIX_PATH:PATH=/app", + "-DCMAKE_INSTALL_PREFIX:PATH=/app", + "-DCMAKE_INSTALL_LIBDIR:STRING=lib", + "-DINSTALL_UDEV_RULE:BOOL=OFF", + "-DWITH_TESTS:BOOL=OFF", + "-DWITH_DOC:BOOL=OFF", + "-DHAVE_DNS_SD:BOOL=ON", + "-DWITH_IIOD:BOOL=OFF", + "-DWITH_LOCAL_BACKEND:BOOL=OFF", + "-DENABLE_IPV6:BOOL=OFF", + "-DWITH_SERIAL_BACKEND:BOOL=ON" + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/analogdevicesinc/libiio", + "tag": "v0.26" + } + ] + }, + { + "name": "libad9361", + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ "-DCMAKE_INSTALL_PREFIX:PATH=/app" ], + "sources": [ + { + "type": "git", + "url": "https://github.com/analogdevicesinc/libad9361-iio", + "branch" : "main" + } + ] + }, + { + "name": "gnuradio", + "cleanup": [ "/bin", "/share" ], + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ + "-DCMAKE_INSTALL_PREFIX:PATH=/app", + "-DENABLE_DEFAULT=OFF", + "-DENABLE_GNURADIO_RUNTIME=ON", + "-DENABLE_GR_ANALOG=ON", + "-DENABLE_GR_BLOCKS=ON", + "-DENABLE_GR_FFT=ON", + "-DENABLE_GR_FILTER=ON", + "-DENABLE_GR_IIO=ON", + EXPAND(CMAKE_C_FLAGS) +#ifdef __ARM__ + , EXPAND(CMAKE_ASM_FLAGS) +#endif + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/analogdevicesinc/gnuradio", + "branch" : "scopy2-maint-3.10" + } + ] + }, + { + "name": "glog", + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ + "-DCMAKE_INSTALL_PREFIX:PATH=/app", + "-DWITH_GFLAGS=OFF" + ], + "sources": [ + { + "type": "git", + "commit": "17e7679fd9beb95277ccd0708056ba85363f892b" , + "url": "https://github.com/google/glog" + } + ] + }, + { + "name": "libm2k", + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ + "-DCMAKE_INSTALL_PREFIX:PATH=/app", + "-DENABLE_PYTHON=OFF", + "-DENABLE_CSHARP=OFF", + "-DENABLE_TOOLS=OFF", + "-DBUILD_EXAMPLES=OFF", + "-DINSTALL_UDEV_RULES=OFF" + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/analogdevicesinc/libm2k", + "branch": "main" + } + ] + }, + { + "name": "gr-m2k", + "cleanup": [ "/share" ], + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ "-DCMAKE_INSTALL_PREFIX:PATH=/app" ], + "sources": [ + { + "type": "git", + "url": "https://github.com/analogdevicesinc/gr-m2k", + "branch" : "main" + } + ] + }, + { + "name": "gr-scopy", + "cleanup": [ "/share" ], + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ "-DCMAKE_INSTALL_PREFIX:PATH=/app" ], + "sources": [ + { + "type": "git", + "url": "https://github.com/analogdevicesinc/gr-scopy", + "branch" : "3.10" + } + ] + }, + { + "name": "qwt", + "cleanup": [ "/features", "/plugins", "/share" ], + "sources": [ + { + "type": "git", + "url": "https://github.com/cseci/qwt", + "branch": "qwt-multiaxes-updated" + }, + { + "type": "script", + "commands": [ + "sed -i \"s/^\\s*QWT_INSTALL_PREFIX.*$/QWT_INSTALL_PREFIX=\\/app/g\" qwtconfig.pri", + "qmake" + ], + "dest-filename": "configure" + } + ] + }, + { + "name": "libzip", + "cleanup": [ "/bin", "/share" ], + "sources": [ + { + "type": "archive", + "url": "https://nih.at/libzip/libzip-1.1.3.tar.xz", + "sha256": "729a295a59a9fd6e5b9fe9fd291d36ae391a9d2be0b0824510a214cfaa05ceee" + } + ] + }, + { + "name": "sigrokdecode", + "builddir": false, + "buildsystem": "autotools", + "config-opts": [ "--prefix=/app" ], + "sources": [ + { + "type": "git", + "url": "https://github.com/sigrokproject/libsigrokdecode", + "commit": "e556e1168af7027df08622ecfe11309811249e81" + } + ] + }, + { + "name": "libtinyiiod", + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ + "-DCMAKE_INSTALL_PREFIX:PATH=/app", + "-DBUILD_SHARED_LIBS=OFF", + "-DBUILD_EXAMPLES=OFF" + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/analogdevicesinc/libtinyiiod", + "commit": "15e79e7c0064b0476ab4608d02d5efb988b93fc9" + } + ] + }, + { + "name": "KDDockWidgets", + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ + "-DCMAKE_INSTALL_PREFIX:PATH=/app" + ], + "sources": [ + { + "type": "git", + "url": "https://github.com/KDAB/KDDockWidgets.git", + "branch": "2.1" + } + ] + }, + { + "name": "iio-emu", + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ "-DCMAKE_INSTALL_PREFIX:PATH=/app" ], + "sources": [ + { + "type": "git", + "url": "https://github.com/analogdevicesinc/iio-emu", + "branch": "master" + } + ] + }, + { + "name": "qtadvanceddocking", + "builddir": true, + "config-opts": [ "-DCMAKE_INSTALL_PREFIX:PATH=/app" ], + "buildsystem": "cmake", + "sources": [ + { + "type": "git", + "url": "https://github.com/githubuser0xFFFF/Qt-Advanced-Docking-System", + "tag": "3.8.1" + } + ] + }, + { + "name": "scopy", + "builddir": true, + "buildsystem": "cmake", + "config-opts": [ "-DCMAKE_INSTALL_PREFIX:PATH=/app", "-DCMAKE_PREFIX_PATH=/app/lib/pkgconfig;/app/lib/cmake", "-DCMAKE_BUILD_TYPE=Release", "-DENABLE_TESTING=OFF"], + "sources": [ + { + "type": "git", + "url": "https://github.com/analogdevicesinc/scopy", + "branch": "dev" + } + ] + } + ] + } \ No newline at end of file diff --git a/ci/flatpak/shared-modules b/ci/flatpak/shared-modules new file mode 160000 index 0000000000..29a1c08976 --- /dev/null +++ b/ci/flatpak/shared-modules @@ -0,0 +1 @@ +Subproject commit 29a1c08976b5f7085448fadc57362f9ef8d07129 diff --git a/CI/appveyor/gen_ci_envs.sh b/ci/general/gen_ci_envs.sh similarity index 95% rename from CI/appveyor/gen_ci_envs.sh rename to ci/general/gen_ci_envs.sh index a8733c2616..62cc021f13 100755 --- a/CI/appveyor/gen_ci_envs.sh +++ b/ci/general/gen_ci_envs.sh @@ -1,6 +1,5 @@ #!/bin/bash -echo CI_SCRIPT=${CI_SCRIPT} echo GITHUB_WORKSPACE=$GITHUB_WORKSPACE echo REPO_URL=${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY} echo BUILD_HOST=${BUILD_HOST} diff --git a/CI/appveyor/before_install_lib.sh b/ci/macOS/before_install_lib.sh similarity index 97% rename from CI/appveyor/before_install_lib.sh rename to ci/macOS/before_install_lib.sh index 28d4a042f1..ad1b0826c5 100755 --- a/CI/appveyor/before_install_lib.sh +++ b/ci/macOS/before_install_lib.sh @@ -1,13 +1,11 @@ #!/bin/bash set -e - NUM_JOBS=4 -if [ "$APPVEYOR" == "true" ] ; then - STAGINGDIR=/usr/local -else - STAGINGDIR="${WORKDIR}/staging" -fi + +STAGING_AREA=$PWD/staging +STAGINGDIR=$STAGING_AREA/dependencies +WORKDIR=$STAGING_AREA export PYTHON3=python3 diff --git a/ci/macOS/build_azure_macos.sh b/ci/macOS/build_azure_macos.sh new file mode 100755 index 0000000000..ab63225923 --- /dev/null +++ b/ci/macOS/build_azure_macos.sh @@ -0,0 +1,52 @@ +#!/bin/bash +set -ex +REPO_SRC=$(git rev-parse --show-toplevel) +source $REPO_SRC/ci/macOS/macos_config.sh + +build_iio-emu(){ + echo "### Clone and Build IIO-Emulator" + pushd $REPO_SRC + if [ ! -d "$REPO_SRC/iio-emu" ]; then + git clone https://github.com/analogdevicesinc/iio-emu $REPO_SRC/iio-emu + fi + mkdir -p $REPO_SRC/iio-emu/build + cd $REPO_SRC/iio-emu/build + cmake \ + -DCMAKE_LIBRARY_PATH="$STAGING_AREA_DEPS" \ + -DCMAKE_INSTALL_PREFIX="$STAGING_AREA_DEPS" \ + -DCMAKE_PREFIX_PATH="${STAGING_AREA_DEPS};${STAGING_AREA_DEPS}/lib/cmake;" \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_STAGING_PREFIX="$STAGING_AREA_DEPS" \ + -DCMAKE_EXE_LINKER_FLAGS="-L${STAGING_AREA_DEPS}/lib" \ + ../ + CFLAGS=-I${STAGING_AREA_DEPS}/include LDFLAGS=-L${STAGING_AREA_DEPS}/lib make ${JOBS} + popd +} + +build_scopy(){ + echo "### Building Scopy" + ls -la $REPO_SRC + pushd $REPO_SRC + + rm -rf $REPO_SRC/build + mkdir -p $REPO_SRC/build + cd $REPO_SRC/build + cmake \ + -DCMAKE_LIBRARY_PATH="$STAGING_AREA_DEPS" \ + -DCMAKE_INSTALL_PREFIX="$STAGING_AREA_DEPS" \ + -DCMAKE_PREFIX_PATH="${STAGING_AREA_DEPS};${STAGING_AREA_DEPS}/lib/cmake;${STAGING_AREA_DEPS}/lib/pkgconfig;${STAGING_AREA_DEPS}/lib/cmake/gnuradio;${STAGING_AREA_DEPS}/lib/cmake/iio" \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_STAGING_PREFIX="$STAGING_AREA_DEPS" \ + -DCMAKE_EXE_LINKER_FLAGS="-L${STAGING_AREA_DEPS}/lib" \ + -DENABLE_TESTING=OFF \ + ../ + CFLAGS=-I${STAGING_AREA_DEPS}/include LDFLAGS=-L${STAGING_AREA_DEPS}/lib make ${JOBS} + otool -l ./Scopy.app/Contents/MacOS/Scopy + otool -L ./Scopy.app/Contents/MacOS/Scopy + popd +} + +build_iio-emu +build_scopy diff --git a/ci/macOS/install_macos_deps.sh b/ci/macOS/install_macos_deps.sh new file mode 100755 index 0000000000..55c374ef07 --- /dev/null +++ b/ci/macOS/install_macos_deps.sh @@ -0,0 +1,328 @@ +#!/bin/bash + +set -ex +REPO_SRC=$(git rev-parse --show-toplevel) +source $REPO_SRC/ci/macOS/macos_config.sh + +PACKAGES="${QT_FORMULAE} volk spdlog boost pkg-config cmake fftw bison gettext autoconf automake libzip glib libusb glog " +PACKAGES="$PACKAGES doxygen wget gnu-sed libmatio dylibbundler libxml2 ghr libsndfile" + +OS_VERSION=${1:-$(sw_vers -productVersion)} +echo "MacOS version $OS_VERSION" + +source ${REPO_SRC}/ci/macOS/before_install_lib.sh + +install_packages() { + + # Workaround: Homebrew fails to upgrade Python's 2to3 due to conflicting symlinks https://github.com/actions/runner-images/issues/6817 + rm /usr/local/bin/2to3 || true + rm /usr/local/bin/idle3 || true + rm /usr/local/bin/pydoc3 || true + rm /usr/local/bin/python3 || true + rm /usr/local/bin/python3-config || true + + brew update + # Workaround for brew taking a long time to upgrade existing packages + # Check if macOS version and upgrade packages only if the version is greater than macOS 12 + if (( $(echo "$(sw_vers -productVersion) > 13.0" | bc -l) )); then + brew upgrade --display-times || true #ignore homebrew upgrade errors + # Workaround: Install or update libtool package only if macOS version is greater than 12 + # Note: libtool (v2.4.7) is pre-installed by default, but it can be updated to v2.5.3 + PACKAGES="$PACKAGES libtool" + brew install --overwrite --display-times $PACKAGES + else + HOMEBREW_NO_AUTO_UPDATE=1 brew install --overwrite --display-times $PACKAGES + fi + + pip3 install --break-system-packages mako +} + +export_paths(){ + QT_PATH="$(brew --prefix ${QT_FORMULAE})/bin" + export PATH="/usr/local/bin:$PATH" + export PATH="/usr/local/opt/bison/bin:$PATH" + export PATH="${QT_PATH}:$PATH" + export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/libzip/lib/pkgconfig" + export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/libffi/lib/pkgconfig" + export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:$STAGING_AREA_DEPS/lib/pkgconfig" + + QMAKE="$(command -v qmake)" + CMAKE_BIN="$(command -v cmake)" + CMAKE_OPTS="-DCMAKE_PREFIX_PATH=$STAGING_AREA_DEPS -DCMAKE_INSTALL_PREFIX=$STAGING_AREA_DEPS" + CMAKE="$CMAKE_BIN ${CMAKE_OPTS[*]}" + + echo -- USING CMAKE COMMAND: + echo $CMAKE + echo -- USING QT: $QT_PATH + echo -- USING QMAKE: $QMAKE + echo -- PATH: $PATH + echo -- PKG_CONFIG_PATH: $PKG_CONFIG_PATH +} + +clone() { + echo "#######CLONE#######" + mkdir -p $STAGING_AREA + pushd $STAGING_AREA + git clone --recursive https://github.com/cseci/libserialport -b $LIBSERIALPORT_BRANCH libserialport + git clone --recursive https://github.com/analogdevicesinc/libiio.git -b $LIBIIO_VERSION libiio + git clone --recursive https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH libad9361 + git clone --recursive https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH libm2k + git clone --recursive https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH gr-scopy + git clone --recursive https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH gr-m2k + git clone --recursive https://github.com/analogdevicesinc/gnuradio.git -b $GNURADIO_BRANCH gnuradio + git clone --recursive https://github.com/cseci/qwt.git -b $QWT_BRANCH qwt + git clone --recursive https://github.com/sigrokproject/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH libsigrokdecode + git clone --recursive https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH libtinyiiod + git clone --recursive https://github.com/KDAB/KDDockWidgets.git -b $KDDOCK_BRANCH KDDockWidgets + popd +} + +generate_status_file(){ + # Generate build status info for the about page + BUILD_STATUS_FILE=${REPO_SRC}/build-status + brew list --versions $PACKAGES > $BUILD_STATUS_FILE +} + +save_version_info() { + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE +} + +build_with_cmake() { + echo $PWD + BUILD_FOLDER=$PWD/build + rm -rf $BUILD_FOLDER + git clean -xdf + mkdir -p $BUILD_FOLDER + cd $BUILD_FOLDER + $CMAKE $CURRENT_BUILD_CMAKE_OPTS ../ + make $JOBS + + #clear variable + CURRENT_BUILD_CMAKE_OPTS="" +} + +build_libserialport(){ + CURRENT_BUILD=libserialport + pushd $STAGING_AREA/$CURRENT_BUILD + save_version_info + git clean -xdf + ./autogen.sh + ./configure --prefix $STAGING_AREA_DEPS + make $JOBS + sudo make install + popd +} + +build_libiio() { + echo "### Building libiio - version $LIBIIO_VERSION" + CURRENT_BUILD=libiio + pushd $STAGING_AREA/libiio + save_version_info + CURRENT_BUILD_CMAKE_OPTS="\ + -DWITH_TESTS:BOOL=OFF \ + -DWITH_DOC:BOOL=OFF \ + -DHAVE_DNS_SD:BOOL=ON \ + -DENABLE_DNS_SD:BOOL=ON \ + -DWITH_MATLAB_BINDINGS:BOOL=OFF \ + -DCSHARP_BINDINGS:BOOL=OFF \ + -DPYTHON_BINDINGS:BOOL=OFF \ + -DINSTALL_UDEV_RULE:BOOL=OFF \ + -DWITH_SERIAL_BACKEND:BOOL=ON \ + -DENABLE_IPV6:BOOL=OFF \ + -DOSX_PACKAGE:BOOL=OFF + " + build_with_cmake + sudo make install + sudo chmod -R 775 $STAGING_AREA_DEPS + sudo chmod 664 $STAGING_AREA_DEPS/lib/pkgconfig/libiio.pc + cp -R $STAGING_AREA/libiio/build/iio.framework $STAGING_AREA_DEPS/lib + popd +} + +build_libm2k() { + echo "### Building libm2k - branch $LIBM2K_BRANCH" + pushd $STAGING_AREA/libm2k + CURRENT_BUILD=libm2k + save_version_info + + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PYTHON=OFF \ + -DENABLE_CSHARP=OFF \ + -DBUILD_EXAMPLES=OFF \ + -DENABLE_TOOLS=OFF \ + -DINSTALL_UDEV_RULES=OFF \ + -DENABLE_LOG=OFF\ + " + build_with_cmake + make install + popd +} + +build_libad9361() { + echo "### Building libad9361 - branch $LIBAD9361_BRANCH" + CURRENT_BUILD=libad9361-iio + pushd $STAGING_AREA/libad9361 + save_version_info + build_with_cmake + make install + popd +} + +build_gnuradio() { + echo "### Building gnuradio - branch $GNURADIO_BRANCH" + CURRENT_BUILD=gnuradio + pushd $STAGING_AREA/gnuradio + save_version_info + CURRENT_BUILD_CMAKE_OPTS="\ + -DPYTHON_EXECUTABLE=/usr/bin/python3 \ + -DENABLE_DEFAULT=OFF \ + -DENABLE_GNURADIO_RUNTIME=ON \ + -DENABLE_GR_ANALOG=ON \ + -DENABLE_GR_BLOCKS=ON \ + -DENABLE_GR_FFT=ON \ + -DENABLE_GR_FILTER=ON \ + -DENABLE_GR_IIO=ON \ + -DENABLE_POSTINSTALL=OFF + " + build_with_cmake + make install + popd +} + +build_grm2k() { + echo "### Building gr-m2k - branch $GRM2K_BRANCH" + CURRENT_BUILD=gr-m2k + pushd $STAGING_AREA/gr-m2k + save_version_info + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PYTHON=OFF \ + -DDIGITAL=OFF + " + build_with_cmake + make install + popd +} + +build_grscopy() { + echo "### Building gr-scopy - branch $GRSCOPY_BRANCH" + CURRENT_BUILD=gr-scopy + pushd $STAGING_AREA/gr-scopy + save_version_info + CURRENT_BUILD_CMAKE_OPTS="-DWITH_PYTHON=OFF " + build_with_cmake + make install + popd +} + +build_libsigrokdecode() { + echo "### Building libsigrokdecode - branch $LIBSIGROKDECODE_BRANCH" + CURRENT_BUILD=libsigrokdecode + pushd $STAGING_AREA/libsigrokdecode + save_version_info + git reset --hard + git clean -xdf + ./autogen.sh + ./configure --prefix $STAGING_AREA_DEPS + make $JOBS install + popd +} + +patch_qwt() { + patch -p1 <<-EOF +--- a/qwtconfig.pri ++++ b/qwtconfig.pri +@@ -19,7 +19,7 @@ QWT_VERSION = \$\${QWT_VER_MAJ}.\$\${QWT_VER_MIN}.\$\${QWT_VER_PAT} + QWT_INSTALL_PREFIX = \$\$[QT_INSTALL_PREFIX] + + unix { +- QWT_INSTALL_PREFIX = /usr/local/qwt-\$\$QWT_VERSION-ma ++ QWT_INSTALL_PREFIX = $STAGING_AREA_DEPS + # QWT_INSTALL_PREFIX = /usr/local/qwt-\$\$QWT_VERSION-ma-qt-\$\$QT_VERSION + } + +@@ -42,7 +42,7 @@ QWT_INSTALL_LIBS = \$\${QWT_INSTALL_PREFIX}/lib + # runtime environment of designer/creator. + ###################################################################### + +-QWT_INSTALL_PLUGINS = \$\${QWT_INSTALL_PREFIX}/plugins/designer ++#QWT_INSTALL_PLUGINS = \$\${QWT_INSTALL_PREFIX}/plugins/designer + + # linux distributors often organize the Qt installation + # their way and QT_INSTALL_PREFIX doesn't offer a good +@@ -164,7 +164,7 @@ QWT_CONFIG += QwtTests + + macx:!static:CONFIG(qt_framework, qt_framework|qt_no_framework) { + +- QWT_CONFIG += QwtFramework ++# QWT_CONFIG += QwtFramework + } + + ###################################################################### +--- a/src/src.pro ++++ b/src/src.pro +@@ -36,6 +36,7 @@ contains(QWT_CONFIG, QwtDll) { + QMAKE_LFLAGS_SONAME= + } + } ++ macx: QWT_SONAME=\$\${QWT_INSTALL_LIBS}/libqwt.dylib + } + else { + CONFIG += staticlib +EOF +} + + +build_qwt() { + echo "### Building qwt - branch qwt-multiaxes" + CURRENT_BUILD=qwt + pushd $STAGING_AREA/qwt + save_version_info + git clean -xdf + git reset --hard + patch_qwt + $QMAKE INCLUDEPATH=$STAGING_AREA_DEPS/include LIBS=-L$STAGING_AREA_DEPS/lib qwt.pro + make $JOBS + make install + popd +} + +build_libtinyiiod() { + echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" + CURRENT_BUILD=libtinyiiod + pushd $STAGING_AREA/libtinyiiod + save_version_info + CURRENT_BUILD_CMAKE_OPTS="-DBUILD_EXAMPLES=OFF" + build_with_cmake + make install + popd +} + +build_kddock () { + echo "### Building KDDockWidgets - version $KDDOCK_BRANCH" + pushd $STAGING_AREA/KDDockWidgets + CURRENT_BUILD_CMAKE_OPTS="" + build_with_cmake + make install + popd +} + +build_deps(){ + build_libserialport + build_libiio + build_libad9361 + build_libm2k + build_gnuradio + build_grscopy + build_grm2k + build_qwt + build_libsigrokdecode + build_libtinyiiod + build_kddock +} + +install_packages +export_paths +clone +generate_status_file +build_deps diff --git a/ci/macOS/macos_config.sh b/ci/macOS/macos_config.sh new file mode 100644 index 0000000000..0a9fd14a90 --- /dev/null +++ b/ci/macOS/macos_config.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +STAGING_AREA=$PWD/staging +STAGING_AREA_DEPS=$STAGING_AREA/dependencies +REPO_SRC=$(git rev-parse --show-toplevel) +BUILDDIR=$REPO_SRC/build +JOBS=-j8 +QT_FORMULAE=qt@5 +QT_PATH="$(brew --prefix ${QT_FORMULAE})/bin" +export PATH="${QT_PATH}:$PATH" +export LD_LIBRARY_PATH="$LD_LIBRARY_PATH;$STAGING_AREA_DEPS;$STAGING_AREA_DEPS/lib" + + +LIBSERIALPORT_BRANCH=scopy-v2 +LIBIIO_VERSION=v0.26 +LIBAD9361_BRANCH=main +LIBM2K_BRANCH=main +GNURADIO_BRANCH=scopy2-maint-3.10 +GRSCOPY_BRANCH=3.10 +GRM2K_BRANCH=main +QWT_BRANCH=qwt-multiaxes-updated +LIBSIGROKDECODE_BRANCH=master +LIBTINYIIOD_BRANCH=master +KDDOCK_BRANCH=2.1 \ No newline at end of file diff --git a/ci/macOS/package_darwin.sh b/ci/macOS/package_darwin.sh new file mode 100755 index 0000000000..b6b390a3f3 --- /dev/null +++ b/ci/macOS/package_darwin.sh @@ -0,0 +1,147 @@ +#!/bin/bash +set -ex +REPO_SRC=$(git rev-parse --show-toplevel) +source $REPO_SRC/ci/macOS/macos_config.sh + +pushd $BUILDDIR + +SCOPYPLUGINS=$(find $BUILDDIR/Scopy.app/Contents/MacOs/plugins -name "*.dylib" -type f) +SCOPYLIBS=$(find $BUILDDIR/Scopy.app/Contents/Frameworks -name "*.dylib" -type f) + +echo "### Copy DLLs to Frameworks folder" +cp -R $REPO_SRC/plugins/dac/res/csv Scopy.app/Contents/MacOS/plugins/ +cp -R $STAGING_AREA/libiio/build/iio.framework Scopy.app/Contents/Frameworks/ +cp -R $STAGING_AREA/libad9361/build/ad9361.framework Scopy.app/Contents/Frameworks/ +cp -R $BUILDDIR/plugins/emu_xml Scopy.app/Contents/MacOS/plugins +mkdir -p Scopy.app/Contents/MacOS/plugins/resources +cp $REPO_SRC/resources/scopy_emu_options_config.json Scopy.app/Contents/MacOS/plugins/resources/ +cp -R $BUILDDIR/translations Scopy.app/Contents/MacOS +cp -R $BUILDDIR/plugins/regmap/xmls Scopy.app/Contents/MacOS/plugins + +cp -R $BUILDDIR/translations $BUILDDIR/Scopy.app/Contents/MacOS/translations +cp -R $BUILDDIR/style $BUILDDIR/Scopy.app/Contents/MacOS/style + +libqwtpath=${STAGING_AREA_DEPS}/lib/libqwt.6.4.0.dylib #hardcoded +libqwtid="$(otool -D ${libqwtpath} | tail -1)" +echo "=== Fixing libqwt" +[ -z "$(otool -L ${libqwtpath} | grep libqwt...dylib)" ] || install_name_tool -id ${libqwtid} ${libqwtpath} +otool -L ${libqwtpath} +install_name_tool -change ${libqwtid} ${libqwtpath} ./Scopy.app/Contents/MacOS/Scopy +for dylib in ${SCOPYLIBS} ${SCOPYPLUGINS} +do + [ -z "$(otool -L ${dylib} | grep libqwt...dylib)" ] || install_name_tool -change ${libqwtid} ${libqwtpath} ${dylib} + otool -L $dylib +done + + +iiorpath="$(otool -D ./Scopy.app/Contents/Frameworks/iio.framework/iio | grep @rpath)" +iioid=${iiorpath#"@rpath/"} + +ad9361rpath="$(otool -D ./Scopy.app/Contents/Frameworks/ad9361.framework/ad9361 | grep @rpath)" +ad9361id=${ad9361rpath#"@rpath/"} + +libusbpath="$(otool -L ./Scopy.app/Contents/Frameworks/iio.framework/iio | grep libusb | cut -d " " -f 1 | awk '{$1=$1};1')" +libusbid="$(echo ${libusbpath} | rev | cut -d "/" -f 1 | rev)" +cp ${libusbpath} ./Scopy.app/Contents/Frameworks/ + +m2kpath=${STAGING_AREA_DEPS}/lib/libm2k.dylib +m2krpath="$(otool -D ${m2kpath} | grep @rpath)" +m2kid=${m2krpath#"@rpath/"} +cp ${STAGING_AREA_DEPS}/lib/libm2k.* ./Scopy.app/Contents/Frameworks +install_name_tool -id @executable_path/../Frameworks/${m2kid} ./Scopy.app/Contents/Frameworks/${m2kid} + +echo "### Get python version" +brewprefix=$(brew --prefix python3) +pyversion=${brewprefix##*@} # extract the text after the last '@' +pythonpath=$brewprefix/Frameworks/Python.framework/Versions/$pyversion/Python +pythonidrpath="$(otool -D $pythonpath | head -2 | tail -1)" + +if [ -z $pyversion ] ; then + echo "No Python 3.8, 3.9, 3.10, 3.11, 3.12 paths found" + exit 1 +fi +echo " - Found python$version at $pythonpath" +pythonid=${pythonidrpath#"$(brew --prefix python3)/Frameworks/"} +cp -R $(brew --prefix python3)/Frameworks/Python.framework Scopy.app/Contents/Frameworks/ + +echo "### Fixing scopy libraries and plugins " +for dylib in ${SCOPYLIBS} ${SCOPYPLUGINS} +do + echo "--- FIXING LIB: ${dylib##*/}" + echo $STAGING_AREA_DEPS/lib | dylibbundler --no-codesign --overwrite-files --bundle-deps --create-dir \ + --fix-file $dylib \ + --dest-dir $BUILDDIR/Scopy.app/Contents/Frameworks/ \ + --install-path @executable_path/../Frameworks/ \ + --search-path $BUILDDIR/Scopy.app/Contents/Frameworks/ +done + + +echo "### Fixing Scopy binary" +echo $STAGING_AREA_DEPS/lib | dylibbundler -ns -of -b \ + -x $BUILDDIR/Scopy.app/Contents/MacOS/Scopy \ + -d $BUILDDIR/Scopy.app/Contents/Frameworks \ + -p @executable_path/../Frameworks \ + -s $BUILDDIR/Scopy.app/Contents/Frameworks + +echo "### Fixing the frameworks dylibbundler failed to copy" +echo "=== Fixing iio.framework" +install_name_tool -id @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/iio.framework/iio +install_name_tool -id @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/${iioid} +install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/MacOS/Scopy +for dylib in ${SCOPYLIBS} ${SCOPYPLUGINS} +do + otool -L $dylib + [ -z "$(otool -L ${dylib}| grep iio.framework)" ] && echo "SKIP ${dylib##*/}" || install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ${dylib} +done + + +echo "=== Fixing ad9361.framework" +install_name_tool -id @executable_path/../Frameworks/${ad9361id} ./Scopy.app/Contents/Frameworks/ad9361.framework/ad9361 +install_name_tool -id @executable_path/../Frameworks/${ad9361id} ./Scopy.app/Contents/Frameworks/${ad9361id} +install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/${ad9361id} +install_name_tool -change ${ad9361rpath} @executable_path/../Frameworks/${ad9361id} ./Scopy.app/Contents/Frameworks/libgnuradio-iio* + +echo "=== Fixing libusb" +install_name_tool -id @executable_path/../Frameworks/${libusbid} ./Scopy.app/Contents/Frameworks/${libusbid} +install_name_tool -change ${libusbpath} @executable_path/../Frameworks/${libusbid} ./Scopy.app/Contents/Frameworks/iio.framework/iio + +echo "=== Fixing python" +install_name_tool -id @executable_path/../Frameworks/${pythonid} ./Scopy.app/Contents/Frameworks/${pythonid} +python_sigrokdecode=$(otool -L ./Scopy.app/Contents/Frameworks/libsigrokdecode* | grep python | cut -d " " -f 1 | awk '{$1=$1};1') +install_name_tool -change ${python_sigrokdecode} @executable_path/../Frameworks/${pythonid} ./Scopy.app/Contents/Frameworks/libsigrokdecode* +python_scopy=$(otool -L ./Scopy.app/Contents/MacOS/Scopy | grep -i python | cut -d " " -f 1 | awk '{$1=$1};1') +install_name_tool -change ${python_scopy} @executable_path/../Frameworks/${pythonid} ./Scopy.app/Contents/MacOS/Scopy +for dylib in ${SCOPYLIBS} ${SCOPYPLUGINS} +do + otool -L $dylib + python=$(otool -L ${dylib} | grep -i python | cut -d " " -f 1 | awk '{$1=$1};1'); + [ -z "${python}" ] && echo "SKIP ${dylib##*/}" || install_name_tool -change ${python} @executable_path/../Frameworks/${pythonid} ${dylib} +done + + +echo "=== Fixing libserialport" +libserialportpath="$(otool -L ./Scopy.app/Contents/Frameworks/iio.framework/iio | grep libserialport | cut -d " " -f 1 | awk '{$1=$1};1')" +libserialportid="$(echo ${libserialportpath} | rev | cut -d "/" -f 1 | rev)" +install_name_tool -change ${libserialportpath} @executable_path/../Frameworks/${libserialportid} ./Scopy.app/Contents/Frameworks/iio.framework/iio + +install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/libm2k.dylib +install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/libm2k.?.?.?.dylib +install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/libgnuradio-m2k* +install_name_tool -change ${iiorpath} @executable_path/../Frameworks/${iioid} ./Scopy.app/Contents/Frameworks/libgnuradio-scopy* +install_name_tool -change ${m2krpath} @executable_path/../Frameworks/${m2kid} ./Scopy.app/Contents/Frameworks/libgnuradio-m2k* +install_name_tool -change ${m2krpath} @executable_path/../Frameworks/${m2kid} ./Scopy.app/Contents/Frameworks/libgnuradio-scopy* + + +echo "=== Fixing iio-emu + libtinyiiod" +cp $REPO_SRC/iio-emu/build/iio-emu ./Scopy.app/Contents/MacOS/ +echo $STAGING_AREA_DEPS/lib | dylibbundler -ns -of -b \ + --fix-file $BUILDDIR/Scopy.app/Contents/MacOS/iio-emu \ + --dest-dir $BUILDDIR/Scopy.app/Contents/Frameworks/ \ + --install-path @executable_path/../Frameworks/ \ + --search-path $BUILDDIR/Scopy.app/Contents/Frameworks/ + +echo "=== Bundle the Qt libraries & Create Scopy.dmg" +macdeployqt Scopy.app -verbose=3 +zip -Xvr ScopyApp.zip Scopy.app +macdeployqt Scopy.app -dmg -verbose=3 +popd diff --git a/CI/travis/before_deploy.sh b/ci/old/before_deploy.sh similarity index 100% rename from CI/travis/before_deploy.sh rename to ci/old/before_deploy.sh diff --git a/CI/travis/before_install_darwin.sh b/ci/old/before_install_darwin.sh similarity index 100% rename from CI/travis/before_install_darwin.sh rename to ci/old/before_install_darwin.sh diff --git a/CI/travis/before_install_lib.sh b/ci/old/before_install_lib_travis.sh similarity index 100% rename from CI/travis/before_install_lib.sh rename to ci/old/before_install_lib_travis.sh diff --git a/CI/travis/before_install_linux.sh b/ci/old/before_install_linux.sh similarity index 100% rename from CI/travis/before_install_linux.sh rename to ci/old/before_install_linux.sh diff --git a/CI/travis/deploy.sh b/ci/old/deploy.sh similarity index 100% rename from CI/travis/deploy.sh rename to ci/old/deploy.sh diff --git a/CI/appveyor/extract_msys_deps.sh b/ci/old/extract_msys_deps.sh similarity index 100% rename from CI/appveyor/extract_msys_deps.sh rename to ci/old/extract_msys_deps.sh diff --git a/CI/travis/inside_centos_docker.sh b/ci/old/inside_centos_docker.sh similarity index 100% rename from CI/travis/inside_centos_docker.sh rename to ci/old/inside_centos_docker.sh diff --git a/CI/travis/inside_ubuntu_docker.sh b/ci/old/inside_ubuntu_docker.sh similarity index 100% rename from CI/travis/inside_ubuntu_docker.sh rename to ci/old/inside_ubuntu_docker.sh diff --git a/CI/travis/inside_ubuntu_flatpak_docker.sh b/ci/old/inside_ubuntu_flatpak_docker.sh similarity index 100% rename from CI/travis/inside_ubuntu_flatpak_docker.sh rename to ci/old/inside_ubuntu_flatpak_docker.sh diff --git a/CI/appveyor/install_msys_deps.sh b/ci/old/install_msys_deps.sh similarity index 100% rename from CI/appveyor/install_msys_deps.sh rename to ci/old/install_msys_deps.sh diff --git a/CI/travis/lib.sh b/ci/old/lib.sh similarity index 100% rename from CI/travis/lib.sh rename to ci/old/lib.sh diff --git a/CI/travis/make_darwin.sh b/ci/old/make_darwin.sh similarity index 100% rename from CI/travis/make_darwin.sh rename to ci/old/make_darwin.sh diff --git a/CI/travis/make_linux.sh b/ci/old/make_linux.sh similarity index 100% rename from CI/travis/make_linux.sh rename to ci/old/make_linux.sh diff --git a/CI/travis/package_darwin.sh b/ci/old/package_darwin_travis.sh similarity index 100% rename from CI/travis/package_darwin.sh rename to ci/old/package_darwin_travis.sh diff --git a/CI/appveyor/patches/boost-darwin.patch b/ci/old/patches/boost-darwin.patch similarity index 100% rename from CI/appveyor/patches/boost-darwin.patch rename to ci/old/patches/boost-darwin.patch diff --git a/CI/appveyor/set_build_env_msys.sh b/ci/old/set_build_env_msys.sh similarity index 100% rename from CI/appveyor/set_build_env_msys.sh rename to ci/old/set_build_env_msys.sh diff --git a/ci/ubuntu/.dockerignore b/ci/ubuntu/.dockerignore new file mode 100644 index 0000000000..c83f2cc688 --- /dev/null +++ b/ci/ubuntu/.dockerignore @@ -0,0 +1,2 @@ +* +!ubuntu_build_process.sh diff --git a/ci/ubuntu/create_docker_image.sh b/ci/ubuntu/create_docker_image.sh new file mode 100755 index 0000000000..267c9f5d77 --- /dev/null +++ b/ci/ubuntu/create_docker_image.sh @@ -0,0 +1,19 @@ +#!/bin/bash -ex + +SRC_SCRIPT=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +ubuntu20(){ + pushd $SRC_SCRIPT + docker build -t cristianbindea/scopy2-ubuntu20:testing -f docker_ubuntu20/Dockerfile . + popd +} + +ubuntu22(){ + pushd $SRC_SCRIPT + docker build -t cristianbindea/scopy2-ubuntu22:testing -f docker_ubuntu22/Dockerfile . + popd +} + +for arg in $@; do + $arg +done diff --git a/ci/ubuntu/docker_ubuntu20/Dockerfile b/ci/ubuntu/docker_ubuntu20/Dockerfile new file mode 100644 index 0000000000..31f753d967 --- /dev/null +++ b/ci/ubuntu/docker_ubuntu20/Dockerfile @@ -0,0 +1,36 @@ +FROM ubuntu:20.04 +SHELL ["/bin/bash", "-c"] + +ARG USER=runner +ENV DEBIAN_FRONTEND=noninteractive +ENV CI_SCRIPT=ON + +ENV TZ=Europe/Bucharest +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +RUN apt-get update && \ + apt-get -y upgrade && \ + apt-get install -y apt-utils sudo tzdata keyboard-configuration software-properties-common + +RUN groupadd -g 1000 -r $USER && \ + useradd -u 1000 -g 1000 --create-home -r $USER + +#Change password +RUN echo "$USER:$USER" | chpasswd + +#Make sudo passwordless +RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-$USER && \ + usermod -aG sudo $USER && \ + usermod -aG plugdev $USER + +USER $USER +WORKDIR /home/${USER}/scripts +COPY ubuntu_build_process.sh . +RUN ./ubuntu_build_process.sh configure_system +WORKDIR /home/${USER} + +# Clean image +RUN rm -rf /home/${USER}/scripts +RUN sudo rm -rf /var/lib/apt/lists/* + +FROM scratch +COPY --from=0 / / \ No newline at end of file diff --git a/ci/ubuntu/docker_ubuntu22/Dockerfile b/ci/ubuntu/docker_ubuntu22/Dockerfile new file mode 100644 index 0000000000..2706be9ef7 --- /dev/null +++ b/ci/ubuntu/docker_ubuntu22/Dockerfile @@ -0,0 +1,36 @@ +FROM ubuntu:22.04 +SHELL ["/bin/bash", "-c"] + +ARG USER=runner +ENV DEBIAN_FRONTEND=noninteractive +ENV CI_SCRIPT=ON + +ENV TZ=Europe/Bucharest +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +RUN apt-get update && \ + apt-get -y upgrade && \ + apt-get install -y apt-utils sudo tzdata keyboard-configuration software-properties-common + +RUN groupadd -g 1000 -r $USER && \ + useradd -u 1000 -g 1000 --create-home -r $USER + +#Change password +RUN echo "$USER:$USER" | chpasswd + +#Make sudo passwordless +RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-$USER && \ + usermod -aG sudo $USER && \ + usermod -aG plugdev $USER + +USER $USER +WORKDIR /home/${USER}/scripts +COPY ubuntu_build_process.sh . +RUN ./ubuntu_build_process.sh configure_system +WORKDIR /home/${USER} + +# Clean image +RUN rm -rf /home/${USER}/scripts +RUN sudo rm -rf /var/lib/apt/lists/* + +FROM scratch +COPY --from=0 / / \ No newline at end of file diff --git a/ci/ubuntu/ubuntu_build_process.sh b/ci/ubuntu/ubuntu_build_process.sh new file mode 100755 index 0000000000..57dc6c8902 --- /dev/null +++ b/ci/ubuntu/ubuntu_build_process.sh @@ -0,0 +1,350 @@ +#!/bin/bash -xe + +## Set STAGING +USE_STAGING=OFF +## + +SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ +SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) + +SRC_SCRIPT=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +[ "$CI_SCRIPT" == "ON" ] && USE_STAGING=OFF + +LIBSERIALPORT_BRANCH=scopy-v2 +LIBIIO_VERSION=v0.26 +LIBAD9361_BRANCH=main +LIBM2K_BRANCH=main +SPDLOG_BRANCH=v1.x +VOLK_BRANCH=main +GNURADIO_BRANCH=scopy2-maint-3.10 +GRSCOPY_BRANCH=3.10 +GRM2K_BRANCH=main +LIBSIGROKDECODE_BRANCH=master +QWT_BRANCH=qwt-multiaxes-updated +LIBTINYIIOD_BRANCH=master +IIOEMU_BRANCH=main +KDDOCK_BRANCH=2.1 + +QT=/opt/Qt/5.15.2/gcc_64 +QMAKE_BIN=$QT/bin/qmake +CMAKE_BIN=/bin/cmake +JOBS=-j14 +STAGING_AREA=$SRC_SCRIPT/staging_ubuntu20 + +if [ "$USE_STAGING" == "ON" ] + then + echo -- USING STAGING FOLDER: $STAGING_AREA_DEPS + STAGING_AREA_DEPS=$STAGING_AREA/dependencies + export LD_LIBRARY_PATH=$STAGING_AREA_DEPS/lib:$QT/lib:$LD_LIBRARY_PATH + CMAKE_OPTS=(\ + -DCMAKE_LIBRARY_PATH=$STAGING_AREA_DEPS \ + -DCMAKE_INSTALL_PREFIX=$STAGING_AREA_DEPS \ + -DCMAKE_PREFIX_PATH=$QT\;$STAGING_AREA_DEPS \ + -DCMAKE_EXE_LINKER_FLAGS=-L$STAGING_AREA_DEPS\;-L$STAGING_AREA_DEPS/lib \ + -DCMAKE_SHARED_LINKER_FLAGS=-L$STAGING_AREA_DEPS\;-L$STAGING_AREA_DEPS/lib \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + ) + echo -- STAGING_DIR $STAGING_AREA_DEPS + else + echo -- NO STAGING: INSTALLING IN SYSTEM + STAGING_AREA_DEPS=/usr/local + export LD_LIBRARY_PATH=$QT/lib:$LD_LIBRARY_PATH: + CMAKE_OPTS=(\ + -DCMAKE_PREFIX_PATH=$QT \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + ) +fi + +CMAKE="$CMAKE_BIN ${CMAKE_OPTS[*]}" +echo -- USING CMAKE COMMAND: +echo $CMAKE +echo -- USING QT: $QT +echo -- USING QMAKE: $QMAKE_BIN + +clone() { + echo "#######CLONE#######" + mkdir -p $STAGING_AREA + pushd $STAGING_AREA + [ -d 'libserialport' ] || git clone --recursive https://github.com/cseci/libserialport -b $LIBSERIALPORT_BRANCH libserialport + [ -d 'libiio' ] || git clone --recursive https://github.com/analogdevicesinc/libiio.git -b $LIBIIO_VERSION libiio + [ -d 'libad9361' ] || git clone --recursive https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH libad9361 + [ -d 'libm2k' ] || git clone --recursive https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH libm2k + [ -d 'spdlog' ] || git clone --recursive https://github.com/gabime/spdlog.git -b $SPDLOG_BRANCH spdlog + [ -d 'gr-scopy' ] || git clone --recursive https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH gr-scopy + [ -d 'gr-m2k' ] || git clone --recursive https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH gr-m2k + [ -d 'volk' ] || git clone --recursive https://github.com/gnuradio/volk.git -b $VOLK_BRANCH volk + [ -d 'gnuradio' ] || git clone --recursive https://github.com/analogdevicesinc/gnuradio.git -b $GNURADIO_BRANCH gnuradio + [ -d 'qwt' ] || git clone --recursive https://github.com/cseci/qwt.git -b $QWT_BRANCH qwt + [ -d 'libsigrokdecode' ] || git clone --recursive https://github.com/sigrokproject/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH libsigrokdecode + [ -d 'libtinyiiod' ] || git clone --recursive https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH libtinyiiod + [ -d 'iio-emu' ] || git clone --recursive https://github.com/analogdevicesinc/iio-emu -b $IIOEMU_BRANCH iio-emu + [ -d 'KDDockWidgets' ] || git clone --recursive https://github.com/KDAB/KDDockWidgets.git -b $KDDOCK_BRANCH KDDockWidgets + popd +} + +build_with_cmake() { + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + BUILD_FOLDER=$PWD/build + rm -rf $BUILD_FOLDER + mkdir -p $BUILD_FOLDER + cd $BUILD_FOLDER + $CMAKE $CURRENT_BUILD_CMAKE_OPTS ../ + make $JOBS + if [ "$INSTALL" == "ON" ]; then + if [ "$USE_STAGING" == "ON" ]; then make install; else sudo make install; fi + fi + CURRENT_BUILD_CMAKE_OPTS="" +} + +install_packages() { + sudo apt update + sudo apt -y upgrade + sudo apt -y --no-install-recommends install autoconf autogen bison build-essential cmake curl doxygen flex g++ git graphviz libaio-dev libavahi-client-dev \ + libboost-all-dev libfftw3-3 libfftw3-bin libfftw3-dev libgmp3-dev libglib2.0-dev libglibmm-2.4-dev \ + libgl1-mesa-dev liblog4cpp5-dev liblog4cpp5v5 libmatio-dev liborc-0.4-dev libpython3-all-dev \ + libsigc++-2.0-dev libsndfile1-dev libtool libusb-1.0-0-dev libxml2-dev libzip-dev \ + libzmq3-dev mesa-common-dev pkg-config python3-dev python3 python3-numpy python3-pip subversion swig vim wget unzip + + pip3 install --no-cache-dir mako + pip3 install --no-cache-dir packaging +} + +install_qt() { + # installing Qt using the aqt tool https://github.com/miurahr/aqtinstall + sudo pip3 install --no-cache-dir aqtinstall + sudo python3 -m aqt install-qt --outputdir /opt/Qt linux desktop 5.15.2 +} + +build_libserialport(){ + CURRENT_BUILD=libserialport + pushd $STAGING_AREA/$CURRENT_BUILD + git clean -xdf + + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + + ./autogen.sh + [ "$USE_STAGING" == "ON" ] && ./configure --prefix $STAGING_AREA_DEPS || ./configure + + make $JOBS + [ "$INSTALL" == "ON" ] && sudo make install + popd +} + +build_libiio() { + echo "### Building libiio - version $LIBIIO_VERSION" + pushd $STAGING_AREA/libiio + CURRENT_BUILD_CMAKE_OPTS="\ + -DWITH_TESTS:BOOL=OFF \ + -DWITH_DOC:BOOL=OFF \ + -DHAVE_DNS_SD:BOOL=OFF\ + -DWITH_MATLAB_BINDINGS:BOOL=OFF \ + -DCSHARP_BINDINGS:BOOL=OFF \ + -DPYTHON_BINDINGS:BOOL=OFF \ + -DWITH_SERIAL_BACKEND:BOOL=ON \ + -DENABLE_IPV6:BOOL=OFF \ + -DINSTALL_UDEV_RULE:BOOL=OFF + " + build_with_cmake $1 + popd +} + +build_glog() { + echo "### Building glog - branch $GLOG_BRANCH" + pushd $STAGING_AREA/glog + CURRENT_BUILD_CMAKE_OPTS="-DWITH_GFLAGS=OFF" + build_with_cmake $1 + popd +} + +build_libad9361() { + echo "### Building libad9361 - branch $LIBAD9361_BRANCH" + pushd $STAGING_AREA/libad9361 + build_with_cmake $1 + popd +} + +build_libm2k() { + echo "### Building libm2k - branch $LIBM2K_BRANCH" + pushd $STAGING_AREA/libm2k + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PYTHON=OFF \ + -DENABLE_CSHARP=OFF \ + -DBUILD_EXAMPLES=OFF \ + -DENABLE_TOOLS=OFF \ + -DINSTALL_UDEV_RULES=OFF \ + " + build_with_cmake $1 + popd +} + +build_spdlog() { + echo "### Building spdlog - branch $SPDLOG_BRANCH" + pushd $STAGING_AREA/spdlog + CURRENT_BUILD_CMAKE_OPTS="-DSPDLOG_BUILD_SHARED=ON" + build_with_cmake $1 + popd +} + +build_volk() { + echo "### Building volk - branch $VOLK_BRANCH" + pushd $STAGING_AREA/volk + CURRENT_BUILD_CMAKE_OPTS="-DPYTHON_EXECUTABLE=/usr/bin/python3" + build_with_cmake $1 + popd +} + +build_gnuradio() { + echo "### Building gnuradio - branch $GNURADIO_BRANCH" + pushd $STAGING_AREA/gnuradio + CURRENT_BUILD_CMAKE_OPTS="\ + -DPYTHON_EXECUTABLE=/usr/bin/python3 \ + -DENABLE_DEFAULT=OFF \ + -DENABLE_GNURADIO_RUNTIME=ON \ + -DENABLE_GR_ANALOG=ON \ + -DENABLE_GR_BLOCKS=ON \ + -DENABLE_GR_FFT=ON \ + -DENABLE_GR_FILTER=ON \ + -DENABLE_GR_IIO=ON \ + -DENABLE_POSTINSTALL=OFF + " + INSTALL="ON" + build_with_cmake $1 + popd +} + +build_grm2k() { + echo "### Building gr-m2k - branch $GRM2K_BRANCH" + pushd $STAGING_AREA/gr-m2k + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PYTHON=OFF \ + -DDIGITAL=OFF + " + build_with_cmake $1 + popd +} + +build_grscopy() { + echo "### Building gr-scopy - branch $GRSCOPY_BRANCH" + pushd $STAGING_AREA/gr-scopy + build_with_cmake $1 + popd +} + +build_libsigrokdecode() { + echo "### Building libsigrokdecode - branch $LIBSIGROKDECODE_BRANCH" + pushd $STAGING_AREA/libsigrokdecode + git clean -xdf + ./autogen.sh + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + + if [ "$USE_STAGING" == "ON" ]; then + ./configure --prefix $STAGING_AREA_DEPS + LD_RUN_PATH=$STAGING_AREA_DEPS/lib make $JOBS + else + ./configure + make $JOBS + fi + + if [ "$INSTALL" == "ON" ];then + if [ "$USE_STAGING" == "ON" ]; then make install; else sudo make install; fi + fi + popd +} + +build_qwt() { + echo "### Building qwt - branch $QWT_BRANCH" + pushd $STAGING_AREA/qwt + git clean -xdf + sed -i 's|/usr/local/qwt-$$QWT_VERSION-ma|/usr/local|g' qwtconfig.pri + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + + if [ "$USE_STAGING" == "ON" ]; then + $QMAKE_BIN INCLUDEPATH=$STAGING_AREA_DEPS/include LIBS=-L$STAGING_AREA_DEPS/lib qwt.pro + make $JOBS + if [ "$INSTALL" == "ON" ];then + make INSTALL_ROOT=$STAGING_AREA_DEPS install + fi + cp -r $STAGING_AREA_DEPS/usr/local/* $STAGING_AREA_DEPS/ + else + $QMAKE_BIN qwt.pro + make $JOBS + if [ "$INSTALL" == "ON" ];then + sudo make install + fi + fi + popd +} + +build_libtinyiiod() { + echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" + pushd $STAGING_AREA/libtinyiiod + CURRENT_BUILD_CMAKE_OPTS="-DBUILD_EXAMPLES=OFF" + build_with_cmake $1 + popd +} + +build_kddock () { + echo "### Building KDDockWidgets - version $KDDOCK_BRANCH" + pushd $STAGING_AREA/KDDockWidgets + CURRENT_BUILD_CMAKE_OPTS="" + build_with_cmake $1 + popd +} + +build_iio-emu() { + echo "### Building iio-emu - branch $IIOEMU_BRANCH" + pushd $STAGING_AREA/iio-emu + build_with_cmake $1 + popd +} + +build_scopy() { + echo "### Building scopy" + ls -la $SRC_DIR + pushd $SRC_DIR + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PLUGIN_TEST=ON \ + -DENABLE_TESTING=ON + " + build_with_cmake OFF + popd +} + +# +# Helper functions +# + +build_deps(){ + clone + build_libserialport ON + build_libiio ON + build_libad9361 ON + build_spdlog ON + build_libm2k ON + build_volk ON + build_gnuradio ON + build_grscopy ON + build_grm2k ON + build_qwt ON + build_libsigrokdecode ON + build_libtinyiiod ON + build_kddock ON + build_iio-emu ON +} + +configure_system(){ + install_packages + install_qt + build_deps +} + +for arg in $@; do + $arg +done diff --git a/ci/windows/.dockerignore b/ci/windows/.dockerignore new file mode 100644 index 0000000000..8c7b1197e9 --- /dev/null +++ b/ci/windows/.dockerignore @@ -0,0 +1,5 @@ +* +!build_and_create_installer.sh +!mingw_toolchain.sh +!windows_build_process.sh +!sigrokdecode-windows-fix.patch \ No newline at end of file diff --git a/ci/windows/README.md b/ci/windows/README.md new file mode 100644 index 0000000000..df18217aaa --- /dev/null +++ b/ci/windows/README.md @@ -0,0 +1,69 @@ +# Scopy MinGW Recipe + +Build Scopy for Windows using MinGW64 + +Scopy is a software oscilloscope and signal analysis toolset. [The official repository](https://github.com/analogdevicesinc/scopy) provides releases for Windows, Linux, macOS and Android. + + +## Building the Docker image +To build the Docker image just execute the command + +``` docker build --tag --isolation=hyperv --memory=16GB --file docker/Dockerfile .``` + +The Dockerfile is available in the docker folder. + +[MSYS2](https://www.msys2.org/) will be installed inside the image along with all dependencies that are required in order to build and package the Scopy app. + + +## Setting up the environment + +### Build prerequisites +- An IDE: + - [QTCreator](https://doc.qt.io/qtcreator/) + - [Visual Studio Code](https://code.visualstudio.com/download) + + - For development the [MSYS2](https://www.msys2.org/) shell is needed. + +### Setup dependencies + +1. Launch the mingw64 by executing the binary file from the location where the MSYS2 was installed + +2. Execute the following script in the mingw64 terminal + + ```bash + windows_build_process.sh + ``` + +- Optionally install GDB for build debugging + ```bash + pacman --noconfirm -S mingw-w64-x86_64-gdb + ``` + +### Building inside MinGW64 shell + +1. Enter the root directory of the repository +2. Create a build directory ```mkdir build``` +3. Enter the build directory ```cd build``` +4. Execute ```cmake ../``` +5. Execute ``` make``` + +Inside the build folder a binary file named Scopy.exe will be generated. This is the starting file of the application. + +### Building in Visual Studio Code + +1. In VS Code, install [**C/C++ Extension Pack**](vscode:extension/ms-vscode.cpptools-extension-pack) + +2. Open Scopy folder in VS Code + + > When opening the Scopy folder for the first time, a popup may appear to ask you to trust the authors of the files in this folder. Simply click on **`Yes, I trust the authors`** + +3. In VS Code, go to the toolbar on your left and locate the CMake tool. On the **`PROJECT OUTLINE`** dropdown, click on the icon for *Configure All Projects*. This will instruct CMake to generate the files necessary for building the source code. + +4. Under the **`PROJECT STATUS`** dropdown in the CMake tool, click on the icon for *Build* to build the project. + +## Generating the Scopy installer +The installer is created using [Inno Setup](https://jrsoftware.org/isinfo.php). This tool is already installed in the Docker Image, but to install Inno Setup locally use this [installer](https://files.jrsoftware.org/is/6/innosetup-6.2.2.exe). + +First follow the **Setup dependencies** part. + +In order to build Scopy and create an installer locally use the **build_and_create_installer.sh** bash script executed from the mingw shell. diff --git a/ci/windows/build_and_create_installer.sh b/ci/windows/build_and_create_installer.sh new file mode 100755 index 0000000000..3a7ccb649b --- /dev/null +++ b/ci/windows/build_and_create_installer.sh @@ -0,0 +1,208 @@ +#!/bin/bash +if [ "$CI_SCRIPT" == "ON" ]; + then + set -ex + export WORKDIR=$HOME + SRC_FOLDER=$WORKDIR/scopy + else + set -x + SRC_FOLDER=$(git rev-parse --show-toplevel) + export WORKDIR=$SRC_FOLDER +fi + +BUILD_TARGET=x86_64 +ARCH_BIT=64 + +## Set STAGING +USE_STAGING=OFF +## + +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +source $SCRIPT_DIR/mingw_toolchain.sh $USE_STAGING + +BUILD_FOLDER=$WORKDIR/build_$ARCH +ARTIFACT_FOLDER=$SRC_FOLDER/artifacts +export DEST_FOLDER=$ARTIFACT_FOLDER/scopy-$ARCH # the export is needed for the packaging step +DEBUG_FOLDER=$ARTIFACT_FOLDER/debug-$ARCH +PYTHON_FILES=$STAGING_DIR/lib/python3.* +EMU_BUILD_FOLDER=$WORKDIR/iio-emu/build +REGMAP_XMLS=$BUILD_FOLDER/plugins/regmap/xmls +DAC_WAVEFORM_CSV=$SRC_FOLDER/plugins/dac/res/csv +EMU_XMLS=$BUILD_FOLDER/plugins/emu_xml +EMU_CONFIG=$SRC_FOLDER/resources/scopy_emu_options_config.json + +download_tools() { + mkdir -p $STAGING_AREA + + # check if wget2 is installed + pacman -Qs mingw-w64-x86_64-wget2 > /dev/null || pacman -S --noconfirm mingw-w64-x86_64-wget2 + + pushd $STAGING_AREA + if [ ! -f windres.exe ]; then + wget2 http://swdownloads.analog.com/cse/build/windres.exe.gz + gunzip windres.exe.gz + fi + + if [ ! -f dpinst.zip ]; then + wget2 http://swdownloads.analog.com/cse/m1k/drivers/dpinst.zip + unzip "dpinst.zip" + fi + + if [ ! -f dfu-util.zip ]; then + wget2 http://swdownloads.analog.com/cse/m1k/drivers/dfu-util.zip + unzip "dfu-util.zip" + fi + + if [ ! -f cv2pdb-dlls.zip ]; then + wget2 https://swdownloads.analog.com/cse/scopydeps/cv2pdb-dlls.zip + unzip "cv2pdb-dlls.zip" + fi + popd +} + +build_scopy(){ + echo "### Building Scopy" + [ -f $HOME/build-status ] && cp $HOME/build-status $SRC_FOLDER/build-status + mkdir -p $BUILD_FOLDER + cd $BUILD_FOLDER + $CMAKE $RC_COMPILER_OPT -DPYTHON_EXECUTABLE=$STAGING_DIR/bin/python3.exe \ + -DENABLE_TESTING=OFF \ + $SRC_FOLDER + $MAKE_BIN $JOBS + ls -la $BUILD_FOLDER +} + +build_iio-emu(){ + echo "### Building IIO-EMU" + if [ ! -d "$WORKDIR/iio-emu" ]; then + git clone https://github.com/analogdevicesinc/iio-emu $WORKDIR/iio-emu + fi + + mkdir -p $EMU_BUILD_FOLDER + cd $EMU_BUILD_FOLDER + $CMAKE -DBUILD_TOOLS=ON ../ + $MAKE_BIN $JOBS + + pushd $WORKDIR/iio-emu + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" >> $BUILD_STATUS_FILE + popd +} + +deploy_app(){ + echo "### Deploying application and dependencies" + if [ -d $DEST_FOLDER ]; then + rm -rf $DEST_FOLDER + fi + rm -rf $DEST_FOLDER + mkdir -p $DEST_FOLDER + cp $BUILD_FOLDER/Scopy.exe $DEST_FOLDER/ + cp $BUILD_FOLDER/qt.conf $DEST_FOLDER/ + cp $BUILD_FOLDER/Scopy-console.exe $DEST_FOLDER/ + + mkdir $DEST_FOLDER/resources + $STAGING_DIR/bin/windeployqt.exe --dir $DEST_FOLDER --no-translations --no-system-d3d-compiler --no-compiler-runtime --no-quick-import --opengl --printsupport $BUILD_FOLDER/Scopy.exe + + cp -r $STAGING_DIR/share/libsigrokdecode/decoders $DEST_FOLDER/ + + pushd $STAGING_DIR/bin + DLL_DEPS=$(cat $SRC_FOLDER/ci/windows/mingw_dll_deps) + cp -n $DLL_DEPS $DEST_FOLDER/ + cp -n iio_*.exe $DEST_FOLDER/ + popd + + cp $EMU_BUILD_FOLDER/iio-emu.exe $DEST_FOLDER + cp -r $PYTHON_FILES $DEST_FOLDER + cp $BUILD_FOLDER/windows/scopy-$ARCH_BIT.iss $DEST_FOLDER + cp -v $BUILD_FOLDER/libscopy-*.dll $DEST_FOLDER + + PLUGINS_DLL=$(find $BUILD_FOLDER/plugins/plugins -type f -name "*.dll") + mkdir -p $DEST_FOLDER/plugins + cp -v $PLUGINS_DLL $DEST_FOLDER/plugins + + TRANSLATIONS_QM=$(find $BUILD_FOLDER/translations -type f -name "*.qm") + mkdir -p $DEST_FOLDER/translations + cp $TRANSLATIONS_QM $DEST_FOLDER/translations + + cp -R $BUILD_FOLDER/style $DEST_FOLDER/style + + if [ -d $REGMAP_XMLS ]; then + cp -r $REGMAP_XMLS $DEST_FOLDER/plugins + fi + cp -r $DAC_WAVEFORM_CSV $DEST_FOLDER/plugins + cp -r $EMU_XMLS $DEST_FOLDER/plugins + mkdir -p $DEST_FOLDER/plugins/resources + cp $EMU_CONFIG $DEST_FOLDER/plugins/resources +} + +extract_debug_symbols(){ + echo "### Duplicating unstripped bundle" + rm -rf $DEBUG_FOLDER + mkdir -p $DEBUG_FOLDER + cp -r $DEST_FOLDER/* $DEBUG_FOLDER/ + echo "### Stripping bundle for installer" + /$MINGW_VERSION/bin/strip.exe --strip-debug --strip-unneeded $DEST_FOLDER/*.exe + /$MINGW_VERSION/bin/strip.exe --strip-debug --strip-unneeded $DEST_FOLDER/*.dll + /$MINGW_VERSION/bin/strip.exe --strip-debug --strip-unneeded $DEST_FOLDER/plugins/*.dll +} + +bundle_drivers(){ + echo "### Bundling drivers" + cp -R $SRC_FOLDER/windows/drivers $DEST_FOLDER + if [[ $ARCH_BIT == "64" ]]; then + cp -R $STAGING_AREA/dfu-util-static-amd64.exe $DEST_FOLDER/drivers/dfu-util.exe + cp -R $STAGING_AREA/dpinst_amd64.exe $DEST_FOLDER/drivers/dpinst.exe + else + cp -R $STAGING_AREA/dfu-util-static.exe $DEST_FOLDER/drivers/dfu-util.exe + cp -R $STAGING_AREA/dpinst.exe $DEST_FOLDER/drivers/dpinst.exe + fi +} + +create_installer() { + echo "### Creating installer" + pushd $ARTIFACT_FOLDER + PATH="/c/innosetup:/c/Program Files (x86)/Inno Setup 6:$PATH" + iscc //p $BUILD_FOLDER/windows/scopy-$ARCH_BIT.iss + + if [ "$CI_SCRIPT" == "ON" ]; then + mv $WORKDIR/scopy-$ARCH_BIT-setup.exe $ARTIFACT_FOLDER + zip scopy-$ARCH_BIT-setup.zip scopy-$ARCH_BIT-setup.exe + pushd $DEBUG_FOLDER + zip -r $ARTIFACT_FOLDER/debug-x86_64.zip . + popd + pushd $DEST_FOLDER + zip -r $ARTIFACT_FOLDER/scopy-x86_64.zip . + popd + else + mv $WORKDIR/scopy-$ARCH_BIT-setup.exe $ARTIFACT_FOLDER + fi + + ls -la $ARTIFACT_FOLDER + echo "Done. Artifacts generated in $ARTIFACT_FOLDER" + popd +} + +# move the staging folder that contains the tools needed for the build to the known location +move_tools(){ + [ -d /home/docker/staging ] && mv /home/docker/staging $STAGING_AREA || echo "Staging folder not found or already moved" + if [ ! -d $STAGING_AREA ]; then + echo "Missing tools folder, downloading now" + download_tools + fi +} + + +run_workflow(){ + [ "$CI_SCRIPT" == "ON" ] && move_tools || download_tools + build_scopy + build_iio-emu + deploy_app + bundle_drivers + extract_debug_symbols + create_installer +} + +# run_workflow + +for arg in $@; do + $arg +done diff --git a/ci/windows/docker/Dockerfile b/ci/windows/docker/Dockerfile new file mode 100644 index 0000000000..0b75f683a6 --- /dev/null +++ b/ci/windows/docker/Dockerfile @@ -0,0 +1,49 @@ +# select as base image matching your host to get process isolation +FROM mcr.microsoft.com/windows/servercore:ltsc2019 + +SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"] +# this creates an empty file named recycle.bin which essentialy disables the recycle bin +# this hack is needed because msys seems to create some files with wierd names in recycle bin +# that freeze the docker layer when commiting +# dirty hack +RUN echo . > 'C:\$RECYCLE.BIN'; + +# install msys2 and innosetup +RUN [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; \ + Invoke-WebRequest -UseBasicParsing -uri "https://github.com/msys2/msys2-installer/releases/download/2023-01-27/msys2-base-x86_64-20230127.sfx.exe" -OutFile msys2.exe; \ + .\msys2.exe -y -oC:\\; \ + Remove-Item msys2.exe; \ + Invoke-WebRequest -UseBasicParsing -uri "https://files.jrsoftware.org/is/6/innosetup-6.2.2.exe" -OutFile innosetup-6.2.2.exe; \ + .\innosetup-6.2.2.exe /VERYSILENT /SP- /SUPPRESSMSGBOXES /NORESTART /NOCANCEL /LOG=C:\iss.log /DIR=C:\innosetup; + +RUN 'mkdir C:/msys64/home/docker/ && cd C:/msys64/home/docker/' + +SHELL ["cmd", "/S", "/C"] +# set envvars PATH HOME CHERE_INVOKING MSYS_SYSTEM - https://www.msys2.org/docs/ci/#other-systems +RUN setx PATH C:\msys64\bin;C:\msys64\mingw64\bin;C:\msys64\usr\bin;%PATH% & \ + setx HOME C:\msys64\home\docker & \ + setx CHERE_INVOKING yes & \ + setx MSYSTEM MINGW64 + +ENV CI_SCRIPT=ON + +RUN C:\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Syyu" +RUN C:\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Sy msys2-keyring" +RUN C:\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Su" +RUN C:\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Syuu" +RUN C:\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Syuu" + +WORKDIR C:\\msys64\\home\\docker + +COPY mingw_toolchain.sh C:/msys64/home/docker/scripts/mingw_toolchain.sh +COPY windows_build_process.sh C:/msys64/home/docker/scripts/windows_build_process.sh +COPY build_and_create_installer.sh C:/msys64/home/docker/scripts/build_and_create_installer.sh +COPY sigrokdecode-windows-fix.patch C:/msys64/home/docker/scripts/sigrokdecode-windows-fix.patch + +RUN C:\msys64\usr\bin\bash.exe -lc "C:/msys64/home/docker/scripts/windows_build_process.sh" +RUN C:\msys64\usr\bin\bash.exe -lc "C:/msys64/home/docker/scripts/build_and_create_installer.sh download_tools" +RUN C:\msys64\usr\bin\bash.exe -lc "mv C:/msys64/home/docker/scripts/staging C:/msys64/home/docker/staging" +RUN C:\msys64\usr\bin\bash.exe -lc "mv C:/msys64/home/docker/scripts/build-status C:/msys64/home/docker/build-status" +RUN C:\msys64\usr\bin\bash.exe -lc "pacman --noconfirm -Scc " +RUN rmdir /s /q C:\msys64\home\docker\scripts +RUN rmdir /s /q C:\msys64\home\docker\staging diff --git a/ci/windows/mingw_dll_deps b/ci/windows/mingw_dll_deps new file mode 100644 index 0000000000..32d06e617a --- /dev/null +++ b/ci/windows/mingw_dll_deps @@ -0,0 +1 @@ +libspdlog.dll* libglibmm-*.dll libsigrokdecode-*.dll libgcc_s_*.dll libstdc++-*.dll Qt5Core.dll libboost_thread-mt.dll libgnuradio-analog.dll libgnuradio-blocks.dll libgnuradio-fft.dll libwinpthread-*.dll libgnuradio-filter.dll libgnuradio-m2k.dll libgnuradio-pmt.dll libgnuradio-runtime.dll libgnuradio-scopy.dll libgnuradio-iio.dll libad9361.dll libiio.dll Qt5Qml.dll Qt5Xml.dll qwt.dll libglib-*.dll libgmodule-*.dll libsigc-*.dll libgobject-*.dll libpython*.dll libboost_filesystem-mt.dll libboost_chrono-mt.dll libboost_program_options-mt.dll libgmp-*.dll libusb-*.dll libxml2-*.dll Qt5Network.dll Qt5OpenGL.dll Qt5Svg.dll Qt5PrintSupport.dll libintl-*.dll libiconv-*.dll zlib1.dll Qt5Gui.dll tinyiiod.dll libm2k.dll libvolk.dll libfftw3f*.dll Qt5Widgets.dll libicudt*.dll libffi-*.dll liblzma-5.dll libdouble-conversion.dll libicuin*.dll libicuuc*.dll libpcre2-*.dll libzstd.dll libharfbuzz-*.dll libpng16-*.dll liborc-*.dll libfreetype-*.dll libgraphite2.dll libbrotlidec.dll libbz2-*.dll libbrotlicommon.dll libcrypto*.dll libssl*.dll libmd4c.dll libserialport-0.dll diff --git a/ci/windows/mingw_toolchain.sh b/ci/windows/mingw_toolchain.sh new file mode 100644 index 0000000000..f795b84daa --- /dev/null +++ b/ci/windows/mingw_toolchain.sh @@ -0,0 +1,84 @@ +#!/usr/bin/bash.exe + +set -ex +# get the full directory path of the script +export WORKFOLDER=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +BUILD_STATUS_FILE=$WORKFOLDER/build-status + +LIBSERIALPORT_BRANCH=scopy-v2 +LIBIIO_VERSION=v0.26 +LIBAD9361_BRANCH=main +LIBM2K_BRANCH=main +SPDLOG_BRANCH=v1.x +LIBSNDFILE_BRANCH=1.2.2 +VOLK_BRANCH=main +GNURADIO_BRANCH=scopy2-maint-3.10 +GRSCOPY_BRANCH=3.10 +GRM2K_BRANCH=main +LIBSIGROKDECODE_BRANCH=master +QWT_BRANCH=qwt-multiaxes-updated +LIBTINYIIOD_BRANCH=master +IIOEMU_BRANCH=master +KDDOCK_BRANCH=2.1 + +STAGING_AREA=$WORKFOLDER/staging +MINGW_VERSION=mingw64 +ARCH=x86_64 + +USE_STAGING=$1 +if [ ! -z "$USE_STAGING" ] && [ "$USE_STAGING" == "ON" ] + then + echo -- USING STAGING FOLDER: $STAGING_AREA_DEPS + export USE_STAGING="ON" + export STAGING_AREA_DEPS=$STAGING_AREA/dependencies + export STAGING_DIR=${WORKFOLDER}/${STAGING_AREA_DEPS}/${MINGW_VERSION} + export PACMAN="pacman -r $STAGING_DIR --noconfirm --needed" + else + echo -- NO STAGING: INSTALLING IN SYSTEM + export USE_STAGING="OFF" + export STAGING_DIR=/${MINGW_VERSION} + export PACMAN="pacman --noconfirm --needed" +fi + +RC_COMPILER_OPT="-DCMAKE_RC_COMPILER=/mingw64/bin/windres.exe" +PATH="/bin:$STAGING_DIR/bin:$WORKFOLDER/cv2pdb:/c/Program Files (x86)/Inno Setup 6:/c/innosetup/:/bin:/usr/bin:${STAGING_DIR}/bin:/c/Program\ Files/Git/cmd:/c/Windows/System32:/c/Program\ Files/Appveyor/BuildAgent:$PATH" +QMAKE=${STAGING_DIR}/bin/qmake +PKG_CONFIG_PATH=$STAGING_DIR/lib/pkgconfig +CC=${STAGING_DIR}/bin/${ARCH}-w64-mingw32-gcc.exe +CXX=${STAGING_DIR}/bin/${ARCH}-w64-mingw32-g++.exe +JOBS="-j22" +MAKE_BIN=/usr/bin/make.exe +MAKE_CMD="$MAKE_BIN $JOBS" +export CMAKE_GENERATOR="Unix Makefiles" + +export CMAKE_OPTS=( \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + -DCMAKE_C_COMPILER:FILEPATH=${CC} \ + -DCMAKE_CXX_COMPILER:FILEPATH=${CXX} \ + -DCMAKE_MAKE_PROGRAM:FILEPATH=${MAKE_BIN}\ + -DPKG_CONFIG_EXECUTABLE=${STAGING_DIR}/bin/pkg-config.exe \ + -DCMAKE_MODULE_PATH=$STAGING_DIR \ + -DCMAKE_PREFIX_PATH=$STAGING_DIR/lib/cmake \ + -DCMAKE_STAGING_PREFIX=$STAGING_DIR \ + -DCMAKE_INSTALL_PREFIX=$STAGING_DIR \ +) + +export CMAKE="${STAGING_DIR}/bin/cmake ${CMAKE_OPTS[@]} " + +AUTOCONF_OPTS="--prefix=$STAGING_DIR \ + --host=${ARCH}-w64-mingw32 \ + --enable-shared \ + --disable-static" + +echo -- BUILD_STATUS_FILE:$BUILD_STATUS_FILE +echo -- MAKE_BIN:$MAKE_BIN +echo -- STAGING_DIR:$STAGING_DIR +echo -- STAGING_AREA:$STAGING_AREA +echo -- MINGW_VERSION:$MINGW_VERSION +echo -- TARGET ARCH:$ARCH +echo -- PATH:$PATH +echo -- USING CMAKE COMMAND +echo $CMAKE +echo diff --git a/ci/windows/sigrokdecode-windows-fix.patch b/ci/windows/sigrokdecode-windows-fix.patch new file mode 100644 index 0000000000..48c0981608 --- /dev/null +++ b/ci/windows/sigrokdecode-windows-fix.patch @@ -0,0 +1,21 @@ +commit 1b13802e324f00f4e840eff7b0f3b6761fffa1a1 +Author: Bindea Cristian +Date: Thu Mar 2 16:07:15 2023 +0200 + + Subject: [PATCH] Update public functions visibility on Windows + + Signed-off-by: Bindea Cristian + +diff --git a/libsigrokdecode.h b/libsigrokdecode.h +index ea17cb8..acc2cbc 100644 +--- a/libsigrokdecode.h ++++ b/libsigrokdecode.h +@@ -112,6 +112,8 @@ enum srd_loglevel { + # else + # define SRD_API extern + # endif ++#elif defined(LIBSIGROKDECODE_EXPORT) ++# define SRD_API __declspec(dllexport) + #else + # define SRD_API __attribute__((visibility("default"))) + #endif diff --git a/ci/windows/windows_build_process.sh b/ci/windows/windows_build_process.sh new file mode 100755 index 0000000000..c70dfa8986 --- /dev/null +++ b/ci/windows/windows_build_process.sh @@ -0,0 +1,399 @@ +#!/usr/bin/bash.exe +set -xe +# get the full directory path of the script +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +## Set STAGING +USE_STAGING=OFF +## + +source $SCRIPT_DIR/mingw_toolchain.sh $USE_STAGING + +install_packages() { + SYSTEM_PKGS="\ + git\ + svn\ + vim\ + unzip\ + zip\ + " + TOOLS_PKGS="\ + mingw-w64-${ARCH}-wget2\ + mingw-w64-${ARCH}-cmake\ + mingw-w64-${ARCH}-gcc\ + mingw-w64-${ARCH}-python3\ + mingw-w64-${ARCH}-python-mako\ + mingw-w64-${ARCH}-python-six\ + mingw-w64-${ARCH}-make\ + mingw-w64-${ARCH}-doxygen\ + mingw-w64-${ARCH}-pcre2\ + base-devel\ + mingw-w64-${ARCH}-autotools\ + libtool\ + mingw-w64-${ARCH}-boost + " + PACMAN_SYNC_DEPS="\ + mingw-w64-${ARCH}-fftw\ + mingw-w64-${ARCH}-orc\ + mingw-w64-${ARCH}-libxml2\ + mingw-w64-${ARCH}-libzip\ + mingw-w64-${ARCH}-fftw\ + mingw-w64-${ARCH}-libffi\ + mingw-w64-${ARCH}-glib2\ + mingw-w64-${ARCH}-glibmm\ + mingw-w64-${ARCH}-doxygen\ + mingw-w64-${ARCH}-qt5\ + mingw-w64-${ARCH}-angleproject\ + mingw-w64-${ARCH}-zlib\ + mingw-w64-${ARCH}-breakpad\ + mingw-w64-${ARCH}-libusb + " + + if [ "$USE_STAGING" == "ON" ]; then + mkdir -p $STAGING_DIR/var/lib/pacman/local + mkdir -p $STAGING_DIR/var/lib/pacman/sync + $PACMAN -Syuu bash filesystem mintty pacman + fi + + pacman --noconfirm -S $SYSTEM_PKGS + $PACMAN -S $TOOLS_PKGS + $PACMAN -S $PACMAN_SYNC_DEPS +} + +clone() { + echo "#######CLONE#######" + mkdir -p $STAGING_AREA + pushd $STAGING_AREA + [ -d 'libserialport' ] || git clone --recursive https://github.com/cseci/libserialport -b $LIBSERIALPORT_BRANCH libserialport + [ -d 'libiio' ] || git clone --recursive https://github.com/analogdevicesinc/libiio.git -b $LIBIIO_VERSION libiio + [ -d 'libad9361' ] || git clone --recursive https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH libad9361 + [ -d 'libm2k' ] || git clone --recursive https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH libm2k + [ -d 'spdlog' ] || git clone --recursive https://github.com/gabime/spdlog.git -b $SPDLOG_BRANCH spdlog + [ -d 'libsndfile' ] || git clone --recursive https://github.com/libsndfile/libsndfile -b $LIBSNDFILE_BRANCH libsndfile + [ -d 'gr-scopy' ] || git clone --recursive https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH gr-scopy + [ -d 'gr-m2k' ] || git clone --recursive https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH gr-m2k + [ -d 'volk' ] || git clone --recursive https://github.com/gnuradio/volk.git -b $VOLK_BRANCH volk + [ -d 'gnuradio' ] || git clone --recursive https://github.com/analogdevicesinc/gnuradio.git -b $GNURADIO_BRANCH gnuradio + [ -d 'qwt' ] || git clone --recursive https://github.com/cseci/qwt.git -b $QWT_BRANCH qwt + [ -d 'libsigrokdecode' ] || git clone --recursive https://github.com/sigrokproject/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH libsigrokdecode + [ -d 'libtinyiiod' ] || git clone --recursive https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH libtinyiiod + [ -d 'iio-emu' ] || git clone --recursive https://github.com/analogdevicesinc/iio-emu -b $IIOEMU_BRANCH iio-emu + [ -d 'KDDockWidgets' ] || git clone --recursive https://github.com/KDAB/KDDockWidgets.git -b $KDDOCK_BRANCH KDDockWidgets + popd +} + +create_build_status_file() { + touch $BUILD_STATUS_FILE + echo "Scopy2-MinGW" >> $BUILD_STATUS_FILE + echo "Docker image built on $(date)" >> $BUILD_STATUS_FILE + echo "Deps installed using pacman" >> $BUILD_STATUS_FILE + echo "" >> $BUILD_STATUS_FILE + echo "All explicitly installed packages on build machine" >> $BUILD_STATUS_FILE + echo "" >> $BUILD_STATUS_FILE + pacman --noconfirm -Qe >> $BUILD_STATUS_FILE + echo "" >> $BUILD_STATUS_FILE + echo "Deps built from sources" >> $BUILD_STATUS_FILE + echo "" >> $BUILD_STATUS_FILE +} + +clean_build_dir() { + git clean -xdf + rm -rf $BUILD_FOLDER + mkdir $BUILD_FOLDER + cd $BUILD_FOLDER +} + +build_with_cmake() { + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + pushd $STAGING_AREA/$CURRENT_BUILD + BUILD_FOLDER=$PWD/build + clean_build_dir + eval $CURRENT_BUILD_POST_CLEAN + eval $CURRENT_BUILD_PATCHES + + $CMAKE $CURRENT_BUILD_CMAKE_OPTS $STAGING_AREA/$CURRENT_BUILD + eval $CURRENT_BUILD_POST_CMAKE + make $JOBS + if [ "$INSTALL" == "ON" ];then + make install + fi + eval $CURRENT_BUILD_POST_MAKE + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE + + #clean deps folder + if [ "$INSTALL" == "ON" ] && [ "$CI_SCRIPT" == "ON" ];then + git clean -xdf + fi + + popd + + # clear vars + CURRENT_BUILD_CMAKE_OPTS="" + CURRENT_BUILD_POST_CLEAN="" + CURRENT_BUILD_PATCHES="" + CURRENT_BUILD_POST_CMAKE="" + CURRENT_BUILD_POST_MAKE="" + CURRENT_BUILD="" +} + +build_libserialport(){ + CURRENT_BUILD=libserialport + pushd $STAGING_AREA/$CURRENT_BUILD + git clean -xdf + + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + + ./autogen.sh + [ "$USE_STAGING" == "ON" ] && ./configure --prefix $STAGING_AREA_DEPS ${AUTOCONF_OPTS} || ./configure ${AUTOCONF_OPTS} + make $JOBS + [ "$INSTALL" == "ON" ] && make install + + #clean deps folder + if [ "$INSTALL" == "ON" ] && [ "$CI_SCRIPT" == "ON" ];then + git clean -xdf + fi + + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE + popd +} + +build_libiio() { + CURRENT_BUILD=libiio + CURRENT_BUILD_CMAKE_OPTS="\ + ${RC_COMPILER_OPT}\ + -DWITH_USB_BACKEND:BOOL=ON\ + -DWITH_SERIAL_BACKEND:BOOL=ON\ + -DCSHARP_BINDINGS:BOOL=OFF\ + -DPYTHON_BINDINGS:BOOL=OFF\ + -DHAVE_DNS_SD:BOOL=ON\ + -DENABLE_IPV6:BOOL=OFF\ + -DWITH_EXAMPLES:BOOL=ON\ + " + build_with_cmake $1 +} + +build_libad9361() { + echo "### Building libad9361 - branch $LIBAD9361_BRANCH" + CURRENT_BUILD=libad9361 + build_with_cmake $1 +} + +build_spdlog() { + # [ -f "/usr/bin/x86_64-w64-mingw32-windres.exe" ] && rm -v /usr/bin/x86_64-w64-mingw32-windres.exe + # ln -s /usr/bin/windres.exe /usr/bin/x86_64-w64-mingw32-windres.exe + CURRENT_BUILD=spdlog + CURRENT_BUILD_CMAKE_OPTS="\ + -DSPDLOG_BUILD_SHARED=ON\ + -DSPDLOG_BUILD_EXAMPLE=OFF\ + " + build_with_cmake $1 +} + +build_libm2k() { + CURRENT_BUILD=libm2k + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PYTHON=OFF\ + -DENABLE_CSHARP=OFF\ + -DBUILD_EXAMPLES=OFF\ + -DENABLE_TOOLS=ON\ + -DINSTALL_UDEV_RULES=OFF\ + " + build_with_cmake $1 +} + +build_libsndfile() { + CURRENT_BUILD=libsndfile + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_EXTERNAL_LIBS=OFF\ + -DENABLE_MPEG=OFF\ + -DBUILD_PROGRAMS=OFF\ + -DBUILD_EXAMPLES=OFF\ + -DENABLE_CPACK=OFF\ + -DBUILD_SHARED_LIBS=OFF\ + -DBUILD_TESTING=OFF" + build_with_cmake $1 +} + +build_volk() { + CURRENT_BUILD=volk + CURRENT_BUILD_POST_CLEAN="git submodule update --init ../cpu_features" + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_MODTOOL=OFF\ + -DENABLE_TESTING=OFF\ + -DPYTHON_EXECUTABLE=$STAGING_DIR/bin/python3.exe\ + -DGR_PYTHON_DIR==$STAGING_DIR/lib/python3.10/site-packages\ + " + build_with_cmake $1 + +} + +build_gnuradio() { + CURRENT_BUILD=gnuradio + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_DEFAULT=OFF\ + -DENABLE_GNURADIO_RUNTIME=ON\ + -DENABLE_GR_ANALOG=ON\ + -DENABLE_GR_BLOCKS=ON\ + -DENABLE_GR_FFT=ON\ + -DENABLE_GR_FILTER=ON\ + -DENABLE_VOLK=ON\ + -DENABLE_GR_IIO=ON\ + -DENABLE_POSTINSTALL=OFF\ + -DCMAKE_C_FLAGS=-fno-asynchronous-unwind-tables\ + -DPYTHON_EXECUTABLE=$STAGING_DIR/bin/python3.exe\ + -DGR_PYTHON_DIR==$STAGING_DIR/lib/python3.10/site-packages\ + " + build_with_cmake $1 +} + +build_grscopy() { + CURRENT_BUILD=gr-scopy + CURRENT_BUILD_CMAKE_OPTS="\ + -DPYTHON_EXECUTABLE=$STAGING_DIR/bin/python3.exe\ + -DGR_PYTHON_DIR==$STAGING_DIR/lib/python3.10/site-packages\ + " + build_with_cmake $1 +} + +build_grm2k() { + CURRENT_BUILD=gr-m2k + CURRENT_BUILD_CMAKE_OPTS="\ + -DPYTHON_EXECUTABLE=$STAGING_DIR/bin/python3.exe\ + -DGR_PYTHON_DIR==$STAGING_DIR/lib/python3.10/site-packages\ + " + build_with_cmake $1 +} + +build_qwt() { + echo "### Building qwt - branch $QWT_BRANCH" + CURRENT_BUILD=qwt + pushd $STAGING_AREA/$CURRENT_BUILD + git clean -xdf + +patch -p1 <<-EOF +--- a/qwtconfig.pri ++++ b/qwtconfig.pri +@@ -24,7 +24,7 @@ unix { + } + + win32 { +- QWT_INSTALL_PREFIX = "" ++ QWT_INSTALL_PREFIX = "/mingw64" + # QWT_INSTALL_PREFIX = C:/Qwt-\$\$QWT_VERSION-dev-qt-\$\$QT_VERSION + } + +EOF + + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + + if [ "$USE_STAGING" == "ON" ] + then + $QMAKE INCLUDEPATH=$STAGING_AREA_DEPS/include LIBS=-L$STAGING_AREA_DEPS/lib qwt.pro + make $JOBS + if [ "$INSTALL" == "ON" ];then + make INSTALL_ROOT=$STAGING_AREA_DEPS install + fi + cp -r $STAGING_AREA_DEPS/usr/local/* $STAGING_AREA_DEPS/ + else + $QMAKE qwt.pro + make $JOBS + make install + fi + + cp $STAGING_DIR/lib/qwt.dll $STAGING_DIR/bin/qwt.dll + + #clean deps folder + if [ "$INSTALL" == "ON" ] && [ "$CI_SCRIPT" == "ON" ];then + git clean -xdf + fi + + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE + popd +} + +build_libsigrokdecode() { + echo "### Building libsigrokdecode - branch $LIBSIGROKDECODE_BRANCH" + CURRENT_BUILD=libsigrokdecode + pushd $STAGING_AREA/$CURRENT_BUILD + git reset --hard + git clean -xdf + patch -p1 < ${WORKFOLDER}/sigrokdecode-windows-fix.patch + ./autogen.sh + + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + + if [ "$USE_STAGING" == "ON" ] + then + CPPFLAGS="-DLIBSIGROKDECODE_EXPORT=1" ./configure --prefix $STAGING_AREA_DEPS ${AUTOCONF_OPTS} + LD_RUN_PATH=$STAGING_AREA_DEPS/lib make $JOBS + else + CPPFLAGS="-DLIBSIGROKDECODE_EXPORT=1" ./configure ${AUTOCONF_OPTS} + make $JOBS + fi + + if [ "$INSTALL" == "ON" ];then + make install + fi + + #clean deps folder + if [ "$INSTALL" == "ON" ] && [ "$CI_SCRIPT" == "ON" ];then + git clean -xdf + fi + + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE + popd +} + +build_libtinyiiod() { + echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" + CURRENT_BUILD=libtinyiiod + CURRENT_BUILD_CMAKE_OPTS="-DBUILD_EXAMPLES=OFF" + build_with_cmake $1 +} + +build_kddock () { + echo "### Building KDDockWidgets - version $KDDOCK_BRANCH" + pushd $STAGING_AREA/KDDockWidgets + CURRENT_BUILD_CMAKE_OPTS="" + build_with_cmake $1 + popd +} + +# +# Helper functions +# + +build_deps() { + install_packages + create_build_status_file + clone + build_libserialport ON + build_libiio ON + build_libad9361 ON + build_libm2k ON + build_spdlog ON + build_libsndfile ON + build_volk ON + build_gnuradio ON + build_grscopy ON + build_grm2k ON + build_qwt ON + build_libsigrokdecode ON + build_libtinyiiod ON + build_kddock ON +} + + +for arg in $@; do + $arg +done + +build_deps diff --git a/ci/x86_64/.dockerignore b/ci/x86_64/.dockerignore new file mode 100644 index 0000000000..4f0c360ef3 --- /dev/null +++ b/ci/x86_64/.dockerignore @@ -0,0 +1,2 @@ +* +!x86-64_appimage_process.sh diff --git a/CI/x86_64/AppRun b/ci/x86_64/AppRun similarity index 100% rename from CI/x86_64/AppRun rename to ci/x86_64/AppRun diff --git a/ci/x86_64/copy-deps.sh b/ci/x86_64/copy-deps.sh new file mode 100755 index 0000000000..1653cb6127 --- /dev/null +++ b/ci/x86_64/copy-deps.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +set -e +SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || \ +SRC_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && cd ../../ && pwd ) + +BINARY=$1 +LOCATION=$2 +LIBS_ARRAY=() +BLACKLISTED=($(wget --quiet https://raw.githubusercontent.com/probonopd/AppImages/master/excludelist -O - | sort | uniq | cut -d '#' -f 1 | grep -v "^#.*" | grep "[^-\s]")) + +export LD_LIBRARY_PATH="${APP_DIR}/usr/lib:${SRC_DIR}/build:$LD_LIBRARY_PATH" +run_ldd(){ + for library in $(ldd "$1" | cut -d '>' -f 2 | awk '{print $1}') + do + # check if the library exists at that path and if it was processed already or blacklisted + if ! [[ "${BLACKLISTED[*]}" =~ "${library##*/}" ]]; then + if [ -f "${library}" ] && ! [[ "${LIBS_ARRAY[*]}" =~ "${library}" ]]; then + LIBS_ARRAY+=("${library}") + echo "---Added new lib: ${library}" + if [ ! -f "${LOCATION}"/"${library##*/}" ]; then + cp "${library}" "${LOCATION}" + [ -L "${library}" ] && cp "$(realpath "${library}")" "${LOCATION}" + strip --strip-unneeded "${LOCATION}"/"${library##*/}" + fi + run_ldd "${library}" + fi + fi + done +} + +for arg in $BINARY; do + run_ldd "${arg}" +done diff --git a/ci/x86_64/create_docker_image.sh b/ci/x86_64/create_docker_image.sh new file mode 100755 index 0000000000..70cf8a3ea7 --- /dev/null +++ b/ci/x86_64/create_docker_image.sh @@ -0,0 +1,8 @@ +#!/bin/bash -ex + +SRC_SCRIPT=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) +pushd $SRC_SCRIPT + +docker build -t cristianbindea/scopy2-x86_64-appimage:testing -f docker/Dockerfile . + +popd diff --git a/ci/x86_64/docker/Dockerfile b/ci/x86_64/docker/Dockerfile new file mode 100644 index 0000000000..a52e9097cf --- /dev/null +++ b/ci/x86_64/docker/Dockerfile @@ -0,0 +1,44 @@ +FROM ubuntu:20.04 +SHELL ["/bin/bash", "-c"] + +ARG USER=runner +ENV DEBIAN_FRONTEND=noninteractive + +ENV TZ=Europe/Bucharest +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone +RUN apt-get update && \ + apt-get -y upgrade && \ + apt-get install -y apt-utils sudo tzdata keyboard-configuration software-properties-common + +RUN groupadd -g 1000 -r $USER && \ + useradd -u 1000 -g 1000 --create-home -r $USER + +#Change password +RUN echo "$USER:$USER" | chpasswd + +#Make sudo passwordless +RUN echo "${USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-$USER && \ + usermod -aG sudo $USER && \ + usermod -aG plugdev $USER + +USER $USER +WORKDIR /home/${USER}/scripts +COPY x86-64_appimage_process.sh . + +RUN ./x86-64_appimage_process.sh configure_system +RUN rm -rf /home/${USER}/scripts/staging +RUN ./x86-64_appimage_process.sh download_tools +RUN mv /home/${USER}/scripts/staging /home/${USER}/staging && \ + mv /home/${USER}/scripts/build-status /home/${USER} + + +ENV CI_SCRIPT=ON +WORKDIR /home/${USER} + + +# Clean image +RUN rm -rf /home/${USER}/scripts +RUN sudo rm -rf /var/lib/apt/lists/* + +FROM scratch +COPY --from=0 / / \ No newline at end of file diff --git a/ci/x86_64/scopy.desktop b/ci/x86_64/scopy.desktop new file mode 100644 index 0000000000..ee6591769f --- /dev/null +++ b/ci/x86_64/scopy.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Icon=scopy +Exec=scopy +Terminal=false +Type=Application +Categories=Science +Name=Scopy +GenericName=Oscilloscope +Comment=A software oscilloscope diff --git a/ci/x86_64/x86-64_appimage_process.sh b/ci/x86_64/x86-64_appimage_process.sh new file mode 100755 index 0000000000..266274652f --- /dev/null +++ b/ci/x86_64/x86-64_appimage_process.sh @@ -0,0 +1,549 @@ +#!/bin/bash +set -ex + +## Set STAGING +USE_STAGING=OFF +## + +SRC_DIR=$(git rev-parse --show-toplevel 2>/dev/null ) || echo "No source directory found" +SRC_SCRIPT=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +if [ "$CI_SCRIPT" == "ON" ]; then + USE_STAGING=OFF + SRC_DIR=$GITHUB_WORKSPACE +fi + + +export APPIMAGE=1 + +LIBSERIALPORT_BRANCH=scopy-v2 +LIBIIO_VERSION=v0.26 +LIBAD9361_BRANCH=main +LIBM2K_BRANCH=main +SPDLOG_BRANCH=v1.x +VOLK_BRANCH=main +GNURADIO_BRANCH=scopy2-maint-3.10 +GRSCOPY_BRANCH=3.10 +GRM2K_BRANCH=main +LIBSIGROKDECODE_BRANCH=master +QWT_BRANCH=qwt-multiaxes-updated +LIBTINYIIOD_BRANCH=master +IIOEMU_BRANCH=master +KDDOCK_BRANCH=2.1 + +# default python version used in CI scripts, can be changed to match locally installed python +PYTHON_VERSION=python3.8 + +QT_LOCATION=/opt/Qt/5.15.2/gcc_64 + +STAGING_AREA=$SRC_SCRIPT/staging +QMAKE_BIN=$QT_LOCATION/bin/qmake +CMAKE_BIN=${STAGING_AREA}/cmake/bin/cmake +JOBS=-j14 + +APP_DIR_NAME=scopy.AppDir +APP_DIR=$SRC_SCRIPT/$APP_DIR_NAME +APP_IMAGE=$SRC_SCRIPT/Scopy-x86_64.AppImage + +BUILD_STATUS_FILE=$SRC_SCRIPT/build-status + +if [ "$USE_STAGING" == "ON" ] + then + echo -- USING STAGING FOLDER: $STAGING_AREA_DEPS + STAGING_AREA_DEPS=$STAGING_AREA/dependencies + export LD_LIBRARY_PATH=$STAGING_AREA_DEPS/lib:$QT_LOCATION/lib:$LD_LIBRARY_PATH + CMAKE_OPTS=(\ + -DCMAKE_LIBRARY_PATH=$STAGING_AREA_DEPS \ + -DCMAKE_INSTALL_PREFIX=$STAGING_AREA_DEPS \ + -DCMAKE_PREFIX_PATH=$QT_LOCATION\;$STAGING_AREA_DEPS \ + -DCMAKE_EXE_LINKER_FLAGS=-L$STAGING_AREA_DEPS\;-L$STAGING_AREA_DEPS/lib \ + -DCMAKE_SHARED_LINKER_FLAGS=-L$STAGING_AREA_DEPS\;-L$STAGING_AREA_DEPS/lib \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + ) + echo -- STAGING_DIR $STAGING_AREA_DEPS + else + echo -- NO STAGING: INSTALLING IN SYSTEM + STAGING_AREA_DEPS=/usr/local + export LD_LIBRARY_PATH=$QT_LOCATION/lib:$LD_LIBRARY_PATH: + CMAKE_OPTS=(\ + -DCMAKE_PREFIX_PATH=$QT_LOCATION \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + ) +fi + +CMAKE="$CMAKE_BIN ${CMAKE_OPTS[*]}" +echo -- USING CMAKE COMMAND: +echo $CMAKE +echo -- USING QT: $QT_LOCATION +echo -- USING QMAKE: $QMAKE_BIN + +clone() { + echo "#######CLONE#######" + mkdir -p $STAGING_AREA + pushd $STAGING_AREA + [ -d 'libserialport' ] || git clone --recursive https://github.com/cseci/libserialport -b $LIBSERIALPORT_BRANCH libserialport + [ -d 'libiio' ] || git clone --recursive https://github.com/analogdevicesinc/libiio.git -b $LIBIIO_VERSION libiio + [ -d 'libad9361' ] || git clone --recursive https://github.com/analogdevicesinc/libad9361-iio.git -b $LIBAD9361_BRANCH libad9361 + [ -d 'libm2k' ] || git clone --recursive https://github.com/analogdevicesinc/libm2k.git -b $LIBM2K_BRANCH libm2k + [ -d 'spdlog' ] || git clone --recursive https://github.com/gabime/spdlog.git -b $SPDLOG_BRANCH spdlog + [ -d 'gr-scopy' ] || git clone --recursive https://github.com/analogdevicesinc/gr-scopy.git -b $GRSCOPY_BRANCH gr-scopy + [ -d 'gr-m2k' ] || git clone --recursive https://github.com/analogdevicesinc/gr-m2k.git -b $GRM2K_BRANCH gr-m2k + [ -d 'volk' ] || git clone --recursive https://github.com/gnuradio/volk.git -b $VOLK_BRANCH volk + [ -d 'gnuradio' ] || git clone --recursive https://github.com/analogdevicesinc/gnuradio.git -b $GNURADIO_BRANCH gnuradio + [ -d 'qwt' ] || git clone --recursive https://github.com/cseci/qwt.git -b $QWT_BRANCH qwt + [ -d 'libsigrokdecode' ] || git clone --recursive https://github.com/sigrokproject/libsigrokdecode.git -b $LIBSIGROKDECODE_BRANCH libsigrokdecode + [ -d 'libtinyiiod' ] || git clone --recursive https://github.com/analogdevicesinc/libtinyiiod.git -b $LIBTINYIIOD_BRANCH libtinyiiod + [ -d 'KDDockWidgets' ] || git clone --recursive https://github.com/KDAB/KDDockWidgets.git -b $KDDOCK_BRANCH KDDockWidgets + popd +} + +install_qt() { + # installing Qt using the aqt tool https://github.com/miurahr/aqtinstall + sudo pip3 install --no-cache-dir aqtinstall + sudo python3 -m aqt install-qt --outputdir /opt/Qt linux desktop 5.15.2 +} + +download_tools() { + mkdir -p ${STAGING_AREA} + pushd ${STAGING_AREA} + + if [ ! -d cmake ];then + wget https://github.com/Kitware/CMake/releases/download/v3.29.0-rc2/cmake-3.29.0-rc2-linux-x86_64.tar.gz + tar -xf cmake*.tar.gz && rm cmake*.tar.gz && mv cmake* cmake # unzip and rename + fi + + # download tools for creating the AppDir and the AppImage + if [ ! -f linuxdeploy-x86_64.AppImage ];then + wget https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20240109-1/linuxdeploy-x86_64.AppImage + chmod +x linuxdeploy-x86_64.AppImage + fi + + if [ ! -f linuxdeploy-plugin-qt-x86_64.AppImage ];then + wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/1-alpha-20240109-1/linuxdeploy-plugin-qt-x86_64.AppImage + chmod +x linuxdeploy-plugin-qt-x86_64.AppImage + fi + + if [ ! -f linuxdeploy-plugin-appimage-x86_64.AppImage ];then + wget https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/1-alpha-20230713-1/linuxdeploy-plugin-appimage-x86_64.AppImage + chmod +x linuxdeploy-plugin-appimage-x86_64.AppImage + fi + + popd +} + +build_with_cmake() { + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + BUILD_FOLDER=$PWD/build + rm -rf $BUILD_FOLDER + mkdir -p $BUILD_FOLDER + cd $BUILD_FOLDER + $CMAKE $CURRENT_BUILD_CMAKE_OPTS ../ + make $JOBS + if [ "$INSTALL" == "ON" ];then + if [ "$USE_STAGING" == "ON" ]; then make install; else sudo make install; fi + fi + CURRENT_BUILD_CMAKE_OPTS="" + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE +} + +install_packages() { + sudo DEBIAN_FRONTEND=noninteractive apt-get -y install + + sudo apt-get update + sudo apt-get -y upgrade + sudo apt-get -y --no-install-recommends install \ + $PYTHON_VERSION-full python3-pip lib$PYTHON_VERSION-dev python3-numpy \ + keyboard-configuration vim git wget unzip\ + g++ build-essential cmake curl autogen autoconf autoconf-archive pkg-config flex bison swig \ + subversion mesa-common-dev graphviz xserver-xorg gettext texinfo mm-common doxygen \ + libboost-all-dev libfftw3-bin libfftw3-dev libfftw3-3 liblog4cpp5v5 liblog4cpp5-dev \ + libxcb-xinerama0 libgmp3-dev libzip-dev libglib2.0-dev libglibmm-2.4-dev libsigc++-2.0-dev \ + libclang1-9 libmatio-dev liborc-0.4-dev libgl1-mesa-dev libavahi-client* libavahi-common* \ + libusb-1.0 libusb-1.0-0 libusb-1.0-0-dev libsndfile1-dev \ + libxkbcommon-x11-0 libqt5gui5 libncurses5 libtool libaio-dev libzmq3-dev libxml2-dev + + pip3 install --no-cache-dir mako + pip3 install --no-cache-dir packaging +} + +build_libserialport(){ + CURRENT_BUILD=libserialport + pushd $STAGING_AREA/$CURRENT_BUILD + git clean -xdf + + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + + ./autogen.sh + [ "$USE_STAGING" == "ON" ] && ./configure --prefix $STAGING_AREA_DEPS || ./configure + make $JOBS + [ "$INSTALL" == "ON" ] && sudo make install + + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE + popd +} + +build_libiio() { + echo "### Building libiio - version $LIBIIO_VERSION" + pushd $STAGING_AREA/libiio + CURRENT_BUILD_CMAKE_OPTS="\ + -DWITH_TESTS:BOOL=OFF \ + -DWITH_DOC:BOOL=OFF \ + -DHAVE_DNS_SD:BOOL=ON\ + -DWITH_MATLAB_BINDINGS:BOOL=OFF \ + -DCSHARP_BINDINGS:BOOL=OFF \ + -DPYTHON_BINDINGS:BOOL=OFF \ + -DWITH_SERIAL_BACKEND:BOOL=ON \ + -DENABLE_IPV6:BOOL=OFF \ + -DINSTALL_UDEV_RULE:BOOL=OFF + " + build_with_cmake $1 + popd +} + +build_libad9361() { + echo "### Building libad9361 - branch $LIBAD9361_BRANCH" + pushd $STAGING_AREA/libad9361 + build_with_cmake $1 + popd +} + +build_spdlog() { + echo "### Building spdlog - branch $SPDLOG_BRANCH" + pushd $STAGING_AREA/spdlog + CURRENT_BUILD_CMAKE_OPTS="-DSPDLOG_BUILD_SHARED=ON" + build_with_cmake $1 + popd +} + +build_libm2k() { + echo "### Building libm2k - branch $LIBM2K_BRANCH" + pushd $STAGING_AREA/libm2k + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PYTHON=OFF \ + -DENABLE_CSHARP=OFF \ + -DBUILD_EXAMPLES=OFF \ + -DENABLE_TOOLS=OFF \ + -DINSTALL_UDEV_RULES=OFF \ + " + build_with_cmake $1 + popd +} + +build_volk() { + echo "### Building volk - branch $VOLK_BRANCH" + pushd $STAGING_AREA/volk + CURRENT_BUILD_CMAKE_OPTS="-DPYTHON_EXECUTABLE=/usr/bin/python3" + build_with_cmake $1 + popd +} + +build_gnuradio() { + echo "### Building gnuradio - branch $GNURADIO_BRANCH" + pushd $STAGING_AREA/gnuradio + CURRENT_BUILD_CMAKE_OPTS="\ + -DPYTHON_EXECUTABLE=/usr/bin/python3 \ + -DENABLE_DEFAULT=OFF \ + -DENABLE_GNURADIO_RUNTIME=ON \ + -DENABLE_GR_ANALOG=ON \ + -DENABLE_GR_BLOCKS=ON \ + -DENABLE_GR_FFT=ON \ + -DENABLE_GR_FILTER=ON \ + -DENABLE_GR_IIO=ON \ + -DENABLE_POSTINSTALL=OFF + " + build_with_cmake $1 + popd +} + +build_grscopy() { + echo "### Building gr-scopy - branch $GRSCOPY_BRANCH" + pushd $STAGING_AREA/gr-scopy + build_with_cmake $1 + popd +} + +build_grm2k() { + echo "### Building gr-m2k - branch $GRM2K_BRANCH" + pushd $STAGING_AREA/gr-m2k + CURRENT_BUILD_CMAKE_OPTS="\ + -DENABLE_PYTHON=OFF \ + -DDIGITAL=OFF + " + build_with_cmake $1 + popd +} + +build_qwt() { + echo "### Building qwt - branch $QWT_BRANCH" + pushd $STAGING_AREA/qwt + git clean -xdf + sed -i 's|/usr/local/qwt-$$QWT_VERSION-ma|/usr/local|g' qwtconfig.pri + + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + + if [ "$USE_STAGING" == "ON" ] + then + $QMAKE_BIN INCLUDEPATH=$STAGING_AREA_DEPS/include LIBS=-L$STAGING_AREA_DEPS/lib qwt.pro + make $JOBS + if [ "$INSTALL" == "ON" ];then + make INSTALL_ROOT=$STAGING_AREA_DEPS install + fi + cp -r $STAGING_AREA_DEPS/usr/local/* $STAGING_AREA_DEPS/ + else + $QMAKE_BIN qwt.pro + make $JOBS + if [ "$INSTALL" == "ON" ];then + sudo make install + fi + fi + + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE + + popd +} + +build_libsigrokdecode() { + echo "### Building libsigrokdecode - branch $LIBSIGROKDECODE_BRANCH" + pushd $STAGING_AREA/libsigrokdecode + git clean -xdf + ./autogen.sh + + INSTALL=$1 + [ -z $INSTALL ] && INSTALL=ON + + if [ "$USE_STAGING" == "ON" ] + then + ./configure --prefix $STAGING_AREA_DEPS + LD_RUN_PATH=$STAGING_AREA_DEPS/lib make $JOBS + else + ./configure + make $JOBS + fi + + if [ "$INSTALL" == "ON" ];then + if [ "$USE_STAGING" == "ON" ]; then make install; else sudo make install; fi + fi + + echo "$(basename -a "$(git config --get remote.origin.url)") - $(git rev-parse --abbrev-ref HEAD) - $(git rev-parse --short HEAD)" \ + >> $BUILD_STATUS_FILE + + popd +} + +build_libtinyiiod() { + echo "### Building libtinyiiod - branch $LIBTINYIIOD_BRANCH" + pushd $STAGING_AREA/libtinyiiod + CURRENT_BUILD_CMAKE_OPTS="-DBUILD_EXAMPLES=OFF" + build_with_cmake $1 + popd +} + +build_iio-emu() { + echo "### Building iio-emu - branch $IIOEMU_BRANCH" + mkdir -p $STAGING_AREA + pushd $STAGING_AREA + [ -d 'iio-emu' ] || git clone --recursive https://github.com/analogdevicesinc/iio-emu -b $IIOEMU_BRANCH iio-emu + pushd $STAGING_AREA/iio-emu + build_with_cmake OFF + popd + popd +} + +build_kddock () { + echo "### Building KDDockWidgets - version $KDDOCK_BRANCH" + pushd $STAGING_AREA/KDDockWidgets + CURRENT_BUILD_CMAKE_OPTS="" + build_with_cmake $1 + popd +} + +build_scopy() { + echo "### Building scopy" + pushd $SRC_DIR + [ -f /home/runner/build-status ] && cp /home/runner/build-status $SRC_DIR/build-status + [ $CI_SCRIPT ] && git config --global --add safe.directory $SRC_DIR + CURRENT_BUILD_CMAKE_OPTS="\ + -DPYTHON_EXECUTABLE=/usr/bin/$PYTHON_VERSION + " + build_with_cmake OFF + popd +} + +create_appdir(){ + pushd ${STAGING_AREA} + BUILD_FOLDER=$SRC_DIR/build + EMU_BUILD_FOLDER=$STAGING_AREA/iio-emu/build + PLUGINS=$BUILD_FOLDER/plugins/plugins + SCOPY_DLL=$(find $BUILD_FOLDER -maxdepth 1 -type f -name "libscopy*") + REGMAP_XMLS=$BUILD_FOLDER/plugins/regmap/xmls + DAC_WAVEFORM_CSV=$SRC_DIR/plugins/dac/res/csv + EMU_XMLS=$BUILD_FOLDER/plugins/emu_xml + EMU_CONFIG=$SRC_DIR/resources/scopy_emu_options_config.json + TRANSLATIONS_QM=$(find $BUILD_FOLDER/translations -type f -name "*.qm") + STYLE_FOLDER=$BUILD_FOLDER/style + LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$STAGING_AREA_DEPS/lib:$QT_LOCATION/lib + DLL_FOLDER=${STAGING_AREA}/dll_folder + COPY_DEPS=${SRC_DIR}/ci/x86_64/copy-deps.sh + export PATH=$QT_LOCATION:$PATH + sudo ldconfig + + rm -rf $APP_DIR + rm -rf $DLL_FOLDER + mkdir $DLL_FOLDER + cp $SCOPY_DLL $DLL_FOLDER + cp $PLUGINS/*.so $DLL_FOLDER + + export QMAKE=$QMAKE_BIN # this is needed for deploy-plugin-qt.AppImage + # inside a docker image you can't run an appimage executable without privileges + # so the solution is to extract the appimage first and only then to run it + export APPIMAGE_EXTRACT_AND_RUN=1 + ${STAGING_AREA}/linuxdeploy-x86_64.AppImage \ + --appdir $APP_DIR \ + --executable $SRC_DIR/build/scopy \ + --custom-apprun $SRC_DIR/ci/x86_64/AppRun \ + --desktop-file $SRC_DIR/ci/x86_64/scopy.desktop \ + --icon-file $SRC_DIR/gui/res/scopy.png \ + --deploy-deps-only $DLL_FOLDER \ + --plugin qt + + $COPY_DEPS "$DLL_FOLDER/*" $APP_DIR/usr/lib + rm -rf $DLL_FOLDER + cp $SCOPY_DLL $APP_DIR/usr/lib + mkdir -p $APP_DIR/usr/lib/scopy/plugins + cp $PLUGINS/*.so $APP_DIR/usr/lib/scopy/plugins + + cp $EMU_BUILD_FOLDER/iio-emu $APP_DIR/usr/bin + cp ${STAGING_AREA_DEPS}/lib/tinyiiod.so* $APP_DIR/usr/lib + + # search for the python version linked by cmake and copy inside the appimage the same version + FOUND_PYTHON_VERSION=$(grep 'PYTHON_VERSION' $SRC_DIR/build/CMakeCache.txt | awk -F= '{print $2}' | grep -o 'python[0-9]\+\.[0-9]\+') + python_path=/usr/lib/$FOUND_PYTHON_VERSION + cp -r $python_path $APP_DIR/usr/lib + + if [ -d $STAGING_AREA_DEPS/share/libsigrokdecode/decoders ]; then + cp -r $STAGING_AREA_DEPS/share/libsigrokdecode/decoders $APP_DIR/usr/lib + elif [ -d $STAGING_AREA/libsigrokdecode/decoders ];then + cp -r $STAGING_AREA/libsigrokdecode/decoders $APP_DIR/usr/lib + else + echo "No decoders for libsigrokdecode found" + exit 1 + fi + + mkdir -p $APP_DIR/usr/lib/scopy/translations + cp $TRANSLATIONS_QM $APP_DIR/usr/lib/scopy/translations + + cp -R $STYLE_FOLDER $APP_DIR/usr/lib/scopy/style + + if [ -d $REGMAP_XMLS ]; then + cp -r $REGMAP_XMLS $APP_DIR/usr/lib/scopy/plugins + fi + + cp -r $DAC_WAVEFORM_CSV $APP_DIR/usr/lib/scopy/plugins + + cp -r $EMU_XMLS $APP_DIR/usr/lib/scopy/plugins + mkdir -p $APP_DIR/usr/lib/scopy/plugins/resources + cp $EMU_CONFIG $APP_DIR/usr/lib/scopy/plugins/resources + + cp $STAGING_AREA_DEPS/lib/libspdlog.so* $APP_DIR/usr/lib + cp -r $QT_LOCATION/plugins $APP_DIR/usr + cp $QT_LOCATION/lib/libQt5XcbQpa.so* $APP_DIR/usr/lib + cp $QT_LOCATION/lib/libQt5EglFSDeviceIntegration.so* $APP_DIR/usr/lib + cp $QT_LOCATION/lib/libQt5DBus.so* $APP_DIR/usr/lib + cp /usr/lib/x86_64-linux-gnu/libXdmcp.so* $APP_DIR/usr/lib + cp /usr/lib/x86_64-linux-gnu/libbsd.so* $APP_DIR/usr/lib + cp /usr/lib/x86_64-linux-gnu/libXau.so* $APP_DIR/usr/lib + cp /usr/lib/x86_64-linux-gnu/libffi.so* $APP_DIR/usr/lib + popd +} + +create_appimage(){ + rm -rf $APP_IMAGE + + pushd ${STAGING_AREA} + export APPIMAGE_EXTRACT_AND_RUN=1 + ${STAGING_AREA}/linuxdeploy-plugin-appimage-x86_64.AppImage --appdir $APP_DIR + mv Scopy*.AppImage $APP_IMAGE + chmod +x $APP_IMAGE + popd +} + +generate_ci_envs(){ + $SRC_DIR/ci/general/gen_ci_envs.sh > $SRC_DIR/ci/x86_64/gh-actions.envs +} + +# move the staging folder that contains the tools needed for the build to the known location +move_tools(){ + [ -d /home/runner/staging ] && mv /home/runner/staging $STAGING_AREA || echo "Staging folder not found or already moved" + if [ ! -d $STAGING_AREA ]; then + echo "Missing tools folder, downloading now" + download_tools + fi +} + +move_appimage(){ + mv $APP_IMAGE $SRC_DIR +} + + +# +# Helper functions +# + +build_deps(){ + clone + download_tools + build_libserialport ON + build_libiio ON + build_libad9361 ON + build_spdlog ON + build_libm2k ON + build_volk ON + build_gnuradio ON + build_grscopy ON + build_grm2k ON + build_qwt ON + build_libsigrokdecode ON + build_libtinyiiod ON + build_kddock ON +} + +run_workflow(){ + [ "$CI_SCRIPT" == "ON" ] && move_tools || download_tools + build_iio-emu + build_scopy + create_appdir + create_appimage + move_appimage +} + +get_tools(){ + install_packages + download_tools +} + +generate_appimage(){ + download_tools + build_iio-emu + build_scopy + create_appdir + create_appimage +} + +configure_system(){ + install_packages + install_qt + build_deps + download_tools +} + +for arg in $@; do + $arg +done diff --git a/cmake/Modules/FindQwt.cmake b/cmake/Modules/FindQwt.cmake index 95ddd4454b..4c58d91c0b 100644 --- a/cmake/Modules/FindQwt.cmake +++ b/cmake/Modules/FindQwt.cmake @@ -1,62 +1,74 @@ -# - try to find Qwt libraries and include files -# QWT_INCLUDE_DIR where to find qwt_global.h, etc. -# QWT_LIBRARIES libraries to link against -# QWT_FOUND If false, do not try to use Qwt -# qwt_global.h holds a string with the QWT version; -# test to make sure it's at least 5.2 +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# -find_path(QWT_INCLUDE_DIRS - NAMES qwt_global.h - HINTS - ${CMAKE_INSTALL_PREFIX}/include/qwt - PATHS - /usr/local/include/qwt-qt5 - /usr/local/include/qwt - /usr/include/qwt6 - /usr/include/qwt-qt5 - /usr/include/qwt - /usr/include/qwt5 - /opt/local/include/qwt - /sw/include/qwt - /usr/local/lib/qwt.framework/Headers +# * try to find Qwt libraries and include files QWT_INCLUDE_DIR where to find qwt_global.h, etc. QWT_LIBRARIES libraries +# to link against QWT_FOUND If false, do not try to use Qwt qwt_global.h holds a string with the QWT version; test to +# make sure it's at least 5.2 + +find_path( + QWT_INCLUDE_DIRS + NAMES qwt_global.h + HINTS ${CMAKE_INSTALL_PREFIX}/include/qwt + PATHS /usr/local/include/qwt-qt5 + /usr/local/include/qwt + /usr/include/qwt6 + /usr/include/qwt-qt5 + /usr/include/qwt + /usr/include/qwt5 + /opt/local/include/qwt + /sw/include/qwt + /usr/local/lib/qwt.framework/Headers ) -find_library (QWT_LIBRARIES - NAMES qwt6 qwt6-qt5 qwt qwt-qt5 - HINTS - ${CMAKE_INSTALL_PREFIX}/lib - ${CMAKE_INSTALL_PREFIX}/lib64 - PATHS - /usr/local/lib - /usr/lib - /opt/local/lib - /sw/lib - /usr/local/lib/qwt.framework +find_library( + QWT_LIBRARIES + NAMES qwt6 qwt6-qt5 qwt qwt-qt5 + HINTS ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS /usr/local/lib + /usr/lib + /opt/local/lib + /sw/lib + /usr/local/lib/qwt.framework ) set(QWT_FOUND FALSE) if(QWT_INCLUDE_DIRS) - file(STRINGS "${QWT_INCLUDE_DIRS}/qwt_global.h" - QWT_STRING_VERSION REGEX "QWT_VERSION_STR") - set(QWT_WRONG_VERSION True) - set(QWT_VERSION "No Version") - string(REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" QWT_VERSION ${QWT_STRING_VERSION}) - string(COMPARE LESS ${QWT_VERSION} "5.2.0" QWT_WRONG_VERSION) - string(COMPARE GREATER ${QWT_VERSION} "6.2.0" QWT_WRONG_VERSION) + file(STRINGS "${QWT_INCLUDE_DIRS}/qwt_global.h" QWT_STRING_VERSION REGEX "QWT_VERSION_STR") + set(QWT_WRONG_VERSION True) + set(QWT_VERSION "No Version") + string(REGEX MATCH "[0-9]+.[0-9]+.[0-9]+" QWT_VERSION ${QWT_STRING_VERSION}) + string(COMPARE LESS ${QWT_VERSION} "5.2.0" QWT_WRONG_VERSION) + string(COMPARE GREATER ${QWT_VERSION} "6.2.0" QWT_WRONG_VERSION) - message(STATUS "QWT Version: ${QWT_VERSION}") - if(NOT QWT_WRONG_VERSION) - set(QWT_FOUND TRUE) - else(NOT QWT_WRONG_VERSION) - message(STATUS "QWT Version must be >= 5.2 and <= 6.2.0, Found ${QWT_VERSION}") - endif(NOT QWT_WRONG_VERSION) + message(STATUS "QWT Version: ${QWT_VERSION}") + if(NOT QWT_WRONG_VERSION) + set(QWT_FOUND TRUE) + else(NOT QWT_WRONG_VERSION) + message(STATUS "QWT Version must be >= 5.2 and <= 6.2.0, Found ${QWT_VERSION}") + endif(NOT QWT_WRONG_VERSION) endif(QWT_INCLUDE_DIRS) if(QWT_FOUND) - # handle the QUIETLY and REQUIRED arguments and set QWT_FOUND to TRUE if - # all listed variables are TRUE - include ( FindPackageHandleStandardArgs ) - find_package_handle_standard_args( Qwt DEFAULT_MSG QWT_LIBRARIES QWT_INCLUDE_DIRS ) - MARK_AS_ADVANCED(QWT_LIBRARIES QWT_INCLUDE_DIRS) + # handle the QUIETLY and REQUIRED arguments and set QWT_FOUND to TRUE if all listed variables are TRUE + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Qwt DEFAULT_MSG QWT_LIBRARIES QWT_INCLUDE_DIRS) + mark_as_advanced(QWT_LIBRARIES QWT_INCLUDE_DIRS) endif(QWT_FOUND) diff --git a/cmake/Modules/ScopyAbout.cmake b/cmake/Modules/ScopyAbout.cmake new file mode 100644 index 0000000000..cc2cef3565 --- /dev/null +++ b/cmake/Modules/ScopyAbout.cmake @@ -0,0 +1,158 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +if(DEFINED __INCLUDED_SCOPY_ABOUT_CMAKE) + return() +endif() +set(__INCLUDED_SCOPY_ABOUT_CMAKE TRUE) + +# Get the GIT hash of the latest commit +include(FindGit OPTIONAL) +if(GIT_FOUND) + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --show-toplevel + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE SCOPY_GIT_REPO + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE SCOPY_VERSION_GIT + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + execute_process( + COMMAND ${GIT_EXECUTABLE} diff + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE SCOPY_GIT_DIFF + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + execute_process( + COMMAND ${GIT_EXECUTABLE} config --get remote.origin.url + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE SCOPY_REMOTE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + execute_process( + COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE SCOPY_BRANCH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + execute_process( + COMMAND ${GIT_EXECUTABLE} log --oneline -n 150 + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE GIT_LOG + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + string(REPLACE "\n" "
" SCOPY_GIT_LOG ${GIT_LOG}) + + if(NOT SCOPY_GIT_DIFF STREQUAL "") + set(SCOPY_GIT_DIRTY "dirty") + endif() +endif() + +function(configure_about ABOUT_RESOURCES_DIR) + string(TIMESTAMP TODAY "%Y-%m-%d") + string(TIMESTAMP NOW "%H:%M:%S") + cmake_host_system_information(RESULT BUILD_HOST QUERY HOSTNAME) + + set(BUILD_INFO) + if(DEFINED ENV{BUILD_HOST}) + if($ENV{USERNAME} STREQUAL "github-actions") + set(CI_URL $ENV{GITHUB_SERVER_URL}) + set(CI_API_URL $ENV{GITHUB_API_URL}) + set(CI_ACCOUNT_NAME $ENV{GITHUB_REPOSITORY_OWNER}) + set(CI_PROJECT_NAME $ENV{GITHUB_REPOSITORY}) + set(CI_RUN_ID $ENV{GITHUB_RUN_ID}) + set(CI_RUN_NUMBER $ENV{GITHUB_RUN_NUMBER}) + set(CI_JOB_ID $ENV{GITHUB_RUN_ID}) + set(CI_JOB_NAME $ENV{GITHUB_JOB}) + set(CI_JOB_NUMBER $ENV{GITHUB_RUN_NUMBER}) + set(CI_JOB_LINK + $ENV{GITHUB_SERVER_URL}/$ENV{GITHUB_REPOSITORY_OWNER}/$ENV{GITHUB_REPOSITORY}/actions/runs/$ENV{GITHUB_RUN_ID} + ) + else($ENV{USERNAME} STREQUAL "azure-pipelines") + set(CI_URL $ENV{BUILD_REPO_URL}) + set(CI_API_URL "-") + set(CI_ACCOUNT_NAME $ENV{ACCOUNT_NAME}) + set(CI_PROJECT_NAME $ENV{PROJECT_NAME}) + set(CI_RUN_ID $ENV{RUN_ID}) + set(CI_RUN_NUMBER $ENV{RUN_NUMBER}) + set(CI_JOB_ID $ENV{JOB_ID}) + set(CI_JOB_NAME $ENV{JOB_NAME}) + set(CI_JOB_NUMBER "-") + set(CI_JOB_LINK "-") + endif() + + set(BUILD_INFO ${BUILD_INFO}Built\ on\ $ENV{USERNAME}\n) + set(BUILD_INFO ${BUILD_INFO}url:\ ${CI_URL}\n) + set(BUILD_INFO ${BUILD_INFO}api_url:\ ${CI_API_URL}\n) + set(BUILD_INFO ${BUILD_INFO}acc_name:\ ${CI_ACCOUNT_NAME}\n) + set(BUILD_INFO ${BUILD_INFO}prj_name:\ ${CI_PROJECT_NAME}\n) + set(BUILD_INFO ${BUILD_INFO}run_id:\ ${CI_RUN_ID}\n) + set(BUILD_INFO ${BUILD_INFO}run_nr:\ ${CI_RUN_NUMBER}\n) + set(BUILD_INFO ${BUILD_INFO}job_id:\ ${CI_JOB_ID}\n) + set(BUILD_INFO ${BUILD_INFO}job_name:\ ${CI_JOB_NAME}\n) + set(BUILD_INFO ${BUILD_INFO}job_nr:\ ${CI_JOB_NUMBER}\n) + set(BUILD_INFO ${BUILD_INFO}job_link:\ ${CI_JOB_LINK}\n) + + if(EXISTS ${CMAKE_SOURCE_DIR}/build-status) + message("build-status found in ${CMAKE_SOURCE_DIR}.. populating") + file(READ ${CMAKE_SOURCE_DIR}/build-status SCOPY_BUILD_STATUS_INFO) + endif() + else() + set(BUILD_INFO ${BUILD_INFO} "Built locally") + endif() + + message(STATUS "AboutPage Info: " ${BUILD_INFO}) + # TODO: Pack these in a GLOB and run foreach + + set(ABOUT_RESOURCES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${ABOUT_RESOURCES_DIR}) + set(ABOUT_RESOURCES_TARGET_DIR ${CMAKE_CURRENT_SOURCE_DIR}/resources) + configure_file(${ABOUT_RESOURCES_DIR}/buildinfo.html.cmakein ${ABOUT_RESOURCES_TARGET_DIR}/buildinfo.html) + configure_file(${ABOUT_RESOURCES_DIR}/scopy_osp.html.cmakein ${ABOUT_RESOURCES_TARGET_DIR}/scopy_osp.html) + configure_file(${ABOUT_RESOURCES_DIR}/credits.html.cmakein ${ABOUT_RESOURCES_TARGET_DIR}/credits.html) + configure_file(${ABOUT_RESOURCES_DIR}/about.html.cmakein ${ABOUT_RESOURCES_TARGET_DIR}/about.html) + configure_file(${ABOUT_RESOURCES_DIR}/scopy_home.html.cmakein ${ABOUT_RESOURCES_TARGET_DIR}/scopy_home.html) + + set(ABOUT_HTML_SOURCES + ${ABOUT_RESOURCES_TARGET_DIR}/buildinfo.html + ${ABOUT_RESOURCES_TARGET_DIR}/scopy_osp.html + ${ABOUT_RESOURCES_TARGET_DIR}/credits.html + ${ABOUT_RESOURCES_TARGET_DIR}/about.html + ${ABOUT_RESOURCES_TARGET_DIR}/scopy_home.html + ${ABOUT_RESOURCES_TARGET_DIR}/license.html + ) + + set(ABOUT_HTML_QRC_SOURCES) + foreach(file ${ABOUT_HTML_SOURCES}) + get_filename_component(file_name ${file} NAME) + set(ABOUT_HTML_QRC_SOURCES "${ABOUT_HTML_QRC_SOURCES}\n${file_name}") + endforeach() + + configure_file(${ABOUT_RESOURCES_DIR}/aboutpage.qrc.cmakein ${ABOUT_RESOURCES_TARGET_DIR}/aboutpage.qrc @ONLY) + message(STATUS "built about page in - " ${ABOUT_RESOURCES_TARGET_DIR}/aboutpage.qrc) + +endfunction() diff --git a/cmake/Modules/ScopyMacOS.cmake b/cmake/Modules/ScopyMacOS.cmake new file mode 100644 index 0000000000..b1399db0ba --- /dev/null +++ b/cmake/Modules/ScopyMacOS.cmake @@ -0,0 +1,80 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +if(DEFINED __INCLUDED_SCOPY_MACOS_CMAKE) + return() +endif() +set(__INCLUDED_SCOPY_MACOS_CMAKE TRUE) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden") + +set(PKGINFO ${CMAKE_BINARY_DIR}/PkgInfo) +file(WRITE ${PKGINFO} "APPLScopy") +set_source_files_properties(${PKGINFO} PROPERTIES MACOSX_PACKAGE_LOCATION .) + +set(QT_CONF ${CMAKE_BINARY_DIR}/qt.conf) +file(APPEND ${QT_CONF} "") +set_source_files_properties(${QT_CONF} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + +set(ICON_FILE ${CMAKE_SOURCE_DIR}/gui/res/Scopy.icns) +set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + +set(CMAKE_EXE_LINKER_FLAGS "-Wl,-headerpad_max_install_names -Wl,-search_paths_first ${CMAKE_EXE_LINKER_FLAGS}") + +foreach(plugin ${Qt5Gui_PLUGINS} ${Qt5Svg_PLUGINS}) + get_target_property(_loc ${plugin} LOCATION) + get_filename_component(_name ${_loc} NAME) + get_filename_component(_dir ${_loc} DIRECTORY) + get_filename_component(_dir ${_dir} NAME) + + set_source_files_properties(${_loc} PROPERTIES MACOSX_PACKAGE_LOCATION plugins/${_dir}) + set(QT_PLUGINS ${QT_PLUGINS} ${_loc}) + set(BUNDLED_QT_PLUGINS ${BUNDLED_QT_PLUGINS} ${CMAKE_BINARY_DIR}/Scopy.app/Contents/plugins/${_dir}/${_name}) +endforeach() + +install( + CODE " + set(BU_CHMOD_BUNDLE_ITEMS ON) + include(BundleUtilities) + fixup_bundle(\"${CMAKE_BINARY_DIR}/Scopy.app\" \"${BUNDLED_QT_PLUGINS}\" \"${CMAKE_SOURCE_DIR}\")" +) + +set(OSX_BUNDLE MACOSX_BUNDLE) + +find_package(PkgConfig) +set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON) +pkg_check_modules(GLIB REQUIRED glib-2.0) +pkg_check_modules(LIBSIGROK_DECODE REQUIRED libsigrokdecode) +pkg_get_variable(LIBSIGROK_DECODERS_DIR libsigrokdecode decodersdir) +file(GLOB_RECURSE DECODERS ${LIBSIGROK_DECODERS_DIR}/*.py) +foreach(_decoder ${DECODERS}) + file(RELATIVE_PATH _file ${LIBSIGROK_DECODERS_DIR} ${_decoder}) + get_filename_component(_path ${_file} DIRECTORY) + set_property(SOURCE ${_decoder} PROPERTY MACOSX_PACKAGE_LOCATION MacOS/decoders/${_path}) + set(EXTRA_BUNDLE_FILES ${EXTRA_BUNDLE_FILES} ${_decoder}) +endforeach() + +set(EXTRA_BUNDLE_FILES + ${EXTRA_BUNDLE_FILES} + ${QT_PLUGINS} + ${ICON_FILE} + ${PKGINFO} + ${QT_CONF} +) diff --git a/cmake/Modules/ScopyStyle.cmake b/cmake/Modules/ScopyStyle.cmake new file mode 100644 index 0000000000..260ad1d3f4 --- /dev/null +++ b/cmake/Modules/ScopyStyle.cmake @@ -0,0 +1,38 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +if(DEFINED __INCLUDED_SCOPY_STYLE_CMAKE) + return() +endif() +set(__INCLUDED_SCOPY_STYLE_CMAKE TRUE) + +# option can be --core or --plugin +function(generate_style option style_folder headers_folder) + find_package(Python3 COMPONENTS Interpreter) + execute_process( + COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/style_generator.py ${option} ${style_folder} + ${headers_folder} ${CMAKE_BINARY_DIR} RESULT_VARIABLE ret + ) + if(NOT ret EQUAL "0") + message(FATAL_ERROR "Failed to generate style files! error: ${ret}") + else() + message("-- Generated style files") + endif() +endfunction() diff --git a/cmake/Modules/ScopyStylesheets.cmake b/cmake/Modules/ScopyStylesheets.cmake new file mode 100644 index 0000000000..d48d824456 --- /dev/null +++ b/cmake/Modules/ScopyStylesheets.cmake @@ -0,0 +1,42 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +if(DEFINED __INCLUDED_SCOPY_TEST_CMAKE) + return() +endif() +set(__INCLUDED_SCOPY_TEST_CMAKE TRUE) + +# Tell CMake to run moc when necessary: +set(CMAKE_AUTOMOC ON) +# As moc files are generated in the binary dir, tell CMake to always look for includes there: +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +function(configure_stylesheets DU_OPTION) + file(GLOB_RECURSE STYLESHEETS ${CMAKE_CURRENT_SOURCE_DIR}/resources/stylesheets/templates/*.qss.c) + + foreach(_stylesheet ${STYLESHEETS}) + string(REPLACE ".c" "" FILE_OUT ${_stylesheet}) + string(REPLACE "templates/" "" FILE_OUT ${FILE_OUT}) + + execute_process(COMMAND ${CMAKE_C_COMPILER} -E -P ${DU_OPTION} ${_stylesheet} -o ${FILE_OUT}) + + message(STATUS "Done preprocessing ${_stylesheet}, file written to: ${FILE_OUT}") + endforeach() +endfunction() diff --git a/cmake/Modules/ScopyTest.cmake b/cmake/Modules/ScopyTest.cmake new file mode 100644 index 0000000000..6fdec3a879 --- /dev/null +++ b/cmake/Modules/ScopyTest.cmake @@ -0,0 +1,39 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +if(DEFINED __INCLUDED_SCOPY_STYLESHEETS_CMAKE) + return() +endif() +set(__INCLUDED_SCOPY_STYLESHEETS_CMAKE TRUE) + +# Tell CMake to run moc when necessary: +set(CMAKE_AUTOMOC ON) +# As moc files are generated in the binary dir, tell CMake to always look for includes there: +set(CMAKE_INCLUDE_CURRENT_DIR ON) +find_package(Qt5 COMPONENTS Test REQUIRED) + +function(SETUP_SCOPY_TESTS) + foreach(_testname ${ARGN}) + set(_test_target ${PROJECT_NAME}_test_${_testname}) + add_executable(${_test_target} tst_${_testname}.cpp) + add_test(NAME ${_test_target} COMMAND ${_test_target}) + target_link_libraries(${_test_target} Qt5::Test ${PROJECT_NAME}) + endforeach() +endfunction() diff --git a/cmake/Modules/ScopyTranslation.cmake b/cmake/Modules/ScopyTranslation.cmake new file mode 100644 index 0000000000..051d5ccb09 --- /dev/null +++ b/cmake/Modules/ScopyTranslation.cmake @@ -0,0 +1,44 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +if(DEFINED __INCLUDED_SCOPY_TRANSLATIONS_CMAKE) + return() +endif() +set(__INCLUDED_SCOPY_TRANSLATIONS_CMAKE TRUE) + +function(generate_translations) + file(GLOB TS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/translations/*.ts) + set_source_files_properties( + ${TS_FILES} PROPERTIES OUTPUT_LOCATION ${CMAKE_BINARY_DIR}/translations + MACOSX_PACKAGE_LOCATION ${CMAKE_BINARY_DIR}/translations + ) + qt_add_translation(QM_FILES ${TS_FILES}) + + set(TRANSLATIONS) + foreach(file ${TS_FILES}) + get_filename_component(file_name ${file} NAME_WE) + set(TRANSLATIONS "${TRANSLATIONS}\n${CMAKE_BINARY_DIR}/translations/${file_name}.qm") + endforeach() + + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/resources/translations.qrc ${CMAKE_CURRENT_BINARY_DIR}/translations.qrc + @ONLY + ) +endfunction() diff --git a/cmake/Modules/ScopyWindows.cmake b/cmake/Modules/ScopyWindows.cmake new file mode 100644 index 0000000000..817467540b --- /dev/null +++ b/cmake/Modules/ScopyWindows.cmake @@ -0,0 +1,44 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +if(DEFINED __INCLUDED_SCOPY_WINDOWS_CMAKE) + return() +endif() +set(__INCLUDED_SCOPY_WINDOWS_CMAKE TRUE) + +# Env variables +set(ENV_WORKDIR $ENV{WORKDIR}) +set(ENV_DEST_FOLDER $ENV{DEST_FOLDER}) + +configure_file(windows/scopy-64.iss.cmakein ${CMAKE_CURRENT_BINARY_DIR}/windows/scopy-64.iss @ONLY) + +macro(duplicate_target ORIGINAL NEW) + get_target_property(original_libraries ${ORIGINAL} LINK_LIBRARIES) + get_target_property(original_includes ${ORIGINAL} INCLUDE_DIRECTORIES) + get_target_property(original_options ${ORIGINAL} COMPILE_OPTIONS) + + add_executable( + ${NEW} WIN32 ${PROJECT_SOURCES} ${SCOPY_RESOURCES} ${SCOPY_DEPENDENCIES} ${SCOPY_WIN32_RESOURCES} + ) + + target_link_libraries(${NEW} PRIVATE ${original_libraries}) + target_include_directories(${NEW} PRIVATE ${original_includes}) + target_compile_options(${NEW} PRIVATE ${original_options}) +endmacro() diff --git a/cmake/Modules/ScopyWindowsInstaller.cmake b/cmake/Modules/ScopyWindowsInstaller.cmake new file mode 100644 index 0000000000..0db8c3356a --- /dev/null +++ b/cmake/Modules/ScopyWindowsInstaller.cmake @@ -0,0 +1,37 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +set(PLUGIN_COMPONENTS "" CACHE STRING "PLUGIN COMPONENTS FOR WINDOWS INSTALLER" FORCE) +set(PLUGIN_COMPONENTS_FILES "" CACHE STRING "PLUGIN COMPONENTS FILES FOR WINDOWS INSTALLER" FORCE) + +function(ConfigureInstallerSettings PLUGIN_TARGET_NAME PLUGIN_INSTALLER_DESCRIPTION FIXED_INSTALLER_OPTION) + set(SELECTABLE "; Flags: disablenouninstallwarning") + if(${FIXED_INSTALLER_OPTION} MATCHES FALSE) + set(SELECTABLE "compact custom ; Flags: fixed") + endif() + set(PLUGIN_COMPONENTS + "${PLUGIN_COMPONENTS} \n Name: \"plugins/${PLUGIN_TARGET_NAME}\"; Description: ${PLUGIN_INSTALLER_DESCRIPTION} ; Types: full plugins ${SELECTABLE} ;" + CACHE STRING "PLUGIN COMPONENTS FOR WINDOWS INSTALLER" FORCE + ) + set(PLUGIN_COMPONENTS_FILES + "${PLUGIN_COMPONENTS_FILES} \n Source:\"{#DestFolder}\\plugins\\libscopy-${PLUGIN_TARGET_NAME}.dll\"; DestDir: \"{app}\\plugins\"; Components: plugins\\${PLUGIN_TARGET_NAME}; Flags: ignoreversion skipifsourcedoesntexist onlyifdoesntexist ;" + CACHE STRING "PLUGIN COMPONENTS FILES FOR WINDOWS INSTALLER" FORCE + ) +endfunction() diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000000..bea702ebfe --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,78 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +cmake_minimum_required(VERSION 3.9) + +set(SCOPY_MODULE common) +project(scopy-${SCOPY_MODULE} VERSION 0.1 LANGUAGES CXX) + +include(GenerateExportHeader) + +# TODO: split stylesheet/resources and add here TODO: export header files correctly + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE) + +set(SCOPY_QT_COMPONENTS Core Widgets Concurrent) + +file(GLOB SRC_LIST src/*.cpp src/*.cc) +file(GLOB HEADER_LIST include/${SCOPY_MODULE}/*.h include/${SCOPY_MODULE}/*.hpp) +file(GLOB UI_LIST ui/*.ui) + +configure_file( + include/${SCOPY_MODULE}/${PROJECT_NAME}_config.h.cmakein + ${CMAKE_CURRENT_SOURCE_DIR}/include/${SCOPY_MODULE}/${PROJECT_NAME}_config.h @ONLY +) +set(SRC_LIST ${SRC_LIST} ${CMAKE_CURRENT_SOURCE_DIR}/include/${SCOPY_MODULE}/${PROJECT_NAME}_config.h) + +set(PROJECT_SOURCES ${SRC_LIST} ${HEADER_LIST} ${UI_LIST}) + +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${SCOPY_QT_COMPONENTS} REQUIRED) + +add_library(${PROJECT_NAME} SHARED ${PROJECT_SOURCES}) + +generate_export_header( + ${PROJECT_NAME} EXPORT_FILE_NAME ${CMAKE_CURRENT_SOURCE_DIR}/include/${SCOPY_MODULE}/${PROJECT_NAME}_export.h +) + +target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/${SCOPY_MODULE}) +target_include_directories(${PROJECT_NAME} PUBLIC scopy-gui scopy-pluginbase scopyiioutil) + +foreach(comp ${SCOPY_QT_COMPONENTS}) + set(SCOPY_QT_LIBRARIES ${SCOPY_QT_LIBRARIES} Qt${QT_VERSION_MAJOR}::${comp}) +endforeach() + +target_link_libraries(${PROJECT_NAME} PUBLIC ${SCOPY_QT_LIBRARIES}) + +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SCOPY_DLL_INSTALL_PATH} COMPONENT ${SCOPY_PDK} + RUNTIME DESTINATION ${SCOPY_DLL_INSTALL_PATH} +) +install(DIRECTORY include/ DESTINATION include/ COMPONENT ${SCOPY_PDK}) diff --git a/common/include/common/common.h b/common/include/common/common.h new file mode 100644 index 0000000000..9e2a50a8ce --- /dev/null +++ b/common/include/common/common.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef COMMON_H +#define COMMON_H + +#include +#include + +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0) +namespace Qt { +static auto endl = ::endl; +static auto SkipEmptyParts = QString::SkipEmptyParts; +} // namespace Qt +#endif + +#endif // COMMON_H diff --git a/common/include/common/scopy-common_config.h.cmakein b/common/include/common/scopy-common_config.h.cmakein new file mode 100644 index 0000000000..a7c6e2c159 --- /dev/null +++ b/common/include/common/scopy-common_config.h.cmakein @@ -0,0 +1,20 @@ +#ifndef SCOPY_COMMON_CONFIG_H_CMAKEIN +#define SCOPY_COMMON_CONFIG_H_CMAKEIN + +#define SCOPY_VERSION "@SCOPY_VERSION@" +#define SCOPY_VERSION_GIT "@SCOPY_VERSION_GIT@" +#define SCOPY_GIT_DIRTY "@SCOPY_GIT_DIRTY@" + +#define SCOPY_PLUGIN_INSTALL_PATH "@SCOPY_PLUGIN_INSTALL_PATH@" +#define SCOPY_PLUGIN_BUILD_PATH "@SCOPY_PLUGIN_BUILD_PATH@" + +#define SCOPY_TRANSLATION_INSTALL_PATH "@SCOPY_TRANSLATION_INSTALL_PATH@/translations" +#define SCOPY_TRANSLATION_BUILD_PATH "./translations" + +#define SCOPY_STYLE_INSTALL_PATH "@SCOPY_STYLE_INSTALL_PATH@/style" +#define SCOPY_STYLE_BUILD_PATH "./style" + +#define SCOPY_TEMP_LOG_FILE ".scopyTmpLog" + +#endif // SCOPY_COMMON_CONFIG_H_CMAKEIN + diff --git a/common/include/common/scopyconfig.h b/common/include/common/scopyconfig.h new file mode 100644 index 0000000000..2727c7fe88 --- /dev/null +++ b/common/include/common/scopyconfig.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCOPYCONFIG_H +#define SCOPYCONFIG_H + +#include "scopy-common_export.h" + +#include + +namespace scopy { + +class SCOPY_COMMON_EXPORT config +{ +public: + static QString tempLogFilePath(); + static QString defaultPluginFolderPath(); + static QString localPluginFolderPath(); + static QString defaultTranslationFolderPath(); + static QString localTranslationFolderPath(); + static QString defaultStyleFolderPath(); + static QString localStyleFolderPath(); + static QString preferencesFolderPath(); + static QString settingsFolderPath(); + static QString executableFolderPath(); + static QString version(); + static QString gitCommit(); + static QString fullversion(); + static QString os(); + static QString pcSpecs(); + + static QString dump(); + static QString getUuid(); + +private: + inline static int uuid = 0; +}; +} // namespace scopy +#endif // SCOPYCONFIG_H diff --git a/common/src/scopyconfig.cpp b/common/src/scopyconfig.cpp new file mode 100644 index 0000000000..4c7bd63a78 --- /dev/null +++ b/common/src/scopyconfig.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "scopyconfig.h" + +#include "scopy-common_config.h" + +#include +#include +#include +#include +#include + +QString scopy::config::tempLogFilePath() { return QDir::cleanPath(settingsFolderPath() + "/" + SCOPY_TEMP_LOG_FILE); } + +QString scopy::config::defaultPluginFolderPath() +{ + +#ifdef WIN32 + // Scopy_install_folder/plugins + return QCoreApplication::applicationDirPath() + "/plugins"; +#elif defined __APPLE__ + // Scopy.app/Contents/MacOS/plugins + return QCoreApplication::applicationDirPath() + "/plugins"; +#elif defined(__appimage__) + // usr/lib/plugins + return QCoreApplication::applicationDirPath() + "/../lib/scopy/plugins"; +#endif + + return SCOPY_PLUGIN_INSTALL_PATH; +} + +QString scopy::config::localPluginFolderPath() { return SCOPY_PLUGIN_BUILD_PATH; } + +QString scopy::config::defaultTranslationFolderPath() +{ +#if defined __APPLE__ + return QCoreApplication::applicationDirPath() + "/translations"; +#elif defined(__appimage__) + return QCoreApplication::applicationDirPath() + "/../lib/scopy/translations"; +#endif + return SCOPY_TRANSLATION_INSTALL_PATH; +} + +QString scopy::config::localTranslationFolderPath() { return SCOPY_TRANSLATION_BUILD_PATH; } + +QString scopy::config::defaultStyleFolderPath() +{ +#if defined __APPLE__ + return QCoreApplication::applicationDirPath() + "/style"; +#elif defined(__appimage__) + return QCoreApplication::applicationDirPath() + "/../lib/scopy/style"; +#endif + return SCOPY_STYLE_INSTALL_PATH; +} + +QString scopy::config::localStyleFolderPath() { return SCOPY_STYLE_BUILD_PATH; } + +QString scopy::config::preferencesFolderPath() +{ + return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); +} + +QString scopy::config::settingsFolderPath() +{ + return QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); +} + +QString scopy::config::executableFolderPath() { return QCoreApplication::applicationDirPath(); } + +QString scopy::config::version() { return SCOPY_VERSION; } + +QString scopy::config::gitCommit() { return SCOPY_VERSION_GIT; } + +QString scopy::config::fullversion() +{ + QString ver = QString("v") + SCOPY_VERSION + "-g" + SCOPY_VERSION_GIT; + if(QString(SCOPY_GIT_DIRTY) == "dirty") { + ver += QString("-") + "dirty"; + } + return ver; +} + +QString scopy::config::os() { return QSysInfo::prettyProductName(); } + +QString scopy::config::pcSpecs() +{ + QString ret; + ret.append("build_abi: " + QSysInfo::buildAbi()); + ret.append("\n"); + ret.append("build_cpu: " + QSysInfo::buildCpuArchitecture()); + ret.append("\n"); + ret.append("host: " + QSysInfo::machineHostName()); + ret.append("\n"); + ret.append("arch: " + QSysInfo::currentCpuArchitecture()); + ret.append("\n"); + ret.append("kernel: " + QSysInfo::kernelType()); + ret.append("\n"); + ret.append("kernel-ver: " + QSysInfo::kernelVersion()); + ret.append("\n"); + + return ret; +} + +QString scopy::config::dump() +{ + QString ret; + + ret = QString("ScopyConfig"); + ret += QString("\n") + "DefaultPluginFolderPath: " + defaultPluginFolderPath(); + ret += QString("\n") + "PreferencesPluginFolderPath: " + preferencesFolderPath(); + ret += QString("\n") + "SettingsFolderPath: " + settingsFolderPath(); + ret += QString("\n") + "ExecutableFolderPath: " + executableFolderPath(); + ret += QString("\n") + "version: " + version(); + ret += QString("\n") + "gitCommit: " + gitCommit(); + ret += QString("\n") + "fullversion: " + fullversion(); + ret += QString("\n") + "os: " + os(); + ret += QString("\n") + "pcSpecs: \n" + pcSpecs(); + + return ret; +} + +QString scopy::config::getUuid() +{ +#ifdef USE_QUUID + return QUuid.createUuid().toString(); +#else + return QString::number(uuid++); +#endif +} diff --git a/config.h.cmakein b/config.h.cmakein deleted file mode 100644 index b7e434c650..0000000000 --- a/config.h.cmakein +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef CONFIG_H_CMAKEIN -#define CONFIG_H_CMAKEIN - -#define PROJECT_VERSION "@PROJECT_VERSION@" -#define SCOPY_VERSION_GIT "@SCOPY_VERSION_GIT@" -#define BREAKPAD_HANDLER @BREAKPAD_HANDLER_BOOL@ -#define PYTHON_VERSION "@PYTHON_VERSION@" - -#endif // CONFIG_H_CMAKEIN diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt new file mode 100644 index 0000000000..1b581110e4 --- /dev/null +++ b/core/CMakeLists.txt @@ -0,0 +1,149 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +cmake_minimum_required(VERSION 3.9) + +set(SCOPY_MODULE core) +project(scopy-${SCOPY_MODULE} VERSION 0.1 LANGUAGES CXX) + +include(GenerateExportHeader) + +# TODO: split stylesheet/resources and add here TODO: export header files correctly + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE) + +set(SCOPY_QT_COMPONENTS Core Widgets Concurrent Network) + +file(GLOB SRC_LIST src/*.cpp src/*.cc) +file(GLOB HEADER_LIST include/${SCOPY_MODULE}/*.h include/${SCOPY_MODULE}/*.hpp) +file(GLOB UI_LIST ui/*.ui) + +if(ENABLE_TESTING) + add_subdirectory(test) +endif() + +option(WITH_PYTHON "Enable Python" ON) +if(${WITH_PYTHON}) + set(Python_ADDITIONAL_VERSIONS 3) + if(PYTHON_EXECUTABLE) + message(STATUS "Using custom Python EXECUTABLE: ${PYTHON_EXECUTABLE}") + set(Python3_EXECUTABLE ${PYTHON_EXECUTABLE}) + else() + message(STATUS "Using default Python EXECUTABLE") + endif() + + if(CMAKE_SYSTEM_PROCESSOR MATCHES arm OR CMAKE_SYSTEM_PROCESSOR MATCHES aarch64) + find_package(Python3 REQUIRED COMPONENTS Development) + else() + find_package(Python3 REQUIRED COMPONENTS Interpreter Development) + endif() + + message(STATUS "Python Interpreter " ${Python3_EXECUTABLE}) + message(STATUS "Python Libraries " ${Python3_LIBRARIES}) + + set(PYTHON_VERSION python${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR} CACHE STRING "PYTHON_USED") + set(PYTHON_VERSION ${PYTHON_VERSION} PARENT_SCOPE) + if(NOT Python3_FOUND) + set(WITH_PYTHON OFF) + message(STATUS "Python not found") + endif() + set(BUILD_PYTHON_LIBRARY_DIRS ${Python3_LIBRARY_DIRS}/${PYTHON_VERSION}) +endif() + +option(WITH_SIGROK "Search for libsigrokdecode" ON) +if(WITH_SIGROK) + find_package(PkgConfig) + set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON) + pkg_check_modules(GLIB REQUIRED glib-2.0) + pkg_check_modules(LIBSIGROK_DECODE REQUIRED libsigrokdecode) + pkg_get_variable(LIBSIGROK_DECODERS_DIR libsigrokdecode decodersdir) + if(NOT LIBSIGROK_DECODE_FOUND) + set(WITH_SIGROK OFF) + message(STATUS "Libsigrokdecode not found") + endif() +endif() + +option(SCOPY_DEV_MODE "Enable development specific scopy behavior - autoconnect to a context on start" FALSE) + +configure_file( + include/${SCOPY_MODULE}/${PROJECT_NAME}_config.h.cmakein + ${CMAKE_CURRENT_SOURCE_DIR}/include/${SCOPY_MODULE}/${PROJECT_NAME}_config.h @ONLY +) +set(SRC_LIST ${SRC_LIST} ${CMAKE_CURRENT_SOURCE_DIR}/include/${SCOPY_MODULE}/${PROJECT_NAME}_config.h) + +set(PROJECT_SOURCES ${SRC_LIST} ${HEADER_LIST} ${UI_LIST}) + +find_package(Qt${QT_VERSION_MAJOR} COMPONENTS ${SCOPY_QT_COMPONENTS} REQUIRED) + +add_library(${PROJECT_NAME} SHARED ${PROJECT_SOURCES}) + +generate_export_header( + ${PROJECT_NAME} EXPORT_FILE_NAME ${CMAKE_CURRENT_SOURCE_DIR}/include/${SCOPY_MODULE}/${PROJECT_NAME}_export.h +) + +target_include_directories(${PROJECT_NAME} INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_include_directories( + ${PROJECT_NAME} + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/${SCOPY_MODULE} + ${GLIB_INCLUDE_DIRS} + ${GLIBCONFIG_INCLUDE_DIRS} + ${LIBSIGROK_DECODE_INCLUDE_DIRS} + ${Python3_INCLUDE_DIRS} +) + +target_include_directories(${PROJECT_NAME} PUBLIC scopy-gui scopy-pluginbase scopy-iioutil scopy-common) + +foreach(comp ${SCOPY_QT_COMPONENTS}) + set(SCOPY_QT_LIBRARIES ${SCOPY_QT_LIBRARIES} Qt${QT_VERSION_MAJOR}::${comp}) +endforeach() + +target_link_libraries( + ${PROJECT_NAME} + PUBLIC ${SCOPY_QT_LIBRARIES} + scopy-common + scopy-gui + scopy-pluginbase + scopy-iioutil + ${LIBSIGROK_DECODE_LINK_LIBRARIES} + ${Python3_LIBRARIES} +) + +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${SCOPY_DLL_INSTALL_PATH} + RUNTIME DESTINATION ${SCOPY_DLL_INSTALL_PATH} +) + +file(COPY ${CMAKE_SOURCE_DIR}/resources/scopy_emu_options_config.json + DESTINATION ${CMAKE_BINARY_DIR}/plugins/resources/ +) +file(GLOB EMU_FILES ${CMAKE_SOURCE_DIR}/resources/emu_xml/*.xml) +foreach(_emu_file ${EMU_FILES}) + file(COPY ${_emu_file} DESTINATION ${CMAKE_BINARY_DIR}/plugins/emu_xml/) +endforeach() diff --git a/core/doc/core.qmodel b/core/doc/core.qmodel new file mode 100644 index 0000000000..477957ed32 --- /dev/null +++ b/core/doc/core.qmodel @@ -0,0 +1,8026 @@ + + + + {cb0e6182-fcf3-4d57-b703-f56fdf70a51f} + + + + + + + + {508cbebe-a13d-4171-b5af-cf6166c8c6e5} + + + ToolLauncher + + + + + + + {111561c6-c2fc-49a1-9a09-d5dd9677ecca} + + + + + + + + + + {111561c6-c2fc-49a1-9a09-d5dd9677ecca} + + + ToolLauncher + + + + + + + + + + + + {cbf1a1b0-39c4-4d44-8113-310d37c9e806} + + + {e5cec4ae-5440-402d-ae73-f83619f01a3a} + ToolMenuItem + x:-415;y:140 + x:-105;y:-45;w:210;h:90 + 0 + + + scopy + true + + + + + + + + + + + {f3fb892a-efcc-4cc7-bc8d-d8c4f2ae5478} + + + {0bc900fd-1c09-48dc-a03b-394f7e17a68f} + ToolMenu + x:45;y:65 + x:-200;y:-75;w:400;h:150 + 0 + + + scopy + true + + + + + + + + + + + {3d2fa280-8947-40f0-9dcd-362b7b3a4d1d} + + + {8c0170c2-f9d0-4491-bf3d-2204f364499a} + ToolBrowser + x:5;y:-105 + x:-80;y:-45;w:160;h:90 + 0 + + + scopy + true + + + + + + + + + + + {e6f4339c-5403-4e5b-a84e-33e7532a3814} + + + {389d9ea9-1cdf-4b08-9a5c-552104a09d78} + IIOScanTask + x:-350;y:1125 + x:-140;y:-60;w:280;h:120 + 0 + + + scopy + true + + + + + + + + + + + {2f13ea6e-7710-49ba-ba5e-0c089c6f89bf} + + + {6eea67d7-35e8-4041-b57f-f991e39eb5b0} + ToolBrowser + x:-385;y:-40 + x:-75;y:-55;w:150;h:110 + 8 + + + Ui + true + + + + + + + + + + + {f25dfcce-a733-48fe-b61f-85da7372dea4} + + + {d82707d1-ed8b-4d71-8be2-b936704d376f} + {3d2fa280-8947-40f0-9dcd-362b7b3a4d1d} + {f3fb892a-efcc-4cc7-bc8d-d8c4f2ae5478} + + + + + 1 + + + + + 1 + 2 + + + + + + + + + + + + + {078c7cdf-513e-49be-9e53-ef9b14ed39f1} + + + {b8905ba5-5a28-4eff-9f32-6b4480a1bdf9} + {cbf1a1b0-39c4-4d44-8113-310d37c9e806} + {f3fb892a-efcc-4cc7-bc8d-d8c4f2ae5478} + + + + + 1 + true + 2 + + + + + N + + + + + + + + + + + + + {e0d560f6-8887-4564-b899-9f69adbdf9d2} + + + {918ef0ee-20c4-4058-98ee-950f9198032d} + ScannedIIOContextCollector + x:0;y:1310 + x:-120;y:-95;w:240;h:190 + false + 0 + + + scopy + true + + + + + + + + + + + {1dadc92d-90a2-4f13-873e-0da9e8850886} + + + {eefce8db-f53b-4c04-a783-53141a58636c} + InfoPageStack + x:705;y:-120 + x:-120;y:-55;w:240;h:110 + 0 + + + scopy + true + + + + + + + + + + + {612af92f-aadd-4bb2-9dc2-d43351278600} + + + {636b2d0b-a84c-49f7-99c4-50acfa484261} + DeviceBrowser + x:880;y:30 + x:-150;y:-80;w:300;h:160 + 0 + + + scopy + true + + + + + + + + + + + {989a4a1a-7a00-4244-8034-3eaf149bd47d} + + + {107011b1-ab5c-41df-ae4c-d3217e9c4cd6} + ToolStack + x:-475;y:505 + x:-65;y:-55;w:130;h:110 + 0 + + + scopy + true + + + + + + + + + + + {d7d56a28-c83a-499f-a32e-a4d5d4793a7e} + + + {29d6cc8f-575c-475d-8969-1a12f0c53306} + ScopyHomePage + x:535;y:455 + x:-130;y:-130;w:260;h:260 + 0 + + + scopy + true + + + + + + + + + + + {a82a6eeb-bc09-482b-8eb0-a36c68edb4e9} + + + {f031329d-0e5c-4a90-a57e-f8a86416952a} + ScopyHomePage + x:470;y:-110 + x:-70;y:-60;w:140;h:120 + 8 + + + Ui + true + + + + + + + + + + + {6b294752-6f89-4dd6-96cd-bd717f4c54dc} + + + {da64f435-c57c-488d-8868-7ee78f602ca1} + {a82a6eeb-bc09-482b-8eb0-a36c68edb4e9} + {d7d56a28-c83a-499f-a32e-a4d5d4793a7e} + + + + + 1 + true + 2 + + + + + + + + + + + + + {129bd5e3-9e46-42b9-806f-8f6d279d7374} + + + {eb5e4992-ae81-4b22-ac67-12549cd7ef64} + {2f13ea6e-7710-49ba-ba5e-0c089c6f89bf} + {f3fb892a-efcc-4cc7-bc8d-d8c4f2ae5478} + + + + + 1 + true + 2 + + + + + + + + + + + + + {2358ec04-d8eb-4c0f-8714-c1fa621cb6fb} + + + {a37e8d5a-2c78-4159-af53-b3d1f513d9ee} + ScopyHomeAddPage + x:950;y:425 + x:-160;y:-195;w:320;h:390 + 0 + + + scopy + true + + + + + + + + + + + {03139944-2440-4bb1-a1e1-26cae263e3b0} + + + {a129f405-975e-421b-83b6-ebdd955755b1} + {2358ec04-d8eb-4c0f-8714-c1fa621cb6fb} + {d7d56a28-c83a-499f-a32e-a4d5d4793a7e} + + + + + 1 + true + 2 + + + + + + + + + + + + + {c48402ba-05a9-4c42-be10-6b9be218d505} + + + {5df62805-153e-4580-ad33-aa575d45b6fe} + {1dadc92d-90a2-4f13-873e-0da9e8850886} + {d7d56a28-c83a-499f-a32e-a4d5d4793a7e} + + + + + 1 + true + 2 + + + + + + + + + + + + + {51bbd323-1840-4ac8-96f0-fa46643eda8e} + + + {25806c5b-f5b2-4140-b3d3-2e86ad2b6303} + {612af92f-aadd-4bb2-9dc2-d43351278600} + {d7d56a28-c83a-499f-a32e-a4d5d4793a7e} + + + + + 1 + true + 2 + + + + + + + + + + + + + {0c8b1776-fff4-4ce4-ae38-3f7964627170} + + + {65918884-8aa9-46c0-91f2-20de46f824fc} + {a82a6eeb-bc09-482b-8eb0-a36c68edb4e9} + {612af92f-aadd-4bb2-9dc2-d43351278600} + + + + + + + + + + + + + {8eeba126-8b79-4a38-a0bf-49f94489c490} + + + {ad47f512-aade-4023-9a91-64a07c0ed8f4} + {a82a6eeb-bc09-482b-8eb0-a36c68edb4e9} + {1dadc92d-90a2-4f13-873e-0da9e8850886} + + + + + + + + + + + + + {25ac4316-5b0b-458b-821c-7daf350511fc} + + + {91cee32b-4c75-42aa-aa16-baa54c9b6810} + {2f13ea6e-7710-49ba-ba5e-0c089c6f89bf} + {3d2fa280-8947-40f0-9dcd-362b7b3a4d1d} + + + + + + + + + + + + + {e5fd782e-263b-4fcb-bf82-b6be6be9618a} + + + {192a9656-2796-478a-a40b-5619baa8acc9} + ScopyMainWindow + x:-280;y:500 + x:-70;y:-35;w:140;h:70 + false + 8 + + + Ui + + + + + + + + + + + {a82cc4e2-4485-4c4a-9ac2-6a3ee8f33ee8} + + + {2cddb820-4d60-4b57-90c6-9dd7e5909d7b} + {e5fd782e-263b-4fcb-bf82-b6be6be9618a} + {989a4a1a-7a00-4244-8034-3eaf149bd47d} + + + + + + + + + + + + + {6fbd0952-2e96-4a21-9edc-45005f7110f7} + + + {2a1d55a6-8f45-46fb-b4a6-cb7316182a4b} + {e5fd782e-263b-4fcb-bf82-b6be6be9618a} + {f3fb892a-efcc-4cc7-bc8d-d8c4f2ae5478} + + + + + + + + + + + + + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + + + {20440a58-5e88-4172-971b-4184419b0636} + ScopyMainWindow + x:100;y:765 + x:-155;y:-180;w:310;h:360 + 0 + + + scopy + true + + + + + + + + + + + {a00ecd28-cd7d-434e-bbe1-b756de93b404} + + + {dff7f0d9-f6de-4896-af57-6cd0209daa56} + DeviceManager + x:720;y:1130 + x:-170;y:-160;w:340;h:320 + 0 + + + scopy + true + + + + + + + + + + + {9d6dca31-5f32-4536-aa47-7b4002b77910} + + + {8dfe726b-edd7-47bd-a36f-539ed5df80a7} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {e0d560f6-8887-4564-b899-9f69adbdf9d2} + + + + + 1 + true + 2 + + + + + + + + + + + + + {2a301dcf-63db-47d4-b655-135d5493f0bc} + + + {9badadce-e007-4165-9edb-c8d769748076} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {a00ecd28-cd7d-434e-bbe1-b756de93b404} + + + + + 1 + true + 2 + + + + + + + + + + + + + {9674d2db-744f-4611-b219-dd07ddb93528} + + + {702be005-47af-4698-ada9-d7b835e35d12} + + + interface + + + Device + x:715;y:1550 + x:-120;y:-185;w:240;h:370 + 5 + 1 + + + scopy + true + + + + + + + + + + + {0553e4ac-74bb-447f-b1ad-cd7348d01f10} + + + {3b61ef28-c6a5-4942-8d56-094ea9b42f5e} + {a00ecd28-cd7d-434e-bbe1-b756de93b404} + {9674d2db-744f-4611-b219-dd07ddb93528} + + + + + n + true + 2 + + + + + + + + + + + + + {47b0c4b4-ca46-47b2-88d1-a86d5c39ef34} + + + {ac3c9c31-229f-40fb-a01c-7cb74c329e3c} + ScopyHomeInfoPage + x:855;y:165 + x:-60;y:-30;w:120;h:60 + 8 + + + Ui + + + + + + + + + + + {7560fdc0-d1ee-4952-89a3-09c32b507caf} + + + {bd470a57-63dc-4d8a-8ed3-80f16bf2eaae} + ScopyHomeAddPage + x:1210;y:430 + x:-60;y:-30;w:120;h:60 + 8 + + + Ui + + + + + + + + + + + {a09ff26f-2b33-4ade-8f18-367d489ee94b} + + + {5bb1f232-8ff5-4cfe-8f23-c1ea90f3a5a6} + {7560fdc0-d1ee-4952-89a3-09c32b507caf} + {2358ec04-d8eb-4c0f-8714-c1fa621cb6fb} + + + + + 1 + true + 2 + + + + + + + + + + + + + {9a64a7d3-be7d-4dff-a9ca-c0cc48ce442b} + + + {11ca845e-4c2d-434a-9c8a-e0fa6a05a291} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {d7d56a28-c83a-499f-a32e-a4d5d4793a7e} + + + + + 1 + true + 2 + + + + + + + + + + + + + {d5152f58-1810-475d-83a4-d9ce3465511b} + + + {d47f0fa8-cf3f-4c10-9b02-f80af3d57baa} + ToolManager + x:40;y:290 + x:-175;y:-105;w:350;h:210 + 0 + + + scopy + true + + + + + + + + + + + {cbd0db94-432f-4015-b9d6-9c96d2d028e5} + + + {153d8a14-7f4c-4bff-b84d-43e9fe9899dc} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {d5152f58-1810-475d-83a4-d9ce3465511b} + + + + + 1 + true + 2 + + + + + + + + + + + + + {63c95c20-64f2-4942-b244-ef718096e1ea} + + + {64a0db56-30d7-4da2-8563-1dbc182717ec} + {d5152f58-1810-475d-83a4-d9ce3465511b} + {f3fb892a-efcc-4cc7-bc8d-d8c4f2ae5478} + + + + + true + + + + + + + + + + + + + {240e01ed-e5fe-4e03-bfd4-ab9647bb2e3a} + + + {e9e0564d-6ef2-4282-8b77-74f81fa6ba6f} + + + singleton + + + Preferences + x:1575;y:350 + x:-155;y:-110;w:310;h:220 + false + 0 + + + scopy + true + + + + + + + + + + + {42fe1fc3-b6ce-4ac8-8209-f31459946b28} + + + {ae9b784c-2da5-4b97-be82-301848323aa1} + ScopyPreferencesPage + x:-280;y:570 + x:-70;y:-30;w:140;h:60 + 8 + + + scopy + + + + + + + + + + + {1ccbb4c6-6f1b-42bc-9ff1-b460fea3d047} + + + {acb80f41-d754-4cc2-95a7-df775fa7157e} + ScopyAboutPage + x:-280;y:645 + x:-70;y:-35;w:140;h:70 + false + 8 + + + scopy + + + + + + + + + + + {efe16de1-4e02-45d5-b47c-537b1c5530fb} + + + {3ada95f5-70ce-4634-9e56-589693e0de37} + + + singleton + + + ScopyJS + x:1575;y:100 + x:-155;y:-130;w:310;h:260 + 0 + + + scopy + true + + + + + + + + + + + {5a698e6a-160c-455b-a745-bbf733dd1522} + + + {e929bb03-3710-4a7f-bccf-85ab7e6df124} + + + singleton + + + ContextProvider + x:1920;y:50 + x:-155;y:-75;w:310;h:150 + false + 0 + + + scopy + true + + + + + + + + + + + {7fcaf349-688a-4308-ab75-74f6d692b1a4} + + + {3e714570-f017-421b-9eac-3f4d4b535143} + + + singleton + + + MessageBroker + x:1920;y:220 + x:-155;y:-85;w:310;h:170 + false + 0 + + + scopy + true + + + + + + + + + + + {60df347c-395a-43a7-8f36-bedf20163d09} + + + {ecebe4b3-59a0-464e-9a27-9310eb87cdb1} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {e5fd782e-263b-4fcb-bf82-b6be6be9618a} + + + + + 1 + true + 2 + + + + + + + + + + + + + {680be7fc-30c7-4d43-b4d0-27157f67889b} + + + {88854229-1455-426f-b2b4-c0bb4020e2ea} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {42fe1fc3-b6ce-4ac8-8209-f31459946b28} + + + + + 1 + true + 2 + + + + + + + + + + + + + {7824be7f-c2fb-4385-8d68-93e4271f089d} + + + {9e8a83cb-c801-4e25-8ba4-faf0903518f8} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {1ccbb4c6-6f1b-42bc-9ff1-b460fea3d047} + + + + + 1 + true + 2 + + + + + + + + + + + + + {88d1388a-63f4-4542-a199-737a9570fd29} + + + {4c2b6925-c606-4fe3-a0eb-197091db6b33} + CyclicalTask + x:-290;y:1310 + x:-145;y:-85;w:290;h:170 + 0 + + + scopy + true + + + + + + + + + + + {d51b3fd8-c984-4bbd-b944-8f88319cae30} + + + {e1a0004d-30e5-4261-86c2-03b0f45bee6e} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {88d1388a-63f4-4542-a199-737a9570fd29} + + + + + 1 + true + 2 + + + + + + + + + + + {b8547173-dd1b-48f7-99e3-d6a3410cc5c6} + + + ToolMenu + x:-135;y:120 + x:-435;y:-310;w:870;h:620 + + + + + + + + + {99e99528-e87b-478e-9dfd-37e0339561cd} + + + HomePage + x:820;y:200 + x:-460;y:-430;w:920;h:860 + + + + + + + + + + + {eb5ec4b1-d69d-4aa5-9afe-c696d435e4fe} + + + {57fd3927-5b73-4f4a-aabc-63bc25097500} + {47b0c4b4-ca46-47b2-88d1-a86d5c39ef34} + {d7d56a28-c83a-499f-a32e-a4d5d4793a7e} + + + + + 1 + true + 2 + + + + + + + + + + + + + {58cccf1c-7b09-4d41-9782-9357ccc68967} + + + {b8f6dbab-53f3-4373-ab10-3a89e33f6b9e} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {e6f4339c-5403-4e5b-a84e-33e7532a3814} + + + + + 1 + true + 2 + + + + + + + + + + + {88c596a6-0703-4a64-ba12-81a269b4aeb9} + + + IIO Scanner + x:-190;y:1220 + x:-340;y:-200;w:680;h:400 + + + + + + + + + + + {1b73a85c-954f-4743-aa0f-de25791a6c71} + + + {ddefe768-27cd-4826-a886-1360d27ca8be} + PluginRepository + x:550;y:725 + x:-165;y:-65;w:330;h:130 + false + 0 + + + scopy + true + + + + + + + + + + + {b2514c89-6ccc-4ec8-82ed-1031864f6f64} + + + {ae24a692-ce63-448e-a073-78f38dc60f1d} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {1b73a85c-954f-4743-aa0f-de25791a6c71} + + + + + 1 + true + 2 + + + + + + + + + + + + + {6ac10fe1-9ee3-4a25-9abf-b29af9a1a4d5} + + + {84a2203a-9901-430d-b2b0-eceb9873acf8} + PluginManager + x:1550;y:880 + x:-265;y:-90;w:530;h:180 + 0 + + + scopy + true + + + + + + + + + {1b8091ae-0ef7-450e-bd6e-27bb301690c6} + + + Loads plugins from files + x:385;y:640 + x:0;y:0;w:134.375;h:29 + + + + + + + + + + + {14c3a9e6-25bb-4833-8ec5-6cf015e8877d} + + + {3f6d361a-88d5-4fff-954e-224fdc5049c6} + {1b73a85c-954f-4743-aa0f-de25791a6c71} + {6ac10fe1-9ee3-4a25-9abf-b29af9a1a4d5} + + + + + 1 + true + 2 + + + + + + + + + + + + + {dcec9ade-b44a-4f1c-b0ab-f0a6c377a3ec} + + + {f9b391c1-641c-43ef-92b7-d9e625384a4d} + {a00ecd28-cd7d-434e-bbe1-b756de93b404} + {6ac10fe1-9ee3-4a25-9abf-b29af9a1a4d5} + + + + + + + + + + + + + {ed9372fe-6800-4643-9c09-6a75193bd68a} + + + {1c24efbf-2095-42da-b591-03ac51de74f5} + + + interface + + + Plugin + x:1325;y:1390 + x:-180;y:-305;w:360;h:610 + 5 + 1 + + + scopy + true + + + + + + + + + + + {ec8c339d-7ab2-477c-9ee9-aadd205087ec} + + + {80f110fd-3e62-4acc-b9ec-e8c4b12aed86} + {ed9372fe-6800-4643-9c09-6a75193bd68a} + {6ac10fe1-9ee3-4a25-9abf-b29af9a1a4d5} + + + + + + + + + + + + + {28fe2748-079e-444e-8bfa-ce9344159918} + + + {34a37e62-1b26-470c-aab8-ff932ba4af4f} + DeviceImpl + x:400;y:1630 + x:-125;y:-320;w:250;h:640 + 0 + + + scopy + true + + + + + + + + + + + {6a3dbf9d-da92-4ced-9188-b1fe3a1ebad7} + + + {2dc09cab-f03e-4ea4-bc60-730da5bd016f} + IIODeviceImpl + x:400;y:2030 + x:-125;y:-45;w:250;h:90 + 0 + + + scopy + true + + + + + + + + + + + {29553b09-f6ba-44d3-86a5-c9e6aceaf964} + + + {e021e2a2-f351-43c2-989e-69e9863fdab5} + {6a3dbf9d-da92-4ced-9188-b1fe3a1ebad7} + {28fe2748-079e-444e-8bfa-ce9344159918} + + + + + + + + + + + + + {36166842-6d70-491c-b937-f1c755b28dbd} + + + {32339e89-b019-479e-acdb-85e33500b5e4} + {28fe2748-079e-444e-8bfa-ce9344159918} + {9674d2db-744f-4611-b219-dd07ddb93528} + + + + + + + + + + + {096cd55c-724d-45de-93bb-6856b4aac365} + + + Devices + x:580;y:1575 + x:-320;y:-630;w:640;h:1260 + + + + + + + + + + + {98f23e06-d72a-4a9b-a17a-2b1535e3120f} + + + {e10a2c0d-269a-4a87-814f-76f00ac85e1d} + virtual Pluginbase + x:1745;y:1365 + x:-195;y:-250;w:390;h:500 + 0 + + + scopy + true + + + + + + + + + + + {225b0ef4-25ab-4719-9823-add711661609} + + + {57c5ba1b-5d78-4df0-9ea5-ae40bd393211} + {98f23e06-d72a-4a9b-a17a-2b1535e3120f} + {ed9372fe-6800-4643-9c09-6a75193bd68a} + + + + + + + + + + + + + {a5330533-f27d-4453-b69c-292066bcf3d5} + + + {e5d799f1-f437-4b60-8f90-c121dcdfffa7} + MinimalPlugin + x:2235;y:1160 + x:-175;y:-75;w:350;h:150 + false + 0 + + + scopy + true + + + + + + + + + + + {a750c1d6-a2c0-4c43-9992-60b1a15bfdf6} + + + {7b13e5c2-2eef-432f-91bf-bcec1227f956} + {a5330533-f27d-4453-b69c-292066bcf3d5} + {98f23e06-d72a-4a9b-a17a-2b1535e3120f} + + + + + + + + + + + + + {e6ad741d-1489-4d2a-8df2-bd7d3af7a2b4} + + + {6a29bb57-e0d9-4d22-b8a5-71202bb598ed} + Some Other plugin + x:2235;y:1590 + x:-175;y:-45;w:350;h:90 + false + 0 + + + true + + + + + + + + + + + {166a2fd1-71f5-45d8-9998-914fb199b6e1} + + + {4e384797-208b-4c31-9e46-719c31291e37} + {e6ad741d-1489-4d2a-8df2-bd7d3af7a2b4} + {98f23e06-d72a-4a9b-a17a-2b1535e3120f} + + + + + + + + + + + + + {8cbf27f4-011a-4974-9f8e-cfe04de06aa2} + + + {c8c59201-be7f-4531-bc4e-4e6483be1f22} + TestPlugin + x:2235;y:1390 + x:-175;y:-130;w:350;h:260 + 0 + + + scopy + true + + + + + + + + + + + {4ee60f60-2861-4744-a9be-6e01487df093} + + + {0f0647cb-bab1-4254-be37-17453948da23} + {8cbf27f4-011a-4974-9f8e-cfe04de06aa2} + {98f23e06-d72a-4a9b-a17a-2b1535e3120f} + + + + + + + + + + + + + {0c68532d-c719-4332-aaf7-5ca00948a1c5} + + + {d24c3ed9-f0fb-455a-94b1-1b2a54f4a3ff} + {ed9372fe-6800-4643-9c09-6a75193bd68a} + {9674d2db-744f-4611-b219-dd07ddb93528} + + + + + + + + + + + {95724acf-5127-4f02-9604-e68a876a8a69} + + + Plugins +-- implemented in pluginbase/core -- + x:1755;y:1265 + x:-700;y:-550;w:1400;h:1100 + + + + + + + + + {112dcc25-6466-461c-aa31-c3941425f8f8} + + + Singleton + x:1740;y:210 + x:-375;y:-290;w:750;h:580 + + + + + + + + + + + {1eb97c8b-dea2-47ad-b34f-0ed781259b76} + + + {fdd7b05c-bb73-4636-952a-c8ce39f7be97} + DeviceLoader + x:395;y:1205 + x:-125;y:-60;w:250;h:120 + 0 + + + scopy + true + + + + + + + + + + + {9c8aec2f-347d-4c37-83b3-75470ee817fc} + + + {b744b131-fa45-4ba3-8a9c-8c15ee06cc4a} + DeviceFactory + x:575;y:2150 + x:-305;y:-40;w:610;h:80 + false + 0 + + + scopy + true + + + + + + + + + + + {beca654d-203c-4f03-8fb3-2baae25a0452} + + + {6a105ad4-9d98-4970-b0c2-e6f14678fdee} + {1eb97c8b-dea2-47ad-b34f-0ed781259b76} + {28fe2748-079e-444e-8bfa-ce9344159918} + + + + + + + + + + + + + {8a822583-9784-4235-bb83-255ac08fa782} + + + {13856c2c-5538-46d8-970f-caf303391d1a} + {a00ecd28-cd7d-434e-bbe1-b756de93b404} + {1eb97c8b-dea2-47ad-b34f-0ed781259b76} + + + + + + + + + + + + + {7ce85ce1-680a-45a8-b79f-a29aa67ef875} + + + {48d6cbe3-addd-452e-979f-13e57400b86c} + ScopyMainWindow_API + x:-335;y:805 + x:-165;y:-110;w:330;h:220 + 0 + + + scopy + true + + + + + + + + + + + {124f058e-4d04-40f6-941a-239bfcfefdc5} + + + {a7dfde00-b313-42be-b1a1-9709b82b389d} + {e6ed922c-ecdc-42c9-b27e-5504f1061806} + {7ce85ce1-680a-45a8-b79f-a29aa67ef875} + + + + + 1 + true + 2 + + + + + + + + + + + + + {77dd9724-9102-457d-a718-308702cd05b8} + + + {43312ec0-6b21-44ef-92c2-11e826117743} + CmdLineHandler + x:-870;y:805 + x:-280;y:-70;w:560;h:140 + 0 + + + scopy + true + + + + + + + + + + + {af857f3c-2f24-4b01-88d9-977f771e4fe2} + + + {e0bf02f8-dd4d-4146-82ff-5262db0d401f} + {77dd9724-9102-457d-a718-308702cd05b8} + {7ce85ce1-680a-45a8-b79f-a29aa67ef875} + + + + + + + + 1695643610058 + General + + + + + + + + + + {e5cec4ae-5440-402d-ae73-f83619f01a3a} + + + + + + + + {e5cec4ae-5440-402d-ae73-f83619f01a3a} + + + ToolMenuItem + + + + + + + {b8905ba5-5a28-4eff-9f32-6b4480a1bdf9} + + + + + + + + {b8905ba5-5a28-4eff-9f32-6b4480a1bdf9} + + + {e5cec4ae-5440-402d-ae73-f83619f01a3a} + {0bc900fd-1c09-48dc-a03b-394f7e17a68f} + + + + + 1 + true + 2 + + + + + N + + + + + + + + + + + + + + scopy + + + + + {36e72e53-f9a5-4cc8-b456-a2687b1d0327} + 2 + 4 + QPushButton *getToolBtn() + + + + + {0b854737-b70d-4ea6-b69d-e42ac838db3d} + 2 + 4 + QPushButton *getToolRunBtn() + + + + + + + + + + + + {0bc900fd-1c09-48dc-a03b-394f7e17a68f} + + + + + + + + {0bc900fd-1c09-48dc-a03b-394f7e17a68f} + + + ToolMenu + + + + + + + {12f9e897-5dbe-4bfa-a31a-c632a2b662f8} + + + + + + + + {12f9e897-5dbe-4bfa-a31a-c632a2b662f8} + + + {0bc900fd-1c09-48dc-a03b-394f7e17a68f} + {e5cec4ae-5440-402d-ae73-f83619f01a3a} + + + + + n + + + + + 2 + + + + + + + + + + + + + + scopy + + + + + {a1b40a7f-98ec-434f-9764-8dc892d67a70} + 1 + QVector<ToolMenuItem*> tools + + + + + {98107d0a-c99a-4fcc-97dd-3b71750e7d76} + 2 + ToolMenuItem *getToolMenuItemFor(QString toolId) + + + + + {11618b28-f6c9-47ff-9512-9592d2577cdb} + 2 + ToolMenuItem *addTool(QString id, QString name, QString icon, int position) + + + + + {4ce8fbed-1bfc-40dc-8539-3719322dd8e8} + 2 + bool removeTool(QString id) + + + + + {95b82167-f1c6-40db-9221-a8c00301f0cd} + 2 + bool removeTool(ToolMenuItem *tmi) + + + + + {b5d2649c-d514-4989-8190-a56986a637d0} + 2 + 256 + void requestTool(QString) + + + + + + + + + + + + {8c0170c2-f9d0-4491-bf3d-2204f364499a} + + + + + + + + {8c0170c2-f9d0-4491-bf3d-2204f364499a} + + + ToolBrowser + + + + + + + {49ee99b0-a8f7-451b-868c-f9ac5bdb8158} + + + + + + + + {49ee99b0-a8f7-451b-868c-f9ac5bdb8158} + + + {8c0170c2-f9d0-4491-bf3d-2204f364499a} + {6eea67d7-35e8-4041-b57f-f991e39eb5b0} + + + + + 1 + + + + + 2 + + + + + + + + + + {d82707d1-ed8b-4d71-8be2-b936704d376f} + + + + + + + + {d82707d1-ed8b-4d71-8be2-b936704d376f} + + + {8c0170c2-f9d0-4491-bf3d-2204f364499a} + {0bc900fd-1c09-48dc-a03b-394f7e17a68f} + + + + + 1 + + + + + 1 + 2 + + + + + + + + + + + + + + scopy + + + + + {fbaa9793-4fa5-4c0b-82e0-c1793eb4f1ff} + 2 + ToolMenu* getToolMenu() + + + + + {216c3368-d660-4606-942b-90cbeed4df4a} + 2 + 512 + void requestTool(QString) + + + + + + + + + + + + {4adea8b3-f07c-4731-9092-f050e7182002} + + + + + + + + {4adea8b3-f07c-4731-9092-f050e7182002} + + + singleton ContextProvider + + + + + + + {d2eea9a3-73e3-42e6-927f-509b7ffd899e} + + + + + + + + {d2eea9a3-73e3-42e6-927f-509b7ffd899e} + + + {4adea8b3-f07c-4731-9092-f050e7182002} + {a37e8d5a-2c78-4159-af53-b3d1f513d9ee} + + + + + + + + + + + + + + adiscope + + + + + {432fdc86-1c3b-471e-b095-5980476ec218} + 2 + 64 + ContextProvider *GetInstance() + + + + + {7c048750-d8e5-41a5-9bc3-d42dc71a7e30} + 2 + struct iio_context* open(QString uri) + + + + + {5f65ce4f-ef81-400e-a4d8-a714ecf23438} + 2 + void close(QString uri) + + + + + + + + + + + + {389d9ea9-1cdf-4b08-9a5c-552104a09d78} + + + + + + + + {389d9ea9-1cdf-4b08-9a5c-552104a09d78} + + + IIOScanTask + + + + + + + {44e0cae5-892c-49ed-be14-2b822a803abf} + + + + + + + + {44e0cae5-892c-49ed-be14-2b822a803abf} + + + {389d9ea9-1cdf-4b08-9a5c-552104a09d78} + {20440a58-5e88-4172-971b-4184419b0636} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + {7faa67cc-7d76-4ad6-b139-c1a6ff0ea38c} + 2 + 9 + void run() + + + + + {c8b24a61-4701-44bf-a245-e1bb0ce63ece} + 2 + void setScanParams(QString s) + + + + + {8ddc84d6-7858-4275-be1a-d6504561bb52} + 2 + 64 + int scan(QStringList *ctxs, QString scanParams) + + + + + {208b0601-5cbc-4ce0-ba09-7950fb38b7b3} + 4 + 2 + void scanFinished(QStringList) + + + + + + + + + + + + {ff8114ef-a9e0-42db-90d2-1857b63825dc} + + + + + + + + {ff8114ef-a9e0-42db-90d2-1857b63825dc} + + + + + + + + + + + + {6eea67d7-35e8-4041-b57f-f991e39eb5b0} + + + + + + + + {6eea67d7-35e8-4041-b57f-f991e39eb5b0} + + + ToolBrowser + + + + + + + {eb5e4992-ae81-4b22-ac67-12549cd7ef64} + + + + + + + + {eb5e4992-ae81-4b22-ac67-12549cd7ef64} + + + {6eea67d7-35e8-4041-b57f-f991e39eb5b0} + {0bc900fd-1c09-48dc-a03b-394f7e17a68f} + + + + + 1 + true + 2 + + + + + + + + + + {91cee32b-4c75-42aa-aa16-baa54c9b6810} + + + + + + + + {91cee32b-4c75-42aa-aa16-baa54c9b6810} + + + {6eea67d7-35e8-4041-b57f-f991e39eb5b0} + {8c0170c2-f9d0-4491-bf3d-2204f364499a} + + + + + + + + + + + + + + Ui + + + + + {7c76fb9c-b90b-4680-8b90-16c1dff823e6} + 1 + QPushButton* btnHome + + + + + {e86d9c16-9df6-4314-87f0-14e3cbb82591} + 1 + QPushButton* btnAbout + + + + + {12aec0be-67e9-4f01-832c-8d22ecc031a4} + 1 + ToolMenu *tm + + + + + + + + + + + + {918ef0ee-20c4-4058-98ee-950f9198032d} + + + + + + + + {918ef0ee-20c4-4058-98ee-950f9198032d} + + + ScannedIIOContextCollector + + + scopy + + + + + {20e65fed-fbe5-4fad-b5d3-5b55e8bfdece} + 7 + 2 + void update(QStringList uris) + + + + + {29a98ba9-7508-461d-aa2d-b0beb726813d} + 7 + 2 + void clearCache() + + + + + {4abbefc1-4fd0-4d2f-bb75-b56f71b0ada0} + 7 + 2 + void lock(QString, Device*) + + + + + {210447f5-e4e3-46cf-816d-dd7007d0e1c6} + 7 + 2 + void unlock(QString, Device*) + + + + + {f460c3ff-4173-44de-beec-9452c6aecfba} + 7 + 2 + void removeDevice(QString id, Device *d) + + + + + {12afad26-817f-47a6-b469-e1184c31c5c2} + 4 + 2 + void foundDevice(QString cat, QString uri) + + + + + {4cbbb274-6d85-4d6c-9087-50298aac2188} + 4 + 2 + void lostDevice(QString cat, QString uri) + + + + + + + + + + + + {eefce8db-f53b-4c04-a783-53141a58636c} + + + + + + + + {eefce8db-f53b-4c04-a783-53141a58636c} + + + InfoPageStack + + + + + + + {b9e42e5a-2a4a-4d6b-a5f7-611e196dc026} + + + + + + + + {b9e42e5a-2a4a-4d6b-a5f7-611e196dc026} + + + {eefce8db-f53b-4c04-a783-53141a58636c} + {f031329d-0e5c-4a90-a57e-f8a86416952a} + + + + + 1 + true + 2 + + + + + + + + + + {5df62805-153e-4580-ad33-aa575d45b6fe} + + + + + + + + {5df62805-153e-4580-ad33-aa575d45b6fe} + + + {eefce8db-f53b-4c04-a783-53141a58636c} + {29d6cc8f-575c-475d-8969-1a12f0c53306} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + {83ddeab3-5dd1-4f34-a021-fd9ea9e5774f} + 2 + 8 + void add(QString key, QWidget* w) + + + + + {7ee7b2f4-1845-4dd3-a36a-dff81d50dca7} + 2 + 8 + bool remove(QString key) + + + + + {e8b78940-11a5-4ce5-b13e-fa26d9fa93ea} + 7 + 2 + 8 + bool show(QString key) + + + + + + + + + + + + {636b2d0b-a84c-49f7-99c4-50acfa484261} + + + + + + + + {636b2d0b-a84c-49f7-99c4-50acfa484261} + + + DeviceBrowser + + + + + + + {c4254d5d-23a2-4a11-91da-22f1a8460849} + + + + + + + + {c4254d5d-23a2-4a11-91da-22f1a8460849} + + + {636b2d0b-a84c-49f7-99c4-50acfa484261} + {f031329d-0e5c-4a90-a57e-f8a86416952a} + + + + + 1 + true + 2 + + + + + + + + + + {25806c5b-f5b2-4140-b3d3-2e86ad2b6303} + + + + + + + + {25806c5b-f5b2-4140-b3d3-2e86ad2b6303} + + + {636b2d0b-a84c-49f7-99c4-50acfa484261} + {29d6cc8f-575c-475d-8969-1a12f0c53306} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + {0eb1bd9c-238d-4f56-b876-f76f3fdbbd65} + 1 + 2 + QAbstractButton *getDeviceWidgetFor(QString id) + + + + + {d987b540-7d2f-4736-882b-807f04f58d14} + 1 + 2 + void addDevice(QString id, Device *d, int position = -1) + + + + + {5ce07faa-ca3c-4b54-952b-e22614b37c8a} + 1 + 2 + void removeDevice(QString id) + + + + + {eada4e16-1550-4812-b566-2ebad4e0dc9d} + 1 + 2 + void connectDevice(QString id) + + + + + {14f3afcf-7a80-4d82-9659-a9cbdc3f94a0} + 1 + 2 + void disconnectDevice(QString id) + + + + + {40ba81ae-13c6-434d-8351-09111531e855} + 4 + 2 + void requestDevice(QString id, int direction) + + + + + {99bcaf5f-2d30-4585-892e-bc31a605b374} + 4 + 2 + void requestRemoveDevice(QString id) + + + + + + + + + + + + {107011b1-ab5c-41df-ae4c-d3217e9c4cd6} + + + + + + + + {107011b1-ab5c-41df-ae4c-d3217e9c4cd6} + + + ToolStack + + + scopy + + + + + {b855cc7d-e32c-41d1-b4a1-79281fbdb325} + 2 + void add(QString) + + + + + {f8b684c5-93b6-4e9b-9c3d-66b895483ce8} + 2 + void remove(QString) + + + + + {b72b4cd3-985a-413b-8a54-0aac988dada2} + 7 + 2 + bool show(QString) + + + + + + + + + + + + {29d6cc8f-575c-475d-8969-1a12f0c53306} + + + + + + + + {29d6cc8f-575c-475d-8969-1a12f0c53306} + + + ScopyHomePage + + + + + + + {f13570ac-eec4-455a-9084-d9849839b9dc} + + + + + + + + {f13570ac-eec4-455a-9084-d9849839b9dc} + + + {29d6cc8f-575c-475d-8969-1a12f0c53306} + {eefce8db-f53b-4c04-a783-53141a58636c} + + + + + + + + + + {ed23e470-6f1d-4fa4-84d1-1b1a5a0b3cc7} + + + + + + + + {ed23e470-6f1d-4fa4-84d1-1b1a5a0b3cc7} + + + {29d6cc8f-575c-475d-8969-1a12f0c53306} + {a37e8d5a-2c78-4159-af53-b3d1f513d9ee} + + + + + 1 + true + 2 + + + + + + + + + + {a4bb44ae-2bd4-4a92-8598-2257c0e3ea58} + + + + + + + + {a4bb44ae-2bd4-4a92-8598-2257c0e3ea58} + + + {29d6cc8f-575c-475d-8969-1a12f0c53306} + {eefce8db-f53b-4c04-a783-53141a58636c} + + + + + 1 + true + 2 + + + + + + + + + + {a4c6fda0-3ae3-4bbf-bb21-9e1f3481ff34} + + + + + + + + {a4c6fda0-3ae3-4bbf-bb21-9e1f3481ff34} + + + {29d6cc8f-575c-475d-8969-1a12f0c53306} + {20440a58-5e88-4172-971b-4184419b0636} + + + + + 1 + true + 2 + + + + + + + + + + {6b46768c-fb1a-4874-a0f2-dbcc8d252d4b} + + + + + + + + {6b46768c-fb1a-4874-a0f2-dbcc8d252d4b} + + + {29d6cc8f-575c-475d-8969-1a12f0c53306} + {ac3c9c31-229f-40fb-a01c-7cb74c329e3c} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + {abd076ba-1de4-427d-a7ad-0307eebb3128} + 4 + 2 + void requestAddDevice(QString cat, QString id) + + + + + {de51d817-4491-4521-a341-5d67a1169018} + 4 + 2 + void requestRemoveDevice(QString id) + + + + + {97e42ccd-928a-42d9-9372-132348a92c03} + 4 + 2 + void requestDevice(QString id) + + + + + {2baf03f2-830a-4207-bf73-19009c78685e} + 4 + 2 + void deviceAddedToUi(QString id) + + + + + {e734afb1-4fa4-4fc4-b17f-99a6e57badc9} + 4 + 2 + void newDeviceAvailable(DeviceImpl *d) + + + + + {27ae4183-b9b3-468c-9210-c93df9193c1f} + 7 + 2 + void addDevice(QString id, Device*) + + + + + {c3ee5d4f-6b89-46cf-b44e-addcc174e0f6} + 7 + 2 + void removeDevice(QString id) + + + + + {9dc3bc78-638e-402f-94a7-9a0daab3bc54} + 7 + 2 + void viewDevice(QString id) + + + + + {1e57a9c1-5eab-4685-afe5-db70e538d147} + 7 + 2 + void connectDevice(QString) + + + + + {1838fda6-ccd1-4f23-b302-23c9819d7a3d} + 7 + 2 + void disconnectDevice(QString) + + + + + {a967ad91-c995-4a4b-b145-c2039239c5d9} + 7 + 1 + Ui::ScopyHomePage *ui + + + + + {2c097f1a-746a-4232-b802-55038dfa8862} + 7 + 1 + ScopyHomeAddPage *add + + + + + {977a6c99-c714-466e-aa2d-1c65d3f50f60} + 7 + 1 + InfoPageStack* is + + + + + {23eb3506-3fa9-4ce6-b685-cdcd834b1baa} + 7 + 1 + DeviceBrowser* db + + + + + + + + + + + + {f031329d-0e5c-4a90-a57e-f8a86416952a} + + + + + + + + {f031329d-0e5c-4a90-a57e-f8a86416952a} + + + ScopyHomePage + + + + + + + {da64f435-c57c-488d-8868-7ee78f602ca1} + + + + + + + + {da64f435-c57c-488d-8868-7ee78f602ca1} + + + {f031329d-0e5c-4a90-a57e-f8a86416952a} + {29d6cc8f-575c-475d-8969-1a12f0c53306} + + + + + 1 + true + 2 + + + + + + + + + + {65918884-8aa9-46c0-91f2-20de46f824fc} + + + + + + + + {65918884-8aa9-46c0-91f2-20de46f824fc} + + + {f031329d-0e5c-4a90-a57e-f8a86416952a} + {636b2d0b-a84c-49f7-99c4-50acfa484261} + + + + + + + + + + {ad47f512-aade-4023-9a91-64a07c0ed8f4} + + + + + + + + {ad47f512-aade-4023-9a91-64a07c0ed8f4} + + + {f031329d-0e5c-4a90-a57e-f8a86416952a} + {eefce8db-f53b-4c04-a783-53141a58636c} + + + + + + + + + + + + + + Ui + + + + + {5b250858-516f-40f5-9750-50ba4eec4beb} + 1 + QPushButton* btnHome + + + + + {adfb724e-0c97-4701-9755-26585ea0219f} + 1 + QPushButton* btnAdd + + + + + {c7c261dc-d213-48e5-9f3e-81ee190147af} + 1 + InfoPageStack* is + + + + + {34c9a82f-1bd9-4fc3-90bb-22ad1218861f} + 1 + DeviceBrowser* db + + + + + + + + + + + + {3493fdef-b41d-4918-8149-080a80848873} + + + + + + + + {3493fdef-b41d-4918-8149-080a80848873} + + + ScopyHomeInfoPage + + + + + + + {774f644c-cc34-4ac4-a192-a27be2ba9af3} + + + + + + + + {774f644c-cc34-4ac4-a192-a27be2ba9af3} + + + {3493fdef-b41d-4918-8149-080a80848873} + {29d6cc8f-575c-475d-8969-1a12f0c53306} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + + + + {a37e8d5a-2c78-4159-af53-b3d1f513d9ee} + + + + + + + + {a37e8d5a-2c78-4159-af53-b3d1f513d9ee} + + + ScopyHomeAddPage + + + + + + + {a129f405-975e-421b-83b6-ebdd955755b1} + + + + + + + + {a129f405-975e-421b-83b6-ebdd955755b1} + + + {a37e8d5a-2c78-4159-af53-b3d1f513d9ee} + {29d6cc8f-575c-475d-8969-1a12f0c53306} + + + + + 1 + true + 2 + + + + + + + + + + {80331562-461b-4a2a-875f-cf28ca85c7a8} + + + + + + + + {80331562-461b-4a2a-875f-cf28ca85c7a8} + + + {a37e8d5a-2c78-4159-af53-b3d1f513d9ee} + {bd470a57-63dc-4d8a-8ed3-80f16bf2eaae} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + {70bca9d7-61ad-40ab-adf7-47174b4b8874} + 4 + 2 + void requestAddDevice(QString, QString) + + + + + {a2c96068-7ee2-43fd-b8a0-e1c76ddbe120} + 4 + 2 + void requestDevice(QString) + + + + + {27ced2ea-59c4-48fb-970b-698c2cfde519} + 4 + 2 + void newDeviceAvailable(DeviceImpl *d) + + + + + {79b53ac5-d5ec-4aa3-a658-93dccec95622} + 4 + 2 + void deviceInfoAvailable(QMap<QString, QString> ctxInfo) + + + + + {eb89293d-580d-49d6-a89d-5038a2972e9b} + 4 + 2 + void uriChanged(QString uri) + + + + + {edb6941c-cf6f-44d6-ad82-0d22f72e1851} + 5 + 2 + void futureVerify() + + + + + {69958132-6a50-4a86-9f9e-027703ae1abf} + 5 + 2 + void futureScan() + + + + + {32793659-dd71-45c1-b128-94380a70b793} + 5 + 2 + bool verify() + + + + + {1bab2795-2f5c-4a25-8cf6-556a2445bd26} + 5 + 2 + void deviceAddedToUi(QString) + + + + + {064f1c8f-801c-477d-9f73-89402d0dafde} + 5 + 2 + void scanFinished() + + + + + {5506f231-f928-4fb7-bf6b-086d09886672} + 5 + 2 + void verifyFinished() + + + + + {23937d97-8a07-4738-b6af-20c9db753cbf} + 5 + 2 + void deviceLoaderInitialized() + + + + + {a2d3d35e-1154-475d-a214-cff115b38d20} + 5 + 2 + void updateUri(QString uri) + + + + + {0f3cc3dd-8309-4421-aeaa-1965e13eb1c6} + 5 + 2 + void addBtnClicked() + + + + + {8ccddcb6-2ce3-42df-984f-880c3b206370} + 3 + 1 + Ui::ScopyHomeAddPage *ui + + + + + {3de0e0a9-c6fd-4367-8096-e43824a701a1} + 3 + 1 + QString pendingUri + + + + + {665234a4-5ef9-4e86-a734-b2de6e9e2034} + 3 + 1 + QFutureWatcher<bool> *fw + + + + + {468dc975-1c8e-4784-8005-a572c22f8d93} + 3 + 1 + QFutureWatcher<int> *fwScan + + + + + {811492e6-4f76-4072-a976-df3df1b5c509} + 3 + 1 + QStringList scanParamsList + + + + + {6f93f862-8533-4313-9e8b-5cda0705c986} + 3 + 1 + QStringList scanList + + + + + {dacc5520-36b8-44d1-802a-391cc19cb21f} + 3 + 1 + InfoPage *deviceInfoPage + + + + + {ec48f745-aefc-45bf-ab4f-33c557361035} + 3 + 1 + PluginManager *pluginManager + + + + + {2748c346-6d51-4487-8397-caced71483a7} + 3 + 1 + DeviceImpl *deviceImpl + + + + + {42116f97-ebd0-4502-ab67-daf329d7e771} + 3 + 1 + QList<PluginEnableWidget*> pluginDescriptionList + + + + + + + + + + + + {192a9656-2796-478a-a40b-5619baa8acc9} + + + + + + + + {192a9656-2796-478a-a40b-5619baa8acc9} + + + ScopyMainWindow + + + + + + + {2cddb820-4d60-4b57-90c6-9dd7e5909d7b} + + + + + + + + {2cddb820-4d60-4b57-90c6-9dd7e5909d7b} + + + {192a9656-2796-478a-a40b-5619baa8acc9} + {107011b1-ab5c-41df-ae4c-d3217e9c4cd6} + + + + + + + + + + {2a1d55a6-8f45-46fb-b4a6-cb7316182a4b} + + + + + + + + {2a1d55a6-8f45-46fb-b4a6-cb7316182a4b} + + + {192a9656-2796-478a-a40b-5619baa8acc9} + {0bc900fd-1c09-48dc-a03b-394f7e17a68f} + + + + + + + + + + {331be05b-83d5-416d-9b33-c0624c52bbda} + + + + + + + + {331be05b-83d5-416d-9b33-c0624c52bbda} + + + {192a9656-2796-478a-a40b-5619baa8acc9} + {20440a58-5e88-4172-971b-4184419b0636} + + + + + 1 + true + 2 + + + + + + + + + + + + + + Ui + + + + + + + + {20440a58-5e88-4172-971b-4184419b0636} + + + + + + + + {20440a58-5e88-4172-971b-4184419b0636} + + + ScopyMainWindow + + + + + + + {6cf7ce23-6027-4043-8dd2-747426252864} + + + + + + + + {6cf7ce23-6027-4043-8dd2-747426252864} + + + {20440a58-5e88-4172-971b-4184419b0636} + {389d9ea9-1cdf-4b08-9a5c-552104a09d78} + + + + + 1 + true + 2 + + + + + + + + + + {8dfe726b-edd7-47bd-a36f-539ed5df80a7} + + + + + + + + {8dfe726b-edd7-47bd-a36f-539ed5df80a7} + + + {20440a58-5e88-4172-971b-4184419b0636} + {918ef0ee-20c4-4058-98ee-950f9198032d} + + + + + 1 + true + 2 + + + + + + + + + + {9badadce-e007-4165-9edb-c8d769748076} + + + + + + + + {9badadce-e007-4165-9edb-c8d769748076} + + + {20440a58-5e88-4172-971b-4184419b0636} + {dff7f0d9-f6de-4896-af57-6cd0209daa56} + + + + + 1 + true + 2 + + + + + + + + + + {11ca845e-4c2d-434a-9c8a-e0fa6a05a291} + + + + + + + + {11ca845e-4c2d-434a-9c8a-e0fa6a05a291} + + + {20440a58-5e88-4172-971b-4184419b0636} + {29d6cc8f-575c-475d-8969-1a12f0c53306} + + + + + 1 + true + 2 + + + + + + + + + + {153d8a14-7f4c-4bff-b84d-43e9fe9899dc} + + + + + + + + {153d8a14-7f4c-4bff-b84d-43e9fe9899dc} + + + {20440a58-5e88-4172-971b-4184419b0636} + {d47f0fa8-cf3f-4c10-9b02-f80af3d57baa} + + + + + 1 + true + 2 + + + + + + + + + + {edbb60c7-a282-4b54-8bd7-0b5dd1d6508b} + + + + + + + + {edbb60c7-a282-4b54-8bd7-0b5dd1d6508b} + + + {20440a58-5e88-4172-971b-4184419b0636} + {ae9b784c-2da5-4b97-be82-301848323aa1} + + + + + 1 + true + 2 + + + + + + + + + + {ecebe4b3-59a0-464e-9a27-9310eb87cdb1} + + + + + + + + {ecebe4b3-59a0-464e-9a27-9310eb87cdb1} + + + {20440a58-5e88-4172-971b-4184419b0636} + {192a9656-2796-478a-a40b-5619baa8acc9} + + + + + 1 + true + 2 + + + + + + + + + + {88854229-1455-426f-b2b4-c0bb4020e2ea} + + + + + + + + {88854229-1455-426f-b2b4-c0bb4020e2ea} + + + {20440a58-5e88-4172-971b-4184419b0636} + {ae9b784c-2da5-4b97-be82-301848323aa1} + + + + + 1 + true + 2 + + + + + + + + + + {9e8a83cb-c801-4e25-8ba4-faf0903518f8} + + + + + + + + {9e8a83cb-c801-4e25-8ba4-faf0903518f8} + + + {20440a58-5e88-4172-971b-4184419b0636} + {acb80f41-d754-4cc2-95a7-df775fa7157e} + + + + + 1 + true + 2 + + + + + + + + + + {e1a0004d-30e5-4261-86c2-03b0f45bee6e} + + + + + + + + {e1a0004d-30e5-4261-86c2-03b0f45bee6e} + + + {20440a58-5e88-4172-971b-4184419b0636} + {4c2b6925-c606-4fe3-a0eb-197091db6b33} + + + + + 1 + true + 2 + + + + + + + + + + {b8f6dbab-53f3-4373-ab10-3a89e33f6b9e} + + + + + + + + {b8f6dbab-53f3-4373-ab10-3a89e33f6b9e} + + + {20440a58-5e88-4172-971b-4184419b0636} + {389d9ea9-1cdf-4b08-9a5c-552104a09d78} + + + + + 1 + true + 2 + + + + + + + + + + {ae24a692-ce63-448e-a073-78f38dc60f1d} + + + + + + + + {ae24a692-ce63-448e-a073-78f38dc60f1d} + + + {20440a58-5e88-4172-971b-4184419b0636} + {ddefe768-27cd-4826-a886-1360d27ca8be} + + + + + 1 + true + 2 + + + + + + + + + + {a7dfde00-b313-42be-b1a1-9709b82b389d} + + + + + + + + {a7dfde00-b313-42be-b1a1-9709b82b389d} + + + {20440a58-5e88-4172-971b-4184419b0636} + {48d6cbe3-addd-452e-979f-13e57400b86c} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + {fc1b35fd-2d53-43b6-b118-51513ee8f447} + 1 + 2 + void initAboutPage(PluginManager*) + + + + + {0adec70a-4711-4f00-95d6-d8658dd060fa} + 1 + 2 + void initPreferencesPage(PluginManager *pm = nullptr) + + + + + {60d964f3-2046-498c-8641-1ddcb22040fa} + 1 + 2 + void initPreferences() + + + + + {cc8ae0be-a418-40ef-b531-0cd7dde8473e} + 7 + 2 + void requestTools(QString id) + + + + + {06fb39bb-5ec3-433a-b8a3-192a047cb54f} + 7 + 2 + void addDeviceToUi(QString id, Device *d) + + + + + {deac0560-ece4-49b3-a076-7d7ca712c644} + 7 + 2 + void removeDeviceFromUi(QString) + + + + + {7c4a8702-a575-4708-9369-d9aabb70971c} + 7 + 2 + void save() + + + + + {a64ef4c2-177b-4333-afad-366efca841bd} + 7 + 2 + void load() + + + + + {5d851c15-2164-44cf-97b3-65e228c06b89} + 7 + 2 + void save(QString file) + + + + + {c95e08ac-d60a-4a5b-92c0-3313bebff906} + 7 + 2 + void load(QString file) + + + + + {44886ad1-34ce-4132-8105-5770ca5545e5} + 3 + 1 + ScopyAboutPage* about + + + + + {b86ed672-6e09-476d-a9bc-9ece0bf66e97} + 3 + 1 + ScopyPreferencesPage* prefPage + + + + + {a548632f-9b23-47f2-89a2-86b088354909} + 3 + 1 + PluginRepository *pr + + + + + {cf40afe4-25e2-4b95-896b-834e6a3890a8} + 3 + 1 + ScopyHomePage *hp + + + + + {1327aede-a20d-45b6-929b-4b5e84b75acf} + 3 + 1 + DeviceManager *dm + + + + + {98599244-3dda-4da0-ac1e-2281667dfe65} + 3 + 1 + Preferences *pref + + + + + {85119d77-0b30-43e0-bed0-b18f05f6d677} + 3 + 1 + CyclicalTask *scanCycle + + + + + {57ef2ead-f1e3-4cf4-ad22-c8cef92824ff} + 3 + 1 + IIOScanTask *scanTask + + + + + {de40d895-c5ac-445d-bb20-3d886b05969b} + 3 + 1 + ScannedIIOContextCollector *scc + + + + + {d7fa7359-b218-440b-aac8-92f1150d6b2c} + 3 + 1 + ToolManager *toolman + + + + + {425d684c-6d10-4d02-adf7-fede819f8972} + 3 + 1 + ScopyMainWindow_API *api + + + + + {3bf74e13-8321-400c-bc69-ca951da09a36} + 3 + 1 + Ui::ScopyMainWindow *ui + + + + + + + + + + + + {dff7f0d9-f6de-4896-af57-6cd0209daa56} + + + + + + + + {dff7f0d9-f6de-4896-af57-6cd0209daa56} + + + DeviceManager + + + + + + + {3b61ef28-c6a5-4942-8d56-094ea9b42f5e} + + + + + + + + {3b61ef28-c6a5-4942-8d56-094ea9b42f5e} + + + {dff7f0d9-f6de-4896-af57-6cd0209daa56} + {702be005-47af-4698-ada9-d7b835e35d12} + + + + + n + true + 2 + + + + + + + + + + {f9b391c1-641c-43ef-92b7-d9e625384a4d} + + + + + + + + {f9b391c1-641c-43ef-92b7-d9e625384a4d} + + + {dff7f0d9-f6de-4896-af57-6cd0209daa56} + {84a2203a-9901-430d-b2b0-eceb9873acf8} + + + + + + + + + + {13856c2c-5538-46d8-970f-caf303391d1a} + + + + + + + + {13856c2c-5538-46d8-970f-caf303391d1a} + + + {dff7f0d9-f6de-4896-af57-6cd0209daa56} + {fdd7b05c-bb73-4636-952a-c8ce39f7be97} + + + + + + + + + + + + + + scopy + + + + + {316ee9d3-11b6-45b8-82c1-908b7be795e6} + 7 + 2 + QString createDevice(QString category, QString param) + + + + + {15e5d3ee-56c6-4ff4-92c4-3bbaaa0ecf73} + 7 + 2 + void addDevice(Device*) + + + + + {91020c6a-3dc7-4527-ad16-d5b32ed512af} + 7 + 2 + void removeDevice(QString category, QString id) + + + + + {c773d40b-6bd9-43fd-a3f9-c59ea1fa3fed} + 7 + 2 + void removeDeviceById(QString id) + + + + + {1dc4f75a-6a38-4e9d-827d-12b9a3ccb149} + 7 + 2 + QString restartDevice(QString id) + + + + + {c4e997de-a5c8-44d2-9cdd-214d77c4791c} + 7 + 2 + void disconnectAll() + + + + + {cfedb1d7-3f90-43b9-ac7d-764142882c99} + 7 + 2 + void save(QSettings &s) + + + + + {08e62c3d-3aa3-42c0-885f-c328242e18dc} + 7 + 2 + void load(QSettings &s) + + + + + {f38b95d6-ec89-4ecb-b9d6-afd198eee17c} + 4 + 2 + void deviceChangedToolList(QString, QList<ToolMenuEntry*>) + + + + + {25dd376b-8795-42eb-9fa1-19a8f8f49af0} + 4 + 2 + void deviceAddStarted(QString) + + + + + {c558430c-ba21-4295-a196-75da1c717603} + 4 + 2 + void deviceAdded(QString, Device*) + + + + + {1811284c-7ccc-41f7-a949-fe692c7b06ab} + 4 + 2 + void deviceRemoveStarted(QString, Device*) + + + + + {196af63e-a8d6-4caf-9667-491dace76e5b} + 4 + 2 + void deviceRemoved(QString) + + + + + {ee44e12a-6270-4095-b56b-12cf30d51cd2} + 4 + 2 + void deviceConnected(QString id, Device*) + + + + + {e693fc77-0db5-4603-99a6-5ef185ac5bf7} + 4 + 2 + void deviceDisconnected(QString id, Device*) + + + + + {45fc3a8b-7d5e-4eaa-a23b-ea2c619ec3fd} + 4 + 2 + void requestDevice(QString id) + + + + + {ff3526e6-720b-4b66-9683-4f8e590be883} + 4 + 2 + void requestTool(QString id) + + + + + {c5b521b2-bcf0-43fb-96f4-8a1fd76a76c1} + 4 + 1 + QMap<QString,Device*> map + + + + + {7ce0ec3f-1526-479d-a20d-36d971129a24} + 4 + 1 + PluginManager *pm + + + + + + + + + + + + {702be005-47af-4698-ada9-d7b835e35d12} + + + + + + + + {702be005-47af-4698-ada9-d7b835e35d12} + + + interface + + + + + Device + + + + + + + {948c22eb-714d-42c7-b722-f45e25506007} + + + + + + + + {948c22eb-714d-42c7-b722-f45e25506007} + + + {702be005-47af-4698-ada9-d7b835e35d12} + {dff7f0d9-f6de-4896-af57-6cd0209daa56} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + {8bf83dc8-d233-4ec6-b0d9-7aa0b6261d44} + 2 + 3 + QString id() + + + + + {082fbef1-292c-4394-8b7c-2acf7922dc51} + 2 + 3 + QString category() + + + + + {3c6f2ced-6f09-4c10-a65d-dfdfd0322ad6} + 2 + 3 + QString name() + + + + + {18bca1d7-a160-46de-b2ec-d1e80e8fdcea} + 2 + 3 + QString param() + + + + + {d4d3554f-4ae9-44d9-bbbd-67439178c035} + 2 + 3 + QString description() + + + + + {b46e9452-72e0-478d-ac9c-1b7154ab5d07} + 2 + 3 + QWidget *icon() + + + + + {acab10d9-2290-4832-9bad-36fd820fea4a} + 2 + 3 + QWidget *page() + + + + + {960a26fd-a6e7-47ee-823b-9a5e07b4f135} + 2 + 3 + QList<ToolMenuEntry*> toolList() + + + + + {561c6f93-ee52-4d02-8853-872ad483087e} + 2 + 3 + void preload() + + + + + {bd19ee36-4725-4ec6-bba7-fb27fc4b0c9c} + 2 + 3 + void loadPlugins() + + + + + {adc422ef-65c9-420d-907b-388940fe2ef5} + 2 + 3 + void unloadPlugins() + + + + + {4d45d122-d3c2-4cc5-bacc-1b91b95548a5} + 7 + 2 + 3 + void connectDev() + + + + + {73681b75-ea70-43c8-a579-ad47000de374} + 7 + 2 + 3 + void disconnectDev() + + + + + {6fe01cdb-29e6-493d-853b-476ca90770a0} + 7 + 2 + 3 + void showPage() + + + + + {f1a078f8-89c3-4922-9938-47b5eeb56ec8} + 7 + 2 + 3 + void hidePage() + + + + + {6df09a8e-e253-49ad-9263-304ab9dc5a19} + 7 + 2 + 3 + void save(QSettings &) + + + + + {9347dfcf-84ef-4418-a86a-f64456d04f4b} + 7 + 2 + 3 + void load(QSettings &) + + + + + {db9e6e18-3815-44b7-8c70-0b374acab2f9} + 4 + 2 + 3 + void toolListChanged() + + + + + {6c5ae3d0-51a9-4a1b-87ed-1a1a5106a1b2} + 4 + 2 + 3 + void connected() + + + + + {385584de-bea4-4a23-b5b7-99316311c42d} + 4 + 2 + 3 + void disconnected() + + + + + {5e039000-adb0-415c-83a6-e21ee07b057c} + 4 + 2 + 3 + void requestedRestart() + + + + + {0c98efa0-eac8-4bd4-90c4-1ae495a0bdb2} + 4 + 2 + 3 + void requestTool(QString) + + + + + + + + + + + + {ac3c9c31-229f-40fb-a01c-7cb74c329e3c} + + + + + + + + {ac3c9c31-229f-40fb-a01c-7cb74c329e3c} + + + ScopyHomeInfoPage + + + + + + + {82803a30-8ff4-4e3b-9a1c-0783e6b860ec} + + + + + + + + {82803a30-8ff4-4e3b-9a1c-0783e6b860ec} + + + {ac3c9c31-229f-40fb-a01c-7cb74c329e3c} + {3493fdef-b41d-4918-8149-080a80848873} + + + + + 1 + true + 2 + + + + + + + + + + {57fd3927-5b73-4f4a-aabc-63bc25097500} + + + + + + + + {57fd3927-5b73-4f4a-aabc-63bc25097500} + + + {ac3c9c31-229f-40fb-a01c-7cb74c329e3c} + {29d6cc8f-575c-475d-8969-1a12f0c53306} + + + + + 1 + true + 2 + + + + + + + + + + + + + + Ui + + + + + + + + {bd470a57-63dc-4d8a-8ed3-80f16bf2eaae} + + + + + + + + {bd470a57-63dc-4d8a-8ed3-80f16bf2eaae} + + + ScopyHomeAddPage + + + + + + + {5bb1f232-8ff5-4cfe-8f23-c1ea90f3a5a6} + + + + + + + + {5bb1f232-8ff5-4cfe-8f23-c1ea90f3a5a6} + + + {bd470a57-63dc-4d8a-8ed3-80f16bf2eaae} + {a37e8d5a-2c78-4159-af53-b3d1f513d9ee} + + + + + 1 + true + 2 + + + + + + + + + + + + + + Ui + + + + + + + + {d47f0fa8-cf3f-4c10-9b02-f80af3d57baa} + + + + + + + + {d47f0fa8-cf3f-4c10-9b02-f80af3d57baa} + + + ToolManager + + + + + + + {70700dc0-3301-40e5-8ac4-bf4a120916b9} + + + + + + + + {70700dc0-3301-40e5-8ac4-bf4a120916b9} + + + {d47f0fa8-cf3f-4c10-9b02-f80af3d57baa} + {0bc900fd-1c09-48dc-a03b-394f7e17a68f} + + + + + 1 + true + 2 + + + + + + + + + + {64a0db56-30d7-4da2-8563-1dbc182717ec} + + + + + + + + {64a0db56-30d7-4da2-8563-1dbc182717ec} + + + {d47f0fa8-cf3f-4c10-9b02-f80af3d57baa} + {0bc900fd-1c09-48dc-a03b-394f7e17a68f} + + + + + true + + + + + + + + + + + + + + scopy + + + + + {a3cf1f47-274b-4268-a20f-aa1e0dacb755} + 7 + 2 + void addToolList(QString, QList<ToolMenuEntry*>) + + + + + {5b511e70-3147-49b4-b86a-591c52e15a6b} + 7 + 2 + void removeToolList(QString) + + + + + {59a2750b-b683-4d75-86c9-a11888392e3a} + 7 + 2 + void changeToolListContents(QString, QList<ToolMenuEntry*>) + + + + + {a25f3a3a-1590-4c1f-a685-0582741a1ac6} + 7 + 2 + void showToolList(QString) + + + + + {0867a13b-1a2d-48ea-bf38-50ad6775b9a1} + 7 + 2 + void hideToolList(QString) + + + + + {6ad9feac-d360-4b32-b537-94c490beeb4b} + 7 + 2 + void lockToolList(QString) + + + + + {f9fca70d-d3b6-4284-9259-e8dd95c974a3} + 7 + 2 + void unlockToolList(QString) + + + + + {bbc26f34-689b-4084-b03c-a8c99d7b7ed4} + 7 + 2 + void updateToolEntry(ToolMenuEntry *tme, QString s) + + + + + {c5b16914-03f2-46e6-8846-152addf3ade3} + 7 + 2 + void updateToolEntry() + + + + + {abc0fc64-ad85-4a0e-9a44-69410b6b205e} + 7 + 2 + void updateTool() + + + + + {fd69cc05-e8b6-4fcf-9d7d-0120e6211fc5} + 7 + 2 + void showTool(QString id) + + + + + + + + + + + + {e9e0564d-6ef2-4282-8b77-74f81fa6ba6f} + + + + + + + + {e9e0564d-6ef2-4282-8b77-74f81fa6ba6f} + + + singleton + + + + + Preferences + + + scopy + + + + + {0e1d88c0-6cc4-4893-8116-33a043d281e4} + 1 + QMap<QString,QVariant> p + + + + + {82e47446-9a92-481c-adc8-06630169a92c} + 1 + QSettings *s + + + + + {b08b83a6-b87b-4ba6-a5f8-f7f461aed52c} + 2 + void init(QString, QVariant) + + + + + {fdd44b7d-eb32-4c7a-bced-1ab8b457d24e} + 2 + void clear() + + + + + {fc6816f4-80ec-456b-be87-d5c8b563bc7b} + 2 + QVariant get(QString) + + + + + {5f6f1087-9867-422b-8648-ab45a875055e} + 2 + void set(QString, QVariant) + + + + + {3ec70006-15de-41f6-97af-1d984208d44f} + 2 + void save() + + + + + {fb9a2e47-9969-4585-b0e8-04b53c698210} + 2 + void load() + + + + + {3b21b3bc-312d-4d87-8b66-141b6999d4cf} + 2 + void setPreferencesFilename(QString s) + + + + + {fda5d712-1156-4fd6-8eaa-e05932498e43} + 4 + 2 + void preferenceChanged(QString, QVariant) + + + + + + + + + + + + {ae9b784c-2da5-4b97-be82-301848323aa1} + + + + + + + + {ae9b784c-2da5-4b97-be82-301848323aa1} + + + ScopyPreferencesPage + + + + + + + {40b9ae4c-211c-4867-b396-d3f6ae303b0f} + + + + + + + + {40b9ae4c-211c-4867-b396-d3f6ae303b0f} + + + {ae9b784c-2da5-4b97-be82-301848323aa1} + {20440a58-5e88-4172-971b-4184419b0636} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + + + + {acb80f41-d754-4cc2-95a7-df775fa7157e} + + + + + + + + {acb80f41-d754-4cc2-95a7-df775fa7157e} + + + ScopyAboutPage + + + + + + + {22c2eed3-f805-4efe-ace8-37a184e52b47} + + + + + + + + {22c2eed3-f805-4efe-ace8-37a184e52b47} + + + {acb80f41-d754-4cc2-95a7-df775fa7157e} + {20440a58-5e88-4172-971b-4184419b0636} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + + + + {3ada95f5-70ce-4634-9e56-589693e0de37} + + + + + + + + {3ada95f5-70ce-4634-9e56-589693e0de37} + + + singleton + + + + + ScopyJS + + + scopy + + + + + {1ca7a14b-18d4-44ee-8731-c188b00675ba} + 1 + QJSEngine m_engine + + + + + {8239fc41-f8a8-4a0f-8837-c5471beb58d4} + 2 + 1024 + void exit() + + + + + {4af835a0-5c8e-46a7-82be-9066b2a04ba6} + 2 + 1024 + void sleep(unsigned long s) + + + + + {59e1ec78-b92e-46df-ba68-6559ea09c870} + 2 + 1024 + void msleep(unsigned long ms) + + + + + {dc60c31c-51bd-498f-a39e-208b8187d553} + 2 + 1024 + void printToConsole(const QString& text) + + + + + {05c96f94-d889-4067-b751-182bcd5be8db} + 2 + 1024 + QString readFromConsole(const QString& text) + + + + + {24305bf2-2877-4a5c-8bb1-86b16ad7a28d} + 2 + 1024 + void returnToApplication() + + + + + {5ced017c-810a-42c6-86c9-b78e7d23246b} + 2 + QJSEngine *engine() + + + + + {df4e2455-ae3c-4a40-958b-ca853f3fc86f} + 2 + void registerApi(ApiObject* obj) + + + + + {85c3f431-fa6e-44fc-b25c-4054aeceffdd} + 2 + void unregisterApi(ApiObject* obj) + + + + + {ec6cc378-439f-4b38-93ee-a94fb98af392} + 2 + void registerApi(ApiObject* obj, QJSValue parentObj) + + + + + {7dfc490b-f612-4a3e-af10-429ff030e517} + 2 + void unregisterApi(ApiObject* obj, QJSValue parentObj) + + + + + {2e740b40-55f0-41b2-923a-22c48959fb12} + 7 + 2 + void hasText() + + + + + + + + + + + + {e929bb03-3710-4a7f-bccf-85ab7e6df124} + + + + + + + + {e929bb03-3710-4a7f-bccf-85ab7e6df124} + + + singleton + + + + + ContextProvider + + + scopy + + + + + {ee5a8ef9-12aa-4c36-9f8b-a7c0c17771b7} + 2 + struct iio_context* open(QString uri) + + + + + {7930fe98-ed60-41cc-ac89-b0f589aed7bd} + 2 + void close(QString uri) + + + + + {f6e617bc-c8d9-4fdf-950b-812921ab99f6} + 3 + 1 + QMap<QString, ContextRefCounter*> map + + + + + + + + + + + + {3e714570-f017-421b-9eac-3f4d4b535143} + + + + + + + + {3e714570-f017-421b-9eac-3f4d4b535143} + + + singleton + + + + + MessageBroker + + + scopy + + + + + {5a4a4576-d612-4993-bff4-98b7166b1972} + 2 + void subscribe(QObject *obj, QString topic) + + + + + {f8327884-c103-4733-b3ea-12e51e3a96ea} + 2 + void unsubscribe(QObject *obj, QString topic) + + + + + {58c2fe94-e355-4091-a61d-3dd8f74a1823} + 2 + void publish(QString topic, QString message) + + + + + {a60d6939-4471-4cb2-9d0b-5bddea1779cf} + 4 + 2 + void messageReceived(QString topic, QString message) + + + + + {f79487da-dfca-48eb-ba73-4f415213d1e7} + 3 + 1 + QMap<QObject*, QSet<QString>> map + + + + + + + + + + + + {d44ab1af-0bea-486a-85d0-abd01d56bcd3} + + + + + + + + {d44ab1af-0bea-486a-85d0-abd01d56bcd3} + + + New Component + + + + + + + + + + {4c2b6925-c606-4fe3-a0eb-197091db6b33} + + + + + + + + {4c2b6925-c606-4fe3-a0eb-197091db6b33} + + + CyclicalTask + + + + + + + {d8b29cd1-6142-470e-a27d-1c6d3e3f1b00} + + + + + + + + {d8b29cd1-6142-470e-a27d-1c6d3e3f1b00} + + + {4c2b6925-c606-4fe3-a0eb-197091db6b33} + {389d9ea9-1cdf-4b08-9a5c-552104a09d78} + + + + + true + + + + + + + + + + {d634c08c-5305-42b6-8de4-6a40d65beec5} + + + + + + + + {d634c08c-5305-42b6-8de4-6a40d65beec5} + + + {4c2b6925-c606-4fe3-a0eb-197091db6b33} + {fb660f1e-c159-42a8-9593-8565a739f5cc} + + + + + true + + + + + + + + + + + + + + scopy + + + + + {fcac333a-2f4a-4696-bbd8-5bea6a48e903} + 2 + CyclicalTask(QThread *task, QObject *parent = nullptr) + + + + + {2c7e1567-ee0f-4785-89e7-e5ae3a16c0ae} + 2 + ~CyclicalTask() + + + + + {e60e6068-a481-45fe-885f-12969a3dade5} + 2 + void start(int period = 5000) + + + + + {c36824d3-7bb5-402a-901f-432834e11f74} + 2 + void stop() + + + + + {0ededff1-6aa8-4bee-99fe-7e982983e62a} + 5 + 2 + void startThread() + + + + + {c81b53cb-b609-426b-8eca-6f1e4f6a35d4} + 3 + 1 + QTimer *t + + + + + {957467b1-e043-4f02-8d4a-e2e2f164d197} + 3 + 1 + QThread* task + + + + + + + + + + + + {fb660f1e-c159-42a8-9593-8565a739f5cc} + + + + + + + + {fb660f1e-c159-42a8-9593-8565a739f5cc} + + + IIOPingTask + + + scopy + + + + + {3382b61b-b7ee-4654-9d8a-3878498f4726} + 2 + 9 + void run() + + + + + {bb9d668f-7b80-4452-96dc-76fc34aff3af} + 2 + 64 + bool ping(iio_context *ctx) + + + + + {6fb096ac-202e-4cdf-a94d-96fe24cc04ff} + 4 + 2 + void pingSuccess() + + + + + {d1155538-ab79-4934-878c-57db85a1121a} + 4 + 2 + void pingFailed() + + + + + + + + + + + + {ddefe768-27cd-4826-a886-1360d27ca8be} + + + + + + + + {ddefe768-27cd-4826-a886-1360d27ca8be} + + + PluginRepository + + + + + + + {3f6d361a-88d5-4fff-954e-224fdc5049c6} + + + + + + + + {3f6d361a-88d5-4fff-954e-224fdc5049c6} + + + {ddefe768-27cd-4826-a886-1360d27ca8be} + {84a2203a-9901-430d-b2b0-eceb9873acf8} + + + + + 1 + true + 2 + + + + + + + + + + + + + + scopy + + + + + {43b66616-a6d9-4f16-b9f9-45f9e042a973} + 2 + void init(QString location) + + + + + {06fbf390-d993-49e9-81dd-2d87dc31ada1} + 2 + PluginManager *getPluginManager() + + + + + {d95d8f85-bc6f-4e32-a2ea-4f519444aa46} + 3 + 1 + PluginManager *pm + + + + + {f99aeb79-3195-496c-adb4-8889fdb219f9} + 3 + 1 + QJsonObject metadata + + + + + + + + + + + + {ae970c30-491a-420a-9e68-36ba9d7ed94c} + + + + + + + + {ae970c30-491a-420a-9e68-36ba9d7ed94c} + + + New Component + + + + + + + + + + {84a2203a-9901-430d-b2b0-eceb9873acf8} + + + + + + + + {84a2203a-9901-430d-b2b0-eceb9873acf8} + + + PluginManager + + + scopy + + + + + {da6674b7-ba4c-42b4-a772-2d2bbac881c3} + 2 + void add(QStringList pluginFileList) + + + + + {9536ee68-633a-4494-b85c-dd39c546c9b4} + 2 + void add(QString pluginFileName) + + + + + {0f552226-e9ee-421f-8981-921885f1a14a} + 2 + void sort() + + + + + {3c623bba-205f-485f-96ea-654ef3a603d6} + 2 + 4 + QList<Plugin*> getOriginalPlugins() + + + + + {7d73c8ce-32cc-4317-936a-c35550e91a34} + 2 + QList<Plugin*> getPlugins(QString category = "", QObject *parent = nullptr) + + + + + {eac522db-be33-4e1a-9582-0a1005d3d31a} + 2 + QList<Plugin*> getCompatiblePlugins(QString param, QString category = "", QObject *parent = nullptr) + + + + + {03969939-3e41-4f13-b82f-a2a777d9f2ce} + 3 + 1 + QList<Plugin*> list + + + + + {b3d948e7-ba07-4618-894b-6c859b66f49d} + 3 + 1 + QJsonObject m_metadata + + + + + + + + + + + + {1c24efbf-2095-42da-b591-03ac51de74f5} + + + + + + + + {1c24efbf-2095-42da-b591-03ac51de74f5} + + + interface + + + + + Plugin + + + + + + + {80f110fd-3e62-4acc-b9ec-e8c4b12aed86} + + + + + + + + {80f110fd-3e62-4acc-b9ec-e8c4b12aed86} + + + {1c24efbf-2095-42da-b591-03ac51de74f5} + {84a2203a-9901-430d-b2b0-eceb9873acf8} + + + + + + + + + + {d24c3ed9-f0fb-455a-94b1-1b2a54f4a3ff} + + + + + + + + {d24c3ed9-f0fb-455a-94b1-1b2a54f4a3ff} + + + {1c24efbf-2095-42da-b591-03ac51de74f5} + {702be005-47af-4698-ada9-d7b835e35d12} + + + + + + + + + + + + + + scopy + + + + + {4014520e-2d52-454f-a3b9-98b04277b48f} + 2 + 3 + void setParam(QString) + + + + + {b65513ca-32b7-4b85-873a-534ad90c4d2a} + 2 + 3 + void preload() + + + + + {d61a5ba2-5128-42af-971a-d6da03a83dcd} + 2 + 3 + void initPreferences() + + + + + {744904e6-825d-47de-8093-49e63df17728} + 2 + 3 + void postload() + + + + + {ad6cf349-7123-486a-8ab3-9bd1e6a7fcc8} + 2 + 3 + bool loadIcon() + + + + + {161576ef-1e28-4523-80aa-042d9a8062b4} + 2 + 3 + bool loadPage() + + + + + {af9e5afa-d450-46ea-81a0-8d93dd47075c} + 2 + 3 + void loadToolList() + + + + + {5de11a32-8f05-4379-b51b-9b7edcf74a3f} + 2 + 3 + bool loadPreferencesPage() + + + + + {d276963b-5edb-40f5-9b4d-34a76f1a2677} + 2 + 3 + bool loadExtraButtons() + + + + + {f86b7407-9549-4ace-a0d3-294e091efc01} + 2 + 3 + void init() + + + + + {892d0f32-d704-463d-86e1-07f17a889dfd} + 2 + 3 + void deinit() + + + + + {aae974af-0770-4745-b7fb-f30213722187} + 2 + 3 + void initMetadata() + + + + + {ffdc813b-f1f3-4649-8c32-07ce8878e10d} + 2 + 3 + void setMetadata(QJsonObject obj) + + + + + {5082a005-f8fb-4d39-8d9a-a7f615ae9e70} + 2 + 3 + void saveSettings(QSettings&) + + + + + {abe020fd-dd39-46fe-b29b-545dc5987b66} + 2 + 3 + void loadSettings(QSettings&) + + + + + {7c899173-841f-462f-b4ae-33b2a272d0c1} + 2 + 3 + void unload() + + + + + {f615e960-9d75-4190-8398-b8d6ff1864f8} + 2 + 3 + bool compatible(QString param) + + + + + {45a72db4-5904-4d0b-8d33-60374f89e18d} + 2 + 3 + Plugin* clone() + + + + + {8d9866fe-204d-46d4-8722-4213139b2687} + 2 + 3 + void cloneExtra(Plugin*) + + + + + {8061db89-00d8-4cab-9729-b566e2c5466e} + 2 + 3 + QString param() + + + + + {00e3264b-01f6-4341-9ba0-92166b576b92} + 2 + 3 + QString name() + + + + + {034243a2-9761-4939-8ae0-7d60d35cf9bc} + 2 + 3 + QString displayName() + + + + + {95867797-d1b7-47ab-ac44-b90a8c20b8f9} + 2 + 3 + QString displayDescription() + + + + + {eef4f581-1426-40d6-a87a-6fda2cfe3b39} + 2 + 3 + QWidget* icon() + + + + + {a72bbe96-a362-4ae8-b642-65282e357d9a} + 2 + 3 + QWidget* page() + + + + + {7a27f674-a15a-4135-bffc-4c55fdb78a75} + 2 + 3 + QWidget* preferencesPage() + + + + + {4e4e3be5-5fef-496b-8c28-f9c58393fed0} + 2 + 3 + QList<QAbstractButton*> extraButtons() + + + + + {fa8d8fe5-3c09-4e86-9ef5-b1bb2f1f4212} + 2 + 3 + QList<adiscope::ToolMenuEntry*> toolList() + + + + + {58ee79ec-a22a-436d-9270-2c8fdd590525} + 2 + 3 + QJsonObject metadata() + + + + + {02712944-5dbe-4489-80bd-710a72e7ed51} + 2 + 3 + QString about() + + + + + {acecc1ff-5e8c-4784-a310-520a8907d903} + 2 + 3 + QString version() + + + + + {cfd573ff-3018-44a3-b692-670bbe8f7ea6} + 7 + 2 + 3 + bool onConnect() + + + + + {460e570b-09a5-4d25-b4fb-234ab1e1033e} + 7 + 2 + 3 + bool onDisconnect() + + + + + {39bf4c11-3e10-4c96-a82e-aad0b6653bd7} + 7 + 2 + 3 + void showPageCallback() + + + + + {e4cffa90-2ef5-4f4c-81e0-a4b5b4966031} + 7 + 2 + 3 + void hidePageCallback() + + + + + {78dd9454-b062-402c-a0ba-b9345c8d6163} + 7 + 2 + 3 + void messageCallback(QString topic, QString message) + + + + + {30acb4aa-3167-48bc-a249-1568dfec7bfc} + 4 + 2 + 3 + void connectDevice() + + + + + {d80efac0-5641-4e04-b2be-d06a600f6873} + 4 + 2 + 3 + void disconnectDevice() + + + + + {51362b24-eacb-4894-887b-ed784af7ecf9} + 4 + 2 + 3 + void restartDevice() + + + + + {9a493c98-d484-425e-9607-38f8d1ce9106} + 4 + 2 + 3 + void toolListChanged() + + + + + {57f4408a-1a37-496a-bc0d-6169cea1f296} + 4 + 2 + 3 + void requestTool(QString) + + + + + + + + + + + + {34a37e62-1b26-470c-aab8-ff932ba4af4f} + + + + + + + + {34a37e62-1b26-470c-aab8-ff932ba4af4f} + + + DeviceImpl + + + + + + + {32339e89-b019-479e-acdb-85e33500b5e4} + + + + + + + + {32339e89-b019-479e-acdb-85e33500b5e4} + + + {34a37e62-1b26-470c-aab8-ff932ba4af4f} + {702be005-47af-4698-ada9-d7b835e35d12} + + + + + + + + + + + + + + scopy + + + + + {6919d632-10d3-46b2-9945-6bda5ef41f42} + 2 + 8 + QString id() + + + + + {f84777e2-5ee7-4bfb-8d68-13a409d10d21} + 2 + 8 + QString name() + + + + + {fb235be5-b78c-4750-a547-d2ff7ae45686} + 2 + 8 + QString category() + + + + + {bd1b5e8e-1a55-4889-878f-70628e331b59} + 2 + 8 + QString param() + + + + + {e1c9cc41-7aba-44b5-99e2-0bad46ca481d} + 2 + 8 + QString description() + + + + + {ce996782-b761-484f-8d06-b30cfd7bf662} + 2 + 8 + QWidget *icon() + + + + + {2d85e7f1-986c-4d32-a2e9-121825e4dc78} + 2 + 8 + QWidget *page() + + + + + {71cc2492-2444-4d4a-8f34-5c226a446253} + 2 + 8 + QList<ToolMenuEntry*> toolList() + + + + + {3dcd7321-09ce-426a-895c-72a535c6fa5e} + 2 + 9 + void loadCompatiblePlugins() + + + + + {98d89d09-1f59-466c-a948-9a9076dacf6e} + 2 + 9 + void compatiblePreload() + + + + + {4fa758eb-ba18-430d-a73d-f96bd93ca3a7} + 2 + 9 + void loadPlugins() + + + + + {923313da-ade2-40d5-8065-b74dbea68d71} + 2 + 9 + void unloadPlugins() + + + + + {45a6868e-7b35-40c8-9031-011d22a382e2} + 2 + 4 + QList<Plugin *> plugins() + + + + + {5c5a34bb-573d-4b32-a11b-df2a2e8a06a1} + 7 + 2 + 9 + void connectDev() + + + + + {9da16634-d0dc-4ae8-9301-e1ee172139df} + 7 + 2 + 9 + void disconnectDev() + + + + + {54b2e983-b341-4911-b168-071df170149e} + 7 + 2 + 9 + void showPage() + + + + + {5afa81d6-7732-460c-94f0-a85d42be6bca} + 7 + 2 + 9 + void hidePage() + + + + + {2d56d1b9-fb74-4265-ac57-523c3b81a8a1} + 7 + 2 + 9 + void save(QSettings &) + + + + + {5c47e0f0-ca7f-4981-9051-e7141f8b3d15} + 7 + 2 + 9 + void load(QSettings &) + + + + + {74cc9204-30d1-4044-a310-9f5cedfcdfec} + 7 + 2 + void onConnectionFailed() + + + + + {f8ba1388-144f-47b1-9463-878cc55916ed} + 4 + 2 + 8 + void toolListChanged() + + + + + {5a520eac-736f-49d8-93fc-5e99d2da6a97} + 4 + 2 + 8 + void connected() + + + + + {6dc36a6e-250c-4ee7-a31b-2fd311528ee1} + 4 + 2 + 8 + void disconnected() + + + + + {73705164-8339-479f-8f86-8e84b336499b} + 4 + 2 + 8 + void requestedRestart() + + + + + {f5f3571d-c1aa-4833-b361-0d320a4c8dc6} + 4 + 2 + 8 + void requestTool(QString) + + + + + {397eb139-5c4a-4943-9a01-8bf0881024d6} + 4 + 2 + void connectionFailed() + + + + + {6b1b67e2-6ab1-468f-80b8-eaf32cc8acaa} + 4 + 2 + void forget() + + + + + {ba2403da-3911-4574-8a14-8034f95c3fc5} + 2 + 2 + void loadName() + + + + + {81909448-34de-496f-95cc-62c491dd8d1a} + 2 + 2 + void loadIcons() + + + + + {2a82c4b7-7545-446c-92fc-4a6405c97464} + 2 + 2 + void loadPages() + + + + + {a3b85fe4-a02b-45d7-babf-e3faf31a35a4} + 2 + 2 + void loadToolList() + + + + + {563fe0ae-d537-43b0-a66d-d402aded421e} + 2 + 2 + void loadBadges() + + + + + {8e14e202-78d2-452f-a260-d7d2d9f1bbc9} + 2 + 1 + PluginManager *p + + + + + {8938d569-53ca-4cbe-98e8-65e47dcfb543} + 2 + 1 + QList<Plugin*> m_plugins + + + + + {bdc6184e-ca0e-4667-9754-3e98f7eaada5} + 2 + 1 + QList<Plugin*> m_connectedPlugins + + + + + {3bf2b371-a394-4f8d-888c-43e5d0513182} + 2 + 1 + QString m_id + + + + + {925d4f15-b06a-470b-bc7d-c3287dc097ee} + 2 + 1 + QString m_category + + + + + {b478cd59-c57f-40db-a481-c5532904fe83} + 2 + 1 + QString m_description + + + + + {2f986d36-8070-4152-abb0-e8ad93b7c215} + 2 + 1 + QString m_param + + + + + {890e5660-73a2-414e-b9f6-908131d73c2d} + 2 + 1 + QString m_name + + + + + {56fa1e5a-4fc9-4d79-b74a-578b96ab3e02} + 2 + 1 + QWidget *m_icon + + + + + {6947a32f-40d8-4e84-bf10-de5d7837a034} + 2 + 1 + QWidget *m_page + + + + + {8625039a-6d00-48a9-8c80-d6bc830964f2} + 2 + 1 + QPushButton *connbtn,*discbtn + + + + + + + + + + + + {2dc09cab-f03e-4ea4-bc60-730da5bd016f} + + + + + + + + {2dc09cab-f03e-4ea4-bc60-730da5bd016f} + + + IIODeviceImpl + + + + + + + {e021e2a2-f351-43c2-989e-69e9863fdab5} + + + + + + + + {e021e2a2-f351-43c2-989e-69e9863fdab5} + + + {2dc09cab-f03e-4ea4-bc60-730da5bd016f} + {34a37e62-1b26-470c-aab8-ff932ba4af4f} + + + + + + + + + + + + + + scopy + + + + + {71e50543-9dc1-4331-bc43-dfcef57ebeb1} + 2 + 9 + void loadCompatiblePlugins() + + + + + {fe8ab21e-a196-4245-a894-1ed7878d7148} + 2 + bool verify() + + + + + + + + + + + + {e10a2c0d-269a-4a87-814f-76f00ac85e1d} + + + + + + + + {e10a2c0d-269a-4a87-814f-76f00ac85e1d} + + + virtual Pluginbase + + + + + + + {57c5ba1b-5d78-4df0-9ea5-ae40bd393211} + + + + + + + + {57c5ba1b-5d78-4df0-9ea5-ae40bd393211} + + + {e10a2c0d-269a-4a87-814f-76f00ac85e1d} + {1c24efbf-2095-42da-b591-03ac51de74f5} + + + + + + + + + + + + + + scopy + + + + + {aa702e6c-62ad-48d6-bfef-fb4a3b9b04a4} + 2 + 9 + void setParam(QString param) + + + + + {6a1cbf22-0822-4c6d-8b74-f0991b931458} + 2 + 9 + void initMetadata() + + + + + {8702f699-9df0-463b-ae1f-2889ad64d1da} + 2 + 9 + void setMetadata(QJsonObject obj) + + + + + {816157c6-80cf-4ec5-a1bd-f4c7c393c0ec} + 2 + 9 + void initPreferences() + + + + + {c0249c0e-ef53-4bfb-87c2-6a628533dbea} + 2 + 9 + void init() + + + + + {89b77718-8c86-46a0-82a5-82466ad86dab} + 2 + 9 + void deinit() + + + + + {db25dba7-96fe-486a-b573-bceb617749c4} + 2 + 9 + void preload() + + + + + {c5758557-ba37-45b9-9c85-324bc3fa383d} + 2 + 9 + void postload() + + + + + {b840ead7-a601-4d73-ab71-1cd039af417b} + 2 + 9 + bool loadIcon() + + + + + {dded54f4-e1f8-4340-82be-5528b953f4b2} + 2 + 9 + bool loadPage() + + + + + {722cd703-5e3e-41fb-8dbd-b7c7b1e63b49} + 2 + 9 + void loadToolList() + + + + + {eb668504-06fe-4808-8758-4a711f957f2f} + 2 + 9 + bool loadPreferencesPage() + + + + + {24595dfa-1fe7-46cb-b545-9b1764ba0c12} + 2 + 9 + bool loadExtraButtons() + + + + + {7ca8ba25-923e-4db7-9004-b061d000c614} + 2 + 9 + void saveSettings(QSettings&) + + + + + {4c448fc9-321b-4466-9928-06ccf5fc7e60} + 2 + 9 + void loadSettings(QSettings&) + + + + + {34b69e11-3745-42c6-aa14-b36e3730bd44} + 2 + 9 + void unload() + + + + + {4d69c34e-fcc4-4c47-9b93-b8c52cfa1caa} + 2 + 9 + QString param() + + + + + {c2bd9186-c298-4745-ac5b-7571f95e4a7f} + 2 + 9 + QString name() + + + + + {e97aedc3-a207-4077-b0d4-60142380fbf5} + 2 + 9 + QString displayName() + + + + + {fec40f3f-8f77-4b21-8056-bf3e8876e3c9} + 2 + 9 + QString displayDescription() + + + + + {a36a3ca7-bd53-44cd-9dea-3b5d70d70f84} + 2 + 9 + QWidget* icon() + + + + + {3a3398f8-df1e-4b5f-884c-1346ebaa2c1e} + 2 + 9 + QWidget* page() + + + + + {8530a746-6f83-4876-95f6-b3750207f1a2} + 2 + 9 + QWidget* preferencesPage() + + + + + {1d1f40e7-582b-46b5-944d-ded0a9023c6e} + 2 + 9 + QList<QAbstractButton*> extraButtons() + + + + + {2650fbae-0d77-4920-9d21-a970f36b6259} + 2 + 9 + QList<ToolMenuEntry*> toolList() + + + + + {8aa3b07e-8b87-4f35-b4da-2c17aff6bf5c} + 2 + 9 + QJsonObject metadata() + + + + + {4bede0da-8542-4347-a2d1-b24caf71510e} + 2 + 9 + QString about() + + + + + {5b750c3a-4ab6-4802-8733-4385c5ddc7a2} + 2 + 9 + QString version() + + + + + {ebd0c058-079e-48fc-85ac-1a7b49a23d07} + 2 + 1 + void loadMetadata(QString data) + + + + + {b43cf0b2-86c8-43f8-a54e-de82700f4537} + 2 + 9 + void cloneExtra(Plugin*) + + + + + {daa703a7-6494-4b06-aede-842045628ca6} + 7 + 2 + 9 + void showPageCallback() + + + + + {59a1dd41-9938-4f6c-8c8b-7c4a0d194303} + 7 + 2 + 9 + void hidePageCallback() + + + + + {70a02e11-359a-464c-b10b-eda69cf508c6} + 7 + 2 + 9 + void messageCallback(QString topic, QString message) + + + + + + + + + + + + {e5d799f1-f437-4b60-8f90-c121dcdfffa7} + + + + + + + + {e5d799f1-f437-4b60-8f90-c121dcdfffa7} + + + MinimalPlugin + + + + + + + {7b13e5c2-2eef-432f-91bf-bcec1227f956} + + + + + + + + {7b13e5c2-2eef-432f-91bf-bcec1227f956} + + + {e5d799f1-f437-4b60-8f90-c121dcdfffa7} + {e10a2c0d-269a-4a87-814f-76f00ac85e1d} + + + + + + + + + + + + + + scopy + + + + + {e5c4d459-e650-4753-8375-1d429f24f97b} + 2 + 8 + void initMetadata() + + + + + {ce4e468e-64ad-42dc-b256-f3a9bf7a65f3} + 2 + 8 + bool compatible(QString m_param) + + + + + {01641cea-04da-4db1-aca8-b0a4ed04b005} + 2 + 8 + void loadToolList() + + + + + {fad6c90f-6a65-4b73-ba80-3fc6436219e1} + 2 + 8 + bool onConnect() + + + + + {e3711cb2-2048-400f-a1d1-383d4d660ce4} + 2 + 8 + bool onDisconnect() + + + + + + + + + + + + {6a29bb57-e0d9-4d22-b8a5-71202bb598ed} + + + + + + + + {6a29bb57-e0d9-4d22-b8a5-71202bb598ed} + + + Some Other plugin + + + + + + + {4e384797-208b-4c31-9e46-719c31291e37} + + + + + + + + {4e384797-208b-4c31-9e46-719c31291e37} + + + {6a29bb57-e0d9-4d22-b8a5-71202bb598ed} + {e10a2c0d-269a-4a87-814f-76f00ac85e1d} + + + + + + + + + + + + + + + + + + {ea87d312-b82c-4119-b976-eea3b0cad043} + 1 + .... + + + + + + + + + + + + {c8c59201-be7f-4531-bc4e-4e6483be1f22} + + + + + + + + {c8c59201-be7f-4531-bc4e-4e6483be1f22} + + + TestPlugin + + + + + + + {0f0647cb-bab1-4254-be37-17453948da23} + + + + + + + + {0f0647cb-bab1-4254-be37-17453948da23} + + + {c8c59201-be7f-4531-bc4e-4e6483be1f22} + {e10a2c0d-269a-4a87-814f-76f00ac85e1d} + + + + + + + + + + + + + + scopy + + + + + {e2ca9ee4-669c-43a8-99d3-bca94f29ca6e} + 2 + 8 + void initPreferences() + + + + + {61691406-5cdd-411e-930f-bff0f59f64de} + 2 + 8 + void initMetadata() + + + + + {3dbb4657-8eb7-4578-a934-e6b741365aad} + 2 + 8 + void init() + + + + + {b6b1916a-0386-4aaf-a855-3cc427a20ef3} + 2 + 8 + bool compatible(QString m_param) + + + + + {5c5fdcb0-0fda-4b63-90a8-fd58fa5ca978} + 2 + 8 + bool loadPreferencesPage() + + + + + {1662dc82-761d-4a02-8949-215ef645caf2} + 2 + 8 + bool loadPage() + + + + + {7d836544-d27e-42ae-8c2f-876343281803} + 2 + 8 + bool loadIcon() + + + + + {7618f1a3-a056-428c-905c-2f1d307dd4cb} + 2 + 8 + void loadToolList() + + + + + {2fac0090-9d0e-4f64-ba2f-668d61b14c85} + 2 + 8 + bool loadExtraButtons() + + + + + {f91467cc-8ddb-43ed-97e6-ff5f2958687d} + 2 + 8 + QString about() + + + + + {9eb47370-e6be-43ec-8c7a-3259fec6a86d} + 2 + 8 + QString version() + + + + + {4fdee1f6-75b3-41a3-9d4f-7b2998b88147} + 2 + 8 + bool onConnect() + + + + + {6a0ed326-5894-4729-953e-d66d66bf7c3b} + 2 + 8 + bool onDisconnect() + + + + + {abef69b6-24ad-4fa1-bf94-338000e28e6f} + 2 + 8 + void cloneExtra(Plugin*) + + + + + {3a1390df-a14d-4d71-8cb1-097c2892343d} + 7 + 2 + 8 + void messageCallback(QString topic, QString message) + + + + + + + + + + + + {fdd7b05c-bb73-4636-952a-c8ce39f7be97} + + + + + + + + {fdd7b05c-bb73-4636-952a-c8ce39f7be97} + + + DeviceLoader + + + + + + + {6a105ad4-9d98-4970-b0c2-e6f14678fdee} + + + + + + + + {6a105ad4-9d98-4970-b0c2-e6f14678fdee} + + + {fdd7b05c-bb73-4636-952a-c8ce39f7be97} + {34a37e62-1b26-470c-aab8-ff932ba4af4f} + + + + + + + + + + + + + + scopy + + + + + {7513a3e9-e68d-4b23-9ade-484403f6d1d2} + 2 + DeviceLoader(DeviceImpl *d, QObject *parent) + + + + + {51827af1-a1be-4bd7-9f33-ac6d0fafdd9d} + 2 + ~DeviceLoader() + + + + + {e0134293-0b3c-49d8-9695-300f971adbe7} + 2 + void init() + + + + + {cebd3c7a-9e9c-4a7f-afd6-6326feb8354f} + 4 + 2 + void initialized() + + + + + + + + + + + + {b744b131-fa45-4ba3-8a9c-8c15ee06cc4a} + + + + + + + + {b744b131-fa45-4ba3-8a9c-8c15ee06cc4a} + + + DeviceFactory + + + scopy + + + + + {504f2a86-6aa9-4780-879c-cfc4fe1c0d99} + 2 + 64 + DeviceImpl* build(QString param, PluginManager *pm, QString category,QObject *parent) + + + + + + + + + + + + {48d6cbe3-addd-452e-979f-13e57400b86c} + + + + + + + + {48d6cbe3-addd-452e-979f-13e57400b86c} + + + ScopyMainWindow_API + + + scopy + + + + + {19984a6a-05ad-49d4-819d-d984cec06dd3} + 1 + 2 + 1024 + void acceptLicense() + + + + + {966c9efd-2d5c-4e8a-8ab2-c16e38a0bf1f} + 1 + 2 + 1024 + QString addDevice(QString cat, QString uri) + + + + + {413ea79b-6694-4090-be84-3f407d473e42} + 1 + 2 + 1024 + bool connectDevice(int idx) + + + + + {b38b83cb-9833-46d6-a295-1e0ff552819f} + 1 + 2 + 1024 + bool connectDevice(QString devID) + + + + + {e22f4d92-3660-4bae-b534-006d1d52746b} + 1 + 2 + 1024 + bool disconnectDevice(QString devID) + + + + + {49739eae-0eb0-4420-914f-6736fd21d3d8} + 1 + 2 + 1024 + bool disconnectDevice() + + + + + {3ddefa2d-53a1-4291-80a8-3e68bcd23e5a} + 1 + 2 + 1024 + void switchTool(QString devID, QString toolName) + + + + + {405673f6-3f4a-4b00-bba7-afd3e361b0af} + 1 + 2 + 1024 + void switchTool(QString toolName) + + + + + {ced55606-80ac-4c59-88f6-4987828a0fe6} + 1 + 2 + 1024 + void runScript(QString content, QString fileName) + + + + + {5bd60966-3a75-47dd-ae5f-66f448392d95} + 3 + 2 + 64 + bool sortByUUID(const QString &k1, const QString &k2) + + + + + {535596bf-5474-424e-bcba-e2a3b38681b7} + 3 + 1 + ScopyMainWindow *m_w + + + + + + + + + + + + {43312ec0-6b21-44ef-92c2-11e826117743} + + + + + + + + {43312ec0-6b21-44ef-92c2-11e826117743} + + + CmdLineHandler + + + + + + + {e0bf02f8-dd4d-4146-82ff-5262db0d401f} + + + + + + + + {e0bf02f8-dd4d-4146-82ff-5262db0d401f} + + + {43312ec0-6b21-44ef-92c2-11e826117743} + {48d6cbe3-addd-452e-979f-13e57400b86c} + + + + + + + + + + + + + + scopy + + + + + {ffe07fe6-2c6a-433a-a0b8-eec580d02817} + 1 + 2 + 64 + int handle(QCommandLineParser &parser, ScopyMainWindow_API &scopyApi) + + + + + {b2da0974-0fb1-4c3d-898f-f035ba271ed8} + 1 + 2 + 64 + void withLogFileOption(QCommandLineParser &parser) + + + + + {3c523f8b-f3cc-489d-b3ef-5eaae0d532ed} + 1 + 2 + 64 + void closeLogFile() + + + + + {b3df2dbf-5038-4d0d-a96d-d7d933b8bbac} + 3 + 2 + 64 + void logOutputHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) + + + + + {eed31e6a-79d6-46a1-a558-9500f8ed9126} + 3 + 1 + 64 + FILE* logFile_ + + + + + + + + + + + + + + + + + + + + diff --git a/core/doc/device_lifecycle.qmodel b/core/doc/device_lifecycle.qmodel new file mode 100644 index 0000000000..2ae9bcde83 --- /dev/null +++ b/core/doc/device_lifecycle.qmodel @@ -0,0 +1,4669 @@ + + + + {234b43b8-3705-4790-91ff-361b66228a07} + + + + + + + + {fe231df4-b10b-46b3-954d-08b9ee6e6deb} + + + device_lifecycle + + + + + + + {9143d673-18f1-4ef5-9ea3-db43af21eba9} + + + + + + + + + + {9143d673-18f1-4ef5-9ea3-db43af21eba9} + + + device_lifecycle + + + + + + + + + + + + {8b586446-64b7-4d54-9b78-d8c38a9c48d4} + + + {dcfd5d9c-4af0-4ef1-89e5-8ab8c7946379} + DeviceManager::createDevice(category, param) + x:-150;y:40 + x:-130;y:-15;w:260;h:30 + 0 + + + activity + false + + + + + + + + + + + {675d06b3-4d88-497a-a1a6-4da49ef1c906} + + + {498d2672-1bda-4ed4-a64d-a1c9805a6bec} + Scan devices + x:-190;y:-30 + x:-80;y:-15;w:160;h:30 + false + 0 + + + activity + false + + + + + + + + + + + {dfd6d6cb-d376-40ba-a2a8-c2f44c4d40d8} + + + {8ea7ecb5-02c8-4c0a-a851-89ca5cba3048} + Add devices from add page + x:320;y:50 + x:-80;y:-15;w:160;h:30 + 0 + + + activity + false + + + + + + + + + + + {906914ed-182f-48ed-98f7-0448a5380d4f} + + + {50b2bb3d-95d7-4860-9b61-eccdb07f5591} + {675d06b3-4d88-497a-a1a6-4da49ef1c906} + {8b586446-64b7-4d54-9b78-d8c38a9c48d4} + + + Controlflow + + + true + + + + + + + + + + + + + {ac274100-8944-43bb-925f-2aade04a14aa} + + + {fa160e35-2e2a-4675-a374-72b2f5d61f42} + DeviceFactory::build() + x:-160;y:120 + x:-85;y:-15;w:170;h:30 + false + 0 + + + activity + false + + + + + + + + + + + {5eb63ad8-dbc4-49dc-9f40-0287802b7f87} + + + {88124b19-0e05-4484-9573-9c7187c4d55d} + {8b586446-64b7-4d54-9b78-d8c38a9c48d4} + {ac274100-8944-43bb-925f-2aade04a14aa} + + + Controlflow + + + true + + + + + + + + + + + + + {1362195f-bf29-452c-9b0e-c4025cd98c60} + + + {31c607d0-ef78-4ca1-ae8e-d166d7d63227} + DeviceLoader::init() + x:-160;y:200 + x:-60;y:-15;w:120;h:30 + 0 + + + activity + false + + + + + + + + + + + {786e0980-8bbf-488b-865d-0185972acf62} + + + {2b322948-a804-44dc-a513-8846dfd5df7b} + {ac274100-8944-43bb-925f-2aade04a14aa} + {1362195f-bf29-452c-9b0e-c4025cd98c60} + + + Controlflow + + + true + + + + + + + + + + + + + {09f96115-1482-48cf-89b3-eb1e660bb1c7} + + + {ae89e41c-3635-4ff9-ab9c-8a06021ff888} + Device::loadPlugins() + x:55;y:390 + x:-85;y:-15;w:170;h:30 + false + 0 + + + activity + false + + + + + + + + + {1aadb286-dd8e-4dee-ad55-53bd0e7da7e3} + + + for(plugin : PluginManager) { + plugin->compatible() + d.devicePlugins.append(plugin::clone()) +} + + x:145;y:265 + x:0;y:0;w:220.344;h:81 + + + + + + + + + {b3d76c9f-139b-4544-b742-bd0e281d7e76} + + + for(plugin : d.devicePlugins) { + plugin->preload() + plugin->loadIcon() + plugin->createInfoPage() + etc ... +} + x:130;y:390 + x:0;y:0;w:160.906;h:94 + + + + + + + + + + + {3dbfb375-757c-47ed-a1a7-f6a7b693fbe1} + + + {e07b6101-21d6-42f5-a2fd-2ba363f970d2} + emit deviceAdded(id, d) + x:55;y:505 + x:-85;y:-15;w:170;h:30 + false + 0 + + + activity + false + + + + + + + + + + + {9546d2da-518b-4a8d-b13f-c2277abfb4bc} + + + {8e780fec-15c6-4e7f-bba9-1727d68134aa} + {09f96115-1482-48cf-89b3-eb1e660bb1c7} + {3dbfb375-757c-47ed-a1a7-f6a7b693fbe1} + + + Controlflow + + + true + + + + + + + + + + + + + {760c684f-6f01-48fa-85a8-9b3948846848} + + + {450e5c3e-5352-47f2-937a-be2e8ec4acb8} + deviceAdded(id, d) + x:705;y:-155 + x:-55;y:-15;w:110;h:30 + 0 + + + activity + false + + + + + + + + + + + {54f22c98-9ff6-4780-a64b-e7945d3090b6} + + + {949194fa-3174-4666-a141-7ddad345ca74} + connectBtnClicked + x:495;y:330 + x:-55;y:-15;w:110;h:30 + 0 + + + activity + false + + + + + + + + + + + {327255ed-8177-4eff-87ff-2676a7105d8c} + + + {b46c96d5-c091-4037-b9ea-516f08efb78e} + HomePage::addDevice + x:645;y:-70 + x:-65;y:-15;w:130;h:30 + 0 + + + activity + false + + + + + + + + + + + {4fdbaabe-e432-4b88-a2e3-b5bd4e12b24b} + + + {bd19dbd4-1ad1-4230-ae36-01ed79ed62df} + toolManager::addToolList + x:845;y:-70 + x:-75;y:-15;w:150;h:30 + 0 + + + activity + false + + + + + + + + + + + {c724145c-56c6-45c5-a706-34af21832105} + + + {60a3e1aa-f367-4582-9340-a3dea89c9c09} + {760c684f-6f01-48fa-85a8-9b3948846848} + {327255ed-8177-4eff-87ff-2676a7105d8c} + + + Controlflow + + + true + + + + + + + + + + + + + {652f94f8-4095-4aed-9da4-8e978bec097f} + + + {64be3a56-6a5d-4fcb-a0ad-fa67de710d2e} + {760c684f-6f01-48fa-85a8-9b3948846848} + {4fdbaabe-e432-4b88-a2e3-b5bd4e12b24b} + + + Controlflow + + + true + + + + + + + + + + + + + {3a1a702d-c7b1-408b-ba11-d84d68645f96} + + + {c1e9f882-a5f3-44aa-a098-6ed5b34e19ab} + DeviceBrowser::addDevice + x:515;y:5 + x:-75;y:-15;w:150;h:30 + 0 + + + activity + false + + + + + + + + + + + {00887906-4ed4-466a-9266-f86d699a2b82} + + + {bae749ba-45ee-41b9-99b2-6cdc91a67c52} + InfoPageStack::addDevice + x:695;y:5 + x:-75;y:-15;w:150;h:30 + 0 + + + activity + false + + + + + + + + + + + {da562d2b-212b-45f3-bb1f-36435a9ebec8} + + + {6b035063-9c12-41bc-9e85-88dfc6d5f203} + {327255ed-8177-4eff-87ff-2676a7105d8c} + {3a1a702d-c7b1-408b-ba11-d84d68645f96} + + + Controlflow + + + true + + + + + + + + + + + + + {6663939f-b913-4de2-81c6-daf119c9d776} + + + {ae6fb710-5581-4da8-a461-e89e1c66af83} + {327255ed-8177-4eff-87ff-2676a7105d8c} + {00887906-4ed4-466a-9266-f86d699a2b82} + + + Controlflow + + + true + + + + + + + + + + + + + {09907f36-f759-4b21-9d2f-848d6a1431d4} + + + {f58a866c-b475-4fa8-97e3-6dabeff265db} + disconnectBtnClicked + x:760;y:310 + x:-65;y:-15;w:130;h:30 + 0 + + + activity + false + + + + + + + + + + + {e4297a21-83e5-4992-b9c2-deaf07be84a5} + + + {df125acc-6b7d-40f1-94eb-f06e1bd342be} + DeviceManager::disconnectSignal + x:930;y:310 + x:-95;y:-15;w:190;h:30 + 0 + + + activity + false + + + + + + + + + + + {5b91af63-5612-46c2-bf22-e3f0693c5ab1} + + + {8132c878-c2da-4e2c-a0a9-5d0d71b88730} + Device::connectDev + x:495;y:405 + x:-60;y:-15;w:120;h:30 + false + 0 + + + activity + false + + + + + + + + + + + {5064fa17-eb84-488a-97c7-873f8e643938} + + + {270a5894-7da7-415f-814d-06539e3403cf} + DeviceManager::deviceConnected(id,device) + x:380;y:770 + x:-120;y:-15;w:240;h:30 + 0 + + + activity + false + + + + + + + + + {2397aef5-5810-4f2f-bbe7-6cf75cf59c63} + + + for(plugin : d.devicePlugins) { +plugin->onConnect() +plugin->loadSettings() +... +} + x:565;y:370 + x:0;y:0;w:160.906;h:81 + + + + + + + + + + + {1563b5a1-49d8-4306-b9c7-4bcdbd45187c} + + + {038b99f6-70a8-42cd-a602-41b391557c1c} + {54f22c98-9ff6-4780-a64b-e7945d3090b6} + {5b91af63-5612-46c2-bf22-e3f0693c5ab1} + + + Controlflow + + + true + + + + + + + + + + + + + {1cddff8a-3d67-4688-a989-19549bf8fa6a} + + + {4b15f10b-9672-4d85-b03c-64fd93912cfc} + {1ba23035-12a5-4844-9dd8-826b9ff8a39d} + {feb3a89b-04ca-4092-ba84-16e93fdea37c} + + + Controlflow + + + true + true + + + + + + + + + + + + + {22d9d194-85c0-47bc-9004-9c1986b1e294} + + + {9fc33909-3e4a-431f-af45-1f4e392a3157} + Turn Off scan + x:235;y:825 + x:-45;y:-15;w:90;h:30 + 0 + + + activity + false + + + + + + + + + + + {e980ee33-f4f1-4e38-b200-b8eb2d84fd5c} + + + {b4b525d0-9ac1-4384-b680-74e174f67a78} + toolManager::lockToolList + x:300;y:870 + x:-75;y:-15;w:150;h:30 + 0 + + + activity + false + + + + + + + + + + + {e5745120-cfec-4c0f-8e12-025ddf7839df} + + + {7f38adbc-d539-4594-b6b6-00bc29237713} + {5064fa17-eb84-488a-97c7-873f8e643938} + {e980ee33-f4f1-4e38-b200-b8eb2d84fd5c} + + + Controlflow + + + true + + + + + + + + + + + + + {0c50ea4f-d4ec-493c-ad7f-bee653e257a0} + + + {cf14eb09-f118-4e0a-b3f5-c61c47d2b38a} + {5064fa17-eb84-488a-97c7-873f8e643938} + {22d9d194-85c0-47bc-9004-9c1986b1e294} + + + Controlflow + + + true + + + + + + + + + + + + + {3ef66f84-acf9-4d51-b105-81be1693a4fd} + + + {12b8f9e2-1e10-44ac-b85a-4bc6a6731a7f} + HomePage::connectDevice + x:460;y:850 + x:-75;y:-15;w:150;h:30 + 0 + + + activity + false + + + + + + + + + + + {8b660c41-5a87-468b-aac2-a2e5e13d79ee} + + + {961c2424-3eff-4c76-88f2-f3887d7fb72f} + {5064fa17-eb84-488a-97c7-873f8e643938} + {3ef66f84-acf9-4d51-b105-81be1693a4fd} + + + Controlflow + + + true + + + + + + + + + + + + + {382d0e08-b7be-47c2-ad0b-8802d2c61247} + + + {e8327051-3966-4bfd-a0a2-e2ea9a7736ea} + DeviceBrowser::connectDevice + x:460;y:925 + x:-85;y:-15;w:170;h:30 + 0 + + + activity + false + + + + + + + + + + + {41fac51c-cf65-4c82-8870-dad3400180e9} + + + {11861734-7bdb-4915-a918-4c74802e2825} + {3ef66f84-acf9-4d51-b105-81be1693a4fd} + {382d0e08-b7be-47c2-ad0b-8802d2c61247} + + + Controlflow + + + true + + + + + + + + + + + + + {a1e73aa9-a8bb-41ae-a48f-8ab98a028723} + + + {9215c91f-1b56-4021-b796-db29ade8be1b} + Device::disconnectDev + x:930;y:395 + x:-65;y:-15;w:130;h:30 + 0 + + + activity + false + + + + + + + + + + + {e23b345b-5bbf-44a7-ad8c-cbd7b7618d47} + + + {d34961a3-7449-49fa-998b-74674d2fa276} + {09907f36-f759-4b21-9d2f-848d6a1431d4} + {a1e73aa9-a8bb-41ae-a48f-8ab98a028723} + + + Controlflow + + + true + + + + + + + + + + + + + {6a4da5b7-9434-4340-beb6-1a82ee340e71} + + + {ed57151d-9ae5-488c-aaa2-dbba96099ed9} + {e4297a21-83e5-4992-b9c2-deaf07be84a5} + {a1e73aa9-a8bb-41ae-a48f-8ab98a028723} + + + Controlflow + + + true + + + + + + + + + + + + + {ba8ff42c-6636-4ecd-8581-6ba6a716523b} + + + {dcf09a8a-c588-42f5-b927-c3ac545b6d2d} + DeviceManager::deviceDisconnected(id) + x:925;y:460 + x:-110;y:-15;w:220;h:30 + 0 + + + activity + false + + + + + + + + + + + {2067aba1-fde6-4adb-bed8-cf7782fde026} + + + {c304010c-0039-487f-976b-f7099953204b} + Turn on scan + x:840;y:545 + x:-45;y:-15;w:90;h:30 + 0 + + + activity + false + + + + + + + + + + + {cb8843cf-305c-4894-9f4f-06ca9a8be595} + + + {e3264376-b740-4cb2-a177-6c940aeab52c} + toolManager::unlockToolList + x:895;y:605 + x:-80;y:-15;w:160;h:30 + 0 + + + activity + false + + + + + + + + + + + {13fc9ed6-dc35-47f8-af2a-2895ba6dd023} + + + {98b7c85f-876f-4b54-b2dc-f6392b7fc2c5} + HomePage::disconnectDevice + x:1035;y:545 + x:-85;y:-15;w:170;h:30 + 0 + + + activity + false + + + + + + + + + + + {f3c989d9-628d-4106-9a30-68b1c231ad51} + + + {0ef41a07-c8c1-469e-a300-425dade081e2} + HomePage::disconnectDevice + x:1050;y:650 + x:-85;y:-15;w:170;h:30 + 0 + + + activity + false + + + + + + + + + + + {59df3f1f-17d6-4dcb-a939-e24c331bf67a} + + + {e1d3e6cc-5f7a-4b52-be1f-abd7c16b0346} + {a1e73aa9-a8bb-41ae-a48f-8ab98a028723} + {ba8ff42c-6636-4ecd-8581-6ba6a716523b} + + + Controlflow + + + true + + + + + + + + + + + + + {5eb79fb9-7a6d-4ee3-b08a-96c4d7918873} + + + {e0fa6e20-050f-402c-aaa5-dcf66e53b03e} + {ba8ff42c-6636-4ecd-8581-6ba6a716523b} + {2067aba1-fde6-4adb-bed8-cf7782fde026} + + + Controlflow + + + true + + + + + + + + + + + + + {2def631d-d19a-470d-95e1-b83d43b9d7f9} + + + {56c812b5-3d31-4c1b-bed9-3f3ed4be1232} + {ba8ff42c-6636-4ecd-8581-6ba6a716523b} + {cb8843cf-305c-4894-9f4f-06ca9a8be595} + + + Controlflow + + + true + + + + + + + + + + + + + {fad092b3-fc6d-4148-bbeb-13ed4b4f2693} + + + {8cf5ac1a-0823-433a-a40a-79b8d470cfbb} + {ba8ff42c-6636-4ecd-8581-6ba6a716523b} + {13fc9ed6-dc35-47f8-af2a-2895ba6dd023} + + + Controlflow + + + true + + + + + + + + + + + + + {919ee79d-88d4-4280-84ab-311089e96d81} + + + {b94f723b-4f20-4e19-82e6-31d3f9e0092e} + {13fc9ed6-dc35-47f8-af2a-2895ba6dd023} + {f3c989d9-628d-4106-9a30-68b1c231ad51} + + + Controlflow + + + true + + + + + + + + + + + + + {4098dc0a-ce54-45ba-98f4-da3478e6f2d2} + + + {b10e95f7-983d-4406-8fbe-969dd5d99c7c} + deviceRemoved(id) + x:900;y:45 + x:-60;y:-15;w:120;h:30 + 0 + + + activity + false + + + + + + + + + + + {e95cd0b9-e251-47bc-96a2-4f2a637a80e6} + + + {b93d1868-8f24-415b-84e4-9804abec0729} + HomePage::removeDevice + x:735;y:110 + x:-75;y:-15;w:150;h:30 + 0 + + + activity + false + + + + + + + + + + + {d155415b-a7ad-47fd-850e-80d07aa981e4} + + + {a47833f4-ec64-4d3e-8ac0-a5e49c7075e8} + DeviceBrowser::removeDevice + x:640;y:185 + x:-85;y:-15;w:170;h:30 + 0 + + + activity + false + + + + + + + + + + + {b95160f8-1361-45f0-9823-7e9fbb631162} + + + {8d5c6aff-b7b1-43af-920b-a68cc73790c1} + InfoPageStack::removeDevice + x:840;y:185 + x:-85;y:-15;w:170;h:30 + 0 + + + activity + false + + + + + + + + + + + {ceb7178c-b309-4f97-bbb8-486fb961bfd5} + + + {aa13b09a-14d0-470e-b1b8-04a7c11c7020} + {4098dc0a-ce54-45ba-98f4-da3478e6f2d2} + {e95cd0b9-e251-47bc-96a2-4f2a637a80e6} + + + Controlflow + + + true + + + + + + + + + + + + + {b7350cc2-0651-4ba6-b924-881675ab5b5e} + + + {3551c740-83da-4f1f-85d1-58073f903278} + {e95cd0b9-e251-47bc-96a2-4f2a637a80e6} + {d155415b-a7ad-47fd-850e-80d07aa981e4} + + + Controlflow + + + true + + + + + + + + + + + + + {89e5b7a8-a2fe-4093-991d-eebbae770990} + + + {1d0ae087-36ba-4dd0-84ba-1867e4611ac3} + {e95cd0b9-e251-47bc-96a2-4f2a637a80e6} + {b95160f8-1361-45f0-9823-7e9fbb631162} + + + Controlflow + + + true + + + + + + + + + + + + + {173f7acd-ce23-4353-b118-15a7796cfd50} + + + {0de9f20f-e41d-4668-bbf0-4a8c62e25ff8} + toolManager::removeToolList + x:990;y:120 + x:-85;y:-15;w:170;h:30 + 0 + + + activity + false + + + + + + + + + + + {4434382b-48b3-45a0-a082-5a6d29e3f216} + + + {642ab7e2-386e-47bc-abae-12c09f7f2b81} + {4098dc0a-ce54-45ba-98f4-da3478e6f2d2} + {173f7acd-ce23-4353-b118-15a7796cfd50} + + + Controlflow + + + true + + + + + + + + + + + + + {d43afd81-92c9-46c6-a602-00579b52a60a} + + + {b71c3466-c9e5-427a-bcc3-da9b26aecbe6} + DeviceManager::addDevice(Device*) + x:50;y:330 + x:-100;y:-15;w:200;h:30 + 0 + + + activity + false + + + + + + + + + + + {95f2a08b-cb94-4d1c-bf3b-e9a2267cf1f1} + + + {f670c895-1d90-40f2-a1e5-9acd1844a3ee} + {1362195f-bf29-452c-9b0e-c4025cd98c60} + {d43afd81-92c9-46c6-a602-00579b52a60a} + + + Controlflow + + + true + + + + + + + + + + + + + {a562e94d-8b05-4229-a1c5-ad61a15044c8} + + + {15be4a11-1f33-44e5-b5e5-702cf5f73a44} + {d43afd81-92c9-46c6-a602-00579b52a60a} + {09f96115-1482-48cf-89b3-eb1e660bb1c7} + + + Controlflow + + + true + + + + + + + + + + + + + {02ba841a-33ca-4cc0-9aa1-8587b17008f7} + + + {e2e93b20-8354-4cf0-85ae-fab7fff2488a} + DeviceFactory::build() + x:315;y:125 + x:-65;y:-15;w:130;h:30 + 0 + + + activity + false + + + + + + + + + + + {f8ba3a48-d104-4549-91b4-2b7a39e4fe5c} + + + {6e64d12e-b70a-4aa8-b1d1-43b6655accfe} + DeviceLoader::init() + x:300;y:185 + x:-60;y:-15;w:120;h:30 + 0 + + + activity + false + + + + + + + + + + + {bad45b10-c449-4dc5-a802-a5febd223ba0} + + + {9d997378-f83a-4e43-9168-20cd03e9d0de} + {dfd6d6cb-d376-40ba-a2a8-c2f44c4d40d8} + {02ba841a-33ca-4cc0-9aa1-8587b17008f7} + + + Controlflow + + + true + + + + + + + + + + + + + {1f00770f-2a65-4391-9d5f-4db50cab116d} + + + {ae37cc42-e517-4d98-9ae6-4881e560bece} + {02ba841a-33ca-4cc0-9aa1-8587b17008f7} + {f8ba3a48-d104-4549-91b4-2b7a39e4fe5c} + + + Controlflow + + + true + + + + + + + + + + + + + {450ef286-21d1-43bd-a4a5-bf63ac51a05f} + + + {a466b152-03e0-4044-8029-ca23b455bc7e} + {f8ba3a48-d104-4549-91b4-2b7a39e4fe5c} + {d43afd81-92c9-46c6-a602-00579b52a60a} + + + Controlflow + + + true + + + + + + + + + + + + + {1ba23035-12a5-4844-9dd8-826b9ff8a39d} + + + {cacf8979-a34d-48a7-beea-6f1573d56769} + Plugin::onConnect() + x:495;y:520 + x:-20;y:-20;w:40;h:40 + 0 + + + condition + false + + + + + + + + + + + {eadfade9-e32d-4c1a-9ab2-bd3006bd4f3a} + + + {5c41df9d-54c6-4fb5-a7c6-7681f126abef} + {5b91af63-5612-46c2-bf22-e3f0693c5ab1} + {1ba23035-12a5-4844-9dd8-826b9ff8a39d} + + + Controlflow + + + true + + + + + + + + + + + + + {d004af97-0937-45b8-a370-5917fda8e311} + + + {069c3266-1486-44f6-9e13-1f73e96390ec} + Add the plugin to the +connected plugins list + x:380;y:680 + x:-65;y:-25;w:130;h:50 + false + 0 + + + activity + false + + + + + + + + + {653820e5-0d02-424f-8b33-7278d3a50a80} + + + For each Plugin + x:520;y:590 + x:-210;y:-140;w:420;h:280 + + + + + + + + + + + {feb3a89b-04ca-4092-ba84-16e93fdea37c} + + + {bcb01831-b3b9-409f-ace6-815810265ed5} + Plugin::loadSettings + x:380;y:605 + x:-60;y:-15;w:120;h:30 + 0 + + + activity + false + + + + + + + + + + + {2c1a3a48-516a-47d8-a81e-20297385b2ba} + + + {2abcfaec-fafb-42f7-b6f0-869f23b4b7af} + {feb3a89b-04ca-4092-ba84-16e93fdea37c} + {d004af97-0937-45b8-a370-5917fda8e311} + + + Controlflow + + + true + + + + + + + + + + + + + {dbc9c9c4-cb91-47f8-8d1f-130d26a4e239} + + + {2df3468c-8f1e-44c3-8355-38e47e79631a} + {d004af97-0937-45b8-a370-5917fda8e311} + {5064fa17-eb84-488a-97c7-873f8e643938} + + + Controlflow + + + true + + + + + + + + + + + + + {74de91a6-a8ce-42de-bf33-2fc303e56579} + + + {182d1eeb-71a7-49b6-accd-cfe4256c2699} + disconnectDevOnConnectFailure + x:615;y:670 + x:-20;y:-20;w:40;h:40 + 0 + + + condition + false + + + + + + + + + + + {089a01eb-39b4-465a-94eb-b1f8976027fc} + + + {52430358-d829-4741-a5f6-220088dc3463} + emit connectionFailed() + x:615;y:770 + x:-70;y:-15;w:140;h:30 + 0 + + + activity + false + + + + + + + + + + + {7168c036-4d7e-461d-8842-1a1087d3edf9} + + + {211c4cec-908f-496e-9e2a-5bf6617df89c} + {1ba23035-12a5-4844-9dd8-826b9ff8a39d} + {abf3d7b1-f926-40cd-b15a-05f630804aa2} + + + Controlflow + + + false + true + + + + + + + + + + + + + {714da7c6-f708-4935-a9d2-117a44308ba1} + + + {5c6b20c4-40fa-4355-9fff-87d61a525523} + {74de91a6-a8ce-42de-bf33-2fc303e56579} + {089a01eb-39b4-465a-94eb-b1f8976027fc} + + + Controlflow + + + true + + + + + true + + + + + + + + + + + + + {4ce23a0d-4cf5-4596-9f45-6f9d44721152} + + + {fd4dc4b2-263f-49ae-83f7-982ad16bbf31} + Show warning badge + x:615;y:850 + x:-65;y:-15;w:130;h:30 + 0 + + + activity + false + + + + + + + + + + + {aceba920-a293-48b8-a4fb-c421e3d05143} + + + {717f99b3-15b7-4eb6-8867-bfdc17892868} + {089a01eb-39b4-465a-94eb-b1f8976027fc} + {4ce23a0d-4cf5-4596-9f45-6f9d44721152} + + + Controlflow + + + true + + + + + + + + + + + + + {abf3d7b1-f926-40cd-b15a-05f630804aa2} + + + {12fc2328-41d4-4191-8a2b-3ef90dcf4444} + Get "disconnectDevOnConnectFailure" +plugin metadata + x:615;y:605 + x:-105;y:-25;w:210;h:50 + 0 + + + activity + false + + + + + + + + + + + {28547bb6-65b5-4a53-8343-1db812f7ede5} + + + {22ee1032-d7a3-444b-b4d5-02a68546a76d} + {abf3d7b1-f926-40cd-b15a-05f630804aa2} + {74de91a6-a8ce-42de-bf33-2fc303e56579} + + + Controlflow + + + true + + + + + + + + + + + + + {0b443c10-0491-4c5c-be73-ac0b1dd05b4e} + + + {283beb3b-bbf6-4808-b77a-1f4183418a5b} + DeviceImpl::connectionFailed() + x:1120;y:310 + x:-85;y:-15;w:170;h:30 + 0 + + + activity + false + + + + + + + + + + + {485b3f9e-e716-495a-9d6b-42b5d6666f29} + + + {d5b54170-970c-4f65-861f-7458898de830} + {0b443c10-0491-4c5c-be73-ac0b1dd05b4e} + {a1e73aa9-a8bb-41ae-a48f-8ab98a028723} + + + Controlflow + + + true + + + + + + + + 1694434093474 + Activities + + + + + + + + + + {8b580cd9-7a28-4238-8810-7d9a71ff0102} + + + + + + + + {8b580cd9-7a28-4238-8810-7d9a71ff0102} + + + + + false + start + + + + + + + + {817d6a7e-d864-45c8-94a1-4c3d24862c0c} + + + + + + + + {817d6a7e-d864-45c8-94a1-4c3d24862c0c} + + + New Condition + + + false + condition + + + + + + + + {dcfd5d9c-4af0-4ef1-89e5-8ab8c7946379} + + + + + + + + {dcfd5d9c-4af0-4ef1-89e5-8ab8c7946379} + + + DeviceManager::createDevice(category, param) + + + + + + + {88124b19-0e05-4484-9573-9c7187c4d55d} + + + + + + + + {88124b19-0e05-4484-9573-9c7187c4d55d} + + + {dcfd5d9c-4af0-4ef1-89e5-8ab8c7946379} + {fa160e35-2e2a-4675-a374-72b2f5d61f42} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {96ff72d7-61ad-44a8-9595-e8da189cfeb0} + + + + + + + + {96ff72d7-61ad-44a8-9595-e8da189cfeb0} + + + ScopyAddPage::addDevice + + + false + activity + + + + + + + + {bf575cc8-e09a-4d70-a5b1-03e54b393884} + + + + + + + + {bf575cc8-e09a-4d70-a5b1-03e54b393884} + + + + + false + start + + + + + + + + {498d2672-1bda-4ed4-a64d-a1c9805a6bec} + + + + + + + + {498d2672-1bda-4ed4-a64d-a1c9805a6bec} + + + Scan devices + + + + + + + {50b2bb3d-95d7-4860-9b61-eccdb07f5591} + + + + + + + + {50b2bb3d-95d7-4860-9b61-eccdb07f5591} + + + {498d2672-1bda-4ed4-a64d-a1c9805a6bec} + {dcfd5d9c-4af0-4ef1-89e5-8ab8c7946379} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {8ea7ecb5-02c8-4c0a-a851-89ca5cba3048} + + + + + + + + {8ea7ecb5-02c8-4c0a-a851-89ca5cba3048} + + + Add devices from add page + + + + + + + {2ca5f30f-a1cb-4904-aac2-9bef6c6615f3} + + + + + + + + {2ca5f30f-a1cb-4904-aac2-9bef6c6615f3} + + + {8ea7ecb5-02c8-4c0a-a851-89ca5cba3048} + {dcfd5d9c-4af0-4ef1-89e5-8ab8c7946379} + + + Controlflow + + + true + + + + + + + + + + {27fff63c-22db-4efb-b200-62913e4424a4} + + + + + + + + {27fff63c-22db-4efb-b200-62913e4424a4} + + + {8ea7ecb5-02c8-4c0a-a851-89ca5cba3048} + {fa160e35-2e2a-4675-a374-72b2f5d61f42} + + + Controlflow + + + true + + + + + + + + + + {9d997378-f83a-4e43-9168-20cd03e9d0de} + + + + + + + + {9d997378-f83a-4e43-9168-20cd03e9d0de} + + + {8ea7ecb5-02c8-4c0a-a851-89ca5cba3048} + {e2e93b20-8354-4cf0-85ae-fab7fff2488a} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {fa160e35-2e2a-4675-a374-72b2f5d61f42} + + + + + + + + {fa160e35-2e2a-4675-a374-72b2f5d61f42} + + + DeviceFactory::build() + + + + + + + {2b322948-a804-44dc-a513-8846dfd5df7b} + + + + + + + + {2b322948-a804-44dc-a513-8846dfd5df7b} + + + {fa160e35-2e2a-4675-a374-72b2f5d61f42} + {31c607d0-ef78-4ca1-ae8e-d166d7d63227} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {31c607d0-ef78-4ca1-ae8e-d166d7d63227} + + + + + + + + {31c607d0-ef78-4ca1-ae8e-d166d7d63227} + + + DeviceLoader::init() + + + + + + + {e04cd05e-93ae-4b9c-a9a1-6b6b3bfaacb6} + + + + + + + + {e04cd05e-93ae-4b9c-a9a1-6b6b3bfaacb6} + + + {31c607d0-ef78-4ca1-ae8e-d166d7d63227} + {ae89e41c-3635-4ff9-ab9c-8a06021ff888} + + + Controlflow + + + true + + + + + + + + + + {f670c895-1d90-40f2-a1e5-9acd1844a3ee} + + + + + + + + {f670c895-1d90-40f2-a1e5-9acd1844a3ee} + + + {31c607d0-ef78-4ca1-ae8e-d166d7d63227} + {b71c3466-c9e5-427a-bcc3-da9b26aecbe6} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {ae89e41c-3635-4ff9-ab9c-8a06021ff888} + + + + + + + + {ae89e41c-3635-4ff9-ab9c-8a06021ff888} + + + Device::loadPlugins() + + + + + + + {8e780fec-15c6-4e7f-bba9-1727d68134aa} + + + + + + + + {8e780fec-15c6-4e7f-bba9-1727d68134aa} + + + {ae89e41c-3635-4ff9-ab9c-8a06021ff888} + {e07b6101-21d6-42f5-a2fd-2ba363f970d2} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {e07b6101-21d6-42f5-a2fd-2ba363f970d2} + + + + + + + + {e07b6101-21d6-42f5-a2fd-2ba363f970d2} + + + emit deviceAdded(id, d) + + + false + activity + + + + + + + + {450e5c3e-5352-47f2-937a-be2e8ec4acb8} + + + + + + + + {450e5c3e-5352-47f2-937a-be2e8ec4acb8} + + + deviceAdded(id, d) + + + + + + + {60a3e1aa-f367-4582-9340-a3dea89c9c09} + + + + + + + + {60a3e1aa-f367-4582-9340-a3dea89c9c09} + + + {450e5c3e-5352-47f2-937a-be2e8ec4acb8} + {b46c96d5-c091-4037-b9ea-516f08efb78e} + + + Controlflow + + + true + + + + + + + + + + {64be3a56-6a5d-4fcb-a0ad-fa67de710d2e} + + + + + + + + {64be3a56-6a5d-4fcb-a0ad-fa67de710d2e} + + + {450e5c3e-5352-47f2-937a-be2e8ec4acb8} + {bd19dbd4-1ad1-4230-ae36-01ed79ed62df} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {8c86f19b-2a8d-49bc-b484-2670781a537b} + + + + + + + + {8c86f19b-2a8d-49bc-b484-2670781a537b} + + + + + false + start + + + + + + + + {949194fa-3174-4666-a141-7ddad345ca74} + + + + + + + + {949194fa-3174-4666-a141-7ddad345ca74} + + + connectBtnClicked + + + + + + + {038b99f6-70a8-42cd-a602-41b391557c1c} + + + + + + + + {038b99f6-70a8-42cd-a602-41b391557c1c} + + + {949194fa-3174-4666-a141-7ddad345ca74} + {8132c878-c2da-4e2c-a0a9-5d0d71b88730} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {98fa7405-bd4a-4a76-a564-c192e220b349} + + + + + + + + {98fa7405-bd4a-4a76-a564-c192e220b349} + + + Remove Device + + + false + activity + + + + + + + + {b46c96d5-c091-4037-b9ea-516f08efb78e} + + + + + + + + {b46c96d5-c091-4037-b9ea-516f08efb78e} + + + HomePage::addDevice + + + + + + + {6b035063-9c12-41bc-9e85-88dfc6d5f203} + + + + + + + + {6b035063-9c12-41bc-9e85-88dfc6d5f203} + + + {b46c96d5-c091-4037-b9ea-516f08efb78e} + {c1e9f882-a5f3-44aa-a098-6ed5b34e19ab} + + + Controlflow + + + true + + + + + + + + + + {ae6fb710-5581-4da8-a461-e89e1c66af83} + + + + + + + + {ae6fb710-5581-4da8-a461-e89e1c66af83} + + + {b46c96d5-c091-4037-b9ea-516f08efb78e} + {bae749ba-45ee-41b9-99b2-6cdc91a67c52} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {bd19dbd4-1ad1-4230-ae36-01ed79ed62df} + + + + + + + + {bd19dbd4-1ad1-4230-ae36-01ed79ed62df} + + + toolManager::addToolList + + + false + activity + + + + + + + + {c1e9f882-a5f3-44aa-a098-6ed5b34e19ab} + + + + + + + + {c1e9f882-a5f3-44aa-a098-6ed5b34e19ab} + + + DeviceBrowser::addDevice + + + false + activity + + + + + + + + {bae749ba-45ee-41b9-99b2-6cdc91a67c52} + + + + + + + + {bae749ba-45ee-41b9-99b2-6cdc91a67c52} + + + InfoPageStack::addDevice + + + false + activity + + + + + + + + {f58a866c-b475-4fa8-97e3-6dabeff265db} + + + + + + + + {f58a866c-b475-4fa8-97e3-6dabeff265db} + + + disconnectBtnClicked + + + + + + + {d34961a3-7449-49fa-998b-74674d2fa276} + + + + + + + + {d34961a3-7449-49fa-998b-74674d2fa276} + + + {f58a866c-b475-4fa8-97e3-6dabeff265db} + {9215c91f-1b56-4021-b796-db29ade8be1b} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {df125acc-6b7d-40f1-94eb-f06e1bd342be} + + + + + + + + {df125acc-6b7d-40f1-94eb-f06e1bd342be} + + + DeviceManager::disconnectSignal + + + + + + + {ed57151d-9ae5-488c-aaa2-dbba96099ed9} + + + + + + + + {ed57151d-9ae5-488c-aaa2-dbba96099ed9} + + + {df125acc-6b7d-40f1-94eb-f06e1bd342be} + {9215c91f-1b56-4021-b796-db29ade8be1b} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {8132c878-c2da-4e2c-a0a9-5d0d71b88730} + + + + + + + + {8132c878-c2da-4e2c-a0a9-5d0d71b88730} + + + Device::connectDev + + + + + + + {5c41df9d-54c6-4fb5-a7c6-7681f126abef} + + + + + + + + {5c41df9d-54c6-4fb5-a7c6-7681f126abef} + + + {8132c878-c2da-4e2c-a0a9-5d0d71b88730} + {cacf8979-a34d-48a7-beea-6f1573d56769} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {270a5894-7da7-415f-814d-06539e3403cf} + + + + + + + + {270a5894-7da7-415f-814d-06539e3403cf} + + + DeviceManager::deviceConnected(id,device) + + + + + + + {7f38adbc-d539-4594-b6b6-00bc29237713} + + + + + + + + {7f38adbc-d539-4594-b6b6-00bc29237713} + + + {270a5894-7da7-415f-814d-06539e3403cf} + {b4b525d0-9ac1-4384-b680-74e174f67a78} + + + Controlflow + + + true + + + + + + + + + + {cf14eb09-f118-4e0a-b3f5-c61c47d2b38a} + + + + + + + + {cf14eb09-f118-4e0a-b3f5-c61c47d2b38a} + + + {270a5894-7da7-415f-814d-06539e3403cf} + {9fc33909-3e4a-431f-af45-1f4e392a3157} + + + Controlflow + + + true + + + + + + + + + + {961c2424-3eff-4c76-88f2-f3887d7fb72f} + + + + + + + + {961c2424-3eff-4c76-88f2-f3887d7fb72f} + + + {270a5894-7da7-415f-814d-06539e3403cf} + {12b8f9e2-1e10-44ac-b85a-4bc6a6731a7f} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {9fc33909-3e4a-431f-af45-1f4e392a3157} + + + + + + + + {9fc33909-3e4a-431f-af45-1f4e392a3157} + + + Turn Off scan + + + false + activity + + + + + + + + {b4b525d0-9ac1-4384-b680-74e174f67a78} + + + + + + + + {b4b525d0-9ac1-4384-b680-74e174f67a78} + + + toolManager::lockToolList + + + false + activity + + + + + + + + {12b8f9e2-1e10-44ac-b85a-4bc6a6731a7f} + + + + + + + + {12b8f9e2-1e10-44ac-b85a-4bc6a6731a7f} + + + HomePage::connectDevice + + + + + + + {11861734-7bdb-4915-a918-4c74802e2825} + + + + + + + + {11861734-7bdb-4915-a918-4c74802e2825} + + + {12b8f9e2-1e10-44ac-b85a-4bc6a6731a7f} + {e8327051-3966-4bfd-a0a2-e2ea9a7736ea} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {e8327051-3966-4bfd-a0a2-e2ea9a7736ea} + + + + + + + + {e8327051-3966-4bfd-a0a2-e2ea9a7736ea} + + + DeviceBrowser::connectDevice + + + false + activity + + + + + + + + {9215c91f-1b56-4021-b796-db29ade8be1b} + + + + + + + + {9215c91f-1b56-4021-b796-db29ade8be1b} + + + Device::disconnectDev + + + + + + + {e1d3e6cc-5f7a-4b52-be1f-abd7c16b0346} + + + + + + + + {e1d3e6cc-5f7a-4b52-be1f-abd7c16b0346} + + + {9215c91f-1b56-4021-b796-db29ade8be1b} + {dcf09a8a-c588-42f5-b927-c3ac545b6d2d} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {dcf09a8a-c588-42f5-b927-c3ac545b6d2d} + + + + + + + + {dcf09a8a-c588-42f5-b927-c3ac545b6d2d} + + + DeviceManager::deviceDisconnected(id) + + + + + + + {e0fa6e20-050f-402c-aaa5-dcf66e53b03e} + + + + + + + + {e0fa6e20-050f-402c-aaa5-dcf66e53b03e} + + + {dcf09a8a-c588-42f5-b927-c3ac545b6d2d} + {c304010c-0039-487f-976b-f7099953204b} + + + Controlflow + + + true + + + + + + + + + + {56c812b5-3d31-4c1b-bed9-3f3ed4be1232} + + + + + + + + {56c812b5-3d31-4c1b-bed9-3f3ed4be1232} + + + {dcf09a8a-c588-42f5-b927-c3ac545b6d2d} + {e3264376-b740-4cb2-a177-6c940aeab52c} + + + Controlflow + + + true + + + + + + + + + + {8cf5ac1a-0823-433a-a40a-79b8d470cfbb} + + + + + + + + {8cf5ac1a-0823-433a-a40a-79b8d470cfbb} + + + {dcf09a8a-c588-42f5-b927-c3ac545b6d2d} + {98b7c85f-876f-4b54-b2dc-f6392b7fc2c5} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {c304010c-0039-487f-976b-f7099953204b} + + + + + + + + {c304010c-0039-487f-976b-f7099953204b} + + + Turn on scan + + + false + activity + + + + + + + + {e3264376-b740-4cb2-a177-6c940aeab52c} + + + + + + + + {e3264376-b740-4cb2-a177-6c940aeab52c} + + + toolManager::unlockToolList + + + false + activity + + + + + + + + {98b7c85f-876f-4b54-b2dc-f6392b7fc2c5} + + + + + + + + {98b7c85f-876f-4b54-b2dc-f6392b7fc2c5} + + + HomePage::disconnectDevice + + + + + + + {b94f723b-4f20-4e19-82e6-31d3f9e0092e} + + + + + + + + {b94f723b-4f20-4e19-82e6-31d3f9e0092e} + + + {98b7c85f-876f-4b54-b2dc-f6392b7fc2c5} + {0ef41a07-c8c1-469e-a300-425dade081e2} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {0ef41a07-c8c1-469e-a300-425dade081e2} + + + + + + + + {0ef41a07-c8c1-469e-a300-425dade081e2} + + + HomePage::disconnectDevice + + + false + activity + + + + + + + + {b10e95f7-983d-4406-8fbe-969dd5d99c7c} + + + + + + + + {b10e95f7-983d-4406-8fbe-969dd5d99c7c} + + + deviceRemoved(id) + + + + + + + {aa13b09a-14d0-470e-b1b8-04a7c11c7020} + + + + + + + + {aa13b09a-14d0-470e-b1b8-04a7c11c7020} + + + {b10e95f7-983d-4406-8fbe-969dd5d99c7c} + {b93d1868-8f24-415b-84e4-9804abec0729} + + + Controlflow + + + true + + + + + + + + + + {642ab7e2-386e-47bc-abae-12c09f7f2b81} + + + + + + + + {642ab7e2-386e-47bc-abae-12c09f7f2b81} + + + {b10e95f7-983d-4406-8fbe-969dd5d99c7c} + {0de9f20f-e41d-4668-bbf0-4a8c62e25ff8} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {b93d1868-8f24-415b-84e4-9804abec0729} + + + + + + + + {b93d1868-8f24-415b-84e4-9804abec0729} + + + HomePage::removeDevice + + + + + + + {3551c740-83da-4f1f-85d1-58073f903278} + + + + + + + + {3551c740-83da-4f1f-85d1-58073f903278} + + + {b93d1868-8f24-415b-84e4-9804abec0729} + {a47833f4-ec64-4d3e-8ac0-a5e49c7075e8} + + + Controlflow + + + true + + + + + + + + + + {1d0ae087-36ba-4dd0-84ba-1867e4611ac3} + + + + + + + + {1d0ae087-36ba-4dd0-84ba-1867e4611ac3} + + + {b93d1868-8f24-415b-84e4-9804abec0729} + {8d5c6aff-b7b1-43af-920b-a68cc73790c1} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {a47833f4-ec64-4d3e-8ac0-a5e49c7075e8} + + + + + + + + {a47833f4-ec64-4d3e-8ac0-a5e49c7075e8} + + + DeviceBrowser::removeDevice + + + false + activity + + + + + + + + {8d5c6aff-b7b1-43af-920b-a68cc73790c1} + + + + + + + + {8d5c6aff-b7b1-43af-920b-a68cc73790c1} + + + InfoPageStack::removeDevice + + + false + activity + + + + + + + + {0de9f20f-e41d-4668-bbf0-4a8c62e25ff8} + + + + + + + + {0de9f20f-e41d-4668-bbf0-4a8c62e25ff8} + + + toolManager::removeToolList + + + false + activity + + + + + + + + {b71c3466-c9e5-427a-bcc3-da9b26aecbe6} + + + + + + + + {b71c3466-c9e5-427a-bcc3-da9b26aecbe6} + + + DeviceManager::addDevice(Device*) + + + + + + + {15be4a11-1f33-44e5-b5e5-702cf5f73a44} + + + + + + + + {15be4a11-1f33-44e5-b5e5-702cf5f73a44} + + + {b71c3466-c9e5-427a-bcc3-da9b26aecbe6} + {ae89e41c-3635-4ff9-ab9c-8a06021ff888} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {e2e93b20-8354-4cf0-85ae-fab7fff2488a} + + + + + + + + {e2e93b20-8354-4cf0-85ae-fab7fff2488a} + + + DeviceFactory::build() + + + + + + + {ae37cc42-e517-4d98-9ae6-4881e560bece} + + + + + + + + {ae37cc42-e517-4d98-9ae6-4881e560bece} + + + {e2e93b20-8354-4cf0-85ae-fab7fff2488a} + {6e64d12e-b70a-4aa8-b1d1-43b6655accfe} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {6e64d12e-b70a-4aa8-b1d1-43b6655accfe} + + + + + + + + {6e64d12e-b70a-4aa8-b1d1-43b6655accfe} + + + DeviceLoader::init() + + + + + + + {a466b152-03e0-4044-8029-ca23b455bc7e} + + + + + + + + {a466b152-03e0-4044-8029-ca23b455bc7e} + + + {6e64d12e-b70a-4aa8-b1d1-43b6655accfe} + {b71c3466-c9e5-427a-bcc3-da9b26aecbe6} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {52430358-d829-4741-a5f6-220088dc3463} + + + + + + + + {52430358-d829-4741-a5f6-220088dc3463} + + + emit connectionFailed() + + + + + + + {717f99b3-15b7-4eb6-8867-bfdc17892868} + + + + + + + + {717f99b3-15b7-4eb6-8867-bfdc17892868} + + + {52430358-d829-4741-a5f6-220088dc3463} + {fd4dc4b2-263f-49ae-83f7-982ad16bbf31} + + + Controlflow + + + true + + + + + + + + + + {9fea4027-3b04-441a-87d4-4b26b8deebd9} + + + + + + + + {9fea4027-3b04-441a-87d4-4b26b8deebd9} + + + {52430358-d829-4741-a5f6-220088dc3463} + {f774d05a-4352-4a74-90f8-962686140379} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {cacf8979-a34d-48a7-beea-6f1573d56769} + + + + + + + + {cacf8979-a34d-48a7-beea-6f1573d56769} + + + Plugin::onConnect() + + + + + + + {4b15f10b-9672-4d85-b03c-64fd93912cfc} + + + + + + + + {4b15f10b-9672-4d85-b03c-64fd93912cfc} + + + {cacf8979-a34d-48a7-beea-6f1573d56769} + {bcb01831-b3b9-409f-ace6-815810265ed5} + + + Controlflow + + + true + true + + + + + + + + + + {0c950c27-3a85-44f8-81f0-1a4ed1ae55c2} + + + + + + + + {0c950c27-3a85-44f8-81f0-1a4ed1ae55c2} + + + false + {cacf8979-a34d-48a7-beea-6f1573d56769} + {52430358-d829-4741-a5f6-220088dc3463} + + + Controlflow + + + true + + + + + + + + + + {211c4cec-908f-496e-9e2a-5bf6617df89c} + + + + + + + + {211c4cec-908f-496e-9e2a-5bf6617df89c} + + + {cacf8979-a34d-48a7-beea-6f1573d56769} + {12fc2328-41d4-4191-8a2b-3ef90dcf4444} + + + Controlflow + + + false + true + + + + + + + + + + + + + + false + condition + + + + + + + + {069c3266-1486-44f6-9e13-1f73e96390ec} + + + + + + + + {069c3266-1486-44f6-9e13-1f73e96390ec} + + + Add the plugin to the +connected plugins list + + + + + + + {2df3468c-8f1e-44c3-8355-38e47e79631a} + + + + + + + + {2df3468c-8f1e-44c3-8355-38e47e79631a} + + + {069c3266-1486-44f6-9e13-1f73e96390ec} + {270a5894-7da7-415f-814d-06539e3403cf} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {bcb01831-b3b9-409f-ace6-815810265ed5} + + + + + + + + {bcb01831-b3b9-409f-ace6-815810265ed5} + + + Plugin::loadSettings + + + + + + + {2abcfaec-fafb-42f7-b6f0-869f23b4b7af} + + + + + + + + {2abcfaec-fafb-42f7-b6f0-869f23b4b7af} + + + {bcb01831-b3b9-409f-ace6-815810265ed5} + {069c3266-1486-44f6-9e13-1f73e96390ec} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {182d1eeb-71a7-49b6-accd-cfe4256c2699} + + + + + + + + {182d1eeb-71a7-49b6-accd-cfe4256c2699} + + + disconnectDevOnConnectFailure + + + + + + + {5c6b20c4-40fa-4355-9fff-87d61a525523} + + + + + + + + {5c6b20c4-40fa-4355-9fff-87d61a525523} + + + {182d1eeb-71a7-49b6-accd-cfe4256c2699} + {52430358-d829-4741-a5f6-220088dc3463} + + + Controlflow + + + true + + + + + true + + + + + + + + + + {1ef7abf3-3e59-48e3-a9bb-59737b5ee755} + + + + + + + + {1ef7abf3-3e59-48e3-a9bb-59737b5ee755} + + + {182d1eeb-71a7-49b6-accd-cfe4256c2699} + {c304010c-0039-487f-976b-f7099953204b} + + + Controlflow + + + false + true + + + + + + + + + + + + + + false + condition + + + + + + + + {fd4dc4b2-263f-49ae-83f7-982ad16bbf31} + + + + + + + + {fd4dc4b2-263f-49ae-83f7-982ad16bbf31} + + + Show warning badge + + + false + activity + + + + + + + + {f774d05a-4352-4a74-90f8-962686140379} + + + + + + + + {f774d05a-4352-4a74-90f8-962686140379} + + + Device::disconnctDev + + + false + activity + + + + + + + + {12fc2328-41d4-4191-8a2b-3ef90dcf4444} + + + + + + + + {12fc2328-41d4-4191-8a2b-3ef90dcf4444} + + + Get "disconnectDevOnConnectFailure" +plugin metadata + + + + + + + {22ee1032-d7a3-444b-b4d5-02a68546a76d} + + + + + + + + {22ee1032-d7a3-444b-b4d5-02a68546a76d} + + + {12fc2328-41d4-4191-8a2b-3ef90dcf4444} + {182d1eeb-71a7-49b6-accd-cfe4256c2699} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {35f9fd25-c76a-486b-919b-edfca514b3dc} + + + + + + + + {35f9fd25-c76a-486b-919b-edfca514b3dc} + + + + + false + horizontalbar + + + + + + + + {283beb3b-bbf6-4808-b77a-1f4183418a5b} + + + + + + + + {283beb3b-bbf6-4808-b77a-1f4183418a5b} + + + DeviceImpl::connectionFailed() + + + + + + + {d5b54170-970c-4f65-861f-7458898de830} + + + + + + + + {d5b54170-970c-4f65-861f-7458898de830} + + + {283beb3b-bbf6-4808-b77a-1f4183418a5b} + {9215c91f-1b56-4021-b796-db29ade8be1b} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {75c170ad-5c36-4b54-9531-50ce00141dcf} + + + + + + + + {75c170ad-5c36-4b54-9531-50ce00141dcf} + + + + + false + activity + + + + + + + + + + + + + + + + diff --git a/core/doc/toolmenu.qmodel b/core/doc/toolmenu.qmodel new file mode 100644 index 0000000000..130bdfdf77 --- /dev/null +++ b/core/doc/toolmenu.qmodel @@ -0,0 +1,1404 @@ + + + + {ed6d7a2b-3bf9-480d-999c-59785f424590} + + + + + + + + {deb6a6e2-e50a-4c64-a7d9-f092203f9f97} + + + toolmenu + + + + + + + {9d29d9bf-1f7e-479e-96db-f8cd48eb1605} + + + + + + + + + + {9d29d9bf-1f7e-479e-96db-f8cd48eb1605} + + + toolmenu + + + + + + + + + + + + {db5e7146-d41e-4907-9d48-4af523df145d} + + + {ebeabab1-6513-4e56-96d2-ba63e7038981} + ToolMenuManager + x:900;y:515 + x:-280;y:-215;w:560;h:430 + 0 + + + scopy + true + + + + + + + + + + + {05e9bb0e-8e76-456a-ac35-205c79efd13d} + + + {5464f43a-fbab-47fe-a8fb-917099cc3173} + ToolMenu + x:895;y:90 + x:-135;y:-130;w:270;h:260 + 0 + + + scopy + true + + + + + + + + + + + {c543e186-ff61-4bbf-911f-23cc61b4434a} + + + {b131c26b-cc66-48a7-8f26-a6bc03fb9264} + ToolMenuItem + x:365;y:510 + x:-145;y:-145;w:290;h:290 + 0 + + + true + + + + + + + + + + + {9213e7d5-c367-42fc-8046-477c13ea4d8e} + + + {635149e7-86e7-4ee1-a3e5-a29a46344bfb} + {db5e7146-d41e-4907-9d48-4af523df145d} + {c543e186-ff61-4bbf-911f-23cc61b4434a} + + + + + * + true + 2 + + + + + + + + + + + + + {bf2cd219-b9f2-4016-a544-1303f80e8735} + + + {b0969698-ecf6-4ed3-8e68-9cef5e698ade} + ToolMenuEntry + x:565;y:60 + x:-50;y:-30;w:100;h:60 + 0 + + + scopy + true + + + + + + + + + + + {1bc88d88-2344-4da1-ac2e-1224ad02aa8f} + + + {1dcc38dd-6e9a-4ef4-a8ee-0b5cbe5c87f2} + {db5e7146-d41e-4907-9d48-4af523df145d} + {05e9bb0e-8e76-456a-ac35-205c79efd13d} + + + + + 1 + true + 2 + + + + + + + + + + + + + {7eeb340e-b5ff-4c0d-8424-1efe53272cf4} + + + {b8855ac1-7d5d-406a-9e92-147672aafe85} + BrowseMenu + x:1455;y:85 + x:-225;y:-150;w:450;h:300 + 0 + + + scopy + true + + + + + + + + + + + {4a7c89e3-117f-4612-a432-fcff2e52482b} + + + {2ed27405-81a1-4d97-95f1-fb75926f2ab3} + {7eeb340e-b5ff-4c0d-8424-1efe53272cf4} + {05e9bb0e-8e76-456a-ac35-205c79efd13d} + + + + + 1 + true + 2 + + + + + + + + + + + + + {23a97ea0-fb44-483a-a1cb-90996610cf1c} + + + {82eaa98d-eaff-44b9-8275-f65cac93c419} + + + interface + + + CompositeWidget + x:1020;y:-165 + x:-20;y:-20;w:40;h:40 + 0 + + + true + + + + + + + + + + + {28932f85-fa65-4493-abe8-20b90057214c} + + + {aed8badc-8d00-42f3-b462-409167519ca5} + {05e9bb0e-8e76-456a-ac35-205c79efd13d} + {23a97ea0-fb44-483a-a1cb-90996610cf1c} + + + + + + + + + + + + + {fcad97e5-123b-4726-b2bb-67d44b56c08c} + + + {59ca7db8-ce98-4e9f-8767-62ccef3b6cce} + QWidget + x:1455;y:-155 + x:-40;y:-30;w:80;h:60 + 0 + + + + + + + + + + + + + {8ef274a4-ea61-4f44-b046-72e50dc1d8f7} + + + {471280a7-2091-486d-b544-3bbb03b47d90} + {7eeb340e-b5ff-4c0d-8424-1efe53272cf4} + {fcad97e5-123b-4726-b2bb-67d44b56c08c} + + + + + + + + + + + + + {c585691f-75d1-4e17-8926-81db7968045a} + + + {60f58c4f-55d5-43bc-9d9a-1925e3e5fe4e} + QWidget + x:840;y:-170 + x:-40;y:-30;w:80;h:60 + 0 + + + + + + + + + + + + + {506ec138-2315-4d50-8d03-9b2291fbe6b8} + + + {04d9b2ff-51f4-4d16-abcb-65b9396d5ab4} + {05e9bb0e-8e76-456a-ac35-205c79efd13d} + {c585691f-75d1-4e17-8926-81db7968045a} + + + + + + + + + + + + + {8bcfc03c-24a5-4998-9d43-6986d571bf44} + + + {3e60c1d3-f7b4-4148-8f04-946fd7b6f241} + {db5e7146-d41e-4907-9d48-4af523df145d} + {bf2cd219-b9f2-4016-a544-1303f80e8735} + + + + + + + + 1727331491570 + Classes + + + + + + + + + + {ebeabab1-6513-4e56-96d2-ba63e7038981} + + + + + + + + {ebeabab1-6513-4e56-96d2-ba63e7038981} + + + ToolMenuManager + + + + + + + {1dcc38dd-6e9a-4ef4-a8ee-0b5cbe5c87f2} + + + + + + + + {1dcc38dd-6e9a-4ef4-a8ee-0b5cbe5c87f2} + + + {ebeabab1-6513-4e56-96d2-ba63e7038981} + {5464f43a-fbab-47fe-a8fb-917099cc3173} + + + + + 1 + true + 2 + + + + + + + + + + {635149e7-86e7-4ee1-a3e5-a29a46344bfb} + + + + + + + + {635149e7-86e7-4ee1-a3e5-a29a46344bfb} + + + {ebeabab1-6513-4e56-96d2-ba63e7038981} + {b131c26b-cc66-48a7-8f26-a6bc03fb9264} + + + + + * + true + 2 + + + + + + + + + + {3e60c1d3-f7b4-4148-8f04-946fd7b6f241} + + + + + + + + {3e60c1d3-f7b4-4148-8f04-946fd7b6f241} + + + {ebeabab1-6513-4e56-96d2-ba63e7038981} + {b0969698-ecf6-4ed3-8e68-9cef5e698ade} + + + + + + + + + + + + + + scopy + + + + + {8316abd9-c997-4bc5-98f1-1a4ead95ee77} + 1 + 2 + void addMenuItem(QString deviceId, DeviceInfo devInfo, QList<ToolMenuEntry *> tools, int itemIndex = -1) + + + + + {15f77e43-69ed-48b7-8e57-3e3d171db00d} + 1 + 2 + void removeMenuItem(QString deviceId) + + + + + {9d72c8e2-2496-4522-bcca-284213f29f8e} + 1 + 2 + void changeToolListContents(QString deviceId, QList<ToolMenuEntry *> tools) + + + + + {69070c70-93d9-4cd0-8806-fc15f3c7b4ed} + 1 + 2 + void showMenuItem(QString id) + + + + + {8ec54021-3311-4262-b396-34b61cd9de14} + 1 + 2 + void hideMenuItem(QString id) + + + + + {c5790234-61ac-4c55-9de8-058e434123c5} + 7 + 2 + void deviceConnected(QString id) + + + + + {5f81e9ad-58dd-444e-9de0-0a02d8180218} + 7 + 2 + void deviceDisconnected(QString id) + + + + + {87d902e2-2957-4e27-9932-f0fc1921c37f} + 7 + 2 + void onDisplayNameChanged(QString id, QString devName) + + + + + {6843aad1-b4bb-406e-882f-2e7d4633fd3c} + 4 + 2 + void requestToolSelect(QString id) + + + + + {4ff9cca3-822e-4cb0-97d2-337fc9259856} + 5 + 2 + void updateTool(QWidget *old) + + + + + {c8405d91-f511-4899-a8ea-7e68946ab8ee} + 5 + 2 + void updateToolAttached(bool oldAttach, ToolMenuItem *instrWidget) + + + + + {ff5961dd-fa1b-4814-b34a-e727084f705d} + 3 + 2 + void loadToolAttachedState(ToolMenuEntry *tme) + + + + + {d46f7321-cb3a-4745-97f5-34c8a0e3587c} + 3 + 2 + void saveToolAttachedState(ToolMenuEntry *tme) + + + + + {3660ac1f-a9e2-4b88-9ff9-b2a3d8f4c3ab} + 3 + 2 + void detachSuccesful(ToolMenuItem *instrWidget) + + + + + {15c79d5f-33ba-48fe-8462-e94f4c2f4603} + 3 + 2 + void attachSuccesful(ToolMenuItem *instrWidget) + + + + + {86b8572d-d630-4219-898d-30b29842aa18} + 3 + 2 + void showTool(ToolMenuItem *instrWidget) + + + + + {fa970f73-79bb-458a-82b4-0170e1a895b0} + 3 + 2 + void selectInstrument(ToolMenuItem *instrWidget, bool on) + + + + + {a4072fda-e0c1-4026-bc2a-ccc934a356da} + 3 + 2 + void setTmeAttached(ToolMenuEntry *tme) + + + + + {8a0c73ee-4c90-47b2-ae64-d28370f235b3} + 3 + 2 + void createMenuSectionLabel(MenuSectionCollapseWidget *section, QString uri) + + + + + {1ee0c6f2-74c9-421c-802d-de2e5a3dd27c} + 3 + 2 + ToolMenuItem *createInstrWidget(ToolMenuEntry *tme, QWidget *parent = nullptr) + + + + + {235fd41e-0d34-4ef0-a802-e5d333185a5d} + 3 + 1 + QString m_prevItem + + + + + {520da86a-6dc9-4276-b247-0d912937f523} + 3 + 1 + QStringList m_connectedDev + + + + + {36fa4ca9-a5ae-4b85-9b14-373c703ad0b5} + 3 + 1 + ToolStack *m_ts + + + + + {255118ef-2be7-4651-b6f1-20eae925c38b} + 3 + 1 + DetachedToolWindowManager *m_dtm + + + + + {5e7a3376-59ff-477e-8a17-4ebc5d91624c} + 3 + 1 + ToolMenu *m_instrumentMenu + + + + + {71886b3a-bf51-4c72-8455-183aa61e06bc} + 3 + 1 + QMap<QString, MenuSectionCollapseWidget *> m_itemMap + + + + + {5582c5ed-4967-4912-b0d0-f52ba5d0e5af} + 3 + 1 + QMap<QString, DeviceInfo> m_devInfoMap + + + + + + + + + + + + {5464f43a-fbab-47fe-a8fb-917099cc3173} + + + + + + + + {5464f43a-fbab-47fe-a8fb-917099cc3173} + + + ToolMenu + + + + + + + {aed8badc-8d00-42f3-b462-409167519ca5} + + + + + + + + {aed8badc-8d00-42f3-b462-409167519ca5} + + + {5464f43a-fbab-47fe-a8fb-917099cc3173} + {82eaa98d-eaff-44b9-8275-f65cac93c419} + + + + + + + + + + {04d9b2ff-51f4-4d16-abcb-65b9396d5ab4} + + + + + + + + {04d9b2ff-51f4-4d16-abcb-65b9396d5ab4} + + + {5464f43a-fbab-47fe-a8fb-917099cc3173} + {60f58c4f-55d5-43bc-9d9a-1925e3e5fe4e} + + + + + + + + + + + + + + scopy + + + + + {5784285b-96e3-44b6-8ec3-346c3f944532} + 1 + 2 + 8 + void add(QWidget *w) + + + + + {bda010c7-37d7-458c-bec1-84c822065e56} + 1 + 2 + void add(int index, QString itemId, QWidget *w) + + + + + {1bf2a456-7ce7-466d-94b7-c5eab5bf80ee} + 1 + 2 + 8 + void remove(QWidget *w) + + + + + {2a5011de-a044-4248-96b4-ab373a3af6c7} + 1 + 2 + int indexOf(QWidget *w) + + + + + {ea57faf2-5402-4126-b5e6-1675e61b7864} + 1 + 2 + void colapseAll() + + + + + {0e0bd006-0bab-42bf-a863-d521a484cf96} + 1 + 2 + 4 + QButtonGroup *btnGroup() + + + + + {cd03fa76-74ed-46bb-ae8a-320a6583ebe3} + 3 + 2 + void add(int index, QWidget *w) + + + + + {2e882217-119e-4c1a-a348-3de4838f9543} + 3 + 2 + QString widgetName(QWidget *w) + + + + + {d03bc93e-a897-4e98-8cf9-a805ab773205} + 3 + 1 + QMap<QString, QWidget *> m_widgetMap + + + + + {875eb690-36aa-440d-9d1a-b61c2456c548} + 3 + 1 + int m_uuid + + + + + {36dbb8eb-f197-43fe-843d-2f0f4e08f542} + 3 + 1 + QScrollArea *m_scroll + + + + + {871083d4-2e13-4ca5-aafd-f49a6ef14118} + 3 + 1 + QVBoxLayout *m_layScroll + + + + + {d3497c2f-e583-453d-89d4-205e3b5c6ea0} + 3 + 1 + QSpacerItem *m_spacer + + + + + {c8d5e454-c534-4779-833b-0345eb199b65} + 3 + 1 + QButtonGroup *m_btnGroup + + + + + + + + + + + + {b131c26b-cc66-48a7-8f26-a6bc03fb9264} + + + + + + + + {b131c26b-cc66-48a7-8f26-a6bc03fb9264} + + + ToolMenuItem + + + + + + + {75a8ce79-14d3-4283-95dc-1359b44da93f} + 1 + 2 + 4 + QPushButton *getToolBtn() + + + + + {8f4373fc-a318-4988-bf41-08926df6fdc6} + 1 + 2 + 4 + QPushButton *getToolRunBtn() + + + + + {c6910d63-6920-4aa9-9c95-3d5c6ee0f45f} + 1 + 2 + void enableDoubleClick(bool enable) + + + + + {d8d5e8dc-b00e-4f8b-b902-2d1e9411fcd3} + 1 + 2 + bool eventFilter(QObject *watched, QEvent *event) + + + + + {09a67221-06ae-4191-bb92-d89bd4f980a8} + 1 + 2 + void setName(QString str) + + + + + {0d0ec418-0b3b-4faa-86f3-37f67c334b33} + 1 + 2 + void setSelected(bool en) + + + + + {dbc62b8d-06de-48b4-931a-fff98a920af5} + 1 + 2 + 4 + QString getId() + + + + + {b32d24c1-5e24-4259-9fd6-47cc93a19e18} + 7 + 2 + void setDisabled(bool disabled) + + + + + {ab5f8e58-f2ee-44a4-80ee-ae589d7b73de} + 7 + 2 + void updateItem() + + + + + {43773ef7-43ab-498d-8412-778f4a61d16f} + 4 + 2 + void doubleclick() + + + + + {c0657537-ec46-437a-8921-6fdbcd3857aa} + 2 + 2 + void enterEvent(QEvent *event) + + + + + {44cc651b-19aa-4e06-92ce-7c9fcb3a2444} + 2 + 2 + void leaveEvent(QEvent *event) + + + + + {000667b7-7e0c-40b5-a9ec-6a47d4b1b31a} + 3 + 1 + QPushButton *m_toolBtn + + + + + {a15bb585-fe92-4b88-a6ce-e50a65c30321} + 3 + 1 + CustomPushButton *m_toolRunBtn + + + + + {b9bcdee2-e7ad-4dc6-90c6-6a80d5e01b08} + 3 + 1 + QString m_uuid + + + + + {75f715ce-c5b3-494b-8f62-ea9630f228ff} + 3 + 1 + QString m_name + + + + + {4f3f4aa6-f0cc-4806-b16b-f2c9ecf37019} + 3 + 1 + QString m_icon + + + + + + + + + + + + {b0969698-ecf6-4ed3-8e68-9cef5e698ade} + + + + + + + + {b0969698-ecf6-4ed3-8e68-9cef5e698ade} + + + ToolMenuEntry + + + scopy + + + + + + + + {b8855ac1-7d5d-406a-9e92-147672aafe85} + + + + + + + + {b8855ac1-7d5d-406a-9e92-147672aafe85} + + + BrowseMenu + + + + + + + {2ed27405-81a1-4d97-95f1-fb75926f2ab3} + + + + + + + + {2ed27405-81a1-4d97-95f1-fb75926f2ab3} + + + {b8855ac1-7d5d-406a-9e92-147672aafe85} + {5464f43a-fbab-47fe-a8fb-917099cc3173} + + + + + 1 + true + 2 + + + + + + + + + + {471280a7-2091-486d-b544-3bbb03b47d90} + + + + + + + + {471280a7-2091-486d-b544-3bbb03b47d90} + + + {b8855ac1-7d5d-406a-9e92-147672aafe85} + {59ca7db8-ce98-4e9f-8767-62ccef3b6cce} + + + + + + + + + + + + + + scopy + + + + + {c4cb9203-943a-44c4-a603-78b0a293b2e0} + 1 + 2 + 4 + ToolMenu *instrumentMenu() + + + + + {53526ec7-2b3a-4d42-9ee9-18ebfba5f569} + 4 + 2 + void requestTool(QString) + + + + + {d038bdb0-4bff-4ad4-b7e8-286fc879a69e} + 4 + 2 + void requestSave() + + + + + {760e9c23-0b8c-44dc-a961-6c990cbda867} + 4 + 2 + void requestLoad() + + + + + {deaf277a-b3b2-4b06-8966-f8faa12333bc} + 4 + 2 + void collapsed(bool collapsed) + + + + + {3f6a3a8e-5331-4798-91a2-34e7d48fdced} + 3 + 2 + void add(QWidget *w, QString name, MenuAlignment position) + + + + + {6598702b-de52-4e8b-97ac-150e43be0cd6} + 3 + 2 + void toggleCollapsed() + + + + + {565a1867-46ca-4a52-9c5e-3d85cee4b8c2} + 3 + 2 + QPushButton *createBtn(QString name, QString iconPath, QWidget *parent = nullptr) + + + + + {b9c02a02-80ea-4cdd-bdcd-4aef354a8e5d} + 3 + 2 + QFrame *createHLine(QWidget *parent = nullptr) + + + + + {88c5e107-b68e-4758-8b68-a037ccc57651} + 3 + 2 + QWidget *createHeader(QWidget *parent = nullptr) + + + + + {c70ea5a6-1a5e-438f-b6fd-21ea37f25831} + 3 + 2 + QLabel *createScopyLogo(QWidget *parent = nullptr) + + + + + {13fbf16b-f814-4b7b-89b9-0eebe270b669} + 3 + 1 + QWidget *m_content + + + + + {3b5a23f5-bbf1-47d2-9ec7-4756300fb23f} + 3 + 1 + QVBoxLayout *m_contentLay + + + + + {8cf4563a-3495-43f4-b6f1-144325fbf4b8} + 3 + 1 + QSpacerItem *m_spacer + + + + + {350b434f-5808-46ca-84d7-02936f931a0d} + 3 + 1 + ToolMenu *m_instrumentMenu + + + + + {4c887596-8e2a-4c22-8b36-68ce5a7c436e} + 3 + 1 + QPushButton *m_btnCollapse + + + + + {ed9aeb76-4d92-43bb-ac1e-bef6b125580a} + 3 + 1 + bool m_collapsed + + + + + + + + + + + + {82eaa98d-eaff-44b9-8275-f65cac93c419} + + + + + + + + {82eaa98d-eaff-44b9-8275-f65cac93c419} + + + interface + + + + + CompositeWidget + + + + + + + {eb93a436-6cb5-49c5-aeed-dd0b48391989} + 1 + 2 + 3 + void add(QWidget *w) + + + + + {ef620d6d-de0c-467b-92a8-028acad7d6b8} + 1 + 2 + 3 + void remove(QWidget *w) + + + + + + + + + + + + {59ca7db8-ce98-4e9f-8767-62ccef3b6cce} + + + + + + + + {59ca7db8-ce98-4e9f-8767-62ccef3b6cce} + + + QWidget + + + + + + + + + + {60f58c4f-55d5-43bc-9d9a-1925e3e5fe4e} + + + + + + + + {60f58c4f-55d5-43bc-9d9a-1925e3e5fe4e} + + + QWidget + + + + + + + + + + + + + + + + + + diff --git a/core/doc/toolmenu_activity.qmodel b/core/doc/toolmenu_activity.qmodel new file mode 100644 index 0000000000..64e4407a91 --- /dev/null +++ b/core/doc/toolmenu_activity.qmodel @@ -0,0 +1,4995 @@ + + + + {c25ad317-5d3c-41d2-945d-f5b1d201d871} + + + + + + + + {3c436283-013b-40d3-a3a2-f4e8e15bdd36} + + + toolmenu_activity + + + + + + + {939e38aa-d01b-4eac-9abb-92b5dd869e48} + + + + + + + + + + {939e38aa-d01b-4eac-9abb-92b5dd869e48} + + + toolmenu_activity + + + + + + + + + + {4a1d9ba6-9497-4e70-84f8-bbba5913477f} + + + ToolMenuManager::addMenuItem + x:945;y:220 + x:-145;y:-175;w:290;h:350 + + + + + + + + + + + {48b945cd-4462-4c36-8cb2-446ba2aed211} + + + {5b6e5820-1f52-49ec-8988-2ec42b875b24} + Create device section + x:940;y:95 + x:-65;y:-15;w:130;h:30 + 0 + + + activity + false + + + + + + + + + {f867f2cc-7d67-4fe4-ac77-320d77f895cc} + + + for each tme + x:940;y:200 + x:-120;y:-85;w:240;h:170 + + + + + + + + + + + {ac323aaa-7537-4185-8a7b-afc9bd09c38e} + + + {9f74efa2-743c-47ba-a021-079346aaaedf} + Create tool menu item + x:940;y:155 + x:-65;y:-15;w:130;h:30 + 0 + + + activity + false + + + + + + + + + + + {916a38eb-8180-483d-a6ab-08c9ed1c34f3} + + + {ea9af9b4-2404-42de-83af-10ac8bba9f47} + {48b945cd-4462-4c36-8cb2-446ba2aed211} + {ac323aaa-7537-4185-8a7b-afc9bd09c38e} + + + Controlflow + + + true + + + + + + + + + + + + + {45359534-f80d-442b-b244-38db23d51bca} + + + {693730ef-84c8-4932-8318-c7560dbf8bfe} + Add tool item to device section + x:940;y:210 + x:-90;y:-15;w:180;h:30 + 0 + + + activity + false + + + + + + + + + + + {d157b27c-ff93-4c8c-9577-5b452e697047} + + + {55b7771e-8102-43da-986c-9a180ab907f5} + {ac323aaa-7537-4185-8a7b-afc9bd09c38e} + {45359534-f80d-442b-b244-38db23d51bca} + + + Controlflow + + + true + + + + + + + + + + + + + {319d06de-e512-4343-9ace-8342dbcae0fb} + + + {fc7e3bc7-9eb9-42bc-ba22-2e62f6436d69} + Add device section to the menu + x:940;y:320 + x:-90;y:-15;w:180;h:30 + 0 + + + activity + false + + + + + + + + + + + {6672df99-6223-4586-9ca8-0d4d37f25e0d} + + + {b613251a-bc1c-47d8-913f-a634d47c2405} + {45359534-f80d-442b-b244-38db23d51bca} + {eddaa2ca-4bf8-4e92-abdd-ea47cc003bf8} + + + Controlflow + + + true + + + + + + + + + + + + + {eddaa2ca-4bf8-4e92-abdd-ea47cc003bf8} + + + {4b9ea928-8a37-4c0a-93ba-261c71004e6b} + Establish connections + x:940;y:260 + x:-65;y:-15;w:130;h:30 + 0 + + + activity + false + + + + + + + + + + + {fa378719-a176-4f61-88da-71ae91756cb3} + + + {7760c0e6-27de-4999-b01c-b02cbe78f603} + {eddaa2ca-4bf8-4e92-abdd-ea47cc003bf8} + {319d06de-e512-4343-9ace-8342dbcae0fb} + + + Controlflow + + + true + + + + + + + + + + + {4da4d4be-9975-455b-968d-d1b3bb471a6c} + + + Change tool list contents + x:665;y:1055 + x:-305;y:-210;w:610;h:420 + + + + + + + + + + + {94a96b11-8067-44d9-884d-e2f2a1b1968d} + + + {7eb21f03-dda6-4236-a0f1-80df024c4bfc} + ToolMenuManager::changeToolListContents + x:485;y:1100 + x:-120;y:-15;w:240;h:30 + 0 + + + activity + false + + + + + + + + + + + {e0f0bf86-6a2a-47c6-ba99-0465d5d2f16d} + + + {69668829-b4e0-472b-a7ed-307172e7b3d0} + DeviceManager::changeToolListDevice + x:485;y:1045 + x:-105;y:-15;w:210;h:30 + 0 + + + activity + false + + + + + + + + + + + {35a80420-31a3-40ab-9994-0d143e238c79} + + + {8498a40c-5ab0-4f97-82a9-568ef0cadd7c} + Device::toolListChanged + x:485;y:985 + x:-70;y:-15;w:140;h:30 + 0 + + + activity + false + + + + + + + + + + + {63fc242e-9c0b-4f3f-8170-eb1fe45de89e} + + + {3199f8c8-b55b-452f-b9c1-ec5104521ca8} + {35a80420-31a3-40ab-9994-0d143e238c79} + {e0f0bf86-6a2a-47c6-ba99-0465d5d2f16d} + + + Controlflow + + + true + + + + + + + + + + + + + {4c190bbe-8c73-4ded-8746-1d6e1769c591} + + + {3c9b954a-8254-4378-8b52-9680cb3d4df5} + {e0f0bf86-6a2a-47c6-ba99-0465d5d2f16d} + {94a96b11-8067-44d9-884d-e2f2a1b1968d} + + + Controlflow + + + true + + + + + + + + + + + + + {39631f65-4d61-4832-b97d-22b73ef7c1f4} + + + {41ac125b-a791-4034-848c-adc206fd9a67} + Plugin::toolListChanged + x:485;y:930 + x:-70;y:-15;w:140;h:30 + 0 + + + activity + false + + + + + + + + + + + {92a983cc-0ab3-40d4-9c38-13daea810fe2} + + + {36871fb9-ed53-47bf-a1dc-376d9c0414bb} + {39631f65-4d61-4832-b97d-22b73ef7c1f4} + {35a80420-31a3-40ab-9994-0d143e238c79} + + + Controlflow + + + true + + + + + + + + + + + {9f7496d9-7969-4ea3-9808-2c2aaaf32c94} + + + ToolMenuManager::changeToolListContents + x:835;y:1065 + x:-120;y:-185;w:240;h:370 + + + + + + + + + + + {365fe2e0-f984-4fb0-9d67-8fd23d9c2e75} + + + {687ffcd9-9aff-4296-9652-c8372571a6d4} + Disconnect tme signals + x:830;y:935 + x:-70;y:-15;w:140;h:30 + 0 + + + activity + false + + + + + + + + + + + {b27615be-bc51-49ef-a27f-5b4ab5c1bbca} + + + {e377f634-cdd9-4870-aa15-15e0f486f498} + Get menu item position + x:830;y:985 + x:-70;y:-15;w:140;h:30 + 0 + + + activity + false + + + + + + + + + + + {76619e12-2529-41e1-a16a-1ac9733425b9} + + + {e370635d-8e52-413b-bbe2-41d22b01d591} + Remove menu item + x:830;y:1030 + x:-60;y:-15;w:120;h:30 + 0 + + + activity + false + + + + + + + + + + + {514b4323-a0e4-4015-9eff-07418f13ce27} + + + {3176face-e38b-476d-8371-7b07b33f1a90} + Add changed menu item +at the same position + x:830;y:1090 + x:-70;y:-25;w:140;h:50 + 0 + + + activity + false + + + + + + + + + + + {450194ce-c688-4819-8d9c-c6e5062febca} + + + {29268d26-5bd7-4bb0-aa59-82de684a31c6} + Show item + x:830;y:1155 + x:-45;y:-15;w:90;h:30 + 0 + + + activity + false + + + + + + + + + + + {4bb74783-269a-425f-8159-529f49b0c597} + + + {f9f09f2c-9b29-402c-b100-3ab3d4c1b70f} + {365fe2e0-f984-4fb0-9d67-8fd23d9c2e75} + {b27615be-bc51-49ef-a27f-5b4ab5c1bbca} + + + Controlflow + + + true + + + + + + + + + + + + + {9353c8ff-4788-419f-81de-761e6d480e23} + + + {ba6c6f8b-03e7-4e5a-a6a1-e346e25053d8} + {b27615be-bc51-49ef-a27f-5b4ab5c1bbca} + {76619e12-2529-41e1-a16a-1ac9733425b9} + + + Controlflow + + + true + + + + + + + + + + + + + {aa4a12c5-d915-45ec-9fba-685bdd829d64} + + + {7146e59d-7092-41e7-a87c-54f1ce60edec} + {76619e12-2529-41e1-a16a-1ac9733425b9} + {514b4323-a0e4-4015-9eff-07418f13ce27} + + + Controlflow + + + true + + + + + + + + + + + + + {6ef2ee2a-5104-4dfe-9d0c-84f496f91881} + + + {4de40438-e0b1-4afc-bb27-a41b9c918e5b} + {514b4323-a0e4-4015-9eff-07418f13ce27} + {450194ce-c688-4819-8d9c-c6e5062febca} + + + Controlflow + + + true + + + + + + + + + + + + + {5f284c37-be58-41ed-b22b-12c30ef9eae5} + + + {ee7fd69a-373e-4efd-901c-97774e07b530} + ToolMenuManager::addMenuItem + x:525;y:355 + x:-95;y:-15;w:190;h:30 + 0 + + + activity + false + + + + + + + + + + + {0a552d4a-5ab4-4043-9b17-93dd2a444f97} + + + {35132055-8156-4230-a01d-420971a13ec8} + ScopyMainWindow::addDeviceToUi + x:525;y:300 + x:-100;y:-15;w:200;h:30 + 0 + + + activity + false + + + + + + + + + + + {b77c31e6-01eb-4037-b411-86e7d051dd59} + + + {a0752731-5da6-4e82-8433-0d346af63186} + DeviceManager::addDevice + x:525;y:240 + x:-80;y:-15;w:160;h:30 + 0 + + + activity + false + + + + + + + + + + + {4fdca43e-2cd1-42b3-a77c-d51977b8ec1f} + + + {29798946-3634-4a6a-acf6-d071f4922357} + DeviceManager::createDevice + x:405;y:135 + x:-85;y:-15;w:170;h:30 + 0 + + + activity + false + + + + + + + + + + + {a6e86fc4-f361-45f1-8916-fea0dd79c473} + + + {765d7ed2-d45b-46bf-b422-4a982b947a4e} + ScopyHomeAddPage::newDeviceAvailable + x:640;y:135 + x:-115;y:-15;w:230;h:30 + 0 + + + activity + false + + + + + + + + + + + {8e9eaa5e-6899-4b08-9eaf-e40fb259fc3e} + + + {550f3fb9-b6d9-4cdc-9c9f-e70df6ccb0e5} + ScopyHomeAddPage::addBtnClicked + x:645;y:75 + x:-100;y:-15;w:200;h:30 + 0 + + + activity + false + + + + + + + + + + + {ea289454-a5fe-47d4-8ad5-6ec945b5f485} + + + {4b2812d8-dea0-4750-9c0e-53501c8e67ec} + {8e9eaa5e-6899-4b08-9eaf-e40fb259fc3e} + {a6e86fc4-f361-45f1-8916-fea0dd79c473} + + + Controlflow + + + true + + + + + + + + + + + + + {2f8795ae-d20c-4376-9480-a47f131b69f5} + + + {db3143e4-30b7-4130-b380-ebce741702ae} + {a6e86fc4-f361-45f1-8916-fea0dd79c473} + {51b5f4d3-bf97-4a16-82e2-a9eca451fbc8} + + + Controlflow + + + true + + + + + + + + + + + + + {4fb8ca47-1571-4e4e-bd04-58bc40c522c0} + + + {9ec5ac4e-8ed0-4c3b-8abe-659d708b08dc} + {4fdca43e-2cd1-42b3-a77c-d51977b8ec1f} + {51b5f4d3-bf97-4a16-82e2-a9eca451fbc8} + + + Controlflow + + + true + + + + + + + + + + + + + {88e2cfc3-e4ca-41b8-a793-f60dd7528de6} + + + {e5f203df-9a5c-4d5a-be26-e21eb6e9f68e} + {b77c31e6-01eb-4037-b411-86e7d051dd59} + {0a552d4a-5ab4-4043-9b17-93dd2a444f97} + + + Controlflow + + + true + + + + + + + + + + + + + {de9f6c94-6ab7-40a2-a2ff-327c2db6530c} + + + {7aa93605-8cf2-4a30-84e8-56d40ed44eba} + {0a552d4a-5ab4-4043-9b17-93dd2a444f97} + {5f284c37-be58-41ed-b22b-12c30ef9eae5} + + + Controlflow + + + true + + + + + + + + + + + + + {efb403c7-5b4e-4b1f-86d8-702cca1b1999} + + + {8d11b6d3-df17-4fb0-9eb5-fa734da666b5} + ScannedIIOContextCollector::foundDevice + x:405;y:75 + x:-115;y:-15;w:230;h:30 + 0 + + + activity + false + + + + + + + + + + + {2776f235-9ee8-4af2-8c2f-1b8338a289ea} + + + {122efb95-c703-4aef-bb6a-532e713d734b} + {efb403c7-5b4e-4b1f-86d8-702cca1b1999} + {4fdca43e-2cd1-42b3-a77c-d51977b8ec1f} + + + Controlflow + + + true + + + + + + + + + + + {75e0bd2b-17f4-4931-b7e4-8e18cc4c045f} + + + Add menu item + x:690;y:195 + x:-430;y:-215;w:860;h:430 + + + + + + + + + + + {8e7d8651-b0ee-400b-8f2b-cd2287dcf62d} + + + {9b267513-3877-43f6-b8a7-f3582e108c61} + ToolMenuManager::removeMenuItem + x:530;y:790 + x:-105;y:-15;w:210;h:30 + 0 + + + activity + false + + + + + + + + + + + {85780831-1fe7-4a7c-aa96-e34447f6a52b} + + + {7568a965-5dca-4d46-b88b-2f940f12ce7c} + ScopyMainWindow::removeDeviceFromUi + x:530;y:740 + x:-115;y:-15;w:230;h:30 + 0 + + + activity + false + + + + + + + + + + + {2ff51671-0e2a-4805-b70d-c55a588db9a9} + + + {18c84ad8-53f8-42f1-bf7a-7842e1e20b27} + DeviceManager::removeDeviceById + x:530;y:685 + x:-100;y:-15;w:200;h:30 + 0 + + + activity + false + + + + + + + + + + + {b0bcab9a-cc94-4619-8259-81038508e0bb} + + + {eae9c13d-0f0d-4162-9c36-e09391f41821} + DeviceManager::removeDevice + x:415;y:595 + x:-90;y:-15;w:180;h:30 + 0 + + + activity + false + + + + + + + + + + + {9a3436cc-366f-47ea-9f63-6a410ac0107f} + + + {3949a8ee-04c0-4679-922c-cef599e03f79} + ScannedIIOContextCollector::lostDevice + x:415;y:525 + x:-110;y:-15;w:220;h:30 + 0 + + + activity + false + + + + + + + + + + + {3b8157ae-cb81-4035-99c6-85b3d69f0682} + + + {3f0f1508-a114-4d3b-aea4-7a086b5a90a3} + {9a3436cc-366f-47ea-9f63-6a410ac0107f} + {b0bcab9a-cc94-4619-8259-81038508e0bb} + + + Controlflow + + + true + + + + + + + + + + + + + {7c47c6ca-d306-422b-8a23-4c6ca9a57ba3} + + + {7985ecdc-8c5b-486e-a952-481560434dde} + {b0bcab9a-cc94-4619-8259-81038508e0bb} + {46796a08-1e1b-4ebc-99a2-cb998b5d5a9b} + + + Controlflow + + + true + + + + + + + + + + + + + {ad899705-39ba-466f-b9b1-67267f46a3f6} + + + {5a3ddb9b-f506-4cec-a9c4-8d1bc19c36a2} + DeviceImpl::forget + x:625;y:595 + x:-55;y:-15;w:110;h:30 + 0 + + + activity + false + + + + + + + + + + + {bc522732-9a69-43a2-8d24-f8a96200eed8} + + + {4673216b-539b-43a8-91c3-4ee18ed58ad8} + forgetBtn::clicked + x:625;y:525 + x:-55;y:-15;w:110;h:30 + 0 + + + activity + false + + + + + + + + + + + {4a622d15-8293-48e2-bdef-ccb29d7ebbfe} + + + {7071fba5-73e5-42bb-ab7e-dac891169e23} + {bc522732-9a69-43a2-8d24-f8a96200eed8} + {ad899705-39ba-466f-b9b1-67267f46a3f6} + + + Controlflow + + + true + + + + + + + + + + + + + {9f2b1a6d-7961-48d1-9df4-90b347ff6cc2} + + + {8f1a5ed6-6bfd-44a2-afc0-c71c614b2d9c} + {ad899705-39ba-466f-b9b1-67267f46a3f6} + {46796a08-1e1b-4ebc-99a2-cb998b5d5a9b} + + + Controlflow + + + true + + + + + + + + + + + + + {8e564869-0921-43d0-b940-f06173198a61} + + + {7ae1c064-8649-4261-b3a2-6ac1c121700b} + {2ff51671-0e2a-4805-b70d-c55a588db9a9} + {85780831-1fe7-4a7c-aa96-e34447f6a52b} + + + Controlflow + + + true + + + + + + + + + + + + + {1a068d5e-7c19-4b2e-a2a5-d0daea6cb7ba} + + + {d89d5f07-60ee-4036-89c7-beb2726c166d} + {85780831-1fe7-4a7c-aa96-e34447f6a52b} + {8e7d8651-b0ee-400b-8f2b-cd2287dcf62d} + + + Controlflow + + + true + + + + + + + + + + + {64a86094-bdd1-41bf-827d-60e88ff6517c} + + + ToolMenuManager::removeMenuItem + x:890;y:645 + x:-140;y:-145;w:280;h:290 + + + + + + + + + + + {4a8c319e-eee0-40d9-8731-67344211f650} + + + {142efc8c-c913-43ac-90e1-10b0275582c9} + Find menu item by id + x:910;y:555 + x:-60;y:-15;w:120;h:30 + 0 + + + activity + false + + + + + + + + + + + {07241d01-e520-4ed1-9256-eed0ad297432} + + + {56607076-7f6a-44b8-aa25-7435d68b3b0e} + Remove item + x:815;y:710 + x:-45;y:-15;w:90;h:30 + 0 + + + activity + false + + + + + + + + + + + {4da2f6ad-fd4a-4dce-a370-51a1011cf7c0} + + + {00d7bfe9-3196-4336-b9cc-551cac3fd1d5} + Menu contains the item + x:910;y:615 + x:-20;y:-20;w:40;h:40 + 0 + + + condition + false + + + + + + + + + + + {8b246d11-c402-421e-ba4c-198e0ccb11a6} + + + {6f9bfe35-2b12-4aab-b452-b57d0af238ea} + {4a8c319e-eee0-40d9-8731-67344211f650} + {4da2f6ad-fd4a-4dce-a370-51a1011cf7c0} + + + Controlflow + + + true + + + + + + + + + + + + + {da47893a-1067-45de-9572-c2b06dc02b9f} + + + {6d45da31-06a2-40f7-898e-4a129fb39d56} + {4da2f6ad-fd4a-4dce-a370-51a1011cf7c0} + {07241d01-e520-4ed1-9256-eed0ad297432} + yes + + + Controlflow + + + true + + + + + + + + + + + + + {f03c1cf5-7cdd-4ea6-83c0-586a7d7f5674} + + + {fb05cd5b-f4fd-4e67-bd96-f875b23105a7} + x:815;y:765 + x:-20;y:-20;w:40;h:40 + 0 + + + termination + false + + + + + + + + + + + {4b33dc24-a2b8-4c36-aa39-4a70ce52c31a} + + + {ea5cf450-4151-4365-9d36-efe054debad5} + {07241d01-e520-4ed1-9256-eed0ad297432} + {f03c1cf5-7cdd-4ea6-83c0-586a7d7f5674} + + + Controlflow + + + true + + + + + + + + + + + + + {eb998ea3-b792-4952-8c79-d234360e6736} + + + {5e5a3cdb-46c7-4486-8e50-4836a7af9c24} + x:990;y:705 + x:-20;y:-20;w:40;h:40 + 0 + + + termination + false + + + + + + + + + + + {7f1ae149-1137-4a65-8f85-d85c064609d2} + + + {14b5f534-0ed7-4487-b8df-7f29159a8a41} + {4da2f6ad-fd4a-4dce-a370-51a1011cf7c0} + {eb998ea3-b792-4952-8c79-d234360e6736} + No + + + Controlflow + + + true + + + + + + + + + + + + + {46796a08-1e1b-4ebc-99a2-cb998b5d5a9b} + + + {be0edb24-92bc-4f17-aefa-0c154654c5fd} + x:530;y:647.5 + x:-45;y:-2.5;w:90;h:5 + false + 0 + + + horizontalbar + false + + + + + + + + + + + {0030f65e-2be9-4e7d-9116-e9289f5e7083} + + + {f10bbb35-3702-4c9f-8eed-4921c7d69afd} + {46796a08-1e1b-4ebc-99a2-cb998b5d5a9b} + {2ff51671-0e2a-4805-b70d-c55a588db9a9} + + + Controlflow + + + true + + + + + + + + + + + + + {ec12a378-9e5e-4545-97d9-84e0c868106f} + + + {2d71f785-f2b8-4e7f-8d9b-d4ace42492ff} + x:415;y:465 + x:-20;y:-20;w:40;h:40 + 0 + + + start + false + + + + + + + + + + + {d2563c54-c8ea-4fb4-8850-b5f1cbeedfd2} + + + {91d52a46-9a9e-4d73-9e98-7fe39ce28da1} + {ec12a378-9e5e-4545-97d9-84e0c868106f} + {9a3436cc-366f-47ea-9f63-6a410ac0107f} + + + Controlflow + + + true + + + + + + + + + + + + + {8901aa04-c0e7-441f-b77e-6a213ccecf55} + + + {c49e84cd-e524-48d0-8209-0f88db861f2e} + x:625;y:470 + x:-20;y:-20;w:40;h:40 + 0 + + + start + false + + + + + + + + + + + {ab1cd035-4be4-47c9-82ea-fe66c9fc87c3} + + + {e484ba64-3a2b-4110-a3dd-86e036d865ba} + {8901aa04-c0e7-441f-b77e-6a213ccecf55} + {bc522732-9a69-43a2-8d24-f8a96200eed8} + + + Controlflow + + + true + + + + + + + + + + + {38852940-b542-4093-859b-83dd100d6854} + + + Remove menu item + x:685;y:630 + x:-405;y:-200;w:810;h:400 + + + + + + + + + + + {dba1bfc6-a802-4eca-9f30-31a0ca0cd1fb} + + + {92090937-a108-4667-865d-f6996e72cd2f} + x:940;y:370 + x:-20;y:-20;w:40;h:40 + 0 + + + termination + false + + + + + + + + + + + {5edcb3d0-cbbd-408e-b55c-40eb7a78f1fd} + + + {348dba2d-ccf3-4227-a3ec-f90f528ef868} + {319d06de-e512-4343-9ace-8342dbcae0fb} + {dba1bfc6-a802-4eca-9f30-31a0ca0cd1fb} + + + Controlflow + + + true + + + + + + + + + + + + + {17d5494e-7f0d-4b2d-9cfb-5c93da2b7818} + + + {aefcfeb6-f513-46ab-8ca9-13aa0aa32e7e} + x:830;y:1215 + x:-20;y:-20;w:40;h:40 + 0 + + + termination + false + + + + + + + + + + + {bae89d46-b6df-4c47-a916-5004ab07d21e} + + + {77bee958-bf4b-45b3-8581-c2ace36b2199} + {450194ce-c688-4819-8d9c-c6e5062febca} + {17d5494e-7f0d-4b2d-9cfb-5c93da2b7818} + + + Controlflow + + + true + + + + + + + + + + + + + {a72d1bc8-2446-44c8-858b-8b53630fce03} + + + {0c4b1fbf-59e5-4b2f-a60a-df72c6a0c318} + x:405;y:20 + x:-20;y:-20;w:40;h:40 + 0 + + + start + false + + + + + + + + + + + {8708e951-0049-4df5-a5c7-201ede1ea63e} + + + {edeb8f38-b4fd-4e09-bcc3-75a375ce61fe} + x:645;y:20 + x:-20;y:-20;w:40;h:40 + 0 + + + start + false + + + + + + + + + + + {dd1de6ed-2b26-4ae8-8851-782fc7c916d5} + + + {8e179f85-b57d-4526-b079-68eaffaa53bd} + {8708e951-0049-4df5-a5c7-201ede1ea63e} + {8e9eaa5e-6899-4b08-9eaf-e40fb259fc3e} + + + Controlflow + + + true + + + + + + + + + + + + + {4b8afc7f-13c9-43e5-8c64-75bc18c42af2} + + + {4d3e8e8d-e585-4d7f-a140-00983a56171b} + {a72d1bc8-2446-44c8-858b-8b53630fce03} + {efb403c7-5b4e-4b1f-86d8-702cca1b1999} + + + Controlflow + + + true + + + + + + + + + + + + + {51b5f4d3-bf97-4a16-82e2-a9eca451fbc8} + + + {86c99bcb-2c3c-4d98-8da2-a0da797d16fd} + x:525;y:192.5 + x:-35;y:-2.5;w:70;h:5 + false + 0 + + + horizontalbar + false + + + + + + + + + + + {629de9b7-1393-455a-a6d3-cb97e7e20a86} + + + {a1d154ef-6fe8-457f-a99e-d9c4e13453da} + {51b5f4d3-bf97-4a16-82e2-a9eca451fbc8} + {b77c31e6-01eb-4037-b411-86e7d051dd59} + + + Controlflow + + + true + + + + + + + + 1727347019662 + Activities + + + + + + + + + + {3d2bcdbd-5b0c-423d-8ff9-78523ee66784} + + + + + + + + {3d2bcdbd-5b0c-423d-8ff9-78523ee66784} + + + ToolMenuManager::addMenuItem + + + + + + + {3770f42d-79f7-41cc-9344-d1a25d98bb70} + + + + + + + + {3770f42d-79f7-41cc-9344-d1a25d98bb70} + + + {3d2bcdbd-5b0c-423d-8ff9-78523ee66784} + {3eb27d8d-80a7-4738-b9c6-a28d091980ef} + + + Controlflow + + + true + + + + + + + + + + {126ce88c-adc3-44d9-89af-e83e9ace7de1} + + + + + + + + {126ce88c-adc3-44d9-89af-e83e9ace7de1} + + + {3d2bcdbd-5b0c-423d-8ff9-78523ee66784} + {5b6e5820-1f52-49ec-8988-2ec42b875b24} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {3eb27d8d-80a7-4738-b9c6-a28d091980ef} + + + + + + + + {3eb27d8d-80a7-4738-b9c6-a28d091980ef} + + + ToolMenu::addMenuItem + + + + + + + {683fcae3-c4c5-4c61-8d76-06c241a981cd} + + + + + + + + {683fcae3-c4c5-4c61-8d76-06c241a981cd} + + + {3eb27d8d-80a7-4738-b9c6-a28d091980ef} + {8f17b033-de57-4ac4-a7f8-13b526407265} + + + + + + + + + + {b7b1bace-097d-43f9-ab3a-9fcde65b4f28} + + + + + + + + {b7b1bace-097d-43f9-ab3a-9fcde65b4f28} + + + {3eb27d8d-80a7-4738-b9c6-a28d091980ef} + {74e029a7-ccd0-4454-b89c-ec8f9b8866aa} + + + + + + + + + + {cea2d7a2-8e72-4d75-b376-8e5047f53ff4} + + + + + + + + {cea2d7a2-8e72-4d75-b376-8e5047f53ff4} + + + {3eb27d8d-80a7-4738-b9c6-a28d091980ef} + {bd9b6c3c-534a-4a6c-9eb3-708b60603375} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {74e029a7-ccd0-4454-b89c-ec8f9b8866aa} + + + + + + + + {74e029a7-ccd0-4454-b89c-ec8f9b8866aa} + + + oolMenuItem::initItemWidgets + + + false + activity + + + + + + + + {8f17b033-de57-4ac4-a7f8-13b526407265} + + + + + + + + {8f17b033-de57-4ac4-a7f8-13b526407265} + + + ToolMenu::createMeuItem + + + false + activity + + + + + + + + {bd9b6c3c-534a-4a6c-9eb3-708b60603375} + + + + + + + + {bd9b6c3c-534a-4a6c-9eb3-708b60603375} + + + ToolMenuItem::createDeviceSection + + + + + + + {712300f2-70e3-4a6f-83a5-c34d6a56995b} + + + + + + + + {712300f2-70e3-4a6f-83a5-c34d6a56995b} + + + {bd9b6c3c-534a-4a6c-9eb3-708b60603375} + {74e029a7-ccd0-4454-b89c-ec8f9b8866aa} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {d81225cf-71a9-479d-b329-104d5ea3ada9} + + + + + + + + {d81225cf-71a9-479d-b329-104d5ea3ada9} + + + ToolMenuItem::createDeviceSection + + + + + + + {aa0c2384-3870-429b-96bc-6c424fd70ef6} + + + + + + + + {aa0c2384-3870-429b-96bc-6c424fd70ef6} + + + {d81225cf-71a9-479d-b329-104d5ea3ada9} + {d7076b59-c2ea-4137-a49a-f5e8b3f53201} + + + Controlflow + + + true + + + + + + + + + + {03e7540f-6a70-46a4-9ead-14df2ccb7b7e} + + + + + + + + {03e7540f-6a70-46a4-9ead-14df2ccb7b7e} + + + {d81225cf-71a9-479d-b329-104d5ea3ada9} + {4805094c-56e0-42d4-822f-f869f3a5bd24} + + + Controlflow + + + true + + + + + + + + + + {043ae223-7c82-47c3-bacd-f56576b5ea1b} + + + + + + + + {043ae223-7c82-47c3-bacd-f56576b5ea1b} + + + {d81225cf-71a9-479d-b329-104d5ea3ada9} + {3263d61b-aa95-452a-98b7-336717052e1d} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {d7076b59-c2ea-4137-a49a-f5e8b3f53201} + + + + + + + + {d7076b59-c2ea-4137-a49a-f5e8b3f53201} + + + Get TME widget + + + false + activity + + + + + + + + {4805094c-56e0-42d4-822f-f869f3a5bd24} + + + + + + + + {4805094c-56e0-42d4-822f-f869f3a5bd24} + + + TME widget is NULL + + + + + + + {6a7df09b-d177-495a-ba9d-df5bf69d53a7} + + + + + + + + {6a7df09b-d177-495a-ba9d-df5bf69d53a7} + + + true + {4805094c-56e0-42d4-822f-f869f3a5bd24} + {a2d3b38f-56ec-4d87-a66c-cad247142f63} + + + Controlflow + + + true + + + + + + + + + + {68dd733f-a4c6-4491-8a3e-1128775f108c} + + + + + + + + {68dd733f-a4c6-4491-8a3e-1128775f108c} + + + false + {4805094c-56e0-42d4-822f-f869f3a5bd24} + {acb0e8c9-1dfe-4a55-954e-5a2baf0c7e07} + + + Controlflow + + + true + + + + + + + + + + + + + + false + condition + + + + + + + + {a2d3b38f-56ec-4d87-a66c-cad247142f63} + + + + + + + + {a2d3b38f-56ec-4d87-a66c-cad247142f63} + + + create DefaultIteWidget + + + + + + + {36fcfb94-dd64-4995-96fd-50c8092d9502} + + + + + + + + {36fcfb94-dd64-4995-96fd-50c8092d9502} + + + {a2d3b38f-56ec-4d87-a66c-cad247142f63} + {73b20444-9558-483b-8c5b-ded1f05aef2b} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {acb0e8c9-1dfe-4a55-954e-5a2baf0c7e07} + + + + + + + + {acb0e8c9-1dfe-4a55-954e-5a2baf0c7e07} + + + use TME widget as itemWidget + + + + + + + {4a25e7d2-aa4e-4f6a-b4f9-35eba0a0331b} + + + + + + + + {4a25e7d2-aa4e-4f6a-b4f9-35eba0a0331b} + + + {acb0e8c9-1dfe-4a55-954e-5a2baf0c7e07} + {73b20444-9558-483b-8c5b-ded1f05aef2b} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {73b20444-9558-483b-8c5b-ded1f05aef2b} + + + + + + + + {73b20444-9558-483b-8c5b-ded1f05aef2b} + + + + + + + + + {c0439a49-d453-4541-bf85-aa314e64e57e} + + + + + + + + {c0439a49-d453-4541-bf85-aa314e64e57e} + + + {73b20444-9558-483b-8c5b-ded1f05aef2b} + {3263d61b-aa95-452a-98b7-336717052e1d} + + + Controlflow + + + true + + + + + + + + + + + + + + false + horizontalbar + + + + + + + + {3263d61b-aa95-452a-98b7-336717052e1d} + + + + + + + + {3263d61b-aa95-452a-98b7-336717052e1d} + + + Add toolMenuWidget to device section + + + + + + + {398cc834-c000-48a0-bc6f-525992982fb9} + + + + + + + + {398cc834-c000-48a0-bc6f-525992982fb9} + + + {3263d61b-aa95-452a-98b7-336717052e1d} + {f1adc24e-ea44-4da0-95bf-92c5e05ff1e7} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {f1adc24e-ea44-4da0-95bf-92c5e05ff1e7} + + + + + + + + {f1adc24e-ea44-4da0-95bf-92c5e05ff1e7} + + + Add toolMenuWidget to widgets map +(Use TME uuid as key) + + + + + + + {5bd862d7-3fa6-4e31-bb90-20befd8fa7db} + + + + + + + + {5bd862d7-3fa6-4e31-bb90-20befd8fa7db} + + + {f1adc24e-ea44-4da0-95bf-92c5e05ff1e7} + {85996e8a-cf92-459a-b02a-522c76fc1a1c} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {85996e8a-cf92-459a-b02a-522c76fc1a1c} + + + + + + + + {85996e8a-cf92-459a-b02a-522c76fc1a1c} + + + Establish connections + + + false + activity + + + + + + + + {5b6e5820-1f52-49ec-8988-2ec42b875b24} + + + + + + + + {5b6e5820-1f52-49ec-8988-2ec42b875b24} + + + Create device section + + + + + + + {ea9af9b4-2404-42de-83af-10ac8bba9f47} + + + + + + + + {ea9af9b4-2404-42de-83af-10ac8bba9f47} + + + {5b6e5820-1f52-49ec-8988-2ec42b875b24} + {9f74efa2-743c-47ba-a021-079346aaaedf} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {9f74efa2-743c-47ba-a021-079346aaaedf} + + + + + + + + {9f74efa2-743c-47ba-a021-079346aaaedf} + + + Create tool menu item + + + + + + + {55b7771e-8102-43da-986c-9a180ab907f5} + + + + + + + + {55b7771e-8102-43da-986c-9a180ab907f5} + + + {9f74efa2-743c-47ba-a021-079346aaaedf} + {693730ef-84c8-4932-8318-c7560dbf8bfe} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {693730ef-84c8-4932-8318-c7560dbf8bfe} + + + + + + + + {693730ef-84c8-4932-8318-c7560dbf8bfe} + + + Add tool item to device section + + + + + + + {b613251a-bc1c-47d8-913f-a634d47c2405} + + + + + + + + {b613251a-bc1c-47d8-913f-a634d47c2405} + + + {693730ef-84c8-4932-8318-c7560dbf8bfe} + {4b9ea928-8a37-4c0a-93ba-261c71004e6b} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {fc7e3bc7-9eb9-42bc-ba22-2e62f6436d69} + + + + + + + + {fc7e3bc7-9eb9-42bc-ba22-2e62f6436d69} + + + Add device section to the menu + + + + + + + {348dba2d-ccf3-4227-a3ec-f90f528ef868} + + + + + + + + {348dba2d-ccf3-4227-a3ec-f90f528ef868} + + + {fc7e3bc7-9eb9-42bc-ba22-2e62f6436d69} + {92090937-a108-4667-865d-f6996e72cd2f} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {4b9ea928-8a37-4c0a-93ba-261c71004e6b} + + + + + + + + {4b9ea928-8a37-4c0a-93ba-261c71004e6b} + + + Establish connections + + + + + + + {7760c0e6-27de-4999-b01c-b02cbe78f603} + + + + + + + + {7760c0e6-27de-4999-b01c-b02cbe78f603} + + + {4b9ea928-8a37-4c0a-93ba-261c71004e6b} + {fc7e3bc7-9eb9-42bc-ba22-2e62f6436d69} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {7eb21f03-dda6-4236-a0f1-80df024c4bfc} + + + + + + + + {7eb21f03-dda6-4236-a0f1-80df024c4bfc} + + + ToolMenuManager::changeToolListContents + + + false + activity + + + + + + + + {69668829-b4e0-472b-a7ed-307172e7b3d0} + + + + + + + + {69668829-b4e0-472b-a7ed-307172e7b3d0} + + + DeviceManager::changeToolListDevice + + + + + + + {3c9b954a-8254-4378-8b52-9680cb3d4df5} + + + + + + + + {3c9b954a-8254-4378-8b52-9680cb3d4df5} + + + {69668829-b4e0-472b-a7ed-307172e7b3d0} + {7eb21f03-dda6-4236-a0f1-80df024c4bfc} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {8498a40c-5ab0-4f97-82a9-568ef0cadd7c} + + + + + + + + {8498a40c-5ab0-4f97-82a9-568ef0cadd7c} + + + Device::toolListChanged + + + + + + + {3199f8c8-b55b-452f-b9c1-ec5104521ca8} + + + + + + + + {3199f8c8-b55b-452f-b9c1-ec5104521ca8} + + + {8498a40c-5ab0-4f97-82a9-568ef0cadd7c} + {69668829-b4e0-472b-a7ed-307172e7b3d0} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {41ac125b-a791-4034-848c-adc206fd9a67} + + + + + + + + {41ac125b-a791-4034-848c-adc206fd9a67} + + + Plugin::toolListChanged + + + + + + + {36871fb9-ed53-47bf-a1dc-376d9c0414bb} + + + + + + + + {36871fb9-ed53-47bf-a1dc-376d9c0414bb} + + + {41ac125b-a791-4034-848c-adc206fd9a67} + {8498a40c-5ab0-4f97-82a9-568ef0cadd7c} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {59ca7db8-ce98-4e9f-8767-62ccef3b6cce} + + + + + + + + {59ca7db8-ce98-4e9f-8767-62ccef3b6cce} + + + QWidget + + + + + + + + + + {687ffcd9-9aff-4296-9652-c8372571a6d4} + + + + + + + + {687ffcd9-9aff-4296-9652-c8372571a6d4} + + + Disconnect tme signals + + + + + + + {f9f09f2c-9b29-402c-b100-3ab3d4c1b70f} + + + + + + + + {f9f09f2c-9b29-402c-b100-3ab3d4c1b70f} + + + {687ffcd9-9aff-4296-9652-c8372571a6d4} + {e377f634-cdd9-4870-aa15-15e0f486f498} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {e377f634-cdd9-4870-aa15-15e0f486f498} + + + + + + + + {e377f634-cdd9-4870-aa15-15e0f486f498} + + + Get menu item position + + + + + + + {ba6c6f8b-03e7-4e5a-a6a1-e346e25053d8} + + + + + + + + {ba6c6f8b-03e7-4e5a-a6a1-e346e25053d8} + + + {e377f634-cdd9-4870-aa15-15e0f486f498} + {e370635d-8e52-413b-bbe2-41d22b01d591} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {e370635d-8e52-413b-bbe2-41d22b01d591} + + + + + + + + {e370635d-8e52-413b-bbe2-41d22b01d591} + + + Remove menu item + + + + + + + {7146e59d-7092-41e7-a87c-54f1ce60edec} + + + + + + + + {7146e59d-7092-41e7-a87c-54f1ce60edec} + + + {e370635d-8e52-413b-bbe2-41d22b01d591} + {3176face-e38b-476d-8371-7b07b33f1a90} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {3176face-e38b-476d-8371-7b07b33f1a90} + + + + + + + + {3176face-e38b-476d-8371-7b07b33f1a90} + + + Add changed menu item +at the same position + + + + + + + {4de40438-e0b1-4afc-bb27-a41b9c918e5b} + + + + + + + + {4de40438-e0b1-4afc-bb27-a41b9c918e5b} + + + {3176face-e38b-476d-8371-7b07b33f1a90} + {29268d26-5bd7-4bb0-aa59-82de684a31c6} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {29268d26-5bd7-4bb0-aa59-82de684a31c6} + + + + + + + + {29268d26-5bd7-4bb0-aa59-82de684a31c6} + + + Show item + + + + + + + {77bee958-bf4b-45b3-8581-c2ace36b2199} + + + + + + + + {77bee958-bf4b-45b3-8581-c2ace36b2199} + + + {29268d26-5bd7-4bb0-aa59-82de684a31c6} + {aefcfeb6-f513-46ab-8ca9-13aa0aa32e7e} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {ee7fd69a-373e-4efd-901c-97774e07b530} + + + + + + + + {ee7fd69a-373e-4efd-901c-97774e07b530} + + + ToolMenuManager::addMenuItem + + + false + activity + + + + + + + + {35132055-8156-4230-a01d-420971a13ec8} + + + + + + + + {35132055-8156-4230-a01d-420971a13ec8} + + + ScopyMainWindow::addDeviceToUi + + + + + + + {7aa93605-8cf2-4a30-84e8-56d40ed44eba} + + + + + + + + {7aa93605-8cf2-4a30-84e8-56d40ed44eba} + + + {35132055-8156-4230-a01d-420971a13ec8} + {ee7fd69a-373e-4efd-901c-97774e07b530} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {a0752731-5da6-4e82-8433-0d346af63186} + + + + + + + + {a0752731-5da6-4e82-8433-0d346af63186} + + + DeviceManager::addDevice + + + + + + + {e5f203df-9a5c-4d5a-be26-e21eb6e9f68e} + + + + + + + + {e5f203df-9a5c-4d5a-be26-e21eb6e9f68e} + + + {a0752731-5da6-4e82-8433-0d346af63186} + {35132055-8156-4230-a01d-420971a13ec8} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {29798946-3634-4a6a-acf6-d071f4922357} + + + + + + + + {29798946-3634-4a6a-acf6-d071f4922357} + + + DeviceManager::createDevice + + + + + + + {9ec5ac4e-8ed0-4c3b-8abe-659d708b08dc} + + + + + + + + {9ec5ac4e-8ed0-4c3b-8abe-659d708b08dc} + + + {29798946-3634-4a6a-acf6-d071f4922357} + {86c99bcb-2c3c-4d98-8da2-a0da797d16fd} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {765d7ed2-d45b-46bf-b422-4a982b947a4e} + + + + + + + + {765d7ed2-d45b-46bf-b422-4a982b947a4e} + + + ScopyHomeAddPage::newDeviceAvailable + + + + + + + {db3143e4-30b7-4130-b380-ebce741702ae} + + + + + + + + {db3143e4-30b7-4130-b380-ebce741702ae} + + + {765d7ed2-d45b-46bf-b422-4a982b947a4e} + {86c99bcb-2c3c-4d98-8da2-a0da797d16fd} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {550f3fb9-b6d9-4cdc-9c9f-e70df6ccb0e5} + + + + + + + + {550f3fb9-b6d9-4cdc-9c9f-e70df6ccb0e5} + + + ScopyHomeAddPage::addBtnClicked + + + + + + + {4b2812d8-dea0-4750-9c0e-53501c8e67ec} + + + + + + + + {4b2812d8-dea0-4750-9c0e-53501c8e67ec} + + + {550f3fb9-b6d9-4cdc-9c9f-e70df6ccb0e5} + {765d7ed2-d45b-46bf-b422-4a982b947a4e} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {8d11b6d3-df17-4fb0-9eb5-fa734da666b5} + + + + + + + + {8d11b6d3-df17-4fb0-9eb5-fa734da666b5} + + + ScannedIIOContextCollector::foundDevice + + + + + + + {122efb95-c703-4aef-bb6a-532e713d734b} + + + + + + + + {122efb95-c703-4aef-bb6a-532e713d734b} + + + {8d11b6d3-df17-4fb0-9eb5-fa734da666b5} + {29798946-3634-4a6a-acf6-d071f4922357} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {9b267513-3877-43f6-b8a7-f3582e108c61} + + + + + + + + {9b267513-3877-43f6-b8a7-f3582e108c61} + + + ToolMenuManager::removeMenuItem + + + false + activity + + + + + + + + {7568a965-5dca-4d46-b88b-2f940f12ce7c} + + + + + + + + {7568a965-5dca-4d46-b88b-2f940f12ce7c} + + + ScopyMainWindow::removeDeviceFromUi + + + + + + + {d89d5f07-60ee-4036-89c7-beb2726c166d} + + + + + + + + {d89d5f07-60ee-4036-89c7-beb2726c166d} + + + {7568a965-5dca-4d46-b88b-2f940f12ce7c} + {9b267513-3877-43f6-b8a7-f3582e108c61} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {18c84ad8-53f8-42f1-bf7a-7842e1e20b27} + + + + + + + + {18c84ad8-53f8-42f1-bf7a-7842e1e20b27} + + + DeviceManager::removeDeviceById + + + + + + + {7ae1c064-8649-4261-b3a2-6ac1c121700b} + + + + + + + + {7ae1c064-8649-4261-b3a2-6ac1c121700b} + + + {18c84ad8-53f8-42f1-bf7a-7842e1e20b27} + {7568a965-5dca-4d46-b88b-2f940f12ce7c} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {eae9c13d-0f0d-4162-9c36-e09391f41821} + + + + + + + + {eae9c13d-0f0d-4162-9c36-e09391f41821} + + + DeviceManager::removeDevice + + + + + + + {7985ecdc-8c5b-486e-a952-481560434dde} + + + + + + + + {7985ecdc-8c5b-486e-a952-481560434dde} + + + {eae9c13d-0f0d-4162-9c36-e09391f41821} + {be0edb24-92bc-4f17-aefa-0c154654c5fd} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {dade1aba-c22b-4d70-b8bd-a4a1e9e42d0d} + + + + + + + + {dade1aba-c22b-4d70-b8bd-a4a1e9e42d0d} + + + + + false + start + + + + + + + + {3949a8ee-04c0-4679-922c-cef599e03f79} + + + + + + + + {3949a8ee-04c0-4679-922c-cef599e03f79} + + + ScannedIIOContextCollector::lostDevice + + + + + + + {3f0f1508-a114-4d3b-aea4-7a086b5a90a3} + + + + + + + + {3f0f1508-a114-4d3b-aea4-7a086b5a90a3} + + + {3949a8ee-04c0-4679-922c-cef599e03f79} + {eae9c13d-0f0d-4162-9c36-e09391f41821} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {5a3ddb9b-f506-4cec-a9c4-8d1bc19c36a2} + + + + + + + + {5a3ddb9b-f506-4cec-a9c4-8d1bc19c36a2} + + + DeviceImpl::forget + + + + + + + {8f1a5ed6-6bfd-44a2-afc0-c71c614b2d9c} + + + + + + + + {8f1a5ed6-6bfd-44a2-afc0-c71c614b2d9c} + + + {5a3ddb9b-f506-4cec-a9c4-8d1bc19c36a2} + {be0edb24-92bc-4f17-aefa-0c154654c5fd} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {4673216b-539b-43a8-91c3-4ee18ed58ad8} + + + + + + + + {4673216b-539b-43a8-91c3-4ee18ed58ad8} + + + forgetBtn::clicked + + + + + + + {7071fba5-73e5-42bb-ab7e-dac891169e23} + + + + + + + + {7071fba5-73e5-42bb-ab7e-dac891169e23} + + + {4673216b-539b-43a8-91c3-4ee18ed58ad8} + {5a3ddb9b-f506-4cec-a9c4-8d1bc19c36a2} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {142efc8c-c913-43ac-90e1-10b0275582c9} + + + + + + + + {142efc8c-c913-43ac-90e1-10b0275582c9} + + + Find menu item by id + + + + + + + {6f9bfe35-2b12-4aab-b452-b57d0af238ea} + + + + + + + + {6f9bfe35-2b12-4aab-b452-b57d0af238ea} + + + {142efc8c-c913-43ac-90e1-10b0275582c9} + {00d7bfe9-3196-4336-b9cc-551cac3fd1d5} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {56607076-7f6a-44b8-aa25-7435d68b3b0e} + + + + + + + + {56607076-7f6a-44b8-aa25-7435d68b3b0e} + + + Remove item + + + + + + + {ea5cf450-4151-4365-9d36-efe054debad5} + + + + + + + + {ea5cf450-4151-4365-9d36-efe054debad5} + + + {56607076-7f6a-44b8-aa25-7435d68b3b0e} + {fb05cd5b-f4fd-4e67-bd96-f875b23105a7} + + + Controlflow + + + true + + + + + + + + + + + + + + false + activity + + + + + + + + {00d7bfe9-3196-4336-b9cc-551cac3fd1d5} + + + + + + + + {00d7bfe9-3196-4336-b9cc-551cac3fd1d5} + + + Menu contains the item + + + + + + + {6d45da31-06a2-40f7-898e-4a129fb39d56} + + + + + + + + {6d45da31-06a2-40f7-898e-4a129fb39d56} + + + yes + {00d7bfe9-3196-4336-b9cc-551cac3fd1d5} + {56607076-7f6a-44b8-aa25-7435d68b3b0e} + + + Controlflow + + + true + + + + + + + + + + {14b5f534-0ed7-4487-b8df-7f29159a8a41} + + + + + + + + {14b5f534-0ed7-4487-b8df-7f29159a8a41} + + + No + {00d7bfe9-3196-4336-b9cc-551cac3fd1d5} + {5e5a3cdb-46c7-4486-8e50-4836a7af9c24} + + + Controlflow + + + true + + + + + + + + + + + + + + false + condition + + + + + + + + {fb05cd5b-f4fd-4e67-bd96-f875b23105a7} + + + + + + + + {fb05cd5b-f4fd-4e67-bd96-f875b23105a7} + + + + + false + termination + + + + + + + + {5e5a3cdb-46c7-4486-8e50-4836a7af9c24} + + + + + + + + {5e5a3cdb-46c7-4486-8e50-4836a7af9c24} + + + + + false + termination + + + + + + + + {be0edb24-92bc-4f17-aefa-0c154654c5fd} + + + + + + + + {be0edb24-92bc-4f17-aefa-0c154654c5fd} + + + + + + + + + {f10bbb35-3702-4c9f-8eed-4921c7d69afd} + + + + + + + + {f10bbb35-3702-4c9f-8eed-4921c7d69afd} + + + {be0edb24-92bc-4f17-aefa-0c154654c5fd} + {18c84ad8-53f8-42f1-bf7a-7842e1e20b27} + + + Controlflow + + + true + + + + + + + + + + + + + + false + horizontalbar + + + + + + + + {2d71f785-f2b8-4e7f-8d9b-d4ace42492ff} + + + + + + + + {2d71f785-f2b8-4e7f-8d9b-d4ace42492ff} + + + + + + + + + {91d52a46-9a9e-4d73-9e98-7fe39ce28da1} + + + + + + + + {91d52a46-9a9e-4d73-9e98-7fe39ce28da1} + + + {2d71f785-f2b8-4e7f-8d9b-d4ace42492ff} + {3949a8ee-04c0-4679-922c-cef599e03f79} + + + Controlflow + + + true + + + + + + + + + + + + + + false + start + + + + + + + + {c49e84cd-e524-48d0-8209-0f88db861f2e} + + + + + + + + {c49e84cd-e524-48d0-8209-0f88db861f2e} + + + + + + + + + {e484ba64-3a2b-4110-a3dd-86e036d865ba} + + + + + + + + {e484ba64-3a2b-4110-a3dd-86e036d865ba} + + + {c49e84cd-e524-48d0-8209-0f88db861f2e} + {4673216b-539b-43a8-91c3-4ee18ed58ad8} + + + Controlflow + + + true + + + + + + + + + + + + + + false + start + + + + + + + + {92090937-a108-4667-865d-f6996e72cd2f} + + + + + + + + {92090937-a108-4667-865d-f6996e72cd2f} + + + + + false + termination + + + + + + + + {aefcfeb6-f513-46ab-8ca9-13aa0aa32e7e} + + + + + + + + {aefcfeb6-f513-46ab-8ca9-13aa0aa32e7e} + + + + + false + termination + + + + + + + + {0c4b1fbf-59e5-4b2f-a60a-df72c6a0c318} + + + + + + + + {0c4b1fbf-59e5-4b2f-a60a-df72c6a0c318} + + + + + + + + + {4d3e8e8d-e585-4d7f-a140-00983a56171b} + + + + + + + + {4d3e8e8d-e585-4d7f-a140-00983a56171b} + + + {0c4b1fbf-59e5-4b2f-a60a-df72c6a0c318} + {8d11b6d3-df17-4fb0-9eb5-fa734da666b5} + + + Controlflow + + + true + + + + + + + + + + + + + + false + start + + + + + + + + {edeb8f38-b4fd-4e09-bcc3-75a375ce61fe} + + + + + + + + {edeb8f38-b4fd-4e09-bcc3-75a375ce61fe} + + + + + + + + + {8e179f85-b57d-4526-b079-68eaffaa53bd} + + + + + + + + {8e179f85-b57d-4526-b079-68eaffaa53bd} + + + {edeb8f38-b4fd-4e09-bcc3-75a375ce61fe} + {550f3fb9-b6d9-4cdc-9c9f-e70df6ccb0e5} + + + Controlflow + + + true + + + + + + + + + + + + + + false + start + + + + + + + + {86c99bcb-2c3c-4d98-8da2-a0da797d16fd} + + + + + + + + {86c99bcb-2c3c-4d98-8da2-a0da797d16fd} + + + + + + + + + {c1b42df8-b851-47d0-b9f2-c73d06575ca1} + + + + + + + + {c1b42df8-b851-47d0-b9f2-c73d06575ca1} + + + {86c99bcb-2c3c-4d98-8da2-a0da797d16fd} + {29798946-3634-4a6a-acf6-d071f4922357} + + + Controlflow + + + true + + + + + + + + + + {a1d154ef-6fe8-457f-a99e-d9c4e13453da} + + + + + + + + {a1d154ef-6fe8-457f-a99e-d9c4e13453da} + + + {86c99bcb-2c3c-4d98-8da2-a0da797d16fd} + {a0752731-5da6-4e82-8433-0d346af63186} + + + Controlflow + + + true + + + + + + + + + + + + + + false + horizontalbar + + + + + + + + + + + + + + + + diff --git a/core/include/core/application_restarter.h b/core/include/core/application_restarter.h new file mode 100644 index 0000000000..75ab6f5f87 --- /dev/null +++ b/core/include/core/application_restarter.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef APPLICATIONRESTARTER_H +#define APPLICATIONRESTARTER_H + +#include "scopy-core_export.h" + +#include +#include + +namespace scopy { +class SCOPY_CORE_EXPORT ApplicationRestarter +{ +public: + ApplicationRestarter(const QString &executable); + + static ApplicationRestarter *GetInstance(); + void setArguments(const QStringList &arguments); + QStringList getArguments() const; + + int restart(int exitCode); + static void triggerRestart(); + +private: + static ApplicationRestarter *pinstance_; + QString m_executable; + QStringList m_arguments; + QString m_currentPath; + bool m_restart; +}; +} // namespace scopy +#endif // APPLICATIONRESTARTER_H diff --git a/core/include/core/browsemenu.h b/core/include/core/browsemenu.h new file mode 100644 index 0000000000..03da7910e2 --- /dev/null +++ b/core/include/core/browsemenu.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef BROWSEMENU_H +#define BROWSEMENU_H + +#include "toolmenu.h" + +#include +#include +#include +#include + +namespace scopy { + +class SCOPY_CORE_EXPORT BrowseMenu : public QWidget +{ + Q_OBJECT +public: + enum MenuAlignment + { + MA_TOPLAST, + MA_BOTTOMLAST + }; + + BrowseMenu(QWidget *parent = nullptr); + ~BrowseMenu(); + + ToolMenu *toolMenu() const; + +Q_SIGNALS: + void requestTool(QString tool); + void requestSave(); + void requestLoad(); + void collapsed(bool collapsed); + +private: + void add(QWidget *w, QString name, MenuAlignment position); + void toggleCollapsed(); + QPushButton *createBtn(QString name, QString iconPath, QWidget *parent = nullptr); + QFrame *createHLine(QWidget *parent = nullptr); + QWidget *createHeader(QWidget *parent = nullptr); + QLabel *createScopyLogo(QWidget *parent = nullptr); + + QWidget *m_content; + QVBoxLayout *m_contentLay; + QSpacerItem *m_spacer; + ToolMenu *m_toolMenu; + QPushButton *m_btnCollapse; + bool m_collapsed; +}; +} // namespace scopy + +#endif // BROWSEMENU_H diff --git a/core/include/core/cmdlinehandler.h b/core/include/core/cmdlinehandler.h new file mode 100644 index 0000000000..53e41b6a79 --- /dev/null +++ b/core/include/core/cmdlinehandler.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef CMDLINEHANDLER_H +#define CMDLINEHANDLER_H + +#include "scopy-core_export.h" +#include "scopymainwindow_api.h" + +#include + +namespace scopy { +class SCOPY_CORE_EXPORT CmdLineHandler +{ +public: + static int handle(QCommandLineParser &parser, ScopyMainWindow_API &scopyApi); + static void withLogFileOption(QCommandLineParser &parser); + static void closeLogFile(); + +private: + static void logOutputHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg); + static void removeTempLogFile(); + static FILE *logFile_; + static bool tempLogFile_; +}; +} // namespace scopy + +#endif // CMDLINEHANDLER_H diff --git a/core/include/core/crashreport.h b/core/include/core/crashreport.h new file mode 100644 index 0000000000..e8ec669be1 --- /dev/null +++ b/core/include/core/crashreport.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef CRASHREPORT_H +#define CRASHREPORT_H + +#include +#include "scopy-core_export.h" + +namespace scopy { +class SCOPY_CORE_EXPORT CrashReport +{ +public: + static void initSignalHandler(); + +private: + static void signalHandler(int); + static QString tmpFilePath_; +}; +} // namespace scopy + +#endif // CRASHREPORT_H diff --git a/core/include/core/detachedtoolwindow.h b/core/include/core/detachedtoolwindow.h new file mode 100644 index 0000000000..d6006fba14 --- /dev/null +++ b/core/include/core/detachedtoolwindow.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DETACHEDTOOLWINDOW_H +#define DETACHEDTOOLWINDOW_H + +#include "scopy-core_export.h" + +#include + +#include + +namespace scopy { +class SCOPY_CORE_EXPORT DetachedToolWindow : public QWidget +{ + Q_OBJECT +public: + DetachedToolWindow(QWidget *parent, ToolMenuEntry *tme); + ~DetachedToolWindow(); + + void saveToolGeometry(ToolMenuEntry *tme, QWidget *w); + void loadToolGeometry(ToolMenuEntry *tme, QWidget *w); + +private: + ToolMenuEntry *tme; + QWidget *w; + +protected: + void closeEvent(QCloseEvent *event) override; +}; +} // namespace scopy +#endif // DETACHEDTOOLWINDOW_H diff --git a/core/include/core/detachedtoolwindowmanager.h b/core/include/core/detachedtoolwindowmanager.h new file mode 100644 index 0000000000..32d157dc36 --- /dev/null +++ b/core/include/core/detachedtoolwindowmanager.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DETACHEDTOOLMANAGER_H +#define DETACHEDTOOLMANAGER_H + +#include "detachedtoolwindow.h" +#include "scopy-core_export.h" + +#include +#include + +#include + +namespace scopy { + +class SCOPY_CORE_EXPORT DetachedToolWindowManager : public QObject +{ + Q_OBJECT +public: + DetachedToolWindowManager(QObject *parent = nullptr); + ~DetachedToolWindowManager(); + + void add(QString id, ToolMenuEntry *tme); + bool remove(QString id); + bool contains(QString key); + QWidget *getWidget(QString key); + +public Q_SLOTS: + void show(QString id); + +private: + QMap map; +}; +} // namespace scopy + +#endif // DETACHEDTOOLMANAGER_H diff --git a/core/include/core/device.h b/core/include/core/device.h new file mode 100644 index 0000000000..9dd382241f --- /dev/null +++ b/core/include/core/device.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICE_H +#define DEVICE_H + +#include "pluginbase/toolmenuentry.h" +#include "scopy-core_export.h" + +#include +#include + +namespace scopy { +class SCOPY_CORE_EXPORT Device +{ +public: + typedef enum + { + DEV_ERROR = -1, + DEV_INIT = 0, + DEV_IDLE = 1, + DEV_CONNECTING = 2, + DEV_CONNECTED = 3, + DEV_DISCONNECTING = 4, + } DeviceState_t; + virtual ~Device(){}; + virtual QString id() = 0; + virtual QString category() = 0; + virtual QString displayName() = 0; + virtual QString param() = 0; + virtual QString displayParam() = 0; + virtual QWidget *icon() = 0; + + virtual QWidget *page() = 0; + + virtual QList toolList() = 0; + virtual void init() = 0; + virtual void preload() = 0; + virtual void loadPlugins() = 0; + virtual void unloadPlugins() = 0; + virtual DeviceState_t state() = 0; + +public Q_SLOTS: + virtual void connectDev() = 0; + virtual void disconnectDev() = 0; + virtual void showPage() = 0; + virtual void hidePage() = 0; + virtual void save(QSettings &) = 0; + virtual void load(QSettings &) = 0; + +Q_SIGNALS: + virtual void toolListChanged() = 0; + virtual void connecting() = 0; + virtual void connected() = 0; + virtual void disconnecting() = 0; + virtual void disconnected() = 0; + virtual void requestedRestart() = 0; + virtual void requestTool(QString) = 0; +}; +} // namespace scopy + +#endif // DEVICE_H diff --git a/core/include/core/deviceautoconnect.h b/core/include/core/deviceautoconnect.h new file mode 100644 index 0000000000..1c163f696e --- /dev/null +++ b/core/include/core/deviceautoconnect.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICEAUTOCONNECT_H +#define DEVICEAUTOCONNECT_H + +#include + +namespace scopy { +class DeviceAutoConnect +{ +public: + static void initPreferences(); + static void addDevice(QString uri, QStringList plugins); + static void removeDevice(QString uri); + static void clear(); + static bool isAutoConnectEnabled(QString uri); +}; +} // namespace scopy +#endif // DEVICEAUTOCONNECT_H diff --git a/core/include/core/devicebrowser.h b/core/include/core/devicebrowser.h new file mode 100644 index 0000000000..beac1ecc46 --- /dev/null +++ b/core/include/core/devicebrowser.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICEBROWSER_H +#define DEVICEBROWSER_H + +#include "scopy-core_export.h" + +#include +#include +#include +#include + +#include + +namespace Ui { +class DeviceBrowser; +} + +namespace scopy { +class SCOPY_CORE_EXPORT DeviceBrowser : public QWidget +{ + Q_OBJECT + +public: + explicit DeviceBrowser(QWidget *parent = nullptr); + ~DeviceBrowser(); + QAbstractButton *getDeviceWidgetFor(QString id); + void addDevice(QString id, Device *d, int position = -1); + void removeDevice(QString id); + void connectingDevice(QString id); + void connectDevice(QString id); + void disconnectDevice(QString id); + +Q_SIGNALS: + void requestDevice(QString id, int direction); + void requestRemoveDevice(QString id); + void displayNameChanged(QString id, QString newName); + +public Q_SLOTS: + + void nextDevice(); + void prevDevice(); + +private Q_SLOTS: + void updateSelectedDeviceIdx(QString); + void forwardRequestDeviceWithDirection(); + +private: + void initBtns(); + DeviceIcon *buildDeviceIcon(Device *d, QWidget *parent = nullptr); + Ui::DeviceBrowser *ui; + QButtonGroup *bg; + QHBoxLayout *layout; + QList list; + int currentIdx; + + int getIndexOfId(QString k); + QString getIdOfIndex(int idx); + const char *devBrowserId = "DeviceBrowserId"; +}; +} // namespace scopy +#endif // DEVICEBROWSER_H diff --git a/core/include/core/devicefactory.h b/core/include/core/devicefactory.h new file mode 100644 index 0000000000..6b68d2f874 --- /dev/null +++ b/core/include/core/devicefactory.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICEFACTORY_H +#define DEVICEFACTORY_H + +#include "deviceimpl.h" + +#include + +namespace scopy { + +class DeviceFactory : public QObject +{ + Q_OBJECT +public: + static DeviceImpl *build(QString param, PluginManager *pm, QString category = "", QObject *parent = nullptr); +}; +} // namespace scopy +#endif // DEVICEFACTORY_H diff --git a/core/include/core/deviceicon.h b/core/include/core/deviceicon.h new file mode 100644 index 0000000000..909e67438d --- /dev/null +++ b/core/include/core/deviceicon.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICEICON_H +#define DEVICEICON_H +#include "scopy-core_export.h" + +#include + +#include + +namespace scopy { +class SCOPY_CORE_EXPORT DeviceIcon : public QAbstractButton +{ + Q_OBJECT +public: + DeviceIcon(QWidget *parent = nullptr) + : QAbstractButton(parent){}; + virtual ~DeviceIcon(){}; +public Q_SLOTS: + // virtual Device* device() = 0; + virtual void setConnected(bool) = 0; + virtual void setConnecting(bool) = 0; +Q_SIGNALS: + void refresh(); + void forget(); +}; +} // namespace scopy + +#endif // DEVICEICON_H diff --git a/core/include/core/deviceiconimpl.h b/core/include/core/deviceiconimpl.h new file mode 100644 index 0000000000..095c3f3438 --- /dev/null +++ b/core/include/core/deviceiconimpl.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICEICONIMPL_H +#define DEVICEICONIMPL_H + +#include "device.h" +#include "gui/utils.h" +#include "scopy-core_export.h" + +#include +#include +#include + +#include + +namespace Ui { +class DeviceButton; +}; + +namespace scopy { +class SCOPY_CORE_EXPORT DeviceIconImpl : public DeviceIcon +{ + Q_OBJECT + QWIDGET_PAINT_EVENT_HELPER +public: + explicit DeviceIconImpl(Device *d, QWidget *parent); + ~DeviceIconImpl(); +public Q_SLOTS: + void setConnecting(bool) override; + void setConnected(bool) override; +Q_SIGNALS: + void displayNameChanged(QString newName); + +private Q_SLOTS: + void onPenBtnPressed(); + void onEditFinished(); + +private: + void createPenBtn(); + + Ui::DeviceButton *ui; +}; +} // namespace scopy + +#endif // DEVICEICONIMPL_H diff --git a/core/include/core/deviceimpl.h b/core/include/core/deviceimpl.h new file mode 100644 index 0000000000..5b6c7b2b8b --- /dev/null +++ b/core/include/core/deviceimpl.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICEIMPL_H +#define DEVICEIMPL_H + +#include "device.h" +#include "pluginbase/plugin.h" +#include "pluginmanager.h" +#include "scopy-core_export.h" + +#include +#include +#include + +namespace scopy { + +class SCOPY_CORE_EXPORT DeviceImpl : public QObject, public Device +{ + Q_OBJECT +public: + explicit DeviceImpl(QString param, PluginManager *p, QString category = "", QObject *parent = nullptr); + virtual ~DeviceImpl(); + + // Device interface +public: + QString id() override; + QString displayName() override; + QString displayParam() override; + QString category() override; + QString param() override; + QWidget *icon() override; + QWidget *page() override; + QList toolList() override; + virtual void init() override; + virtual void preload() override; + virtual void loadPlugins() override; + virtual void unloadPlugins() override; + virtual bool verify(); + virtual QMap readDeviceInfo(); + + QList plugins() const; + DeviceImpl::DeviceState_t state() override; +public Q_SLOTS: + virtual void connectDev() override; + virtual void disconnectDev() override; + virtual void showPage() override; + virtual void hidePage() override; + virtual void save(QSettings &) override; + virtual void load(QSettings &) override; + // void forgetDev() override; + void onConnectionFailed(); +Q_SIGNALS: + void toolListChanged() override; + void connecting() override; + void connected() override; + void disconnecting() override; + void disconnected() override; + void requestedRestart() override; + void requestTool(QString) override; + void connectionFailed(); + void forget(); + +protected: + void removeDisabledPlugins(); + void loadName(); + void loadIcons(); + void loadPages(); + void loadToolList(); + void loadBadges(); + void setPingPlugin(Plugin *plugin); + void bindPing(); + void unbindPing(); + +protected: + PluginManager *p; + QList m_plugins; + QList m_connectedPlugins; + DeviceState_t m_state; + QString m_id; + QString m_category; + QString m_displayName; + QString m_displayParam; + QString m_param; + QWidget *m_icon; + QWidget *m_page; + QPushButton *connbtn, *discbtn; + Plugin *m_pingPlugin = nullptr; +}; +} // namespace scopy + +#endif // DEVICEIMPL_H diff --git a/core/include/core/deviceloader.h b/core/include/core/deviceloader.h new file mode 100644 index 0000000000..6a470bb9ec --- /dev/null +++ b/core/include/core/deviceloader.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICELOADER_H +#define DEVICELOADER_H + +#include "deviceimpl.h" + +#include + +namespace scopy { + +class DeviceLoader : public QObject +{ + Q_OBJECT +public: + DeviceLoader(DeviceImpl *d, QObject *parent = nullptr); + ~DeviceLoader(); + void init(bool async = true); + void asyncInit(); + void syncInit(); +Q_SIGNALS: + void initialized(); + +private: + DeviceImpl *d; + QObject *oldParent; +}; +} // namespace scopy + +#endif // DEVICELOADER_H diff --git a/core/include/core/devicemanager.h b/core/include/core/devicemanager.h new file mode 100644 index 0000000000..7b05dd6bda --- /dev/null +++ b/core/include/core/devicemanager.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef DEVICEMANAGER_H +#define DEVICEMANAGER_H + +#include "device.h" +#include "deviceimpl.h" +#include "pluginmanager.h" +#include "scopy-core_export.h" + +#include +#include +#include + +namespace scopy { +class SCOPY_CORE_EXPORT DeviceManager : public QObject +{ + Q_OBJECT + friend class ScopyMainWindow_API; + +public: + explicit DeviceManager(PluginManager *pm, QObject *parent = nullptr); + ~DeviceManager(); + Device *getDevice(QString id); + void setExclusive(bool); + bool getExclusive() const; + bool busy(); + int connectedDeviceCount(); + void saveSessionDevices(); + +public Q_SLOTS: + + void addDevice(Device *d); + QString createDevice(QString category, QString param, bool async = true, + QList plugins = QList()); + void removeDevice(QString category, QString id); + + void removeDeviceById(QString id); + QString restartDevice(QString id); + void disconnectAll(); + + void save(QSettings &s); + void load(QSettings &s); + void requestConnectedDev(); + // void updateScan(QStringList ls); + +Q_SIGNALS: + void connectedDevices(QMap connectedDev); + +private Q_SLOTS: + void changeToolListDevice(); + void connectingDevice(); + void connectDevice(); + void disconnectingDevice(); + void disconnectDevice(); + void restartDevice(); + +Q_SIGNALS: + void deviceChangedToolList(QString, QList); + void deviceAddStarted(QString); + void deviceAdded(QString, Device *); + void deviceRemoveStarted(QString, Device *); + void deviceRemoved(QString); + void deviceConnecting(QString); + void deviceConnected(QString id, Device *); + void deviceDisconnecting(QString id); + void deviceDisconnected(QString id, Device *); + void requestDevice(QString id); + void requestTool(QString id); + +private: + void connectDeviceToManager(DeviceImpl *d); + void disconnectDeviceFromManager(DeviceImpl *d); + +private: + bool exclusive = false; + QStringList scannedDev; + QStringList connectedDev; + QMap map; + PluginManager *pm; + + void disconnectingDevice(QString id); + void disconnectDevice(QString id); + void connectingDevice(QString id); + void connectDevice(QString id); +}; +} // namespace scopy + +#endif // DEVICEMANAGER_H diff --git a/core/include/core/emuwidget.h b/core/include/core/emuwidget.h new file mode 100644 index 0000000000..7b4443f06c --- /dev/null +++ b/core/include/core/emuwidget.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef EMUWIDGET_H +#define EMUWIDGET_H + +#include "scopy-core_export.h" + +#include +#include +#include +#include +#include +#include + +namespace scopy { +class SCOPY_CORE_EXPORT EmuWidget : public QWidget +{ + Q_OBJECT +public: + explicit EmuWidget(QWidget *parent = nullptr); + ~EmuWidget(); + +Q_SIGNALS: + void emuDeviceAvailable(QString uri); + +protected: + void showEvent(QShowEvent *event) override; +private Q_SLOTS: + void onEnableDemoClicked(); + void browseFile(QLineEdit *lineEditPath); + +Q_SIGNALS: + void demoEnabled(bool en); + +private: + QWidget *createDemoOptWidget(QWidget *parent); + QWidget *createXmlPathWidget(QWidget *parent); + QWidget *createRxTxDevWidget(QWidget *parent); + QWidget *createUriWidget(QWidget *parent); + void initEnBtn(QWidget *parent); + void init(); + void enGenericOptWidget(QWidget *xmlPathWidget, QWidget *rxTxDevWidget, QString crtOpt); + QStringList createArgList(); + void setStatusMessage(QString msg); + QString findEmuPath(); + void stopEnableBtn(QString btnText); + bool startIioEmuProcess(QString processPath, QStringList arg = {}); + void killEmuProcess(); + + void getEmuOptions(); + void configureOption(QString option); + void getJsonConfiguration(); + void setEnableDemo(bool en); + + QComboBox *m_demoOptCb; + QLineEdit *m_xmlPathEdit; + QLineEdit *m_rxTxDevEdit; + QLineEdit *m_uriEdit; + QLabel *m_uriMsgLabel; + AnimationPushButton *m_enDemoBtn; + + QString m_emuPath; + QString m_workingDir; + bool m_enableDemo; + QProcess *m_emuProcess; + QStringList m_availableOptions; + + QString m_jsonConfigVal; + QString m_emuType = "generic"; +}; +} // namespace scopy + +#endif // EMUWIDGET_H diff --git a/core/include/core/iiodeviceimpl.h b/core/include/core/iiodeviceimpl.h new file mode 100644 index 0000000000..d21f7915d1 --- /dev/null +++ b/core/include/core/iiodeviceimpl.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef IIODEVICEIMPL_H +#define IIODEVICEIMPL_H + +#include "deviceimpl.h" + +namespace scopy { + +class SCOPY_CORE_EXPORT IIODeviceImpl : public DeviceImpl +{ +public: + explicit IIODeviceImpl(QString param, PluginManager *p, QObject *parent = nullptr) + : DeviceImpl(param, p, "iio", parent) + {} + ~IIODeviceImpl() {} + + virtual void init() override; + bool verify() override; + QMap readDeviceInfo() override; +}; + +} // namespace scopy + +#endif // IIODEVICEIMPL_H diff --git a/core/include/core/iiotabwidget.h b/core/include/core/iiotabwidget.h new file mode 100644 index 0000000000..67ad1837bd --- /dev/null +++ b/core/include/core/iiotabwidget.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef IIOTABWIDGET_H +#define IIOTABWIDGET_H + +#include "menucombo.h" +#include +#include +#include +#include +#include +#include + +namespace scopy { + +class IioTabWidget : public QWidget +{ + Q_OBJECT +public: + IioTabWidget(QWidget *parent = nullptr); + ~IioTabWidget(); + +public Q_SLOTS: + void onVerifyFinished(bool result); + void updateUri(QString uri); +Q_SIGNALS: + void uriChanged(QString uri); + void startVerify(QString uri, QString cat); + +protected: + void showEvent(QShowEvent *event) override; +private Q_SLOTS: + void scanFinished(); + void serialScanFinished(); + void futureScan(); + void futureSerialScan(); + void verifyBtnClicked(); + +private: + void setupConnections(); + QStringList computeBackendsList(); + void addScanFeedbackMsg(QString message); + QCheckBox *createBackendCheckBox(QString backEnd, QWidget *parent); + void setupFilterWidget(QStringList backednsList); + QString getSerialPath(); + bool isSerialCompatible(); + void setupBtnLdIcon(AnimationPushButton *btn); + void rstUriMsgLabel(); + QWidget *createFilterWidget(QWidget *parent); + QWidget *createAvlCtxWidget(QWidget *parent); + QWidget *createSerialSettWidget(QWidget *parent); + QWidget *createUriWidget(QWidget *parent); + QWidget *createVerifyBtnWidget(QWidget *parent); + + QWidget *m_filterWidget; + QLabel *m_ctxUriLabel; + QComboBox *m_avlCtxCb; + MenuCombo *m_serialPortCb; + MenuCombo *m_baudRateCb; + QLineEdit *m_serialFrameEdit; + QLineEdit *m_uriEdit; + QLabel *m_uriMsgLabel; + AnimationPushButton *m_btnScan; + AnimationPushButton *m_btnSerialScan; + AnimationPushButton *m_btnVerify; + + QFutureWatcher *m_fwScan; + QFutureWatcher> *m_fwSerialScan; + QStringList m_scanParamsList; + QVector> m_scanList; + + const QVector m_availableBaudRates = {2400, 4800, 9600, 14400, 19200, 38400, + 57600, 115200, 230400, 460800, 921600}; +}; +} // namespace scopy + +#endif // IIOTABWIDGET_H diff --git a/core/include/core/infopagestack.h b/core/include/core/infopagestack.h new file mode 100644 index 0000000000..56d66a82f1 --- /dev/null +++ b/core/include/core/infopagestack.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef INFOPAGESTACK_H +#define INFOPAGESTACK_H + +#include "device.h" +#include "gui/homepage_controls.h" +#include "mapstackedwidget.h" +#include "scopy-core_export.h" + +#include + +namespace scopy { + +// implement slide to-from next/prev page - emits to button selection group + +class SCOPY_CORE_EXPORT InfoPageStack : public MapStackedWidget +{ + Q_OBJECT +public: + explicit InfoPageStack(QWidget *parent = nullptr); + ~InfoPageStack(); + void add(QString key, Device *d); + + HomepageControls *getHomepageControls() const; + + void add(QString key, QWidget *w) override; + bool remove(QString key) override; +public Q_SLOTS: + bool show(QString key) override; + bool slideInKey(QString key, int direction); + +protected Q_SLOTS: + void animationDone(); + +private: + QMap idDevMap; + void slideInWidget(QWidget *newWidget, int direction); + HomepageControls *hc; + + enum QEasingCurve::Type animationType; + int speed; + + bool active; + QPoint now; + int current; + int next; +}; +} // namespace scopy +#endif // INFOPAGESTACK_H diff --git a/core/include/core/licenseoverlay.h b/core/include/core/licenseoverlay.h new file mode 100644 index 0000000000..9389c4e748 --- /dev/null +++ b/core/include/core/licenseoverlay.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef LICENSEOVERLAY_H +#define LICENSEOVERLAY_H + +#include "qpushbutton.h" +#include "scopy-core_export.h" +#include + +namespace scopy { +class SCOPY_CORE_EXPORT LicenseOverlay : public QWidget +{ + Q_OBJECT +public: + LicenseOverlay(QWidget *parent = nullptr); + ~LicenseOverlay(); + QString static getLicense(); + void showOverlay(); + QPushButton *getContinueBtn(); + +private: + QWidget *parent; + PopupWidget *m_popupWidget; +}; +} // namespace scopy +#endif // LICENSEOVERLAY_H diff --git a/core/include/core/logging_categories.h b/core/include/core/logging_categories.h new file mode 100644 index 0000000000..5849e3d171 --- /dev/null +++ b/core/include/core/logging_categories.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of Scopy + * (see http://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef LOGGING_CATEGORIES_H +#define LOGGING_CATEGORIES_H + +#include "scopy-core_export.h" + +#include + +#define QDEBUG_LOG_TIME +//#define QDEBUG_LOG_DATE +#define QDEBUG_LOG_MSG_TYPE +#define QDEBUG_LOG_FILE +//#define QDEBUG_CATEGORY +#define QDEBUG_FUNCTION + +#define QDEBUG_LOG_TIME_STR "%{time h:mm:ss.zzz}" +#define QDEBUG_LOG_DATE_STR "%{time yyyyMMdd }" +#define QDEBUG_LOG_MSG_TYPE_STR \ + "%{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}" +#define QDEBUG_LOG_FILE_PREFIX_STR "file://" +#define QDEBUG_LOG_FILE_NAME_STR "%{file}:" +#define QDEBUG_LOG_LINE_NR_STR "%{line}" +#define QDEBUG_LOG_FILE_STR QDEBUG_LOG_FILE_PREFIX_STR QDEBUG_LOG_FILE_NAME_STR QDEBUG_LOG_LINE_NR_STR +#define QDEBUG_CATEGORY_STR "%{category}" +#define QDEBUG_FUNCTION_STR "%{function}" +#define QDEBUG_THREAD_STR "%{threadid}" +#define QDEBUG_PID_STR "%{pid}" +#define QDEBUG_APP_STR "%{appname}" +#define QCRITICAL_BACKTRACE_STR "%{if-critical}%{backtrace depth=100 separator=\n}%{endif}" + +#ifndef QT_NO_DEBUG_OUTPUT +Q_DECLARE_LOGGING_CATEGORY(CAT_TOOL_LAUNCHER) +Q_DECLARE_LOGGING_CATEGORY(CAT_OSCILLOSCOPE) +Q_DECLARE_LOGGING_CATEGORY(CAT_SIGNAL_GENERATOR) +Q_DECLARE_LOGGING_CATEGORY(CAT_VOLTMETER) +Q_DECLARE_LOGGING_CATEGORY(CAT_POWER_CONTROLLER) +Q_DECLARE_LOGGING_CATEGORY(CAT_SPECTRUM_ANALYZER) +Q_DECLARE_LOGGING_CATEGORY(CAT_NETWORK_ANALYZER) +Q_DECLARE_LOGGING_CATEGORY(CAT_DIGITAL_IO) +Q_DECLARE_LOGGING_CATEGORY(CAT_LOGIC_ANALYZER) +Q_DECLARE_LOGGING_CATEGORY(CAT_PATTERN_GENERATOR) +Q_DECLARE_LOGGING_CATEGORY(CAT_CALIBRATION) +Q_DECLARE_LOGGING_CATEGORY(CAT_CALIBRATION_MANUAL) +Q_DECLARE_LOGGING_CATEGORY(CAT_IIO_MANAGER) +Q_DECLARE_LOGGING_CATEGORY(CAT_PLOT) +Q_DECLARE_LOGGING_CATEGORY(CAT_BENCHMARK) +Q_DECLARE_LOGGING_CATEGORY(CAT_CRASH_REPORT) +#else +#define CAT_TOOL_LAUNCHER +#define CAT_OSCILLOSCOPE +#define CAT_SIGNAL_GENERATOR +#define CAT_VOLTMETER +#define CAT_POWER_CONTROLLER +#define CAT_SPECTRUM_ANALYZER +#define CAT_NETWORK_ANALYZER +#define CAT_DIGITAL_IO +#define CAT_LOGIC_ANALYZER +#define CAT_PATTERN_GENERATOR +#define CAT_CALIBRATION +#define CAT_CALIBRATION_MANUAL +#define CAT_IIO_MANAGER +#define CAT_PLOT +#define CAT_BENCHMARK +#define CAT_CRASH_REPORT +#endif + +void SetScopyQDebugMessagePattern(); + +#endif // LOGGING_CATEGORIES_H diff --git a/core/include/core/pluginenablewidget.h b/core/include/core/pluginenablewidget.h new file mode 100644 index 0000000000..7f742ed717 --- /dev/null +++ b/core/include/core/pluginenablewidget.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef PLUGINENABLEWIDGET_H +#define PLUGINENABLEWIDGET_H + +#include "gui/utils.h" +#include "qcheckbox.h" +#include "qlabel.h" + +#include + +namespace scopy { + +class PluginEnableWidget : public QWidget +{ + Q_OBJECT + QWIDGET_PAINT_EVENT_HELPER +public: + explicit PluginEnableWidget(QWidget *parent = nullptr); + ~PluginEnableWidget(); + + void setDescription(QString description); + QCheckBox *checkBox() const; + +private: + QCheckBox *m_checkBox; + QLabel *m_descriptionLabel; +}; + +} // namespace scopy + +#endif // PLUGINENABLEWIDGET_H diff --git a/core/include/core/pluginfilter.h b/core/include/core/pluginfilter.h new file mode 100644 index 0000000000..40ec957e41 --- /dev/null +++ b/core/include/core/pluginfilter.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef PLUGINFILTER_H +#define PLUGINFILTER_H +#include + +namespace scopy { +class PluginFilter +{ + +private: + PluginFilter(); + +public: + static bool pluginInCategory(Plugin *p, QString category); // PluginFilter class (?) + static bool pluginInExclusionList(QList pl, Plugin *p); + static bool pluginForcedInclusionList(QList pl, Plugin *p); +}; +} // namespace scopy + +#endif // PLUGINFILTER_H diff --git a/core/include/core/pluginmanager.h b/core/include/core/pluginmanager.h new file mode 100644 index 0000000000..e058de430f --- /dev/null +++ b/core/include/core/pluginmanager.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef PLUGINMANAGER_H +#define PLUGINMANAGER_H + +#include "scopy-core_export.h" + +#include +#include + +#include + +namespace scopy { +class SCOPY_CORE_EXPORT PluginManager : public QObject +{ + Q_OBJECT +public: + PluginManager(QObject *parent = nullptr); + ~PluginManager(); + void add(QStringList pluginFileList); + void add(QString pluginFileName); + int count(); + void sort(); + void clear(); + QList getOriginalPlugins() const; + QList getPlugins(QString category = ""); + QList getCompatiblePlugins(QString param, QString category = ""); + void setMetadata(QJsonObject metadata); + QJsonObject metadata() const; + +private: + Plugin *loadPlugin(QString file); + QList list; + QJsonObject m_metadata; + + void applyMetadata(Plugin *plugin, QJsonObject *metadata); + bool pluginInCategory(Plugin *p, QString category); +}; +} // namespace scopy +#endif // PLUGINMANAGER_H diff --git a/core/include/core/pluginrepository.h b/core/include/core/pluginrepository.h new file mode 100644 index 0000000000..7014a37937 --- /dev/null +++ b/core/include/core/pluginrepository.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef PLUGINREPOSITORY_H +#define PLUGINREPOSITORY_H + +#include "pluginmanager.h" +#include "scopy-core_export.h" + +#include +#include + +namespace scopy { + +class SCOPY_CORE_EXPORT PluginRepository : public QObject +{ + Q_OBJECT +public: + PluginRepository(QObject *parent); + ~PluginRepository(); + void init(QString location); + PluginManager *getPluginManager() { return pm; } + +private: + PluginManager *pm; + QJsonObject metadata; +}; +} // namespace scopy + +#endif // PLUGINREPOSITORY_H diff --git a/core/include/core/scanbuttoncontroller.h b/core/include/core/scanbuttoncontroller.h new file mode 100644 index 0000000000..ae846e8a0a --- /dev/null +++ b/core/include/core/scanbuttoncontroller.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCANBUTTONCONTROLLER_H +#define SCANBUTTONCONTROLLER_H + +#include "iioutil/cyclicaltask.h" +#include "scopy-core_export.h" + +#include +#include + +namespace scopy { +class SCOPY_CORE_EXPORT ScanButtonController : public QObject +{ + Q_OBJECT +public: + explicit ScanButtonController(CyclicalTask *cs, QCheckBox *btn, QObject *parent); + ~ScanButtonController(); + + int scanTimeout() const; + void setScanTimeout(int newScanTimeout); + +public Q_SLOTS: + void enableScan(bool b); + void startScan(); + void stopScan(); + +private: + QCheckBox *btn; + CyclicalTask *cs; + QMetaObject::Connection conn; + + int m_scanTimeout; +}; +} // namespace scopy + +#endif // SCANBUTTONCONTROLLER_H diff --git a/core/include/core/scannediiocontextcollector.h b/core/include/core/scannediiocontextcollector.h new file mode 100644 index 0000000000..40db2ae22a --- /dev/null +++ b/core/include/core/scannediiocontextcollector.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCANNEDIIOCONTEXTCOLLECTOR_H +#define SCANNEDIIOCONTEXTCOLLECTOR_H + +#include "device.h" +#include "scopy-core_export.h" + +#include +#include + +namespace scopy { +class SCOPY_CORE_EXPORT ScannedIIOContextCollector : public QObject +{ + Q_OBJECT +public: + explicit ScannedIIOContextCollector(QObject *parent = nullptr); + ~ScannedIIOContextCollector(); + +public Q_SLOTS: + void update(QVector> ctxsDescription); + void clearCache(); + void lock(QString, Device *); + void unlock(QString, Device *); + void removeDevice(QString id, Device *d); +Q_SIGNALS: + void foundDevice(QString cat, QString uri); + void lostDevice(QString cat, QString uri); + +private: + QSet uris; + QSet lockedUris; +}; +} // namespace scopy + +#endif // SCANNEDIIOCONTEXTCOLLECTOR_H diff --git a/core/include/core/scopy-core_config.h.cmakein b/core/include/core/scopy-core_config.h.cmakein new file mode 100644 index 0000000000..e5615b390e --- /dev/null +++ b/core/include/core/scopy-core_config.h.cmakein @@ -0,0 +1,10 @@ +#ifndef SCOPY_CORE_CONFIG_H_CMAKEIN +#define SCOPY_CORE_CONFIG_H_CMAKEIN + +#cmakedefine WITH_PYTHON +#cmakedefine WITH_SIGROK +#cmakedefine PYTHON_VERSION "@PYTHON_VERSION@" +#cmakedefine SCOPY_DEV_MODE +#cmakedefine BUILD_PYTHON_LIBRARY_DIRS "@BUILD_PYTHON_LIBRARY_DIRS@" + +#endif // SCOPY_CORE_CONFIG_H_CMAKEIN diff --git a/core/include/core/scopyaboutpage.h b/core/include/core/scopyaboutpage.h new file mode 100644 index 0000000000..146d7e205e --- /dev/null +++ b/core/include/core/scopyaboutpage.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCOPYABOUTPAGE_H +#define SCOPYABOUTPAGE_H + +#include "scopy-core_export.h" + +#include +#include +#include +#include + +namespace scopy { +class SCOPY_CORE_EXPORT ScopyAboutPage : public QWidget +{ + Q_OBJECT +public: + ScopyAboutPage(QWidget *parent = nullptr); + ~ScopyAboutPage(); + void addHorizontalTab(QWidget *w, QString text); + QWidget *buildPage(QString src); + +private: + void initUI(); + void initNavigationWidget(QTextBrowser *browser); + QWidget *buildStylePage(); + QWidget *buildPageColors(); + QTabWidget *tabWidget; + QVBoxLayout *layout; +}; +} // namespace scopy + +#endif // SCOPYABOUTPAGE_H diff --git a/core/include/core/scopyhomeaddpage.h b/core/include/core/scopyhomeaddpage.h new file mode 100644 index 0000000000..3d49290280 --- /dev/null +++ b/core/include/core/scopyhomeaddpage.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCOPYHOMEADDPAGE_H +#define SCOPYHOMEADDPAGE_H + +#include "deviceimpl.h" +#include "emuwidget.h" +#include "iiotabwidget.h" +#include "infopage.h" +#include "pluginenablewidget.h" +#include "pluginmanager.h" +#include "scopy-core_export.h" + +#include + +#include +#include +#include +#include + +namespace Ui { +class ScopyHomeAddPage; +} + +namespace scopy { +class SCOPY_CORE_EXPORT ScopyHomeAddPage : public QWidget +{ + Q_OBJECT + +public: + explicit ScopyHomeAddPage(QWidget *parent = nullptr, PluginManager *pm = nullptr); + ~ScopyHomeAddPage(); + +Q_SIGNALS: + void requestDevice(QString); + void newDeviceAvailable(scopy::DeviceImpl *d); + void verifyFinished(bool valid); + +private Q_SLOTS: + void futureVerify(QString uri, QString cat); + void deviceAddedToUi(QString); + void onVerifyFinished(); + void deviceLoaderInitialized(); + void addBtnClicked(); + void backBtnClicked(); + void onEmuDeviceAvailable(QString uri); + +private: + void loadDeviceInfoPage(); + void initializeDevice(); + void removePluginsCheckBoxes(); + QTabWidget *createTabWidget(QWidget *parent); + QWidget *createInfoSection(QWidget *parent); + QWidget *createBtnsWidget(QWidget *parent); + QWidget *createAddPage(QWidget *parent); + + EmuWidget *m_emuWidget; + IioTabWidget *m_iioTabWidget; + QString m_pendingUri; + QFutureWatcher *m_fw; + + InfoPage *m_deviceInfoPage; + PluginManager *m_pluginManager; + DeviceImpl *m_deviceImpl; + QList m_pluginDescriptionList; + + MenuCollapseSection *m_pluginBrowserSection; + QPushButton *m_addBtn; + QPushButton *m_backBtn; + QLabel *m_connLostLabel; + QWidget *m_addPage; + QTabWidget *m_tabWidget; + QStackedWidget *m_stackedWidget; +}; +} // namespace scopy +#endif // SCOPYHOMEADDPAGE_H diff --git a/core/include/core/scopyhomeinfopage.h b/core/include/core/scopyhomeinfopage.h new file mode 100644 index 0000000000..e2e5b8bef4 --- /dev/null +++ b/core/include/core/scopyhomeinfopage.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCOPYHOMEINFOPAGE_H +#define SCOPYHOMEINFOPAGE_H + +#include "scopy-core_export.h" + +#include +#include + +#include + +namespace Ui { +class ScopyHomeInfoPage; +} + +namespace scopy { +class SCOPY_CORE_EXPORT ScopyHomeInfoPage : public QWidget +{ + Q_OBJECT + +public: + explicit ScopyHomeInfoPage(QWidget *parent = nullptr); + ~ScopyHomeInfoPage(); + +private: + Ui::ScopyHomeInfoPage *ui; + + void initReportButton(); +}; +} // namespace scopy + +#endif // SCOPYHOMEINFOPAGE_H diff --git a/core/include/core/scopyhomepage.h b/core/include/core/scopyhomepage.h new file mode 100644 index 0000000000..500b3795c0 --- /dev/null +++ b/core/include/core/scopyhomepage.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCOPYHOMEPAGE_H +#define SCOPYHOMEPAGE_H + +#include "device.h" +#include "pluginmanager.h" +#include "scopy-core_export.h" +#include "scopyhomeaddpage.h" + +#include +#include + +namespace Ui { +class ScopyHomePage; +} + +namespace scopy { +class SCOPY_CORE_EXPORT ScopyHomePage : public QWidget +{ + Q_OBJECT + +public: + explicit ScopyHomePage(QWidget *parent = nullptr, PluginManager *pm = nullptr); + ~ScopyHomePage(); + QCheckBox *scanControlBtn(); + QPushButton *scanBtn(); + void setScannerEnable(bool b); + +Q_SIGNALS: + void requestDevice(QString id); + void deviceAddedToUi(QString id); + void newDeviceAvailable(DeviceImpl *d); + + void displayNameChanged(QString id, QString newName); + +public Q_SLOTS: + void addDevice(QString id, Device *); + void removeDevice(QString id); + void viewDevice(QString id); + void connectingDevice(QString); + void connectDevice(QString); + void disconnectDevice(QString); + +private: + Ui::ScopyHomePage *ui; + ScopyHomeAddPage *add; +}; +} // namespace scopy + +#endif // SCOPYHOMEPAGE_H diff --git a/core/include/core/scopymainwindow.h b/core/include/core/scopymainwindow.h new file mode 100644 index 0000000000..5e4263e017 --- /dev/null +++ b/core/include/core/scopymainwindow.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCOPYMAINWINDOW_H +#define SCOPYMAINWINDOW_H + +#include +#include + +#include "licenseoverlay.h" +#include "scanbuttoncontroller.h" +#include "scopyhomepage.h" +#include "devicemanager.h" +#include "scannediiocontextcollector.h" +#include "detachedtoolwindowmanager.h" +#include "pluginrepository.h" +#include "scopy-core_export.h" +#include "scopyaboutpage.h" +#include "scopypreferencespage.h" +#include "pluginbase/preferences.h" +#include "pluginbase/versionchecker.h" +#include "iioutil/cyclicaltask.h" +#include "iioutil/iioscantask.h" +#include +#include "versioncheckmessage.h" + +#include "toolmenumanager.h" + +QT_BEGIN_NAMESPACE +namespace Ui { +class ScopyMainWindow; +} +QT_END_NAMESPACE +namespace scopy { +class ScopyMainWindow_API; +class SCOPY_CORE_EXPORT ScopyMainWindow : public QMainWindow +{ + friend class ScopyMainWindow_API; + Q_OBJECT + +public: + ScopyMainWindow(QWidget *parent = nullptr); + ~ScopyMainWindow(); + void initAboutPage(PluginManager *pm = nullptr); + void initPreferencesPage(PluginManager *pm = nullptr); + void initPreferences(); + void setupPreferences(); + void initTranslations(); + void loadPluginsFromRepository(PluginRepository *pr = nullptr); + + void showEvent(QShowEvent *event) override; + +public Q_SLOTS: + void requestTools(QString id); + void receiveVersionDocument(QJsonDocument document); + void addDeviceToUi(QString id, Device *d); + void removeDeviceFromUi(QString); + void save(); + void load(); + void save(QString file); + void load(QString file); + void handlePreferences(QString, QVariant); + +private: + ScopyAboutPage *about; + ScopyPreferencesPage *prefPage; + PluginRepository *pr; + ScopyHomePage *hp; + DeviceManager *dm; + + CyclicalTask *scanCycle; + IIOScanTask *scanTask; + ScannedIIOContextCollector *scc; + DetachedToolWindowManager *dtm; + + LicenseOverlay *license = nullptr; + VersionCheckMessage *checkUpdate = nullptr; + ScopyStatusBar *statusBar; + ScopyMainWindow_API *api; + Ui::ScopyMainWindow *ui; + QOpenGLWidget *m_glLoader; + ToolMenuManager *m_toolMenuManager; + ScanButtonController *m_sbc; + + void loadOpenGL(); + void initPythonWIN32(); + void loadDecoders(); + void initApi(); + void initStatusBar(); + void handleScanner(); + void enableScanner(); + void deviceAutoconnect(); + +protected: + void closeEvent(QCloseEvent *event) override; +}; +} // namespace scopy +#endif // SCOPYMAINWINDOW_H diff --git a/core/include/core/scopymainwindow_api.h b/core/include/core/scopymainwindow_api.h new file mode 100644 index 0000000000..e39b653d92 --- /dev/null +++ b/core/include/core/scopymainwindow_api.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCOPYMAINWINDOW_API_H +#define SCOPYMAINWINDOW_API_H + +#include "scopy-core_export.h" +#include "scopymainwindow.h" +#include "scanbuttoncontroller.h" +#include "iioutil/connectionprovider.h" +#include + +namespace scopy { +class SCOPY_CORE_EXPORT ScopyMainWindow_API : public ApiObject +{ + Q_OBJECT +public: + explicit ScopyMainWindow_API(ScopyMainWindow *w); + ~ScopyMainWindow_API(); + + Q_INVOKABLE void acceptLicense(); + Q_INVOKABLE QString addDevice(QString uri, QString cat = "iio", bool async = false); + Q_INVOKABLE QString addDevice(QString uri, QList plugins, QString cat = "iio", bool async = false); + Q_INVOKABLE bool removeDevice(QString uri, QString cat = "iio"); + Q_INVOKABLE bool removeDevice(int idx); + Q_INVOKABLE bool startScan(bool scanState); + Q_INVOKABLE QStringList getDevicesName(); + Q_INVOKABLE bool connectDevice(int idx); + Q_INVOKABLE bool connectDevice(QString devID); + Q_INVOKABLE bool disconnectDevice(QString devID); + Q_INVOKABLE bool disconnectDevice(); + Q_INVOKABLE bool switchTool(QString devID, QString toolName); + Q_INVOKABLE bool switchTool(QString toolName); + Q_INVOKABLE void runScript(QString scriptPath, bool exitApp = true); + Q_INVOKABLE void runScriptList(QStringList scriptPathList, bool exitApp = true); + Q_INVOKABLE void exit(); + Q_INVOKABLE QStringList getTools(); + Q_INVOKABLE QStringList getToolsForPlugin(QString plugin); + Q_INVOKABLE QPair getPreference(QString prfName); + Q_INVOKABLE QMap getPreferences(); + Q_INVOKABLE void setPreference(QString preName, QVariant value); + Q_INVOKABLE void aboutPage(); + Q_INVOKABLE QStringList getPlugins(int idx); + Q_INVOKABLE QStringList getPlugins(QString param, QString cat = "iio"); + Q_INVOKABLE bool getToolBtnState(QString tool); + Q_INVOKABLE bool runTool(QString tool, bool flag); + Q_INVOKABLE bool loadSetup(QString fileName, QString path = QCoreApplication::applicationDirPath()); + Q_INVOKABLE bool saveSetup(QString fileName, QString path = QCoreApplication::applicationDirPath()); + +private: + static bool sortByUUID(const QString &k1, const QString &k2); + const QString getScriptContent(QFile *file); + QStringList availablePlugins(QString param, QString cat, Device *dev); + Device *getDevice(int idx); + ScopyMainWindow *m_w; +}; + +} // namespace scopy + +#endif // SCOPYMAINWINDOW_API_H diff --git a/core/include/core/scopypreferencespage.h b/core/include/core/scopypreferencespage.h new file mode 100644 index 0000000000..14db347fdc --- /dev/null +++ b/core/include/core/scopypreferencespage.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCOPYPREFERENCESPAGE_H +#define SCOPYPREFERENCESPAGE_H + +#include "scopy-core_export.h" + +#include +#include +#include +#include + +namespace scopy { +class SCOPY_CORE_EXPORT ScopyPreferencesPage : public QWidget +{ + Q_OBJECT +public: + ScopyPreferencesPage(QWidget *parent = nullptr); + ~ScopyPreferencesPage(); + QWidget *buildGeneralPreferencesPage(); + void addHorizontalTab(QWidget *w, QString text); + void initSessionDevices(); +public Q_SLOTS: + void updateSessionDevices(QMap devices); +Q_SIGNALS: + void refreshDevicesPressed(); +private Q_SLOTS: + void resetScopyPreferences(); + +private: + QTabWidget *tabWidget; + void initRestartWidget(); + QWidget *restartWidget; + QMap m_connDevices; + QPushButton *m_devRefresh; + QWidget *buildSaveSessionPreference(); + QWidget *buildResetScopyDefaultButton(); + QVBoxLayout *layout; + MenuSectionCollapseWidget *m_autoConnectWidget; + void removeIniFiles(bool backup = true); + void initUI(); +}; +} // namespace scopy +#endif // SCOPYPREFERENCESPAGE_H diff --git a/core/include/core/scopytitlemanager.h b/core/include/core/scopytitlemanager.h new file mode 100644 index 0000000000..f135a8de0a --- /dev/null +++ b/core/include/core/scopytitlemanager.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef SCOPY_SCOPYTITLEMANAGER_H +#define SCOPY_SCOPYTITLEMANAGER_H + +#include +#include + +namespace scopy { +class ScopyTitleManager : public QObject +{ + Q_OBJECT +protected: + ScopyTitleManager(QObject *parent = nullptr); + ~ScopyTitleManager(); + + void buildTitle(); + +public: + ScopyTitleManager(ScopyTitleManager &other) = delete; + void operator=(const ScopyTitleManager &other) = delete; + + static ScopyTitleManager *GetInstance(); + + static void setApplicationName(QString title); + static void clearApplicationName(); + + static void setScopyVersion(QString version); + static void clearScopyVersion(); + + static void setGitHash(QString hash); + static void clearGitHash(); + + static void setIniFileName(QString filename); + static void clearIniFileName(); + + static void clearAll(); + static QString getCurrentTitle(); + + static void setMainWindow(QWidget *window); + +private: + static ScopyTitleManager *pinstance_; + QString m_title; + QString m_version; + QString m_hash; + QString m_filename; + QString m_currentTitle; + QWidget *m_mainWindow; +}; +} // namespace scopy + +#endif // SCOPY_SCOPYTITLEMANAGER_H diff --git a/core/include/core/toolmenu.h b/core/include/core/toolmenu.h new file mode 100644 index 0000000000..d7ee90c99f --- /dev/null +++ b/core/include/core/toolmenu.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef TOOLMENU_H +#define TOOLMENU_H + +#include +#include +#include +#include +#include + +namespace scopy { + +class ToolMenu : public QWidget, public CompositeWidget +{ + Q_OBJECT +public: + ToolMenu(QWidget *parent); + ~ToolMenu(); + + void add(QWidget *w) override; + void add(int index, QString itemId, QWidget *w); + void remove(QWidget *w) override; + int indexOf(QWidget *w); + void colapseAll(); + + QButtonGroup *btnGroup() const; + +private: + void add(int index, QWidget *w); + QString widgetName(QWidget *w); + void onScrollRangeChanged(int min, int max); + + QMap m_widgetMap; + int m_uuid; + QScrollArea *m_scroll; + QVBoxLayout *m_layScroll; + QSpacerItem *m_spacer; + QButtonGroup *m_btnGroup; +}; + +} // namespace scopy + +#endif // TOOLMENU_H diff --git a/core/include/core/toolmenuitem.h b/core/include/core/toolmenuitem.h new file mode 100644 index 0000000000..2dc1a7f8ca --- /dev/null +++ b/core/include/core/toolmenuitem.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef TOOLMENUITEM_H +#define TOOLMENUITEM_H + +#include +#include +#include + +namespace scopy { +class ToolMenuItem : public QPushButton +{ + Q_OBJECT +public: + ToolMenuItem(QString uuid, QString name, QString icon, QWidget *parent = nullptr); + ~ToolMenuItem(); + + QPushButton *getToolRunBtn() const; + + void enableDoubleClick(bool enable); + bool eventFilter(QObject *watched, QEvent *event); + + void setName(QString str); + void setSelected(bool en); + + QString getId() const; +Q_SIGNALS: + void doubleclick(); + +public Q_SLOTS: + void setDisabled(bool disabled); + void updateItem(); + +protected: + void enterEvent(QEvent *event); + void leaveEvent(QEvent *event); + +private: + // QPushButton *m_toolBtn; + CustomPushButton *m_toolRunBtn; + + QString m_uuid; + QString m_name; + QString m_icon; +}; +} // namespace scopy + +#endif // TOOLMENUITEM_H diff --git a/core/include/core/toolmenumanager.h b/core/include/core/toolmenumanager.h new file mode 100644 index 0000000000..930ec2983f --- /dev/null +++ b/core/include/core/toolmenumanager.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef TOOLMENUMANAGER_H +#define TOOLMENUMANAGER_H + +#include "detachedtoolwindowmanager.h" +#include "toolmenu.h" +#include "toolmenuitem.h" + +#include +#include +#include +#include "toolstack.h" +#include +#include "scopy-core_export.h" + +namespace scopy { +class SCOPY_CORE_EXPORT ToolMenuManager : public QObject +{ + Q_OBJECT +public: + ToolMenuManager(ToolStack *ts, DetachedToolWindowManager *dtm, ToolMenu *toolMenu, QObject *parent = nullptr); + ~ToolMenuManager(); + + void addMenuItem(QString deviceId, QString devName, QList tools, int itemIndex = -1); + void removeMenuItem(QString deviceId); + void changeToolListContents(QString deviceId, QList tools); + + void showMenuItem(QString id); + void hideMenuItem(QString id); + +public Q_SLOTS: + void deviceConnected(QString id); + void deviceDisconnected(QString id); + void onDisplayNameChanged(QString id, QString devName); + +Q_SIGNALS: + void requestToolSelect(QString id); + +private Q_SLOTS: + void updateTool(QWidget *old); + void updateToolAttached(bool oldAttach, ToolMenuItem *toolMenuItem); + +private: + void loadToolAttachedState(ToolMenuEntry *tme); + void saveToolAttachedState(ToolMenuEntry *tme); + void detachSuccesful(ToolMenuItem *toolMenuItem); + void attachSuccesful(ToolMenuItem *toolMenuItem); + void showTool(ToolMenuItem *toolMenuItem); + void selectTool(ToolMenuItem *toolMenuItem, bool on); + void setTmeAttached(ToolMenuEntry *tme); + MenuSectionCollapseWidget *createMenuSectionItem(QString deviceName, QString uri = ""); + ToolMenuItem *createToolMenuItem(ToolMenuEntry *tme, QWidget *parent = nullptr); + + QString m_prevItem; + QStringList m_connectedDev; + ToolStack *m_ts; + DetachedToolWindowManager *m_dtm; + ToolMenu *m_toolMenu; + QMap m_itemMap; +}; +} // namespace scopy + +#endif // TOOLMENUMANAGER_H diff --git a/core/include/core/toolstack.h b/core/include/core/toolstack.h new file mode 100644 index 0000000000..209393af5d --- /dev/null +++ b/core/include/core/toolstack.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef TOOLSTACK_H +#define TOOLSTACK_H + +#include "gui/mapstackedwidget.h" +#include "scopy-core_export.h" + +#include + +namespace Ui { +class SCOPY_CORE_EXPORT ToolStack; +} +/** + * @brief The ToolStack class + */ +namespace scopy { +class SCOPY_CORE_EXPORT ToolStack : public MapStackedWidget +{ + Q_OBJECT + +public: + explicit ToolStack(QWidget *parent = nullptr); + ~ToolStack(); +}; +} // namespace scopy + +#endif // TOOLSTACK_H diff --git a/core/include/core/translationsrepository.h b/core/include/core/translationsrepository.h new file mode 100644 index 0000000000..4e06d09ccd --- /dev/null +++ b/core/include/core/translationsrepository.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef TRANSLATIONS_CONTROLLER +#define TRANSLATIONS_CONTROLLER + +#include "scopy-core_export.h" + +#include + +namespace scopy { +class SCOPY_CORE_EXPORT TranslationsRepository : public QObject +{ + Q_OBJECT +protected: + TranslationsRepository(QObject *parent = nullptr); + ~TranslationsRepository(); + +public: + static TranslationsRepository *GetInstance(); + QString getTranslationsPath(); + QStringList getLanguages(); + void loadTranslations(QString language); + +private: + static TranslationsRepository *pinstance_; +}; +} // namespace scopy + +#endif // TRANSLATIONS_CONTROLLER diff --git a/core/include/core/versioncheckmessage.h b/core/include/core/versioncheckmessage.h new file mode 100644 index 0000000000..8eaa37d019 --- /dev/null +++ b/core/include/core/versioncheckmessage.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SCOPY_VERSIONCHECKMESSAGE_H +#define SCOPY_VERSIONCHECKMESSAGE_H + +#include +#include + +#include + +namespace scopy { +class VersionCheckMessage : public QWidget +{ + Q_OBJECT + +public: + explicit VersionCheckMessage(QWidget *parent = nullptr); + ~VersionCheckMessage(); + +Q_SIGNALS: + void setCheckVersion(bool allowed); + +private Q_SLOTS: + void saveCheckVersion(bool allowed); +}; +} // namespace scopy + +#endif // SCOPY_VERSIONCHECKMESSAGE_H diff --git a/core/src/application_restarter.cpp b/core/src/application_restarter.cpp new file mode 100644 index 0000000000..1f175670ce --- /dev/null +++ b/core/src/application_restarter.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "application_restarter.h" + +#include +#include +#include + +#ifdef __ANDROID__ +#include +#endif + +using namespace scopy; + +ApplicationRestarter *ApplicationRestarter::pinstance_{nullptr}; + +ApplicationRestarter::ApplicationRestarter(const QString &executable) + : m_executable(executable) + , m_currentPath(QDir::currentPath()) + , m_restart(false) +{ + pinstance_ = this; +} + +ApplicationRestarter *ApplicationRestarter::GetInstance() { return pinstance_; } + +void ApplicationRestarter::setArguments(const QStringList &arguments) { m_arguments = arguments; } + +QStringList ApplicationRestarter::getArguments() const { return m_arguments; } + +int ApplicationRestarter::restart(int exitCode) +{ + if(m_restart) { +#ifdef __ANDROID__ + QAndroidJniObject activity = QtAndroid::androidActivity(); + activity.callMethod("restart"); +#else + QProcess::startDetached(m_executable, m_arguments, m_currentPath); +#endif + } + + return exitCode; +} + +void ApplicationRestarter::triggerRestart() +{ + GetInstance()->m_restart = true; + qApp->exit(); +} diff --git a/core/src/browsemenu.cpp b/core/src/browsemenu.cpp new file mode 100644 index 0000000000..576496570b --- /dev/null +++ b/core/src/browsemenu.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "browsemenu.h" +#include "style.h" + +#include +#include + +using namespace scopy; + +BrowseMenu::BrowseMenu(QWidget *parent) + : QWidget(parent) + , m_collapsed(false) +{ + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + QVBoxLayout *lay = new QVBoxLayout(this); + lay->setMargin(0); + lay->setSpacing(0); + lay->setAlignment(Qt::AlignTop); + setLayout(lay); + + QWidget *menuHeader = createHeader(this); + + m_content = new QWidget(this); + m_content->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + m_contentLay = new QVBoxLayout(m_content); + m_contentLay->setMargin(0); + m_contentLay->setSpacing(0); + m_content->setLayout(m_contentLay); + + m_spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Preferred); + m_contentLay->addSpacerItem(m_spacer); + + m_toolMenu = new ToolMenu(m_content); + QButtonGroup *btnGroup = m_toolMenu->btnGroup(); + m_toolMenu->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + + QPushButton *homeBtn = new QPushButton(tr("Home"), m_content); + Style::setStyle(homeBtn, style::properties::button::toolButton); + homeBtn->setIcon( + QIcon(":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + "/icons/tool_home.svg")); + homeBtn->setFixedHeight(Style::getDimension(json::global::unit_4)); + homeBtn->setCheckable(true); + homeBtn->setIconSize(QSize(32, 32)); + homeBtn->setStyleSheet("text-align: left"); + connect(homeBtn, &QPushButton::clicked, this, [=]() { Q_EMIT requestTool("home"); }); + + QWidget *saveLoadWidget = new QWidget(m_content); + saveLoadWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + saveLoadWidget->setLayout(new QHBoxLayout(saveLoadWidget)); + saveLoadWidget->layout()->setSpacing(0); + saveLoadWidget->layout()->setMargin(0); + + QPushButton *saveBtn = createBtn( + "Save", ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + "/icons/save.svg", + saveLoadWidget); + connect(saveBtn, &QPushButton::clicked, this, &BrowseMenu::requestSave); + + QPushButton *loadBtn = createBtn( + "Load", ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + "/icons/load.svg", + saveLoadWidget); + connect(loadBtn, &QPushButton::clicked, this, &BrowseMenu::requestLoad); + + saveLoadWidget->layout()->addWidget(saveBtn); + saveLoadWidget->layout()->addWidget(loadBtn); + + QPushButton *preferencesBtn = createBtn("Preferences", + ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + + "/icons/preferences.svg", + m_content); + connect(preferencesBtn, &QPushButton::clicked, this, [=]() { Q_EMIT requestTool("preferences"); }); + preferencesBtn->setCheckable(true); + + QPushButton *aboutBtn = createBtn( + "About", ":/gui/icons/" + Style::getAttribute(json::theme::icon_theme_folder) + "/icons/info.svg", + m_content); + connect(aboutBtn, &QPushButton::clicked, this, [=]() { Q_EMIT requestTool("about"); }); + aboutBtn->setCheckable(true); + + QLabel *logo = createScopyLogo(m_content); + + btnGroup->addButton(homeBtn); + btnGroup->addButton(preferencesBtn); + btnGroup->addButton(aboutBtn); + + add(createHLine(m_content), "headerLine", MA_TOPLAST); + add(homeBtn, "homeBtn", MA_TOPLAST); + add(createHLine(m_content), "toolMenuLine1", MA_TOPLAST); + add(m_toolMenu, "toolMenu", MA_TOPLAST); + + add(createHLine(m_content), "toolMenuLine2", MA_BOTTOMLAST); + add(saveLoadWidget, "saveLoad", MA_BOTTOMLAST); + add(preferencesBtn, "preferencesBtn", MA_BOTTOMLAST); + add(aboutBtn, "aboutBtn", MA_BOTTOMLAST); + add(logo, "logo", MA_BOTTOMLAST); + + lay->addWidget(menuHeader); + lay->addWidget(m_content); +} + +BrowseMenu::~BrowseMenu() {} + +ToolMenu *BrowseMenu::toolMenu() const { return m_toolMenu; } + +void BrowseMenu::add(QWidget *w, QString name, MenuAlignment position) +{ + int spacerIndex = m_contentLay->indexOf(m_spacer); + switch(position) { + + case MA_TOPLAST: + m_contentLay->insertWidget(spacerIndex, w); + break; + case MA_BOTTOMLAST: + m_contentLay->insertWidget(-1, w); + break; + } +} + +void BrowseMenu::toggleCollapsed() +{ + m_collapsed = !m_collapsed; + m_btnCollapse->setHidden(m_collapsed); + m_content->setHidden(m_collapsed); + Q_EMIT collapsed(m_collapsed); +} + +QPushButton *BrowseMenu::createBtn(QString name, QString iconPath, QWidget *parent) +{ + QPushButton *btn = new QPushButton(parent); + btn->setIcon(QIcon(iconPath)); + btn->setText(tr(name.toStdString().c_str())); + Style::setStyle(btn, style::properties::button::toolButton); + return btn; +} + +QFrame *BrowseMenu::createHLine(QWidget *parent) +{ + QFrame *line = new QFrame(parent); + line->setFrameShape(QFrame::HLine); + line->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + Style::setStyle(line, style::properties::widget::bottomBorder); + return line; +} + +QWidget *BrowseMenu::createHeader(QWidget *parent) +{ + QWidget *menuHeader = new QWidget(parent); + menuHeader->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + QHBoxLayout *headerLay = new QHBoxLayout(menuHeader); + headerLay->setSpacing(0); + headerLay->setMargin(0); + QPushButton *btnCollapseMini = new QPushButton(menuHeader); + Style::setStyle(btnCollapseMini, style::properties::widget::toolMenu); + Style::setStyle(btnCollapseMini, style::properties::button::toolButton); + btnCollapseMini->setCheckable(true); + btnCollapseMini->setFixedSize(Style::getDimension(json::global::unit_4), + Style::getDimension(json::global::unit_4)); + headerLay->addWidget(btnCollapseMini); + + m_btnCollapse = new QPushButton(menuHeader); + Style::setStyle(m_btnCollapse, style::properties::widget::toolMenuLogo); + Style::setStyle(m_btnCollapse, style::properties::widget::notInteractive); + + headerLay->addWidget(m_btnCollapse); + connect(btnCollapseMini, &QPushButton::clicked, this, &BrowseMenu::toggleCollapsed); + + return menuHeader; +} + +QLabel *BrowseMenu::createScopyLogo(QWidget *parent) +{ + QLabel *logo = new QLabel(m_content); + Style::setStyle(logo, style::properties::widget::logo); + return logo; +} + +#include "moc_browsemenu.cpp" diff --git a/core/src/cmdlinehandler.cpp b/core/src/cmdlinehandler.cpp new file mode 100644 index 0000000000..730d777dfe --- /dev/null +++ b/core/src/cmdlinehandler.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "cmdlinehandler.h" + +using namespace scopy; + +FILE *CmdLineHandler::logFile_{nullptr}; +bool CmdLineHandler::tempLogFile_{false}; + +int CmdLineHandler::handle(QCommandLineParser &parser, ScopyMainWindow_API &scopyApi) +{ + bool acceptLicense = parser.isSet("accept-license"); + if(acceptLicense) { + scopyApi.acceptLicense(); + } + + QString param = parser.value("connect"); + if(!param.isEmpty()) { + QString deviceID = ""; + deviceID = scopyApi.addDevice("", param); + scopyApi.connectDevice(deviceID); + QString tool = parser.value("tool"); + if(!tool.isEmpty()) { + scopyApi.switchTool(deviceID, tool); + } + } + + bool keepRunning = parser.isSet("keep-running"); + if(keepRunning) { + qInfo() << "keep-running option is only useful with a script!"; + } + + QString scriptPath = parser.value("script"); + if(!scriptPath.isEmpty()) { + bool exitApp = !keepRunning; + QMetaObject::invokeMethod(&scopyApi, "runScript", Qt::QueuedConnection, Q_ARG(QString, scriptPath), + Q_ARG(bool, exitApp)); + } + + QStringList scriptListPath = parser.values("script-list"); + if(!scriptListPath.isEmpty()) { + bool exitApp = !keepRunning; + QMetaObject::invokeMethod(&scopyApi, "runScriptList", Qt::QueuedConnection, + Q_ARG(QStringList, scriptListPath), Q_ARG(bool, exitApp)); + } + return EXIT_SUCCESS; +} + +void CmdLineHandler::withLogFileOption(QCommandLineParser &parser) +{ + QString fileName = parser.value("logfile"); + if(fileName.isEmpty()) { + fileName = scopy::config::tempLogFilePath(); + tempLogFile_ = true; + } + logFile_ = fopen(fileName.toStdString().c_str(), "w"); + if(logFile_) { + qInstallMessageHandler(logOutputHandler); + } +} + +void CmdLineHandler::closeLogFile() +{ + if(logFile_) { + fclose(logFile_); + logFile_ = nullptr; + removeTempLogFile(); + } +} + +void CmdLineHandler::removeTempLogFile() +{ + if(tempLogFile_) { + QFile::remove(scopy::config::tempLogFilePath()); + } +} + +void CmdLineHandler::logOutputHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) +{ + QString message = qFormatLogMessage(type, context, msg); + fprintf(stderr, "%s\n", message.toStdString().c_str()); + if(logFile_) { + fprintf(logFile_, "%s\n", message.toStdString().c_str()); + } +} diff --git a/core/src/crashreport.cpp b/core/src/crashreport.cpp new file mode 100644 index 0000000000..40aaf45370 --- /dev/null +++ b/core/src/crashreport.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "crashreport.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "logging_categories.h" + +Q_LOGGING_CATEGORY(CAT_CRASH_REPORT, "CrashReport") + +using namespace scopy; + +QString CrashReport::tmpFilePath_; + +void CrashReport::signalHandler(int) +{ + ::signal(SIGSEGV, SIG_DFL); + ::signal(SIGABRT, SIG_DFL); + qSetMessagePattern("[ " +#ifdef QCRITICAL_BACKTRACE_STR + QCRITICAL_BACKTRACE_STR " " +#endif +#ifdef QDEBUG_LOG_TIME + QDEBUG_LOG_TIME_STR +#endif + " ] " + " - " + "%{message}"); + + qCritical(CAT_CRASH_REPORT) << "Scopy finished with error\n"; +} + +void CrashReport::initSignalHandler() +{ + tmpFilePath_ = scopy::config::tempLogFilePath(); + QString qd = scopy::config::settingsFolderPath(); + + QFileInfo previousLog(tmpFilePath_); + + if(previousLog.exists()) { + qInfo(CAT_CRASH_REPORT) << "Found existing crash stack trace"; + QString dumpDateAndTime = + previousLog.lastModified().toString(Qt::ISODate).replace("T", "--").replace(":", "-"); + QString currentLog = QDir::cleanPath(qd + "/" + "ScopyCrashDump--" + dumpDateAndTime + ".log"); + bool renamed = QFile::rename(previousLog.filePath(), currentLog); + if(renamed) { + qInfo(CAT_CRASH_REPORT) << "Successfully renamed crash log to: " << currentLog; + } + QFile::remove(tmpFilePath_); + } + + signal(SIGILL, &signalHandler); + signal(SIGSEGV, &signalHandler); + signal(SIGABRT, &signalHandler); + signal(SIGFPE, &signalHandler); +} diff --git a/core/src/detachedtoolwindow.cpp b/core/src/detachedtoolwindow.cpp new file mode 100644 index 0000000000..79b1a1e493 --- /dev/null +++ b/core/src/detachedtoolwindow.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "detachedtoolwindow.h" + +#include +#include +#include +#include +#include + +#include + +Q_LOGGING_CATEGORY(CAT_DETACHEDTOOL, "DetachedToolWindow") + +using namespace scopy; +DetachedToolWindow::DetachedToolWindow(QWidget *parent, ToolMenuEntry *tme) + : QWidget(parent) +{ + + this->tme = tme; + + QHBoxLayout *lay = new QHBoxLayout(this); + lay->setSpacing(0); + lay->setMargin(0); + w = tme->tool(); + setWindowIcon(QApplication::windowIcon()); + setWindowTitle("Scopy - " + tme->pluginName() + " - " + tme->name() + " - " + tme->param()); + + lay->addWidget(w); + loadToolGeometry(tme, this); + Style::setStyle(this, style::properties::widget::basicBackground); + tme->tool()->show(); + show(); +} + +DetachedToolWindow::~DetachedToolWindow() +{ + saveToolGeometry(tme, this); + w->setParent(nullptr); +} + +void DetachedToolWindow::closeEvent(QCloseEvent *event) +{ + saveToolGeometry(tme, w); + tme->setAttached(true); +} + +void DetachedToolWindow::saveToolGeometry(ToolMenuEntry *tme, QWidget *w) +{ + Preferences *p = Preferences::GetInstance(); + if(!p->get("general_save_attached").toBool()) + return; + QString prefId; + if(w) { + prefId = tme->id() + "_geometry"; + QRect geometry = w->geometry(); + p->set(prefId, geometry); + qDebug(CAT_DETACHEDTOOL) << "Saving " << prefId << " " << geometry; + } +} + +void DetachedToolWindow::loadToolGeometry(ToolMenuEntry *tme, QWidget *w) +{ + Preferences *p = Preferences::GetInstance(); + if(!p->get("general_save_attached").toBool()) + return; + QString prefId; + + if(w && !tme->attached()) { + prefId = tme->id() + "_geometry"; + QRect geometry = p->get(prefId).toRect(); + if(!geometry.isNull()) + w->setGeometry(geometry); + + qDebug(CAT_DETACHEDTOOL) << "Loading " << prefId << tme->tool()->geometry(); + } +} + +#include "moc_detachedtoolwindow.cpp" diff --git a/core/src/detachedtoolwindowmanager.cpp b/core/src/detachedtoolwindowmanager.cpp new file mode 100644 index 0000000000..5b6d7fe69e --- /dev/null +++ b/core/src/detachedtoolwindowmanager.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "detachedtoolwindowmanager.h" + +using namespace scopy; + +DetachedToolWindowManager::DetachedToolWindowManager(QObject *parent) + : QObject(parent) +{} + +DetachedToolWindowManager::~DetachedToolWindowManager() {} + +void DetachedToolWindowManager::add(QString id, ToolMenuEntry *tme) +{ + if(map.contains(id)) { + delete map.take(id); + } + map.insert(id, new DetachedToolWindow(nullptr, tme)); +} + +bool DetachedToolWindowManager::remove(QString id) +{ + if(map.contains(id)) { + delete map.take(id); + return true; + } + return false; +} + +void DetachedToolWindowManager::show(QString id) +{ + if(map.contains(id)) { + map[id]->raise(); + map[id]->show(); + } +} + +QWidget *DetachedToolWindowManager::getWidget(QString key) +{ + if(map.contains(key)) + return map[key]; + return nullptr; +} + +bool DetachedToolWindowManager::contains(QString key) { return map.contains(key); } + +#include "moc_detachedtoolwindowmanager.cpp" diff --git a/core/src/deviceautoconnect.cpp b/core/src/deviceautoconnect.cpp new file mode 100644 index 0000000000..7bf787128f --- /dev/null +++ b/core/src/deviceautoconnect.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "deviceautoconnect.h" + +#include +#include + +using namespace scopy; + +void DeviceAutoConnect::initPreferences() { Preferences::init("autoconnect_devices", QMap()); } + +void DeviceAutoConnect::addDevice(QString uri, QStringList plugins) +{ + QMap devicesMap = Preferences::get("autoconnect_devices").toMap(); + if(!devicesMap.contains(uri) && !plugins.isEmpty()) { + devicesMap[uri] = plugins.join(";"); + Preferences::set("autoconnect_devices", devicesMap); + } +} + +void DeviceAutoConnect::removeDevice(QString uri) +{ + QMap devicesMap = Preferences::get("autoconnect_devices").toMap(); + QString prefId = uri + "_sticky"; + if(devicesMap.contains(uri) && !Preferences::get(prefId).toBool()) { + devicesMap.remove(uri); + Preferences::set("autoconnect_devices", devicesMap); + } +} + +void DeviceAutoConnect::clear() +{ + const QStringList uris = Preferences::get("autoconnect_devices").toMap().keys(); + for(const QString &key : uris) { + removeDevice(key); + } +} + +bool DeviceAutoConnect::isAutoConnectEnabled(QString uri) +{ + QMap devicesMap = Preferences::get("autoconnect_devices").toMap(); + return devicesMap.contains(uri); +} diff --git a/core/src/devicebrowser.cpp b/core/src/devicebrowser.cpp new file mode 100644 index 0000000000..5ae442c989 --- /dev/null +++ b/core/src/devicebrowser.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "devicebrowser.h" + +#include "deviceicon.h" +#include "deviceiconimpl.h" +#include "dynamicWidget.h" +#include "style_attributes.h" +#include "stylehelper.h" + +#include "ui_devicebrowser.h" + +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_DEVBROWSER, "DeviceBrowser") + +using namespace scopy; +DeviceBrowser::DeviceBrowser(QWidget *parent) + : QWidget(parent) + , ui(new Ui::DeviceBrowser) +{ + qDebug(CAT_DEVBROWSER) << "ctor"; + ui->setupUi(this); + this->setFixedHeight(185); + + auto dbm = ui->wDeviceBrowserMenu; + layout = new QHBoxLayout(dbm); + + initBtns(); + + connect(ui->btnHome, SIGNAL(clicked()), this, SLOT(forwardRequestDeviceWithDirection())); + connect(ui->btnAdd, SIGNAL(clicked()), this, SLOT(forwardRequestDeviceWithDirection())); + connect(this, SIGNAL(requestDevice(QString, int)), this, SLOT(updateSelectedDeviceIdx(QString))); +} + +DeviceBrowser::~DeviceBrowser() +{ + qDebug(CAT_DEVBROWSER) << "dtor"; + delete ui; +} + +void DeviceBrowser::initBtns() +{ + bg = new QButtonGroup(this); + bg->addButton(ui->btnAdd); + bg->addButton(ui->btnHome); + + ui->btnHome->setProperty(devBrowserId, "home"); + ui->btnHome->setIcon(Style::getPixmap(":/gui/icons/home.svg", Style::getColor(json::theme::content_inverse))); + Style::setStyle(ui->btnHome, style::properties::button::squareIconBrowserButton); + + ui->btnAdd->setProperty(devBrowserId, "add"); + ui->btnAdd->setIcon(Style::getPixmap(":/gui/icons/add.svg", Style::getColor(json::theme::content_inverse))); + Style::setStyle(ui->btnAdd, style::properties::button::squareIconBrowserButton); + + list.append(ui->btnHome); + list.append(ui->btnAdd); + + Style::setStyle(ui->containerHome, style::properties::frame::frameContainer, "selected"); + ui->btnHome->setChecked(true); + currentIdx = 0; +} + +QAbstractButton *DeviceBrowser::getDeviceWidgetFor(QString id) +{ + for(auto &&w : list) { + if(w->property(devBrowserId) == id) + return w; + } + + return nullptr; +} + +void DeviceBrowser::addDevice(QString id, Device *d, int position) +{ + qInfo(CAT_DEVBROWSER) << "adding device " << id; + auto w = dynamic_cast(buildDeviceIcon(d, this)); + w->setProperty(devBrowserId, id); + layout->insertWidget(position, w); + bg->addButton(w); + if(position == -1) + list.append(w); + else + list.insert(position, w); + + connect(w, &QAbstractButton::clicked, this, &DeviceBrowser::forwardRequestDeviceWithDirection); +} + +void DeviceBrowser::removeDevice(QString id) +{ + qInfo(CAT_DEVBROWSER) << "removing device " << id; + QAbstractButton *w = getDeviceWidgetFor(id); + layout->removeWidget(w); + bg->removeButton(w); + int idx = getIndexOfId(id); + list.removeAt(idx); + disconnect(w, &QAbstractButton::clicked, this, nullptr); // disconnect all signals connected to this instance + delete(w); + + if(currentIdx == idx) { // removed currently selected device + currentIdx = 0; + Q_EMIT requestDevice("home", -1); + } else if(currentIdx > idx) { + currentIdx--; + } +} + +int DeviceBrowser::getIndexOfId(QString k) +{ + for(int i = 0; i < list.size(); i++) { + if(list[i]->property(devBrowserId) == k) + return i; + } + return -1; +} + +QString DeviceBrowser::getIdOfIndex(int idx) { return (list[idx]->property(devBrowserId).toString()); } + +void DeviceBrowser::nextDevice() +{ + int maxIdx = list.size(); + int nextIdx = currentIdx; + + do { // find next visible and enabled button + nextIdx = (nextIdx + 1) % maxIdx; + nextIdx = (nextIdx < 0) ? nextIdx + maxIdx : nextIdx; // wrap around negative + } while(!(list.at(nextIdx)->isVisible() && list.at(nextIdx)->isEnabled())); + + QString nextId = getIdOfIndex(nextIdx); + Q_EMIT requestDevice(nextId, 1); // start animation + list[nextIdx]->setChecked(true); // set checked afterwards +} + +void DeviceBrowser::prevDevice() +{ + int maxIdx = list.size(); + int nextIdx = currentIdx; + + do { // find next visible and enabled button + nextIdx = (nextIdx - 1) % maxIdx; + nextIdx = (nextIdx < 0) ? nextIdx + maxIdx : nextIdx; // wrap around negative + } while(!(list.at(nextIdx)->isVisible() && list.at(nextIdx)->isEnabled())); + + QString nextId = getIdOfIndex(nextIdx); + Q_EMIT requestDevice(nextId, -1); // start animation + list[nextIdx]->setChecked(true); // set checked afterwards +} + +void DeviceBrowser::forwardRequestDeviceWithDirection() +{ + QString id = QObject::sender()->property(devBrowserId).toString(); + int idx = getIndexOfId(id); + int direction = currentIdx - idx; + Q_EMIT requestDevice(id, direction); +} + +void DeviceBrowser::updateSelectedDeviceIdx(QString k) +{ + int prevIdx = currentIdx; // local, just for debug + currentIdx = getIndexOfId(k); + + QWidget *prevDevice = getDeviceWidgetFor(getIdOfIndex(prevIdx)); + QWidget *currentDevice = getDeviceWidgetFor(getIdOfIndex(currentIdx)); + + // hackish -- the btnHome and btnAdd already have a background color so their container must display the shadow + if(currentDevice == ui->btnHome) + currentDevice = ui->containerHome; + + if(currentDevice == ui->btnAdd) + currentDevice = ui->containerAdd; + + if(prevDevice == ui->btnHome) + prevDevice = ui->containerHome; + + if(prevDevice == ui->btnAdd) + prevDevice = ui->containerAdd; + + Style::setStyle(prevDevice, style::properties::frame::frameContainer, "idle", true); + Style::setStyle(currentDevice, style::properties::frame::frameContainer, "selected", true); + + qDebug(CAT_DEVBROWSER) << "prev: " + << "[" << prevIdx << "] -" << getIdOfIndex(prevIdx) << "-> current: " + << "[" << currentIdx << "] -" << getIdOfIndex(currentIdx); +} + +void DeviceBrowser::connectingDevice(QString id) +{ + auto w = dynamic_cast(getDeviceWidgetFor(id)); + w->setConnecting(true); +} + +void DeviceBrowser::connectDevice(QString id) +{ + auto w = dynamic_cast(getDeviceWidgetFor(id)); + w->setConnected(true); +} + +void DeviceBrowser::disconnectDevice(QString id) +{ + auto w = dynamic_cast(getDeviceWidgetFor(id)); + w->setConnected(false); +} + +DeviceIcon *DeviceBrowser::buildDeviceIcon(Device *d, QWidget *parent) +{ + DeviceIconImpl *devIcon = new DeviceIconImpl(d, parent); + connect(devIcon, &DeviceIconImpl::displayNameChanged, this, + [this, d](QString newName) { Q_EMIT displayNameChanged(d->id(), newName); }); + return devIcon; +} + +/* + auto &&is = ui->wInfoPageStack; + auto &&hc = is->getHomepageControls(); + + is->add("home",new ScopyHomeInfoPage()); + is->add("add", new ScopyHomeAddPage()); + is->add("dev1", new QLabel("dev1")); + is->add("dev2", new QLabel("dev2")); + + auto &&db = ui->wDeviceBrowser; + QPushButton *w1 = new QPushButton("dev1"); + QPushButton *w2 = new QPushButton("dev2"); + w1->setCheckable(true); + w2->setCheckable(true); + db->addDevice("dev1",w1); + db->addDevice("dev2",w2); + w1->setVisible(false); + */ + +/* + ui->setupUi(this); + auto &&is = ui->wInfoPageStack; + auto &&hc = is->getHomepageControls(); + + is->add("home",new ScopyHomeInfoPage()); + is->add("add", new ScopyHomeAddPage()); + is->add("dev1", new QLabel("dev1")); + is->add("dev2", new QLabel("dev2")); + + auto &&db = ui->wDeviceBrowser; + DeviceIcon *w1 = new DeviceIcon("dev1","uri",this); + DeviceIcon *w2 = new DeviceIcon("dev2","uri2",this); + w1->setCheckable(true); + w2->setCheckable(true); + db->addDevice("dev1",w1); + db->addDevice("dev2",w2); + w1->setVisible(false); + */ + +#include "moc_devicebrowser.cpp" diff --git a/core/src/devicefactory.cpp b/core/src/devicefactory.cpp new file mode 100644 index 0000000000..92bf2c9c89 --- /dev/null +++ b/core/src/devicefactory.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "devicefactory.h" + +#include "iiodeviceimpl.h" + +using namespace scopy; + +DeviceImpl *DeviceFactory::build(QString param, PluginManager *pm, QString category, QObject *parent) +{ + QString cat = category.toLower(); + if(cat.compare("iio") == 0) { + return new IIODeviceImpl(param, pm, parent); + } else { + return new DeviceImpl(param, pm, category, parent); + } +} + +#include "moc_devicefactory.cpp" diff --git a/core/src/deviceiconimpl.cpp b/core/src/deviceiconimpl.cpp new file mode 100644 index 0000000000..4ba14ba581 --- /dev/null +++ b/core/src/deviceiconimpl.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "deviceiconimpl.h" + +#include "gui/dynamicWidget.h" + +#include "ui_devicebutton.h" + +#include +#include +#include +#include +#include +#include + +using namespace scopy; +DeviceIconImpl::DeviceIconImpl(Device *d, QWidget *parent) + : DeviceIcon{parent} +{ + ui = new Ui::DeviceButton; + ui->setupUi(this); + ui->description->setText(d->displayParam()); + ui->name->setText(d->displayName()); + ui->name->setStyleSheet("border: none;"); + ui->name->setReadOnly(true); + ui->name->home(false); + connect(ui->name, &QLineEdit::editingFinished, this, &DeviceIconImpl::onEditFinished); + createPenBtn(); + + ui->iconPlaceHolder->layout()->addWidget(d->icon()); + ui->iconPlaceHolder->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setStyleSheet("QWidget { background-color: transparent; }"); + Style::setStyle(this, style::properties::widget::deviceIcon, true, true); + + setCheckable(true); +} + +DeviceIconImpl::~DeviceIconImpl() {} + +void DeviceIconImpl::setConnecting(bool val) +{ + setDynamicProperty(ui->line, "connected", false); + setDynamicProperty(ui->line, "connecting", val); + // ensurePolished(); +} + +void DeviceIconImpl::setConnected(bool val) +{ + setDynamicProperty(ui->line, "connecting", false); + setDynamicProperty(ui->line, "connected", val); +} + +void DeviceIconImpl::createPenBtn() +{ + QPushButton *penBtn = new QPushButton(); + penBtn->setMaximumSize(20, 20); + penBtn->setStyleSheet("QPushButton {" + "background-color: transparent;" + "border: 0px;" + "}" + "QPushButton:hover {" + "border-image: url(:/gui/icons/edit_pen.svg);" + "}"); + connect(penBtn, &QPushButton::clicked, this, &DeviceIconImpl::onPenBtnPressed); + HoverWidget *penHover = new HoverWidget(penBtn, ui->name, this); + penHover->setStyleSheet("background-color: transparent; border: 0px;"); + penHover->setAnchorPos(HoverPosition::HP_RIGHT); + penHover->setContentPos(HoverPosition::HP_RIGHT); + penHover->setVisible(true); + penHover->raise(); +} + +void DeviceIconImpl::onPenBtnPressed() +{ + ui->name->setReadOnly(false); + ui->name->end(false); + // ui->name->setEnabled(true); + ui->name->setFocus(); +} + +void DeviceIconImpl::onEditFinished() +{ + ui->name->setReadOnly(true); + ui->name->end(false); + // ui->name->setEnabled(false); + Q_EMIT displayNameChanged(ui->name->text()); +} + +#include "moc_deviceiconimpl.cpp" diff --git a/core/src/deviceimpl.cpp b/core/src/deviceimpl.cpp new file mode 100644 index 0000000000..67d6c9d01d --- /dev/null +++ b/core/src/deviceimpl.cpp @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "deviceimpl.h" + +#include "logging_categories.h" +#include "pluginbase/preferences.h" +#include "qboxlayout.h" +#include "qpushbutton.h" +#include "qscrollarea.h" + +#include "ui_devicepage.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_DEVICEIMPL, "Device") + +namespace scopy { +DeviceImpl::DeviceImpl(QString param, PluginManager *p, QString category, QObject *parent) + : QObject{parent} + , m_param(param) + , m_category(category) + , p(p) +{ + m_state = DEV_INIT; + m_id = "dev_" + category + "_" + param + "_" + scopy::config::getUuid(); + qDebug(CAT_DEVICEIMPL) << m_param << "ctor"; +} + +void DeviceImpl::init() +{ + QElapsedTimer timer; + timer.start(); + m_plugins = p->getCompatiblePlugins(m_param, m_category); + for(Plugin *p : qAsConst(m_plugins)) { + QObject *obj = dynamic_cast(p); + if(obj) { + obj->setParent(this); + } else { + qWarning(CAT_DEVICEIMPL, "Plugin not a QObject"); + } + } + qInfo(CAT_BENCHMARK) << this->displayName() << " init took: " << timer.elapsed() << "ms"; +} + +void DeviceImpl::preload() +{ + for(auto &p : m_plugins) { + p->preload(); + } +} + +void DeviceImpl::loadPlugins() +{ + QElapsedTimer timer; + timer.start(); + removeDisabledPlugins(); + preload(); + loadName(); + loadIcons(); + loadBadges(); + loadPages(); + loadToolList(); + if(m_plugins.isEmpty()) { + connbtn->hide(); + } + for(auto &p : m_plugins) { + connect(dynamic_cast(p), SIGNAL(connectDevice()), this, SLOT(connectDev())); + connect(dynamic_cast(p), SIGNAL(disconnectDevice()), this, SLOT(disconnectDev())); + connect(dynamic_cast(p), SIGNAL(toolListChanged()), this, SIGNAL(toolListChanged())); + connect(dynamic_cast(p), SIGNAL(restartDevice()), this, SIGNAL(requestedRestart())); + connect(dynamic_cast(p), SIGNAL(requestToolByUuid(QString)), this, + SIGNAL(requestTool(QString))); + p->postload(); + } + m_state = DEV_IDLE; + qInfo(CAT_BENCHMARK) << this->displayName() << " plugins load took: " << timer.elapsed() << "ms"; +} + +void DeviceImpl::unloadPlugins() +{ + QElapsedTimer timer; + timer.start(); + QList::const_iterator pI = m_plugins.constEnd(); + while(pI != m_plugins.constBegin()) { + --pI; + disconnect(dynamic_cast(*pI), SIGNAL(connectDevice()), this, SLOT(connectDev())); + disconnect(dynamic_cast(*pI), SIGNAL(disconnectDevice()), this, SLOT(disconnectDev())); + disconnect(dynamic_cast(*pI), SIGNAL(toolListChanged()), this, SIGNAL(toolListChanged())); + disconnect(dynamic_cast(*pI), SIGNAL(restartDevice()), this, SIGNAL(requestedRestart())); + disconnect(dynamic_cast(*pI), SIGNAL(requestToolByUuid(QString)), this, + SIGNAL(requestTool(QString))); + (*pI)->unload(); + delete(*pI); + } + m_plugins.clear(); + qInfo(CAT_BENCHMARK) << this->displayName() << " plugins unload took: " << timer.elapsed() << "ms"; +} + +bool DeviceImpl::verify() { return true; } + +QMap DeviceImpl::readDeviceInfo() +{ + QMap map; + return map; +} + +void DeviceImpl::removeDisabledPlugins() +{ + QMutableListIterator i(m_plugins); + while(i.hasNext()) { + if(i.next()->enabled() == false) + i.remove(); + } +} + +void DeviceImpl::loadName() +{ + if(m_plugins.count()) { + m_displayName = m_plugins[0]->displayName(); + m_displayParam = m_plugins[0]->displayParam(); + } else { + m_displayName = "NO_PLUGIN"; + } +} + +void DeviceImpl::loadIcons() +{ + m_icon = new QWidget(); + m_icon->setFixedHeight(100); + m_icon->setFixedWidth(100); + new QHBoxLayout(m_icon); + for(auto &p : m_plugins) { + if(p->loadIcon()) { + m_icon->layout()->addWidget(p->icon()); + return; + } + } + + new QLabel("No PLUGIN", m_icon); +} + +void DeviceImpl::loadPages() +{ + m_page = new QWidget(); + Ui::DevicePage *ui = new Ui::DevicePage(); + ui->setupUi(m_page); + + m_page->setProperty("device_page", true); + connbtn = new QPushButton("Connect", m_page); + discbtn = new QPushButton("Disconnect", m_page); + auto m_buttonLayout = ui->m_buttonLayout; + auto m_scrollArea = ui->m_scrollArea; + auto m_scrollAreaContents = ui->m_scrollAreaContents; + auto m_scrollAreaLayout = ui->m_scrollAreaLayout; + + Style::setStyle(connbtn, style::properties::button::basicButton); + connbtn->setAutoDefault(true); + m_buttonLayout->addWidget(connbtn); + + Style::setStyle(discbtn, style::properties::button::basicButton); + discbtn->setAutoDefault(true); + m_buttonLayout->addWidget(discbtn); + discbtn->setVisible(false); + + connect(connbtn, &QPushButton::clicked, this, &DeviceImpl::connectDev); + connect(discbtn, &QPushButton::clicked, this, &DeviceImpl::disconnectDev); + connect(this, &DeviceImpl::connectionFailed, this, &DeviceImpl::onConnectionFailed, Qt::QueuedConnection); + + for(auto &&p : plugins()) { + if(p->loadExtraButtons()) { + for(auto &&b : p->extraButtons()) { + b->setProperty("blue_button", true); + b->setProperty("device_page", true); + m_buttonLayout->addWidget(b); + } + } + } + m_buttonLayout->addSpacerItem(new QSpacerItem(40, 40, QSizePolicy::Expanding)); + + for(auto &&p : plugins()) { + if(p->loadPage()) { + m_scrollAreaLayout->addWidget(p->page()); + break; // Only display the page from the plugin with the highest priority + } + } +} + +void DeviceImpl::loadToolList() +{ + for(auto &&p : m_plugins) { + p->loadToolList(); + } +} + +void DeviceImpl::loadBadges() +{ + QPushButton *forgetBtn = new QPushButton(); + forgetBtn->setMaximumSize(25, 25); + forgetBtn->setIcon(QPixmap(":/gui/icons/orange_close.svg")); + connect(forgetBtn, &QPushButton::clicked, this, &DeviceImpl::forget); + HoverWidget *forgetHover = new HoverWidget(forgetBtn, m_icon, m_icon); + forgetHover->setStyleSheet("background-color: transparent; border: 0px;"); + forgetHover->setAnchorPos(HoverPosition::HP_BOTTOMRIGHT); + forgetHover->setContentPos(HoverPosition::HP_TOPLEFT); + forgetHover->setVisible(true); + forgetHover->raise(); + + QPushButton *warningBtn = new QPushButton(); + warningBtn->setMaximumSize(25, 25); + warningBtn->setIcon(QPixmap(":/gui/icons/warning.svg")); + warningBtn->setToolTip(tr("The device is not available!\n" + "Verify the connection!")); + HoverWidget *warningHover = new HoverWidget(warningBtn, m_icon, m_icon); + warningHover->setStyleSheet("background-color: transparent; border: 0px;"); + warningHover->setAnchorPos(HoverPosition::HP_TOPRIGHT); + warningHover->setContentPos(HoverPosition::HP_BOTTOMLEFT); + warningHover->raise(); + warningHover->hide(); + connect(this, &DeviceImpl::connectionFailed, warningHover, &HoverWidget::show); + connect(this, &DeviceImpl::connecting, warningHover, &HoverWidget::hide); +} + +void DeviceImpl::setPingPlugin(Plugin *plugin) +{ + if(!m_pingPlugin && plugin->pingTask()) { + m_pingPlugin = plugin; + } +} + +void DeviceImpl::bindPing() +{ + if(!m_pingPlugin) { + return; + } + for(auto &&p : m_connectedPlugins) { + connect(dynamic_cast(p), SIGNAL(pausePingTask(bool)), dynamic_cast(m_pingPlugin), + SLOT(onPausePingTask(bool))); + } + connect(m_pingPlugin->pingTask(), &PingTask::pingFailed, this, &DeviceImpl::disconnectDev); + m_pingPlugin->startPingTask(); +} + +void DeviceImpl::unbindPing() +{ + if(!m_pingPlugin) { + return; + } + for(auto &&p : m_connectedPlugins) { + disconnect(dynamic_cast(p), SIGNAL(pausePingTask(bool)), + dynamic_cast(m_pingPlugin), SLOT(onPausePingTask(bool))); + } + m_pingPlugin->stopPingTask(); + disconnect(m_pingPlugin->pingTask(), &PingTask::pingFailed, this, &DeviceImpl::disconnectDev); + m_pingPlugin = nullptr; +} + +void DeviceImpl::onConnectionFailed() +{ + m_state = DEV_ERROR; + disconnectDev(); +} + +QList DeviceImpl::plugins() const { return m_plugins; } + +void DeviceImpl::showPage() +{ + for(auto &&p : m_plugins) + p->showPageCallback(); + if(connbtn->isHidden()) { + discbtn->setFocus(); + } else { + connbtn->setFocus(); + } +} + +void DeviceImpl::hidePage() +{ + for(auto &&p : m_plugins) + p->hidePageCallback(); +} + +void DeviceImpl::save(QSettings &s) +{ + for(Plugin *p : qAsConst(m_plugins)) { + s.beginGroup(p->name()); + p->saveSettings(s); + s.endGroup(); + } +} + +void DeviceImpl::load(QSettings &s) +{ + for(Plugin *p : qAsConst(m_plugins)) { + s.beginGroup(p->name()); + p->loadSettings(s); + s.endGroup(); + } +} + +void DeviceImpl::connectDev() +{ + m_state = DEV_CONNECTING; + QElapsedTimer pluginTimer; + QElapsedTimer timer; + ConnectionLoadingBar *connectionLoadingBar = new ConnectionLoadingBar(); + connectionLoadingBar->setProgressBarMaximum(m_plugins.size()); + StatusBarManager::pushUrgentWidget(connectionLoadingBar, "Connection Loading Bar"); + timer.start(); + Preferences *pref = Preferences::GetInstance(); + bool disconnectDevice = false; + connbtn->hide(); + discbtn->show(); + discbtn->setEnabled(false); + m_icon->setFocus(); // temporarily set focus somewhere else + + // the device will always signal connecting->connected->disconnecting->disconnected + // connection process started + Q_EMIT connecting(); + QCoreApplication::processEvents(); + for(int i = 0; i < m_plugins.size(); ++i) { + pluginTimer.start(); + connectionLoadingBar->setCurrentPlugin(m_plugins[i]->name()); + QCoreApplication::processEvents(); + bool pluginConnectionSucceeded = m_plugins[i]->onConnect(); + qInfo(CAT_BENCHMARK) << m_plugins[i]->name() << " connection took: " << pluginTimer.elapsed() << "ms"; + connectionLoadingBar->addProgress(1); // TODO: might change to better reflect the time + QCoreApplication::processEvents(); + if(pluginConnectionSucceeded) { + if(pref->get("general_save_session").toBool()) { + QSettings s = QSettings(scopy::config::settingsFolderPath() + "/" + + m_plugins[i]->name() + ".ini", + QSettings::IniFormat); + m_plugins[i]->loadSettings(s); + } + m_connectedPlugins.push_back(m_plugins[i]); + setPingPlugin(m_plugins[i]); + } else { + QJsonValue obj = m_plugins[i]->metadata().value("disconnectDevOnConnectFailure"); + if(obj != QJsonValue::Undefined) { + disconnectDevice = obj.toBool(); + } else { + qWarning(CAT_DEVICEIMPL) << "Undefined json value"; + } + + if(disconnectDevice) { + break; + } + } + } + + if(disconnectDevice || m_connectedPlugins.isEmpty()) { + // connectionFailed will trigger deviceDisconnect on a queued connection + Q_EMIT connectionFailed(); + } else { + discbtn->setEnabled(true); + discbtn->setFocus(); + bindPing(); + } + // connected will be sent regardless of connection result indicating that the process finished + m_state = DEV_CONNECTED; + Q_EMIT connected(); + delete connectionLoadingBar; + qInfo(CAT_BENCHMARK) << this->displayName() << " device connection took: " << timer.elapsed() << "ms"; +} + +void DeviceImpl::disconnectDev() +{ + QElapsedTimer pluginTimer; + QElapsedTimer timer; + timer.start(); + m_state = DEV_DISCONNECTING; + Q_EMIT disconnecting(); + unbindPing(); + connbtn->show(); + discbtn->hide(); + Preferences *pref = Preferences::GetInstance(); + for(auto &&p : m_connectedPlugins) { + if(pref->get("general_save_session").toBool()) { + QSettings s = QSettings(scopy::config::settingsFolderPath() + "/" + p->name() + ".ini", + QSettings::IniFormat); + p->saveSettings(s); + } + pluginTimer.start(); + p->onDisconnect(); + qInfo(CAT_BENCHMARK) << p->name() << " disconnection took: " << pluginTimer.elapsed() << "ms"; + } + m_connectedPlugins.clear(); + connbtn->setFocus(); + m_state = DEV_IDLE; + Q_EMIT disconnected(); + qInfo(CAT_BENCHMARK) << this->displayName() << " device disconnection took: " << timer.elapsed() << "ms"; +} + +DeviceImpl::~DeviceImpl() { qDebug(CAT_DEVICEIMPL) << m_id << "dtor"; } + +QString DeviceImpl::id() { return m_id; } + +QString DeviceImpl::displayName() { return m_displayName; } + +QString DeviceImpl::category() { return m_category; } + +QString DeviceImpl::displayParam() { return m_displayParam; } + +QString DeviceImpl::param() { return m_param; } + +QWidget *DeviceImpl::icon() { return m_icon; } + +QWidget *DeviceImpl::page() { return m_page; } + +QList DeviceImpl::toolList() +{ + static int i; + QList ret; + + for(auto &&p : m_plugins) { + ret.append(p->toolList()); + } + return ret; +} + +DeviceImpl::DeviceState_t DeviceImpl::state() { return m_state; } + +} // namespace scopy + +#include "moc_deviceimpl.cpp" diff --git a/core/src/deviceloader.cpp b/core/src/deviceloader.cpp new file mode 100644 index 0000000000..d6b864420c --- /dev/null +++ b/core/src/deviceloader.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "deviceloader.h" + +#include + +using namespace scopy; + +DeviceLoader::DeviceLoader(DeviceImpl *d, QObject *parent) + : d(d) + , QObject(parent) +{} + +DeviceLoader::~DeviceLoader() {} + +void DeviceLoader::init(bool async) +{ + if(async) { + asyncInit(); + } else { + syncInit(); + } +} + +void DeviceLoader::asyncInit() +{ + QThread *th = QThread::create([=] { + // initializer thread + d->init(); + }); + oldParent = d->parent(); + d->setParent(nullptr); + d->moveToThread(th); + + connect( + th, &QThread::destroyed, this, + [=]() { + ; + // back to main thread + d->moveToThread(QThread::currentThread()); + d->setParent(oldParent); + Q_EMIT initialized(); + }, + Qt::QueuedConnection); + connect(th, &QThread::finished, th, &QThread::deleteLater); + + th->start(); +} + +void DeviceLoader::syncInit() +{ + d->init(); + Q_EMIT initialized(); +} + +#include "moc_deviceloader.cpp" diff --git a/core/src/devicemanager.cpp b/core/src/devicemanager.cpp new file mode 100644 index 0000000000..c72981f428 --- /dev/null +++ b/core/src/devicemanager.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "devicemanager.h" + +#include "QApplication" +#include "devicefactory.h" +#include "deviceimpl.h" +#include "deviceloader.h" +#include "pluginbase/preferences.h" +#include "pluginbase/statusbarmanager.h" + +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_DEVICEMANAGER, "DeviceManager") +using namespace scopy; +DeviceManager::DeviceManager(PluginManager *pm, QObject *parent) + : QObject{parent} + , pm(pm) +{} + +DeviceManager::~DeviceManager() {} + +Device *DeviceManager::getDevice(QString id) +{ + + Device *d = nullptr; + if(map.contains(id)) { + d = map.value(id, nullptr); + } + return d; +} + +void DeviceManager::addDevice(Device *d) +{ + + DeviceImpl *di = dynamic_cast(d); + QString id = d->id(); + map[id] = d; + di->setParent(this); + di->loadPlugins(); + connectDeviceToManager(di); + Q_EMIT deviceAdded(id, d); +} + +QString DeviceManager::createDevice(QString category, QString param, bool async, QList plugins) +{ + qInfo(CAT_DEVICEMANAGER) << category << "device with params" << param << "added"; + Q_EMIT deviceAddStarted(param); + + DeviceImpl *d = DeviceFactory::build(param, pm, category); + DeviceLoader *dl = new DeviceLoader(d, this); + + connect(dl, &DeviceLoader::initialized, this, [=]() { + QList availablePlugins = d->plugins(); + if(!plugins.isEmpty()) { + for(Plugin *p : qAsConst(availablePlugins)) { + if(!plugins.contains(p->name())) { + p->setEnabled(false); + } + } + } + addDevice(d); + }); // add device to manager once it is initialized + connect(dl, &DeviceLoader::initialized, dl, + &QObject::deleteLater); // don't forget to delete loader once we're done + dl->init(async); + + return d->id(); +} + +// This is only used by scan context collector - should I rethink this ? +// Map all devices to uris in scan context collector +void DeviceManager::removeDevice(QString category, QString param) +{ + + for(Device *d : qAsConst(map)) { + if(d->category() == category && d->param() == param) { + removeDeviceById(d->id()); + return; + } + } +} + +void DeviceManager::removeDeviceById(QString id) +{ + Device *d = nullptr; + + if(connectedDev.contains(id)) { + getDevice(id)->disconnectDev(); + } + + if(!map.contains(id)) { + qWarning(CAT_DEVICEMANAGER) << id << "Device does not exist"; + return; + } + d = map.take(id); + Q_EMIT deviceRemoveStarted(id, d); + + disconnectDeviceFromManager(dynamic_cast(d)); + d->unloadPlugins(); + delete(d); + + qInfo(CAT_DEVICEMANAGER) << "device" << id << "removed"; + + Q_EMIT deviceRemoved(id); +} + +void DeviceManager::connectDeviceToManager(DeviceImpl *d) +{ + connect(d, &DeviceImpl::connecting, this, [=]() { connectingDevice(d->id()); }); + connect(d, &DeviceImpl::connected, this, [=]() { connectDevice(d->id()); }); + connect(d, &DeviceImpl::disconnecting, this, [=]() { disconnectingDevice(d->id()); }); + connect(d, &DeviceImpl::disconnected, this, [=]() { disconnectDevice(d->id()); }); + connect(d, &DeviceImpl::forget, this, [=]() { removeDeviceById(d->id()); }); + connect(d, SIGNAL(requestedRestart()), this, SLOT(restartDevice())); + connect(d, SIGNAL(toolListChanged()), this, SLOT(changeToolListDevice())); + connect(d, SIGNAL(requestTool(QString)), this, SIGNAL(requestTool(QString))); +} + +void DeviceManager::disconnectDeviceFromManager(DeviceImpl *d) +{ + disconnect(d, SIGNAL(connecting())); + disconnect(d, SIGNAL(connected())); + disconnect(d, SIGNAL(disconnecting())); + disconnect(d, SIGNAL(disconnected())); + disconnect(d, SIGNAL(forget())); + disconnect(d, SIGNAL(requestedRestart()), this, SLOT(restartDevice())); + disconnect(d, SIGNAL(toolListChanged()), this, SLOT(changeToolListDevice())); + disconnect(d, SIGNAL(requestTool(QString)), this, SIGNAL(requestTool(QString))); +} + +QString DeviceManager::restartDevice(QString id) +{ + QString cat = map[id]->category(); + QString params = map[id]->param(); + removeDeviceById(id); + QString newId = createDevice(cat, params); + return newId; +} + +void DeviceManager::disconnectAll() +{ + for(const QString &d : qAsConst(connectedDev)) { + map[d]->disconnectDev(); + } +} + +void DeviceManager::save(QSettings &s) +{ + for(const QString &d : qAsConst(connectedDev)) { + map[d]->save(s); + } +} + +void DeviceManager::load(QSettings &s) +{ + for(const QString &d : qAsConst(connectedDev)) { + map[d]->load(s); + } +} + +void DeviceManager::requestConnectedDev() +{ + QMap devMap; + for(const QString &d : qAsConst(connectedDev)) { + QList availablePlugins = dynamic_cast(map[d])->plugins(); + QStringList enabledPlugins; + for(Plugin *p : qAsConst(availablePlugins)) { + if(p->enabled()) { + enabledPlugins.append(p->name()); + } + } + devMap[map[d]->param()] = enabledPlugins; + } + Q_EMIT connectedDevices(devMap); +} + +void DeviceManager::changeToolListDevice() +{ + QString id = dynamic_cast(QObject::sender())->id(); + Q_EMIT deviceChangedToolList(id, map[id]->toolList()); +} + +void DeviceManager::connectingDevice() +{ + QString id = dynamic_cast(QObject::sender())->id(); + connectingDevice(id); +} + +void DeviceManager::connectingDevice(QString id) +{ + qDebug(CAT_DEVICEMANAGER) << "connecting " << id << "..."; + if(connectedDev.contains(id)) { + qDebug(CAT_DEVICEMANAGER) << "connecting to the same device, disconnecting first .. "; + map[id]->disconnectDev(); + } + if(exclusive) { + if(connectedDev.size() > 0) { + qDebug(CAT_DEVICEMANAGER) << "exclusive mode, disconnecting all connected devices .. "; + for(int i = 0; i < connectedDev.count(); i++) + map[connectedDev[i]]->disconnectDev(); + } + } + Q_EMIT deviceConnecting(id); +} + +void DeviceManager::connectDevice() +{ + QString id = dynamic_cast(QObject::sender())->id(); + connectDevice(id); +} + +void DeviceManager::connectDevice(QString id) +{ + connectedDev.append(id); + StatusBarManager::pushMessage("Connected to " + map[id]->id(), 3000); + Q_EMIT deviceConnected(id, map[id]); +} + +void DeviceManager::disconnectingDevice() +{ + QString id = dynamic_cast(QObject::sender())->id(); + disconnectingDevice(id); +} + +void DeviceManager::disconnectingDevice(QString id) { Q_EMIT deviceDisconnecting(id); } + +void DeviceManager::disconnectDevice() +{ + QString id = dynamic_cast(QObject::sender())->id(); + disconnectDevice(id); +} + +void DeviceManager::disconnectDevice(QString id) +{ + qDebug(CAT_DEVICEMANAGER) << "disconnecting " << id << "..."; + connectedDev.removeOne(id); + StatusBarManager::pushMessage("Disconnected from " + map[id]->id(), 3000); + Q_EMIT requestTool("home"); + Q_EMIT deviceDisconnected(id, map[id]); +} + +void DeviceManager::setExclusive(bool val) +{ + exclusive = val; + if(val == true && connectedDev.size() > 1) { + disconnectAll(); + } +} +bool DeviceManager::getExclusive() const { return exclusive; } + +void DeviceManager::restartDevice() +{ + QString id = dynamic_cast(QObject::sender())->id(); + qDebug(CAT_DEVICEMANAGER) << "restarting " << id << "..."; + QString newId = restartDevice(id); + // connect(this,SIGNAL(deviceAdded(QString,Device*)),this,SIGNAL(requestDevice(QString))); + // Q_EMIT requestDevice(newId); +} + +bool DeviceManager::busy() +{ + bool ret = false; + + for(auto dev : qAsConst(map)) { + if(dev->state() == Device::DEV_ERROR || dev->state() == Device::DEV_INIT || + dev->state() == Device::DEV_CONNECTING || dev->state() == Device::DEV_DISCONNECTING) + return true; + } + return false; +} + +int DeviceManager::connectedDeviceCount() { return connectedDev.size(); } + +void DeviceManager::saveSessionDevices() +{ + for(const QString &d : qAsConst(connectedDev)) { + QList availablePlugins = dynamic_cast(map[d])->plugins(); + QStringList enabledPlugins; + for(Plugin *p : qAsConst(availablePlugins)) { + if(p->enabled()) + enabledPlugins.append(p->name()); + } + DeviceAutoConnect::addDevice(map[d]->param(), enabledPlugins); + } +} + +#include "moc_devicemanager.cpp" diff --git a/core/src/emuwidget.cpp b/core/src/emuwidget.cpp new file mode 100644 index 0000000000..52fa476d2c --- /dev/null +++ b/core/src/emuwidget.cpp @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "emuwidget.h" + +#include "common/scopyconfig.h" +#include "pluginbase/preferences.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_EMU_ADD_PAGE, "EmuAddPage") +using namespace scopy; + +EmuWidget::EmuWidget(QWidget *parent) + : QWidget(parent) + , m_enableDemo(false) + , m_emuProcess(nullptr) + , m_workingDir("") + , m_enDemoBtn(nullptr) +{ + QGridLayout *layout = new QGridLayout(this); + layout->setSpacing(10); + setLayout(layout); + + getJsonConfiguration(); + getEmuOptions(); + + QWidget *container = new QWidget(this); + container->setLayout(new QGridLayout(container)); + Style::setStyle(container, style::properties::widget::border_interactive); + + MenuSectionCollapseWidget *configSection = new MenuSectionCollapseWidget( + "CONFIGURATION", MenuCollapseSection::MHCW_NONE, MenuCollapseSection::MHW_BASEWIDGET, container); + configSection->menuSection()->layout()->setMargin(0); + + QWidget *emuWidget = new QWidget(configSection); + QGridLayout *emuWidgetLay = new QGridLayout(emuWidget); + emuWidgetLay->setMargin(0); + emuWidgetLay->setSpacing(10); + + QLabel *demoLabel = new QLabel("Device", emuWidget); + QWidget *demoOptWidget = createDemoOptWidget(emuWidget); + emuWidgetLay->addWidget(demoLabel, 0, 0); + emuWidgetLay->addWidget(demoOptWidget, 0, 1); + connect(this, &EmuWidget::demoEnabled, demoLabel, &QWidget::setDisabled); + connect(this, &EmuWidget::demoEnabled, demoOptWidget, &QWidget::setDisabled); + + QLabel *xmlLabel = new QLabel("XML", emuWidget); + QWidget *xmlPathWidget = createXmlPathWidget(emuWidget); + emuWidgetLay->addWidget(xmlLabel, 1, 0); + emuWidgetLay->addWidget(xmlPathWidget, 1, 1); + connect(this, &EmuWidget::demoEnabled, xmlLabel, &QWidget::setDisabled); + connect(this, &EmuWidget::demoEnabled, xmlPathWidget, &QWidget::setDisabled); + + QLabel *rxTxLabel = new QLabel("Rx/Tx", emuWidget); + QWidget *rxTxDevWidget = createRxTxDevWidget(emuWidget); + emuWidgetLay->addWidget(rxTxLabel, 2, 0); + emuWidgetLay->addWidget(rxTxDevWidget, 2, 1); + connect(this, &EmuWidget::demoEnabled, rxTxLabel, &QWidget::setDisabled); + connect(this, &EmuWidget::demoEnabled, rxTxDevWidget, &QWidget::setDisabled); + + QLabel *uriLabel = new QLabel("URI", emuWidget); + QWidget *uriWidget = createUriWidget(emuWidget); + emuWidgetLay->addWidget(uriLabel, 3, 0); + emuWidgetLay->addWidget(uriWidget, 3, 1); + connect(this, &EmuWidget::demoEnabled, uriLabel, &QWidget::setDisabled); + + m_uriMsgLabel = new QLabel(emuWidget); + m_uriMsgLabel->setVisible(false); + emuWidgetLay->addWidget(m_uriMsgLabel, 4, 1); + + configSection->add(emuWidget); + container->layout()->addWidget(configSection); + + layout->addWidget(container, 0, 0); + layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed), 0, 1); + layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding), 1, 0); + + enGenericOptWidget(xmlPathWidget, rxTxDevWidget, m_demoOptCb->currentText()); + connect(m_demoOptCb, &QComboBox::currentTextChanged, this, + [this, xmlPathWidget, rxTxDevWidget](QString option) { + enGenericOptWidget(xmlPathWidget, rxTxDevWidget, option); + }); + connect(m_enDemoBtn, &QPushButton::clicked, this, &EmuWidget::onEnableDemoClicked); + + m_emuProcess = new QProcess(this); + init(); +} + +EmuWidget::~EmuWidget() +{ + if(m_emuProcess) { + killEmuProcess(); + } +} + +void EmuWidget::init() +{ + Preferences *p = Preferences::GetInstance(); + p->init("iio_emu_path", QCoreApplication::applicationDirPath()); + + QString systemEmuCall = "iio-emu"; + if(startIioEmuProcess(systemEmuCall)) { + m_emuPath = systemEmuCall; + } else { + m_emuPath = findEmuPath(); + } + if(m_emuPath.isEmpty()) { + setStatusMessage("Can't find iio-emu in the system!"); + } else { + setStatusMessage(""); + } + this->setEnabled(!m_emuPath.isEmpty()); +} + +void EmuWidget::enGenericOptWidget(QWidget *xmlPathWidget, QWidget *rxTxDevWidget, QString crtOpt) +{ + // when a new option is selected clear all fields + m_xmlPathEdit->setText(""); + m_rxTxDevEdit->setText(""); + m_uriEdit->setText(""); + + bool isNotAdalm2000 = !crtOpt.contains("adalm2000"); + + xmlPathWidget->setEnabled(isNotAdalm2000); + rxTxDevWidget->setEnabled(isNotAdalm2000); + m_enDemoBtn->setFocus(); + + configureOption(crtOpt); +} + +void EmuWidget::setStatusMessage(QString msg) +{ + m_uriMsgLabel->setHidden(msg.isEmpty()); + m_uriMsgLabel->clear(); + m_uriMsgLabel->setText(msg); +} + +void EmuWidget::onEnableDemoClicked() +{ + m_enDemoBtn->startAnimation(); + if(!m_enableDemo) { + QStringList arg = createArgList(); + bool started = startIioEmuProcess(m_emuPath, arg); + if(!started) { + stopEnableBtn("Enable"); + return; + } + stopEnableBtn("Disable"); + if(m_uriEdit->text().isEmpty()) { + m_uriEdit->setText("ip:127.0.0.1"); + } + setEnableDemo(!m_enableDemo); + Q_EMIT emuDeviceAvailable(m_uriEdit->text()); + } else { + killEmuProcess(); + } +} + +QStringList EmuWidget::createArgList() +{ + QString option = m_demoOptCb->currentText(); + QStringList arguments; + arguments.append(m_emuType); + + if(option.compare("adalm2000") != 0) { + auto xmlFullPath = m_xmlPathEdit->text(); + QFileInfo f(xmlFullPath); + m_workingDir = f.absoluteDir().path(); + + arguments.append(f.fileName()); + arguments.append(m_rxTxDevEdit->text()); + } else { + m_workingDir = ""; + } + + return arguments; +} + +QString EmuWidget::findEmuPath() +{ + Preferences *p = Preferences::GetInstance(); + QString program = p->get("iio_emu_path").toString() + "/iio-emu"; +#ifdef WIN32 + program += ".exe"; +#endif + + QFileInfo fi(program); + if(!fi.exists()) { + program = ""; + } + return program; +} + +void EmuWidget::stopEnableBtn(QString btnText) +{ + m_enDemoBtn->stopAnimation(); + m_enDemoBtn->setText(btnText); +} + +bool EmuWidget::startIioEmuProcess(QString processPath, QStringList arg) +{ + m_emuProcess->setProgram(processPath); + m_emuProcess->setWorkingDirectory(m_workingDir); + m_emuProcess->setArguments(arg); + m_emuProcess->start(); + + auto started = m_emuProcess->waitForStarted(); + if(!started) { + setStatusMessage("Server failed to start!"); + qDebug(CAT_EMU_ADD_PAGE) << "Process failed to start"; + } else { + qDebug(CAT_EMU_ADD_PAGE) << "Process " << m_emuPath << "started"; + } + return started; +} + +void EmuWidget::killEmuProcess() +{ + m_emuProcess->kill(); + stopEnableBtn("Enable"); + setEnableDemo(!m_enableDemo); +} + +void EmuWidget::getEmuOptions() +{ + // Populate emu devices from json + QJsonDocument d = QJsonDocument::fromJson(m_jsonConfigVal.toUtf8()); + QJsonArray valuesList = d.array(); + + for(auto object : valuesList) { + QString device = object.toObject().value(QString("device")).toString(); + m_availableOptions.append(device); + } +} + +void EmuWidget::configureOption(QString option) +{ + QJsonDocument d = QJsonDocument::fromJson(m_jsonConfigVal.toUtf8()); + QJsonArray valuesList = d.array(); + + for(auto jsonArrayItem : valuesList) { + QJsonObject jsonObject = jsonArrayItem.toObject(); + QString device = jsonObject.value(QString("device")).toString(); + if(device == option) { + + // Check the local folder first + QString currentPath = QCoreApplication::applicationDirPath() + "/plugins/emu_xml/"; + if(!QDir(currentPath).exists()) { + currentPath = config::defaultPluginFolderPath() + "/emu_xml/"; + } + + qDebug(CAT_EMU_ADD_PAGE) << "Emu xmls path: " << currentPath; + + if(jsonObject.contains("xml_path")) { + QString xmlPath = jsonObject.value(QString("xml_path")).toString(); + m_xmlPathEdit->setText(currentPath + xmlPath); + } + + if(jsonObject.contains("rx_tx_device")) { + QString rxTxDevice = jsonObject.value(QString("rx_tx_device")).toString(); + rxTxDevice += "@"; + rxTxDevice += currentPath; + rxTxDevice += jsonObject.value(QString("rx_tx_bin_path")).toString(); + m_rxTxDevEdit->setText(rxTxDevice); + } + + if(jsonObject.contains("uri")) { + QString uri = jsonObject.value(QString("uri")).toString(); + m_uriEdit->setText(uri); + } + + if(jsonObject.contains("emu-type")) { + m_emuType = jsonObject.value(QString("emu-type")).toString(); + } else { + m_emuType = "generic"; + } + + break; + } + } +} + +void EmuWidget::getJsonConfiguration() +{ + // Check the local file first + QString filePath = QCoreApplication::applicationDirPath() + "/plugins/resources/scopy_emu_options_config.json"; + QFile file(filePath); + if(!file.exists()) { + filePath = config::defaultPluginFolderPath() + "/resources/scopy_emu_options_config.json"; + file.setFileName(filePath); + } + qDebug(CAT_EMU_ADD_PAGE) << "Emu configuration file: " << filePath; + + if(file.exists()) { + file.open(QIODevice::ReadOnly | QIODevice::Text); + m_jsonConfigVal = file.readAll(); + file.close(); + } else { + qWarning(CAT_EMU_ADD_PAGE) << "Emu configuration file is missing "; + m_availableOptions.append("generic"); + } +} + +void EmuWidget::setEnableDemo(bool en) +{ + m_enableDemo = en; + Q_EMIT demoEnabled(en); +} + +void EmuWidget::browseFile(QLineEdit *lineEditPath) +{ + QString filePath = + QFileDialog::getOpenFileName(this, "Open a file", "directoryToOpen", + "All (*);;XML Files (*.xml);;Text Files (*.txt);;BIN Files (*.bin)"); + lineEditPath->setText(filePath); + m_enDemoBtn->setFocus(); +} + +QWidget *EmuWidget::createDemoOptWidget(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + QHBoxLayout *layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->setSpacing(10); + w->setLayout(layout); + + m_demoOptCb = new QComboBox(w); + for(const QString &opt : qAsConst(m_availableOptions)) { + m_demoOptCb->addItem(opt); + } + layout->addWidget(m_demoOptCb); + return w; +} + +QWidget *EmuWidget::createXmlPathWidget(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + QHBoxLayout *layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->setSpacing(10); + w->setLayout(layout); + + m_xmlPathEdit = new QLineEdit(w); + + QPushButton *xmlPathBtn = new QPushButton("...", w); + StyleHelper::BrowseButton(xmlPathBtn); + connect(xmlPathBtn, &QPushButton::clicked, this, [=]() { browseFile(m_xmlPathEdit); }); + + layout->addWidget(m_xmlPathEdit); + layout->addWidget(xmlPathBtn); + return w; +} + +QWidget *EmuWidget::createRxTxDevWidget(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + QHBoxLayout *layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->setSpacing(10); + w->setLayout(layout); + + m_rxTxDevEdit = new QLineEdit(w); + m_rxTxDevEdit->setPlaceholderText("iio:device0@/absolutePathTo/data.bin"); + + QPushButton *rxTxDevBtn = new QPushButton("...", w); + StyleHelper::BrowseButton(rxTxDevBtn); + + connect(rxTxDevBtn, &QPushButton::clicked, this, [=]() { browseFile(m_rxTxDevEdit); }); + + layout->addWidget(m_rxTxDevEdit); + layout->addWidget(rxTxDevBtn); + return w; +} + +QWidget *EmuWidget::createUriWidget(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + QHBoxLayout *layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->setSpacing(10); + w->setLayout(layout); + + m_uriEdit = new QLineEdit(w); + m_uriEdit->setPlaceholderText("ip:127.0.0.1"); + connect(this, &EmuWidget::demoEnabled, m_uriEdit, &QWidget::setDisabled); + + initEnBtn(w); + + layout->addWidget(m_uriEdit); + layout->addWidget(m_enDemoBtn); + return w; +} + +void EmuWidget::initEnBtn(QWidget *parent) +{ + m_enDemoBtn = new AnimationPushButton(parent); + m_enDemoBtn->setText("Enable"); + StyleHelper::BasicButton(m_enDemoBtn); + m_enDemoBtn->setFixedWidth(Style::getDimension(json::global::unit_6)); + QMovie *loadingIcon(new QMovie(this)); + loadingIcon->setFileName(":/gui/loading.gif"); + m_enDemoBtn->setAnimation(loadingIcon); + m_enDemoBtn->setAutoDefault(true); +} + +void EmuWidget::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + m_enDemoBtn->setFocus(); +} + +#include "moc_emuwidget.cpp" diff --git a/core/src/iiodeviceimpl.cpp b/core/src/iiodeviceimpl.cpp new file mode 100644 index 0000000000..2aa9fa14e5 --- /dev/null +++ b/core/src/iiodeviceimpl.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "iiodeviceimpl.h" + +#include "iioutil/connectionprovider.h" + +#include + +Q_LOGGING_CATEGORY(CAT_IIO_DEVICEIMPL, "IIODevice") + +using namespace scopy; +void IIODeviceImpl::init() +{ + auto cp = ConnectionProvider::GetInstance(); + + // Optimization for iio plugins - keep context open while running compatible + + cp->open(m_param); + DeviceImpl::init(); + cp->close(m_param); +} + +bool IIODeviceImpl::verify() +{ + Connection *conn = ConnectionProvider::GetInstance()->open(m_param); + if(!conn) { + return false; + } + ConnectionProvider::GetInstance()->close(m_param); + return true; +} + +QMap IIODeviceImpl::readDeviceInfo() +{ + QMap contextAttributes; + Connection *conn = ConnectionProvider::GetInstance()->open(m_param); + if(!conn) { + qWarning(CAT_IIO_DEVICEIMPL) << "Cannot read the device info! (unavailable context)"; + } else { + for(int i = 0; i < iio_context_get_attrs_count(conn->context()); i++) { + const char *name; + const char *value; + int ret = iio_context_get_attr(conn->context(), i, &name, &value); + if(ret != 0) + continue; + contextAttributes[name] = value; + } + ConnectionProvider::GetInstance()->close(m_param); + } + + return contextAttributes; +} diff --git a/core/src/iiotabwidget.cpp b/core/src/iiotabwidget.cpp new file mode 100644 index 0000000000..50b38f1775 --- /dev/null +++ b/core/src/iiotabwidget.cpp @@ -0,0 +1,450 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "iiotabwidget.h" + +#include "iioutil/scopy-iioutil_config.h" +#include "menusectionwidget.h" +#include "qtconcurrentrun.h" + +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_IIO_ADD_PAGE, "IIOTabWidget") + +using namespace scopy; + +IioTabWidget::IioTabWidget(QWidget *parent) + : QWidget(parent) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setSpacing(10); + setLayout(layout); + + QWidget *contentWidget = new QWidget(this); + QVBoxLayout *contentLay = new QVBoxLayout(this); + contentLay->setSpacing(10); + contentLay->setMargin(0); + contentWidget->setLayout(contentLay); + + QStringList backendsList = computeBackendsList(); + + MenuSectionCollapseWidget *scanSection = new MenuSectionCollapseWidget( + "SCAN", MenuCollapseSection::MHCW_NONE, MenuCollapseSection::MHW_BASEWIDGET, contentWidget); + scanSection->menuSection()->layout()->setMargin(0); + + QWidget *scanWidget = new QWidget(scanSection); + QGridLayout *scanGrid = new QGridLayout(scanWidget); + scanGrid->setMargin(0); + scanWidget->setLayout(scanGrid); + + m_filterWidget = createFilterWidget(scanWidget); + scanGrid->addWidget(new QLabel("Filter", scanWidget), 0, 0); + scanGrid->addWidget(m_filterWidget, 0, 1); + setupFilterWidget(backendsList); + + QWidget *avlContextWidget = createAvlCtxWidget(scanWidget); + m_btnScan->setVisible(!backendsList.isEmpty()); + m_ctxUriLabel = new QLabel(scanWidget); + m_ctxUriLabel->setVisible(false); + scanGrid->addWidget(new QLabel("Context", scanWidget), 1, 0); + scanGrid->addWidget(avlContextWidget, 1, 1); + scanGrid->addWidget(m_ctxUriLabel, 2, 1); + scanSection->add(scanWidget); + + QWidget *scanContainer = new QWidget(parent); + scanContainer->setLayout(new QGridLayout(scanContainer)); + Style::setStyle(scanContainer, style::properties::widget::border_interactive); + scanContainer->layout()->addWidget(scanSection); + + contentLay->addWidget(scanContainer); + + MenuSectionCollapseWidget *serialSection = new MenuSectionCollapseWidget( + "SERIAL PORT", MenuCollapseSection::MHCW_NONE, MenuCollapseSection::MHW_BASEWIDGET, contentWidget); + serialSection->menuSection()->layout()->setMargin(0); + + QWidget *serialSettWidget = createSerialSettWidget(serialSection); + bool serialCompatible = isSerialCompatible(); + serialSettWidget->setEnabled(serialCompatible); + serialSection->add(serialSettWidget); + + QWidget *serialContainer = new QWidget(parent); + serialContainer->setLayout(new QGridLayout(serialContainer)); + Style::setStyle(serialContainer, style::properties::widget::border_interactive); + serialContainer->layout()->addWidget(serialSection); + + contentLay->addWidget(serialContainer); + + QWidget *uriWidget = createUriWidget(contentWidget); + contentLay->addWidget(uriWidget); + contentLay->addItem(new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding)); + + layout->addWidget(contentWidget); + layout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed)); + addScanFeedbackMsg("No scanned devices... Press the refresh button!"); + + m_fwScan = new QFutureWatcher(this); + m_fwSerialScan = new QFutureWatcher>(this); + setupConnections(); + if(serialCompatible) + futureSerialScan(); +} + +IioTabWidget::~IioTabWidget() {} + +void IioTabWidget::setupConnections() +{ + connect(m_btnVerify, &QPushButton::clicked, this, &IioTabWidget::verifyBtnClicked, Qt::QueuedConnection); + // scanfilterLayout + connect(m_fwScan, &QFutureWatcher::started, m_btnScan, &AnimationPushButton::startAnimation, + Qt::QueuedConnection); + connect(m_fwScan, &QFutureWatcher::finished, this, &IioTabWidget::scanFinished, Qt::QueuedConnection); + connect(m_btnScan, SIGNAL(clicked()), this, SLOT(futureScan()), Qt::QueuedConnection); + + connect(m_avlCtxCb, QOverload::of(&QComboBox::currentIndexChanged), this, [this](int idx) { + if(idx >= 0 && idx < m_scanList.size()) { + m_ctxUriLabel->setText(m_scanList[idx].second); + Q_EMIT uriChanged(m_scanList[idx].second); + } + }); + + // serial scan + connect(m_fwSerialScan, &QFutureWatcher::started, m_btnSerialScan, &AnimationPushButton::startAnimation, + Qt::QueuedConnection); + connect(m_fwSerialScan, &QFutureWatcher::finished, this, &IioTabWidget::serialScanFinished, + Qt::QueuedConnection); + connect(m_btnSerialScan, SIGNAL(clicked()), this, SLOT(futureSerialScan()), Qt::QueuedConnection); + // serial widget connections + connect(m_serialPortCb->combo(), &QComboBox::textActivated, this, + [=]() { Q_EMIT uriChanged(getSerialPath()); }); + connect(m_baudRateCb->combo(), &QComboBox::textActivated, this, [=]() { Q_EMIT uriChanged(getSerialPath()); }); + connect(m_serialFrameEdit, &QLineEdit::returnPressed, this, [=]() { Q_EMIT uriChanged(getSerialPath()); }); + connect(this, &IioTabWidget::uriChanged, this, &IioTabWidget::updateUri); + connect(m_uriEdit, &QLineEdit::returnPressed, this, [=]() { Q_EMIT m_btnVerify->clicked(); }); + connect(m_uriEdit, &QLineEdit::textChanged, this, + [=](QString uri) { m_btnVerify->setEnabled(!uri.isEmpty()); }); +} + +QStringList IioTabWidget::computeBackendsList() +{ + QStringList list; + int backEndsCount = iio_get_backends_count(); + for(int i = 0; i < backEndsCount; i++) { + QString backEnd(iio_get_backend(i)); + if(backEnd.compare("xml") == 0 || backEnd.compare("serial") == 0) { + continue; + } + list.append(backEnd); + } + return list; +} + +QCheckBox *IioTabWidget::createBackendCheckBox(QString backEnd, QWidget *parent) +{ + QCheckBox *cb = new QCheckBox(backEnd, m_filterWidget); + cb->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + connect(cb, &QCheckBox::toggled, this, [=](bool en) { + if(en) { + m_scanParamsList.push_back(backEnd + ":"); + } else { + m_scanParamsList.removeOne(backEnd + ":"); + } + futureScan(); + m_btnScan->setFocus(); + }); + return cb; +} + +void IioTabWidget::setupFilterWidget(QStringList backednsList) +{ + QHBoxLayout *filterLayout = dynamic_cast(m_filterWidget->layout()); + for(const QString &backend : backednsList) { + QCheckBox *cb = createBackendCheckBox(backend, m_filterWidget); + filterLayout->addWidget(cb, 0, Qt::AlignLeft); + } + filterLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed)); +} + +void IioTabWidget::verifyBtnClicked() +{ + QRegExp ipRegex("^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-" + "4][0-9]|25[0-5])$"); + QString uri(m_uriEdit->text()); + bool isIp = uri.contains(ipRegex); + if(isIp && !m_uriEdit->text().contains("ip:")) { + m_uriEdit->blockSignals(true); + m_uriEdit->setText("ip:" + uri); + m_uriEdit->blockSignals(false); + } + m_btnScan->setDisabled(true); + m_btnSerialScan->setDisabled(true); + m_btnVerify->startAnimation(); + Q_EMIT startVerify(m_uriEdit->text(), "iio"); +} + +void IioTabWidget::onVerifyFinished(bool result) +{ + rstUriMsgLabel(); + if(!result) { + m_uriMsgLabel->setVisible(true); + m_uriMsgLabel->setText("\"" + m_uriEdit->text() + "\" not a valid context!"); + } + m_btnVerify->stopAnimation(); + m_btnScan->setEnabled(true); + m_btnSerialScan->setEnabled(true); +} + +void IioTabWidget::futureScan() +{ + m_scanList.clear(); + QString scanParams = m_scanParamsList.join(""); + QFuture f = QtConcurrent::run(std::bind(&IIOScanTask::scan, &m_scanList, scanParams)); + m_fwScan->setFuture(f); +} + +void IioTabWidget::futureSerialScan() +{ + QFuture> f = QtConcurrent::run(std::bind(&IIOScanTask::getSerialPortsName)); + m_fwSerialScan->setFuture(f); +} + +void IioTabWidget::scanFinished() +{ + int retCode = m_fwScan->result(); + m_btnScan->stopAnimation(); + m_avlCtxCb->clear(); + m_ctxUriLabel->clear(); + rstUriMsgLabel(); + if(retCode < 0) { + m_ctxUriLabel->setVisible(false); + addScanFeedbackMsg("Scan command failed!"); + qWarning(CAT_IIO_ADD_PAGE) << "iio_scan_context_get_info_list error " << retCode; + return; + } + if(m_scanList.isEmpty()) { + m_ctxUriLabel->setVisible(false); + addScanFeedbackMsg("No scanned devices available!"); + return; + } + if(!m_avlCtxCb->isEnabled()) { + m_avlCtxCb->setEnabled(true); + } + for(const auto &ctx : qAsConst(m_scanList)) { + QString cbEntry = ctx.first + " [" + ctx.second + "]"; + m_avlCtxCb->addItem(cbEntry); + } + int crtIdx = m_avlCtxCb->currentIndex(); + m_ctxUriLabel->setVisible(true); + m_ctxUriLabel->setText(m_scanList[crtIdx].second); + updateUri(m_scanList[crtIdx].second); +} + +void IioTabWidget::serialScanFinished() +{ + QVector portsName = m_fwSerialScan->result(); + m_btnSerialScan->stopAnimation(); + m_serialPortCb->combo()->clear(); + if(!portsName.empty()) { + for(const QString &port : portsName) { + m_serialPortCb->combo()->addItem(port); + } + } +} + +QString IioTabWidget::getSerialPath() +{ + QString serialPath = "serial:"; + serialPath.append(m_serialPortCb->combo()->currentText()); + serialPath.append("," + m_baudRateCb->combo()->currentText()); + serialPath.append("," + m_serialFrameEdit->text()); + return serialPath; +} + +bool IioTabWidget::isSerialCompatible() +{ + bool hasLibSerialPort = false; +#ifdef WITH_LIBSERIALPORT + hasLibSerialPort = true; +#endif + bool hasSerialBackend = iio_has_backend("serial"); + return hasLibSerialPort && hasSerialBackend; +} + +void IioTabWidget::updateUri(QString uri) +{ + m_uriEdit->clear(); + m_uriEdit->setText(uri); + if(!uri.isEmpty()) { + m_btnVerify->setFocus(); + } +} + +void IioTabWidget::addScanFeedbackMsg(QString message) +{ + m_ctxUriLabel->clear(); + m_avlCtxCb->clear(); + m_avlCtxCb->addItem(message); + m_avlCtxCb->setEnabled(false); + updateUri(""); +} + +void IioTabWidget::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + m_btnScan->setFocus(); +} + +void IioTabWidget::setupBtnLdIcon(AnimationPushButton *btn) +{ + QMovie *icon(new QMovie(this)); + icon->setFileName(":/gui/loading.gif"); + btn->setAnimation(icon); +} + +void IioTabWidget::rstUriMsgLabel() +{ + m_uriMsgLabel->setVisible(false); + m_uriMsgLabel->clear(); +} + +QWidget *IioTabWidget::createFilterWidget(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + QHBoxLayout *layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->setSpacing(10); + w->setLayout(layout); + return w; +} + +QWidget *IioTabWidget::createAvlCtxWidget(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + QHBoxLayout *layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->setSpacing(10); + w->setLayout(layout); + + m_avlCtxCb = new QComboBox(w); + + m_btnScan = new AnimationPushButton(w); + setupBtnLdIcon(m_btnScan); + StyleHelper::RefreshButton(m_btnScan); + m_btnScan->setAutoDefault(true); + + layout->addWidget(m_avlCtxCb); + layout->addWidget(m_btnScan); + return w; +} + +QWidget *IioTabWidget::createSerialSettWidget(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + QHBoxLayout *layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->setSpacing(10); + w->setLayout(layout); + + m_serialPortCb = new MenuCombo("Name", w); + m_baudRateCb = new MenuCombo("Baud Rate", w); + for(int baudRate : m_availableBaudRates) { + m_baudRateCb->combo()->addItem(QString::number(baudRate)); + } + + QWidget *lineEditWidget = new QWidget(w); + lineEditWidget->setLayout(new QVBoxLayout(lineEditWidget)); + lineEditWidget->layout()->setMargin(0); + lineEditWidget->layout()->setSpacing(0); + QLabel *serialFrameLabel = new QLabel("Config", lineEditWidget); + + QRegExp re("[5-9]{1}(n|o|e|m|s){1}[1-2]{1}(x|r|d){0,1}$"); + QRegExpValidator *validator = new QRegExpValidator(re, this); + m_serialFrameEdit = new QLineEdit(lineEditWidget); + m_serialFrameEdit->setValidator(validator); + m_serialFrameEdit->setText("8n1"); + m_serialFrameEdit->setFocusPolicy(Qt::ClickFocus); + + lineEditWidget->layout()->addWidget(serialFrameLabel); + lineEditWidget->layout()->addWidget(m_serialFrameEdit); + lineEditWidget->setFixedWidth(serialFrameLabel->width()); + + m_btnSerialScan = new AnimationPushButton(w); + setupBtnLdIcon(m_btnSerialScan); + StyleHelper::RefreshButton(m_btnSerialScan); + + layout->addWidget(m_serialPortCb); + layout->addWidget(m_baudRateCb); + layout->addWidget(lineEditWidget); + layout->addWidget(m_btnSerialScan); + return w; +} + +QWidget *IioTabWidget::createUriWidget(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + QGridLayout *layout = new QGridLayout(w); + w->setLayout(layout); + Style::setStyle(w, style::properties::widget::border_interactive); + + QLabel *uriLabel = new QLabel("URI", w); + StyleHelper::MenuLargeLabel(uriLabel); + + m_uriEdit = new QLineEdit(w); + m_uriEdit->setPlaceholderText("The device you are connecting to"); + m_uriEdit->setFocusPolicy(Qt::ClickFocus); + m_uriMsgLabel = new QLabel(w); + m_uriMsgLabel->setVisible(false); + + QWidget *btnVerifyWidget = createVerifyBtnWidget(w); + + layout->addWidget(uriLabel, 0, 0); + layout->addWidget(m_uriEdit, 0, 1); + layout->addWidget(btnVerifyWidget, 0, 2, Qt::AlignRight); + layout->addWidget(m_uriMsgLabel, 1, 1); + + return w; +} + +QWidget *IioTabWidget::createVerifyBtnWidget(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + QHBoxLayout *layout = new QHBoxLayout(w); + layout->setMargin(0); + layout->setAlignment(Qt::AlignRight); + w->setLayout(layout); + + m_btnVerify = new AnimationPushButton(w); + setupBtnLdIcon(m_btnVerify); + StyleHelper::BasicButton(m_btnVerify); + m_btnVerify->setText("Verify"); + m_btnVerify->setIconSize(QSize(30, 30)); + m_btnVerify->setFixedWidth(Style::getDimension(json::global::unit_6)); + m_btnVerify->setEnabled(false); + m_btnVerify->setAutoDefault(true); + + layout->addWidget(m_btnVerify); + return w; +} + +#include "moc_iiotabwidget.cpp" diff --git a/core/src/infopagestack.cpp b/core/src/infopagestack.cpp new file mode 100644 index 0000000000..3b3487e5fb --- /dev/null +++ b/core/src/infopagestack.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "infopagestack.h" + +#include "gui/customanimation.h" + +#include +#include +#include + +#include + +using namespace scopy; + +Q_LOGGING_CATEGORY(CAT_INFOPAGESTACK, "InfoPageStack") + +InfoPageStack::InfoPageStack(QWidget *parent) + : MapStackedWidget(parent) +{ + hc = new HomepageControls(this); + this->installEventFilter(hc); + + speed = 200; + animationType = QEasingCurve::InOutCubic; + active = false; + now = QPoint(0, 0); + current = 0; + next = 0; + hc->setVisible(true); + hc->raise(); + qDebug(CAT_INFOPAGESTACK) << "ctor"; +} + +InfoPageStack::~InfoPageStack() +{ + this->removeEventFilter(hc); + qDebug(CAT_INFOPAGESTACK) << "dtor"; +} + +void InfoPageStack::add(QString key, QWidget *w) +{ + MapStackedWidget::add(key, w); + if(count() == 1) { + hc->raise(); + } + hc->setVisible(count() > 1); +} + +void InfoPageStack::add(QString key, Device *d) +{ + QWidget *w = d->page(); + add(key, w); + idDevMap.insert(key, d); +} + +bool InfoPageStack::remove(QString key) +{ + bool ret = MapStackedWidget::remove(key); + idDevMap.take(key); + return ret; +} + +bool InfoPageStack::show(QString key) +{ + QString oldKey = getKey(currentWidget()); + + if(idDevMap.contains(oldKey)) + idDevMap[oldKey]->hidePage(); + auto ret = MapStackedWidget::show(key); + if(idDevMap.contains(key)) + idDevMap[key]->showPage(); + hc->raise(); + hc->setVisible(count() > 1); + return ret; +} + +bool InfoPageStack::slideInKey(QString key, int direction) +{ + QString oldKey = getKey(currentWidget()); + + if(idDevMap.contains(oldKey)) + idDevMap[oldKey]->hidePage(); + + QWidget *w = map.value(key); + + if(idDevMap.contains(key)) + idDevMap[key]->showPage(); + + if(!w) + return false; + slideInWidget(w, direction); + return true; +} + +void InfoPageStack::animationDone() +{ + setCurrentIndex(next); + // widget(current)->hide(); + // widget(current)->move(now); + active = false; + hc->raise(); +} + +void InfoPageStack::slideInWidget(QWidget *newWidget, int direction) +{ + if(active) { + if(this->next != indexOf(newWidget)) { + animationDone(); + } else { + return; + } + } + active = true; + + int current = currentIndex(); + int next = indexOf(newWidget); + if(current == next) { + active = false; + return; + } + + int offsetx = frameRect().width(); + int offsety = frameRect().height(); + widget(next)->setGeometry(0, 0, offsetx, offsety); + + if(direction < 0) { + offsetx = -offsetx; + offsety = 0; + } else { + offsety = 0; + } + + QPoint pnext = widget(next)->pos(); + QPoint pcurrent = widget(current)->pos(); + now = pcurrent; + + widget(next)->move(pnext.x() - offsetx, pnext.y() - offsety); + widget(next)->show(); + widget(next)->raise(); + + CustomAnimation *animNow = new CustomAnimation(widget(current), "pos"); + animNow->setDuration(speed); + animNow->setEasingCurve(animationType); + animNow->setStartValue(QPoint(pcurrent.x(), pcurrent.y())); + animNow->setEndValue(QPoint(offsetx + pcurrent.x(), offsety + pcurrent.y())); + CustomAnimation *animNext = new CustomAnimation(widget(next), "pos"); + animNext->setDuration(speed); + animNext->setEasingCurve(animationType); + animNext->setStartValue(QPoint(-offsetx + pnext.x(), offsety + pnext.y())); + animNext->setEndValue(QPoint(pnext.x(), pnext.y())); + QParallelAnimationGroup *animGroup = new QParallelAnimationGroup(this); + + animGroup->addAnimation(animNow); + animGroup->addAnimation(animNext); + + connect(animGroup, &QParallelAnimationGroup::finished, this, &InfoPageStack::animationDone); + + this->next = next; + this->current = current; + active = true; + animGroup->start(); +} + +HomepageControls *InfoPageStack::getHomepageControls() const { return hc; } + +#include "moc_infopagestack.cpp" diff --git a/core/src/licenseoverlay.cpp b/core/src/licenseoverlay.cpp new file mode 100644 index 0000000000..184f78bd22 --- /dev/null +++ b/core/src/licenseoverlay.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include + +#include +#include +using namespace scopy; + +LicenseOverlay::LicenseOverlay(QWidget *parent) + : QWidget(parent) + , parent(parent) +{ + m_popupWidget = new PopupWidget(parent); + m_popupWidget->setFocusOnContinueButton(); + m_popupWidget->setEnableExternalLinks(true); + m_popupWidget->enableTitleBar(false); + m_popupWidget->enableTintedOverlay(true); + m_popupWidget->setDescription(getLicense()); + m_popupWidget->enableCenterOnParent(true); + + connect(m_popupWidget->getContinueBtn(), &QAbstractButton::clicked, [&]() { + Preferences::GetInstance()->set("general_first_run", false); + deleteLater(); + m_popupWidget->deleteLater(); + }); + Preferences::connect(m_popupWidget->getExitBtn(), &QAbstractButton::clicked, + [&]() { QCoreApplication::quit(); }); + + Style::setBackgroundColor(m_popupWidget, json::theme::background_primary); +} + +LicenseOverlay::~LicenseOverlay() {} + +void LicenseOverlay::showOverlay() +{ + raise(); + show(); +} + +QPushButton *LicenseOverlay::getContinueBtn() { return m_popupWidget->getContinueBtn(); } + +QString LicenseOverlay::getLicense() +{ + QFile file(":/license.html"); + file.open(QIODevice::ReadOnly); + QString text = QString(file.readAll()); + file.close(); + + return text; +} + +#include "moc_licenseoverlay.cpp" diff --git a/core/src/logging_categories.cpp b/core/src/logging_categories.cpp new file mode 100644 index 0000000000..d61be09bad --- /dev/null +++ b/core/src/logging_categories.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019 Analog Devices Inc. + * + * This file is part of Scopy + * (see http://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "logging_categories.h" + +#ifndef QT_NO_DEBUG_OUTPUT +Q_LOGGING_CATEGORY(CAT_TOOL_LAUNCHER, "toolLauncher") +Q_LOGGING_CATEGORY(CAT_OSCILLOSCOPE, "oscilloscope") +Q_LOGGING_CATEGORY(CAT_SIGNAL_GENERATOR, "signalGenerator") +Q_LOGGING_CATEGORY(CAT_VOLTMETER, "voltmeter") +Q_LOGGING_CATEGORY(CAT_POWER_CONTROLLER, "powerController") +Q_LOGGING_CATEGORY(CAT_SPECTRUM_ANALYZER, "spectrumAnalyzer") +Q_LOGGING_CATEGORY(CAT_NETWORK_ANALYZER, "networkAnalyzer") +Q_LOGGING_CATEGORY(CAT_DIGITAL_IO, "digitalIO") +Q_LOGGING_CATEGORY(CAT_LOGIC_ANALYZER, "logicAnalyzer") +Q_LOGGING_CATEGORY(CAT_PATTERN_GENERATOR, "patternGenerator") +Q_LOGGING_CATEGORY(CAT_CALIBRATION, "calibration") +Q_LOGGING_CATEGORY(CAT_CALIBRATION_MANUAL, "calibration.manual") +Q_LOGGING_CATEGORY(CAT_IIO_MANAGER, "iioManager") +Q_LOGGING_CATEGORY(CAT_PLOT, "plot") +Q_LOGGING_CATEGORY(CAT_BENCHMARK, "Benchmark") +#endif diff --git a/core/src/pluginenablewidget.cpp b/core/src/pluginenablewidget.cpp new file mode 100644 index 0000000000..58f50e7ff5 --- /dev/null +++ b/core/src/pluginenablewidget.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "pluginenablewidget.h" + +#include "qboxlayout.h" + +using namespace scopy; + +PluginEnableWidget::PluginEnableWidget(QWidget *parent) + : QWidget(parent) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->setContentsMargins(5, 5, 5, 5); + + m_checkBox = new QCheckBox(); + m_checkBox->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + m_descriptionLabel = new QLabel(); + m_descriptionLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + m_descriptionLabel->setWordWrap(true); + + layout->addWidget(m_checkBox); + layout->setAlignment(m_checkBox, Qt::AlignTop); + layout->addWidget(m_descriptionLabel); + layout->setAlignment(m_descriptionLabel, Qt::AlignTop); + layout->setStretch(0, 1); + layout->setStretch(1, 3); +} + +PluginEnableWidget::~PluginEnableWidget() {} + +void PluginEnableWidget::setDescription(QString description) +{ + m_descriptionLabel->clear(); + m_descriptionLabel->setText(description); +} + +QCheckBox *PluginEnableWidget::checkBox() const { return m_checkBox; } + +#include "moc_pluginenablewidget.cpp" diff --git a/core/src/pluginfilter.cpp b/core/src/pluginfilter.cpp new file mode 100644 index 0000000000..af45862a98 --- /dev/null +++ b/core/src/pluginfilter.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "pluginfilter.h" + +#include + +namespace scopy { +bool PluginFilter::pluginInCategory(Plugin *p, QString category) +{ // PluginFilter class (?) + if(category.isEmpty()) // no category selected + return true; + if(!p->metadata().contains("category")) // plugin metadata does not have category + return true; + QJsonValue categoryVal = p->metadata().value("category"); + if(categoryVal.isString()) // single category + return category == p->metadata().value("category").toString(); + if(categoryVal.isArray()) { // list category + for(auto v : categoryVal.toArray()) { + if(!v.isString()) { + continue; + } + if(v.toString() == category) { + return true; + } + } + } + return false; +} + +bool PluginFilter::pluginForcedInclusionList(QList pl, Plugin *p) +{ + bool ret = false; + QStringList includeList; + for(Plugin *test : pl) { + if(test->enabled() == false) + continue; + if(!test->metadata().contains("include-forced")) + continue; + QJsonValue includeVal = test->metadata().value("include-forced"); + if(includeVal.isString()) + includeList.append(includeVal.toString()); + if(includeVal.isArray()) { + for(auto v : includeVal.toArray()) { + if(!v.isString()) { + continue; + } + includeList.append(v.toString()); + } + } + } + + for(const QString &include : includeList) { + if(include.toLower() == p->name().toLower()) { + ret = true; + } + } + return ret; +} + +bool PluginFilter::pluginInExclusionList(QList pl, Plugin *p) +{ + bool ret = false; + QStringList excludeList; + for(Plugin *test : pl) { + if(test->enabled() == false) + continue; + if(!test->metadata().contains("exclude")) + continue; + QJsonValue excludeVal = test->metadata().value("exclude"); + if(excludeVal.isString()) + excludeList.append(excludeVal.toString()); + if(excludeVal.isArray()) { + for(auto v : excludeVal.toArray()) { + if(!v.isString()) { + continue; + } + excludeList.append(v.toString()); + } + } + } + + for(const QString &exclude : excludeList) { + if(exclude == "*") { + ret = true; + } + if(exclude.toLower() == p->name().toLower()) { + ret = true; + } + if(exclude.toLower() == QString("!" + p->name()).toLower()) { + ret = false; + break; + } + } + return ret; +} + +} // namespace scopy diff --git a/core/src/pluginmanager.cpp b/core/src/pluginmanager.cpp new file mode 100644 index 0000000000..1bcc2482f0 --- /dev/null +++ b/core/src/pluginmanager.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "pluginmanager.h" + +#include "pluginfilter.h" +#include "qpluginloader.h" + +#include +#include +#include + +#include + +Q_LOGGING_CATEGORY(CAT_PLUGINMANAGER, "PluginManager") +using namespace scopy; + +struct less_than_key +{ + inline bool operator()(Plugin *p1, Plugin *p2) + { + return (p1->metadata()["priority"].toInt() < p2->metadata()["priority"].toInt()); + } +}; + +struct greater_than_key +{ + inline bool operator()(Plugin *p1, Plugin *p2) + { + return (p1->metadata()["priority"].toInt() > p2->metadata()["priority"].toInt()); + } +}; + +PluginManager::PluginManager(QObject *parent) + : QObject(parent) +{} + +PluginManager::~PluginManager() +{ + for(Plugin *p : qAsConst(list)) { + p->deinit(); + } +} + +void PluginManager::add(QStringList pluginFileList) +{ + for(const QString &pluginFileName : pluginFileList) { + add(pluginFileName); + } +} + +void PluginManager::add(QString pluginFileName) +{ + Plugin *p = nullptr; + p = loadPlugin(pluginFileName); + if(p) { + qInfo(CAT_PLUGINMANAGER) << "Found plugin:" << p->name() << "in " << pluginFileName; + list.append(p); + p->initMetadata(); + applyMetadata(p, &m_metadata); + p->init(); + QObject *obj = dynamic_cast(p); + if(obj) + obj->setParent(this); + } +} + +int PluginManager::count() { return list.count(); } + +void PluginManager::applyMetadata(Plugin *plugin, QJsonObject *metadata) +{ + if(metadata->contains(plugin->name())) { + plugin->setMetadata(metadata->value(plugin->name()).toObject()); + } +} + +void PluginManager::sort() +{ + std::sort(list.begin(), list.end(), greater_than_key()); + + qDebug(CAT_PLUGINMANAGER) << "New plugin order:"; + for(Plugin *plugin : qAsConst(list)) { + qDebug(CAT_PLUGINMANAGER) << plugin->name(); + } +} + +void PluginManager::clear() { list.clear(); } + +QList PluginManager::getPlugins(QString category) +{ + QList newlist; + for(Plugin *plugin : qAsConst(list)) { + if(!PluginFilter::pluginInCategory(plugin, category)) + continue; + Plugin *p = plugin->clone(); + newlist.append(p); + } + return newlist; +} + +QList PluginManager::getCompatiblePlugins(QString param, QString category) +{ + QList comp; + for(Plugin *plugin : qAsConst(list)) { + if(!PluginFilter::pluginInCategory(plugin, category)) + continue; + bool enable = (!PluginFilter::pluginInExclusionList(comp, plugin)); + bool forcedInclusion = (PluginFilter::pluginForcedInclusionList(comp, plugin)); + + if(plugin->compatible(param, category) || forcedInclusion) { + Plugin *p = plugin->clone(); + p->setParam(param, category); + p->setEnabled(enable); + comp.append(p); + } + } + return comp; +} + +void PluginManager::setMetadata(QJsonObject metadata) { m_metadata = metadata; } + +Plugin *PluginManager::loadPlugin(QString file) +{ + bool ret; + Plugin *original = nullptr; + Plugin *clone = nullptr; + + if(!QFile::exists(file)) + return nullptr; + + if(!QLibrary::isLibrary(file)) + return nullptr; + + QPluginLoader qp(file); + ret = qp.load(); + if(!ret) { + qWarning(CAT_PLUGINMANAGER) << "Cannot load library " + qp.fileName() + "- err: " + qp.errorString(); + return nullptr; + } + + QObject *inst = qp.instance(); + if(!inst) { + qWarning(CAT_PLUGINMANAGER) << "Cannot create QObject instance from loaded library"; + return nullptr; + } + + original = qobject_cast(qp.instance()); + if(!original) { + qWarning(CAT_PLUGINMANAGER) << "Loaded library instance is not a Plugin*"; + return nullptr; + } + + clone = original->clone(this); + if(!clone) { + qWarning(CAT_PLUGINMANAGER) << "clone method does not clone the object"; + return nullptr; + } + + QString cloneName; + cloneName = clone->name(); + + if(cloneName == "") + return nullptr; + + return clone; +} + +QList PluginManager::getOriginalPlugins() const { return list; } + +QJsonObject PluginManager::metadata() const { return m_metadata; } + +#include "moc_pluginmanager.cpp" diff --git a/core/src/pluginrepository.cpp b/core/src/pluginrepository.cpp new file mode 100644 index 0000000000..414deb3bb7 --- /dev/null +++ b/core/src/pluginrepository.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "pluginrepository.h" + +#ifdef Q_OS_WINDOWS +#include +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_PLUGINREPOSTIORY, "PluginRepository"); + +using namespace scopy; +PluginRepository::PluginRepository(QObject *parent) + : QObject(parent) +{ + pm = new PluginManager(this); +} + +PluginRepository::~PluginRepository() {} + +void PluginRepository::init(QString location) +{ + qInfo(CAT_PLUGINREPOSTIORY) << "initializing plugins from: " << location; + const QString pluginMetaFileName = "plugin.json"; + QString pluginMetaFilePath = ""; + QDir loc(location); + + QFileInfoList plugins = loc.entryInfoList(QDir::Files); + QStringList pluginFiles; + + for(const QFileInfo &p : plugins) { + if(p.fileName() == pluginMetaFileName) { + pluginMetaFilePath = p.absoluteFilePath(); + continue; + } + pluginFiles.append(p.absoluteFilePath()); + } + + if(!pluginMetaFilePath.isEmpty()) { + QFile f(pluginMetaFilePath); + f.open(QFile::ReadOnly); + QString json = f.readAll(); + QJsonParseError err; + QJsonDocument pluginMetaDocument = QJsonDocument::fromJson(json.toUtf8(), &err); + if(err.error != QJsonParseError::NoError) { + qCritical(CAT_PLUGINREPOSTIORY) << "JSON Parse error !" << err.errorString(); + qCritical(CAT_PLUGINREPOSTIORY) << json; + qCritical(CAT_PLUGINREPOSTIORY) << QString(" ").repeated(err.offset) + "^"; + } else { + qDebug(CAT_PLUGINREPOSTIORY) << "Found valid json @ " << pluginMetaFilePath; + } + metadata = pluginMetaDocument.object(); + pm->setMetadata(metadata); + } + +#ifdef Q_OS_WINDOWS + bool b = SetDllDirectoryA(QApplication::applicationDirPath().toStdString().c_str()); + if(!b) { + DWORD error = ::GetLastError(); + std::string message = std::system_category().message(error); + qWarning(CAT_PLUGINREPOSTIORY) + << "cannot add .exe folder to library search path - " << QString::fromStdString(message); + ; + } +#endif + + pm->clear(); + pm->add(pluginFiles); + pm->sort(); +} + +#include "moc_pluginrepository.cpp" diff --git a/core/src/scanbuttoncontroller.cpp b/core/src/scanbuttoncontroller.cpp new file mode 100644 index 0000000000..3756e064d2 --- /dev/null +++ b/core/src/scanbuttoncontroller.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "scanbuttoncontroller.h" + +using namespace scopy; + +ScanButtonController::ScanButtonController(CyclicalTask *cs, QCheckBox *btn, QObject *parent) + : QObject{parent} +{ + this->cs = cs; + this->btn = btn; + m_scanTimeout = 2000; + conn = connect(this->btn, SIGNAL(toggled(bool)), this, SLOT(enableScan(bool))); +} +ScanButtonController::~ScanButtonController() { disconnect(conn); } + +void ScanButtonController::enableScan(bool b) +{ + if(b) { + startScan(); + } else { + stopScan(); + } +} + +void ScanButtonController::startScan() +{ + cs->start(m_scanTimeout); + btn->setChecked(true); +} + +void ScanButtonController::stopScan() +{ + cs->stop(); + btn->setChecked(false); +} + +int ScanButtonController::scanTimeout() const { return m_scanTimeout; } + +void ScanButtonController::setScanTimeout(int newScanTimeout) { m_scanTimeout = newScanTimeout; } + +#include "moc_scanbuttoncontroller.cpp" diff --git a/core/src/scannediiocontextcollector.cpp b/core/src/scannediiocontextcollector.cpp new file mode 100644 index 0000000000..fee716f975 --- /dev/null +++ b/core/src/scannediiocontextcollector.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "scannediiocontextcollector.h" + +#include +#include + +Q_LOGGING_CATEGORY(CAT_SCANCTXCOLLECTOR, "ScannedIIOContextCollector") + +using namespace scopy; +ScannedIIOContextCollector::ScannedIIOContextCollector(QObject *parent) + : QObject{parent} +{ + qDebug(CAT_SCANCTXCOLLECTOR) << "ctor"; +} + +ScannedIIOContextCollector::~ScannedIIOContextCollector() { qDebug(CAT_SCANCTXCOLLECTOR) << "dtor"; } + +void ScannedIIOContextCollector::update(QVector> ctxsDescription) +{ + // Do we need to map Device* to uri in this class ? + QSet updatedUris; + for(const auto &pair : ctxsDescription) { + // pair.first = description, pair.second = uri + updatedUris.insert(pair.second); + } + + updatedUris = updatedUris + lockedUris; + + auto newUris = updatedUris - uris; + auto deletedUris = uris - updatedUris; + + qDebug(CAT_SCANCTXCOLLECTOR) << "cached uris:" << uris; + for(const auto &uri : newUris) { + qInfo(CAT_SCANCTXCOLLECTOR) << "new device found: " << uri; + // if(!lockedUris.contains(uri)) + Q_EMIT foundDevice("iio", uri); + } + + for(const auto &uri : deletedUris) { + qInfo(CAT_SCANCTXCOLLECTOR) << "to delete device: " << uri; + // if(!lockedUris.contains(uri)) + Q_EMIT lostDevice("iio", uri); + } + uris = updatedUris; +} + +void ScannedIIOContextCollector::removeDevice(QString id, Device *d) { uris.remove(d->param()); } + +void ScannedIIOContextCollector::lock(QString uri, Device *d) +{ + + if(uris.contains(d->param())) + lockedUris.insert(d->param()); +} + +void ScannedIIOContextCollector::unlock(QString uri, Device *d) +{ + if(uris.contains(d->param())) + lockedUris.remove(d->param()); +} + +void ScannedIIOContextCollector::clearCache() { uris.clear(); } + +#include "moc_scannediiocontextcollector.cpp" diff --git a/core/src/scopyaboutpage.cpp b/core/src/scopyaboutpage.cpp new file mode 100644 index 0000000000..56bbc392cc --- /dev/null +++ b/core/src/scopyaboutpage.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "scopyaboutpage.h" + +#include "widgets/hoverwidget.h" +#include "widgets/pagenavigationwidget.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace scopy; +ScopyAboutPage::ScopyAboutPage(QWidget *parent) + : QWidget(parent) + , tabWidget(new VerticalTabWidget(this)) + , layout(new QVBoxLayout(this)) +{ + initUI(); + addHorizontalTab(buildPage(QString("qrc:/about.html")), "Scopy"); +} + +void ScopyAboutPage::initUI() +{ + layout->setMargin(0); + layout->setSpacing(0); + this->setLayout(layout); + layout->addWidget(tabWidget); +} + +QWidget *ScopyAboutPage::buildPage(QString src) +{ + QWidget *page = new QWidget(this); + QVBoxLayout *lay = new QVBoxLayout(page); + QTextBrowser *browser = new QTextBrowser(page); + browser->setOpenExternalLinks(true); + + lay->addWidget(browser); + lay->setMargin(0); + initNavigationWidget(browser); + Style::setStyle(browser, style::properties::widget::textBrowser); + + if(QFile::exists(QString(src).replace("qrc:/", ":/"))) { + browser->setSource(src); + } else { + browser->setMarkdown(src); + } + + return page; +} + +void ScopyAboutPage::initNavigationWidget(QTextBrowser *browser) +{ + PageNavigationWidget *navWidget = new PageNavigationWidget(true, false, this); + QPushButton *homeButton = navWidget->getHomeBtn(); + connect(homeButton, SIGNAL(clicked()), browser, SLOT(home())); + + QPushButton *backwardButton = navWidget->getBackwardBtn(); + backwardButton->setEnabled(false); + connect(backwardButton, SIGNAL(clicked()), browser, SLOT(backward())); + connect(browser, &QTextBrowser::backwardAvailable, backwardButton, + [=](bool available) { backwardButton->setEnabled(available); }); + + QPushButton *forwardButton = navWidget->getForwardBtn(); + forwardButton->setEnabled(false); + connect(forwardButton, SIGNAL(clicked()), browser, SLOT(forward())); + connect(browser, &QTextBrowser::forwardAvailable, forwardButton, + [=](bool available) { forwardButton->setEnabled(available); }); + + HoverWidget *hover = new HoverWidget(navWidget, browser, browser); + hover->setAnchorPos(HoverPosition::HP_TOPRIGHT); + hover->setContentPos(HoverPosition::HP_BOTTOMLEFT); + hover->setAnchorOffset(QPoint(-10, 0)); + hover->show(); +} + +void ScopyAboutPage::addHorizontalTab(QWidget *w, QString text) { tabWidget->addTab(w, text); } + +ScopyAboutPage::~ScopyAboutPage() {} + +#include "moc_scopyaboutpage.cpp" diff --git a/core/src/scopyhomeaddpage.cpp b/core/src/scopyhomeaddpage.cpp new file mode 100644 index 0000000000..aac6db7840 --- /dev/null +++ b/core/src/scopyhomeaddpage.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "scopyhomeaddpage.h" +#include "devicefactory.h" +#include "deviceloader.h" + +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_HOME_ADD_PAGE, "ScopyHomeAddPage") + +using namespace scopy; + +ScopyHomeAddPage::ScopyHomeAddPage(QWidget *parent, PluginManager *pm) + : QWidget(parent) + , m_pluginManager(pm) + , m_deviceImpl(nullptr) +{ + setProperty("device_page", true); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QVBoxLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + + m_stackedWidget = new QStackedWidget(this); + m_tabWidget = createTabWidget(m_stackedWidget); + m_addPage = createAddPage(m_stackedWidget); + m_stackedWidget->addWidget(m_tabWidget); + m_stackedWidget->addWidget(m_addPage); + m_stackedWidget->setCurrentWidget(m_tabWidget); + + layout->addWidget(m_stackedWidget); + m_pendingUri = ""; + + connect(m_addBtn, &QPushButton::clicked, this, &ScopyHomeAddPage::addBtnClicked); + connect(m_backBtn, &QPushButton::clicked, this, &ScopyHomeAddPage::backBtnClicked); + + // verify iio device + m_fw = new QFutureWatcher(this); + connect(m_fw, &QFutureWatcher::finished, this, &ScopyHomeAddPage::onVerifyFinished, Qt::QueuedConnection); + connect(this, &ScopyHomeAddPage::verifyFinished, m_iioTabWidget, &IioTabWidget::onVerifyFinished); + connect(m_iioTabWidget, &IioTabWidget::startVerify, this, &ScopyHomeAddPage::futureVerify); + + connect(m_emuWidget, &EmuWidget::emuDeviceAvailable, this, &ScopyHomeAddPage::onEmuDeviceAvailable); + + connect(m_stackedWidget, &QStackedWidget::currentChanged, this, [=]() { + if(m_stackedWidget->currentWidget() == m_addPage) { + m_addBtn->setFocus(); + } + }); +} + +ScopyHomeAddPage::~ScopyHomeAddPage() +{ + if(m_deviceImpl) { + delete m_deviceImpl; + m_deviceImpl = nullptr; + } +} + +void ScopyHomeAddPage::futureVerify(QString uri, QString cat) +{ + removePluginsCheckBoxes(); + m_deviceInfoPage->clear(); + m_deviceImpl = DeviceFactory::build(uri, m_pluginManager, cat); + QFuture f = QtConcurrent::run(std::bind(&DeviceImpl::verify, m_deviceImpl)); + m_fw->setFuture(f); +} + +void ScopyHomeAddPage::onVerifyFinished() +{ + bool result = m_fw->result(); + if(result) { + loadDeviceInfoPage(); + initializeDevice(); + } else { + if(m_deviceImpl) { + delete m_deviceImpl; + m_deviceImpl = nullptr; + } + Q_EMIT verifyFinished(result); + } +} + +void ScopyHomeAddPage::loadDeviceInfoPage() +{ + QMap deviceInfoMap = m_deviceImpl->readDeviceInfo(); + foreach(const QString &key, deviceInfoMap.keys()) { + m_deviceInfoPage->update(key, deviceInfoMap[key]); + } +} + +void ScopyHomeAddPage::initializeDevice() +{ + if(m_deviceImpl) { + DeviceLoader *dl = new DeviceLoader(m_deviceImpl, this); + dl->init(); + connect(dl, &DeviceLoader::initialized, this, &ScopyHomeAddPage::deviceLoaderInitialized); + connect(dl, &DeviceLoader::initialized, dl, + &QObject::deleteLater); // don't forget to delete loader once we're done + } +} + +void ScopyHomeAddPage::deviceLoaderInitialized() +{ + QList plugins = m_deviceImpl->plugins(); + for(Plugin *p : qAsConst(plugins)) { + PluginEnableWidget *pluginDescription = new PluginEnableWidget(m_pluginBrowserSection); + pluginDescription->setDescription(p->description()); + pluginDescription->checkBox()->setText(p->name()); + pluginDescription->checkBox()->setChecked(p->enabled()); + m_pluginBrowserSection->contentLayout()->addWidget(pluginDescription); + m_pluginDescriptionList.push_back(pluginDescription); + connect(pluginDescription->checkBox(), &QCheckBox::toggled, this, [=](bool en) { p->setEnabled(en); }); + } + m_stackedWidget->setCurrentWidget(m_addPage); + Q_EMIT verifyFinished(true); +} + +void ScopyHomeAddPage::addBtnClicked() +{ + bool connection = m_deviceImpl->verify(); + if(!connection) { + m_connLostLabel->setText("Connection with " + m_deviceImpl->param() + " has been lost!"); + return; + } + m_pendingUri = m_deviceImpl->param(); + m_connLostLabel->clear(); + Q_EMIT newDeviceAvailable(m_deviceImpl); +} + +void ScopyHomeAddPage::deviceAddedToUi(QString id) +{ + if(!m_pendingUri.isEmpty()) { + m_deviceImpl = nullptr; + m_iioTabWidget->updateUri(""); + m_stackedWidget->setCurrentWidget(m_tabWidget); + Q_EMIT requestDevice(id); + m_pendingUri = ""; + } +} + +void ScopyHomeAddPage::backBtnClicked() +{ + if(m_deviceImpl) { + delete m_deviceImpl; + m_deviceImpl = nullptr; + } + m_connLostLabel->clear(); + m_stackedWidget->setCurrentWidget(m_tabWidget); +} + +void ScopyHomeAddPage::onEmuDeviceAvailable(QString uri) +{ + m_tabWidget->setCurrentWidget(m_iioTabWidget); + m_iioTabWidget->updateUri(uri); +} + +void ScopyHomeAddPage::removePluginsCheckBoxes() +{ + qDeleteAll(m_pluginDescriptionList); + m_pluginDescriptionList.clear(); +} + +QTabWidget *ScopyHomeAddPage::createTabWidget(QWidget *parent) +{ + QTabWidget *tabWidget = new QTabWidget(parent); + m_iioTabWidget = new IioTabWidget(tabWidget); + tabWidget->addTab(m_iioTabWidget, "IIO"); + m_emuWidget = new EmuWidget(tabWidget); + tabWidget->addTab(m_emuWidget, "Emulator"); + tabWidget->setCurrentWidget(m_iioTabWidget); + return tabWidget; +} + +QWidget *ScopyHomeAddPage::createInfoSection(QWidget *parent) +{ + QScrollArea *infoScrollArea = new QScrollArea(parent); + QWidget *infoSection = new QWidget(infoScrollArea); + infoSection->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QVBoxLayout *layInfoSection = new QVBoxLayout(infoSection); + layInfoSection->setSpacing(10); + layInfoSection->setMargin(0); + infoSection->setLayout(layInfoSection); + + infoScrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + infoScrollArea->setWidgetResizable(true); + infoScrollArea->setWidget(infoSection); + + MenuCollapseSection *deviceInfoSection = new MenuCollapseSection( + "Device info", MenuCollapseSection::MHCW_ONOFF, MenuCollapseSection::MHW_BASEWIDGET, infoSection); + deviceInfoSection->contentLayout()->setSpacing(10); + deviceInfoSection->contentLayout()->setMargin(0); + + m_deviceInfoPage = new InfoPage(deviceInfoSection); + m_deviceInfoPage->setAdvancedMode(false); + m_deviceInfoPage->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + deviceInfoSection->contentLayout()->addWidget(m_deviceInfoPage); + + m_pluginBrowserSection = new MenuCollapseSection("Compatible plugins", MenuCollapseSection::MHCW_ONOFF, + MenuCollapseSection::MHW_BASEWIDGET, infoSection); + m_pluginBrowserSection->contentLayout()->setSpacing(10); + m_pluginBrowserSection->contentLayout()->setMargin(0); + + layInfoSection->addWidget(deviceInfoSection); + layInfoSection->addWidget(m_pluginBrowserSection); + layInfoSection->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); + return infoScrollArea; +} + +QWidget *ScopyHomeAddPage::createBtnsWidget(QWidget *parent) +{ + QWidget *btnsWidget = new QWidget(parent); + QHBoxLayout *btnsLay = new QHBoxLayout(btnsWidget); + btnsLay->setMargin(0); + btnsLay->setAlignment(Qt::AlignRight); + + m_backBtn = new QPushButton(btnsWidget); + m_backBtn->setText("BACK"); + StyleHelper::BasicButton(m_backBtn); + m_backBtn->setFixedWidth(Style::getDimension(json::global::unit_6)); + + m_addBtn = new QPushButton(btnsWidget); + m_addBtn->setText("ADD DEVICE"); + m_addBtn->setAutoDefault(true); + StyleHelper::BasicButton(m_addBtn); + m_addBtn->setFixedWidth(Style::getDimension(json::global::unit_6)); + + btnsLay->addWidget(m_backBtn); + btnsLay->addWidget(m_addBtn); + return btnsWidget; +} + +QWidget *ScopyHomeAddPage::createAddPage(QWidget *parent) +{ + QWidget *addPage = new QWidget(parent); + addPage->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QVBoxLayout *addPageLay = new QVBoxLayout(addPage); + addPageLay->setSpacing(10); + addPage->setLayout(addPageLay); + + QWidget *infoSection = createInfoSection(addPage); + QWidget *buttons = createBtnsWidget(addPage); + m_connLostLabel = new QLabel(parent); + m_connLostLabel->setText(""); + + addPageLay->addItem(new QSpacerItem(20, 40, QSizePolicy::Preferred, QSizePolicy::Preferred)); + addPageLay->addWidget(infoSection); + addPageLay->addWidget(m_connLostLabel); + addPageLay->addWidget(buttons); + return addPage; +} + +#include "moc_scopyhomeaddpage.cpp" diff --git a/core/src/scopyhomeinfopage.cpp b/core/src/scopyhomeinfopage.cpp new file mode 100644 index 0000000000..0af0ff0527 --- /dev/null +++ b/core/src/scopyhomeinfopage.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "scopyhomeinfopage.h" +#include "style.h" + +#include "ui_scopyhomeinfopage.h" + +#include + +#include + +using namespace scopy; + +ScopyHomeInfoPage::ScopyHomeInfoPage(QWidget *parent) + : QWidget(parent) + , ui(new Ui::ScopyHomeInfoPage) +{ + ui->setupUi(this); + // initReportButton(); +} + +ScopyHomeInfoPage::~ScopyHomeInfoPage() { delete ui; } + +void ScopyHomeInfoPage::initReportButton() +{ + auto reportButton = new QPushButton("Report a bug"); + StyleHelper::BasicButton(reportButton, "reportButton"); + reportButton->setFixedSize(100, 40); + + auto reportBtnHoverWidget = new HoverWidget(reportButton, ui->textBrowser, this); + reportBtnHoverWidget->setContentPos(HP_TOPLEFT); + reportBtnHoverWidget->setAnchorPos(HP_BOTTOMRIGHT); + reportBtnHoverWidget->setAnchorOffset(QPoint(-10, -10)); + reportBtnHoverWidget->setVisible(true); + reportBtnHoverWidget->raise(); + + connect(reportButton, &QPushButton::clicked, []() { + const QUrl url("https://wiki.analog.com/university/tools/m2k/scopy/report"); + QDesktopServices::openUrl(url); + }); +} + +#include "moc_scopyhomeinfopage.cpp" diff --git a/core/src/scopyhomepage.cpp b/core/src/scopyhomepage.cpp new file mode 100644 index 0000000000..17ec1876af --- /dev/null +++ b/core/src/scopyhomepage.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "scopyhomepage.h" + +#include "scopyhomeinfopage.h" + +#include "ui_scopyhomepage.h" + +#include + +#include +#include + +using namespace scopy; +ScopyHomePage::ScopyHomePage(QWidget *parent, PluginManager *pm) + : QWidget(parent) + , ui(new Ui::ScopyHomePage) +{ + ui->setupUi(this); + auto &&is = ui->wInfoPageStack; + auto &&hc = is->getHomepageControls(); + auto &&db = ui->wDeviceBrowser; + add = new ScopyHomeAddPage(this, pm); + + ui->wInfoPageStack->setStyleSheet(".QWidget {border-radius: " + Style::getAttribute(json::global::radius_1) + + ";}"); + ui->container->setStyleSheet(".QWidget#container { background-color: " + + Style::getAttribute(json::theme::background_subtle) + "; }"); + Style::setStyle(ui->horizontalLayout_2, style::properties::widget::basicComponent); + Style::setStyle(ui->wInfoPageStack, style::properties::widget::basicComponent); + + is->add("home", new ScopyHomeInfoPage()); + is->add("add", add); + + // addDevice("dev1","dev1","descr1",new QPushButton("abc"),new QLabel("page1")); + + Style::setStyle(scanBtn(), style::properties::button::basicButton); + scanBtn()->setFixedWidth(Style::getDimension(json::global::unit_5)); + connect(hc, SIGNAL(goLeft()), db, SLOT(prevDevice())); + connect(hc, SIGNAL(goRight()), db, SLOT(nextDevice())); + connect(db, SIGNAL(requestDevice(QString, int)), is, SLOT(slideInKey(QString, int))); + connect(db, SIGNAL(requestDevice(QString, int)), this, SIGNAL(requestDevice(QString))); + connect(this, SIGNAL(deviceAddedToUi(QString)), add, SLOT(deviceAddedToUi(QString))); + + connect(add, &ScopyHomeAddPage::requestDevice, this, [=](QString id) { Q_EMIT db->requestDevice(id, -1); }); + connect(add, &ScopyHomeAddPage::newDeviceAvailable, this, [=](DeviceImpl *d) { Q_EMIT newDeviceAvailable(d); }); + + connect(db, &DeviceBrowser::displayNameChanged, this, &ScopyHomePage::displayNameChanged); +} + +ScopyHomePage::~ScopyHomePage() { delete ui; } + +void ScopyHomePage::addDevice(QString id, Device *d) +{ + auto &&is = ui->wInfoPageStack; + auto &&db = ui->wDeviceBrowser; + db->addDevice(id, d); + is->add(id, d); + Q_EMIT deviceAddedToUi(id); +} + +void ScopyHomePage::removeDevice(QString id) +{ + auto &&is = ui->wInfoPageStack; + auto &&db = ui->wDeviceBrowser; + db->removeDevice(id); + is->remove(id); +} + +void ScopyHomePage::viewDevice(QString id) +{ + auto &&db = ui->wDeviceBrowser; + Q_EMIT db->requestDevice(id, -1); +} + +void ScopyHomePage::connectingDevice(QString id) +{ + auto &&db = ui->wDeviceBrowser; + db->connectingDevice(id); +} + +void ScopyHomePage::connectDevice(QString id) +{ + auto &&db = ui->wDeviceBrowser; + db->connectDevice(id); +} +void ScopyHomePage::disconnectDevice(QString id) +{ + auto &&db = ui->wDeviceBrowser; + db->disconnectDevice(id); +} + +void ScopyHomePage::setScannerEnable(bool b) +{ + ui->btnScan->setVisible(b); + ui->label_2->setVisible(b); + ui->btnScanNow->setVisible(!b); +} +QCheckBox *ScopyHomePage::scanControlBtn() { return ui->btnScan; } + +QPushButton *ScopyHomePage::scanBtn() { return ui->btnScanNow; } + +#include "moc_scopyhomepage.cpp" diff --git a/core/src/scopymainwindow.cpp b/core/src/scopymainwindow.cpp new file mode 100644 index 0000000000..f3c6b3f016 --- /dev/null +++ b/core/src/scopymainwindow.cpp @@ -0,0 +1,646 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "logging_categories.h" +#include "qmessagebox.h" +#include "scopymainwindow.h" +#include "animationmanager.h" + +#include "scanbuttoncontroller.h" +#include "ui_scopymainwindow.h" +#include "scopyhomepage.h" +#include "scopyaboutpage.h" +#include "scopypreferencespage.h" +#include "device.h" + +#include +#include "application_restarter.h" +#include "pluginbase/preferences.h" +#include "pluginbase/scopyjs.h" +#include "iioutil/connectionprovider.h" +#include "pluginbase/messagebroker.h" +#include "scopy-core_config.h" +#include "pluginbase/statusbarmanager.h" +#include "scopytitlemanager.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace scopy; +using namespace scopy::gui; + +Q_LOGGING_CATEGORY(CAT_SCOPY, "Scopy") + +ScopyMainWindow::ScopyMainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::ScopyMainWindow) + , m_glLoader(nullptr) +{ + QElapsedTimer timer; + timer.start(); + initPreferences(); + ui->setupUi(this); + + ScopyTitleManager::setMainWindow(this); + ScopyTitleManager::setApplicationName("Scopy"); + ScopyTitleManager::setScopyVersion("v" + QString(scopy::config::version())); + ScopyTitleManager::setGitHash(QString(SCOPY_VERSION_GIT)); + + IIOUnitsManager::GetInstance(); + setAttribute(Qt::WA_QuitOnClose, true); + initPythonWIN32(); + initStatusBar(); + setupPreferences(); + + ConnectionProvider::GetInstance(); + MessageBroker::GetInstance(); + + // get the version document + auto vc = VersionChecker::GetInstance(); // get VersionCache instance + vc->subscribe(this, + &ScopyMainWindow::receiveVersionDocument); // 'subscribe' to receive the version QJsonDocument + + auto ts = ui->wsToolStack; + + //////// + BrowseMenu *browseMenu = new BrowseMenu(ui->wToolBrowser); + ui->wToolBrowser->layout()->addWidget(browseMenu); + + connect(browseMenu, &BrowseMenu::requestTool, ts, &ToolStack::show, Qt::QueuedConnection); + connect(browseMenu, SIGNAL(requestLoad()), this, SLOT(load())); + connect(browseMenu, SIGNAL(requestSave()), this, SLOT(save())); + connect(browseMenu, &BrowseMenu::collapsed, this, [this](bool coll) { + if(coll) { + ui->animHolder->setAnimMin(Style::getDimension(json::global::unit_4)); + } else { + ui->animHolder->setAnimMax(230); + } + ui->animHolder->toggleMenu(!coll); + }); + //////// + Style::setBackgroundColor(ui->centralwidget, json::theme::background_primary); + + scanTask = new IIOScanTask(this); + scanTask->setScanParams("usb"); + scanCycle = new CyclicalTask(scanTask, this); + scc = new ScannedIIOContextCollector(this); + pr = new PluginRepository(this); + loadPluginsFromRepository(pr); + + PluginManager *pm = pr->getPluginManager(); + + initAboutPage(pm); + initPreferencesPage(pm); + initTranslations(); + + hp = new ScopyHomePage(this, pm); + m_sbc = new ScanButtonController(scanCycle, hp->scanControlBtn(), this); + connect(hp->scanBtn(), &QPushButton::clicked, this, [=]() { scanTask->run(); }); + + DeviceAutoConnect::initPreferences(); + dm = new DeviceManager(pm, this); + bool general_connect_to_multiple_devices = Preferences::get("general_connect_to_multiple_devices").toBool(); + dm->setExclusive(!general_connect_to_multiple_devices); + + dtm = new DetachedToolWindowManager(this); + m_toolMenuManager = new ToolMenuManager(ts, dtm, browseMenu->toolMenu(), this); + + ts->add("home", hp); + ts->add("about", about); + ts->add("preferences", prefPage); + + connect(scanTask, &IIOScanTask::scanFinished, scc, &ScannedIIOContextCollector::update, Qt::QueuedConnection); + + connect(scc, SIGNAL(foundDevice(QString, QString)), dm, SLOT(createDevice(QString, QString))); + connect(scc, SIGNAL(lostDevice(QString, QString)), dm, SLOT(removeDevice(QString, QString))); + + connect(hp, SIGNAL(requestDevice(QString)), this, SLOT(requestTools(QString))); + + connect(dm, SIGNAL(deviceAdded(QString, Device *)), this, SLOT(addDeviceToUi(QString, Device *))); + + connect(dm, SIGNAL(deviceRemoveStarted(QString, Device *)), scc, SLOT(removeDevice(QString, Device *))); + connect(dm, SIGNAL(deviceRemoveStarted(QString, Device *)), this, SLOT(removeDeviceFromUi(QString))); + + connect(dm, SIGNAL(deviceConnecting(QString)), hp, SLOT(connectingDevice(QString))); + + connect(dm, &DeviceManager::deviceConnecting, this, [=]() { handleScanner(); }); + connect(dm, &DeviceManager::deviceConnected, this, [=]() { handleScanner(); }); + connect(dm, &DeviceManager::deviceDisconnecting, this, [=]() { handleScanner(); }); + connect(dm, &DeviceManager::deviceDisconnected, this, [=]() { handleScanner(); }); + + connect(dm, SIGNAL(deviceConnected(QString, Device *)), scc, SLOT(lock(QString, Device *))); + connect(dm, SIGNAL(deviceConnected(QString, Device *)), hp, SLOT(connectDevice(QString))); + connect(dm, SIGNAL(deviceDisconnected(QString, Device *)), scc, SLOT(unlock(QString, Device *))); + connect(dm, SIGNAL(deviceDisconnected(QString, Device *)), hp, SLOT(disconnectDevice(QString))); + + connect(dm, SIGNAL(requestDevice(QString)), hp, SLOT(viewDevice(QString))); + + if(Preferences::get("general_scan_for_devices").toBool()) { + scanTask->run(); + } + + enableScanner(); + + connect(dm, &DeviceManager::deviceChangedToolList, m_toolMenuManager, &ToolMenuManager::changeToolListContents); + connect(dm, SIGNAL(deviceConnected(QString, Device *)), m_toolMenuManager, SLOT(deviceConnected(QString))); + connect(dm, SIGNAL(deviceDisconnected(QString, Device *)), m_toolMenuManager, + SLOT(deviceDisconnected(QString))); + connect(dm, &DeviceManager::requestTool, m_toolMenuManager, &ToolMenuManager::showMenuItem); + connect(m_toolMenuManager, &ToolMenuManager::requestToolSelect, ts, &ToolStack::show); + connect(m_toolMenuManager, &ToolMenuManager::requestToolSelect, dtm, &DetachedToolWindowManager::show); + connect(hp, &ScopyHomePage::displayNameChanged, m_toolMenuManager, &ToolMenuManager::onDisplayNameChanged); + + connect(hp, &ScopyHomePage::newDeviceAvailable, dm, &DeviceManager::addDevice); + + connect(prefPage, &ScopyPreferencesPage::refreshDevicesPressed, dm, &DeviceManager::requestConnectedDev); + connect(dm, &DeviceManager::connectedDevices, prefPage, &ScopyPreferencesPage::updateSessionDevices); + + initApi(); +#ifdef SCOPY_DEV_MODE + // this is an example of how autoconnect is done + + // auto id = api->addDevice("ip:127.0.0.1", "m2k"); + // auto id = api->addDevice("ip:10.48.65.163", "iio"); + auto id = api->addDevice("ip:192.168.2.1", "iio"); + // auto id = api->addDevice("", "test"); + + api->connectDevice(id); + // api->switchTool(id, "Time"); +#endif + if(Preferences::get("autoconnect_previous").toBool()) + deviceAutoconnect(); + prefPage->initSessionDevices(); + + qInfo(CAT_BENCHMARK) << "ScopyMainWindow constructor took: " << timer.elapsed() << "ms"; +} + +void ScopyMainWindow::initStatusBar() +{ + // clear all margin, except the bottom one, to make room the status bar + statusBar = new ScopyStatusBar(this); + ui->mainWidget->layout()->addWidget(statusBar); +} + +void ScopyMainWindow::handleScanner() +{ + if(Preferences::get("general_scan_for_devices").toBool()) { + if(dm->busy()) { + m_sbc->enableScan(false); + return; + } + if(dm->getExclusive() && dm->connectedDeviceCount() == 1) { + m_sbc->enableScan(false); + return; + } + m_sbc->enableScan(true); + } else { + m_sbc->enableScan(false); + } +} + +void ScopyMainWindow::enableScanner() +{ + bool b = Preferences::get("general_scan_for_devices").toBool(); + hp->setScannerEnable(b); + handleScanner(); +} + +void ScopyMainWindow::deviceAutoconnect() +{ + QMap devicesMap = Preferences::get("autoconnect_devices").toMap(); + const QStringList &keys = devicesMap.keys(); + for(const QString &uri : keys) { + QStringList plugins = devicesMap[uri].toString().split(";"); + auto id = api->addDevice(uri, plugins, "iio"); + if(id.isEmpty()) { + DeviceAutoConnect::removeDevice(uri); + } else { + api->connectDevice(id); + } + } +} + +void ScopyMainWindow::save() +{ + QString selectedFilter; + QString fileName = QFileDialog::getSaveFileName(this, tr("Save"), "", "", &selectedFilter); + save(fileName); + ScopyTitleManager::setIniFileName(fileName); +} + +void ScopyMainWindow::load() +{ + QString selectedFilter; + QString fileName = QFileDialog::getOpenFileName(this, tr("Open"), "", "", &selectedFilter); + load(fileName); + ScopyTitleManager::setIniFileName(fileName); +} + +void ScopyMainWindow::save(QString file) +{ + QSettings s(file, QSettings::Format::IniFormat); + dm->save(s); + ScopyTitleManager::setIniFileName(file); +} + +void ScopyMainWindow::load(QString file) +{ + QSettings s(file, QSettings::Format::IniFormat); + dm->load(s); + ScopyTitleManager::setIniFileName(file); +} + +void ScopyMainWindow::closeEvent(QCloseEvent *event) +{ + DeviceAutoConnect::clear(); + if(Preferences::get("autoconnect_previous").toBool()) { + dm->saveSessionDevices(); + } + dm->disconnectAll(); +} + +void ScopyMainWindow::requestTools(QString id) { m_toolMenuManager->showMenuItem(id); } + +ScopyMainWindow::~ScopyMainWindow() +{ + scanCycle->stop(); + delete ui; +} + +void ScopyMainWindow::initAboutPage(PluginManager *pm) +{ + QElapsedTimer timer; + timer.start(); + about = new ScopyAboutPage(this); + if(!pm) + return; + QList plugin = pm->getOriginalPlugins(); + for(Plugin *p : plugin) { + QString content = p->about(); + if(!content.isEmpty()) { + about->addHorizontalTab(about->buildPage(content), p->name()); + } + } + qInfo(CAT_BENCHMARK) << " Init about page took: " << timer.elapsed() << "ms"; +} + +void ScopyMainWindow::initPreferencesPage(PluginManager *pm) +{ + prefPage = new ScopyPreferencesPage(this); + if(!pm) + return; + + QList plugin = pm->getOriginalPlugins(); + for(Plugin *p : plugin) { + p->initPreferences(); + if(p->loadPreferencesPage()) { + prefPage->addHorizontalTab(p->preferencesPage(), p->name()); + } + } +} + +void ScopyMainWindow::initTranslations() +{ + TranslationsRepository *t = TranslationsRepository::GetInstance(); + t->loadTranslations(Preferences::GetInstance()->get("general_language").toString()); +} + +void ScopyMainWindow::setupPreferences() +{ + auto p = Preferences::GetInstance(); + if(p->get("general_use_opengl").toBool()) { + m_glLoader = new QOpenGLWidget(this); + } + if(p->get("general_load_decoders").toBool()) { + loadDecoders(); + } + if(p->get("general_show_status_bar").toBool()) { + StatusBarManager::GetInstance()->setEnabled(true); + } + if(p->get("general_first_run").toBool()) { + license = new LicenseOverlay(this); + auto versionCheckInfo = new VersionCheckMessage(this); + + StatusBarManager::pushWidget(versionCheckInfo, "Should Scopy check for online versions?"); + + QMetaObject::invokeMethod(license, &LicenseOverlay::showOverlay, Qt::QueuedConnection); + } +} + +void ScopyMainWindow::initPreferences() +{ + QElapsedTimer timer; + timer.start(); + QString preferencesPath = scopy::config::preferencesFolderPath() + "/preferences.ini"; + Preferences *p = Preferences::GetInstance(); + p->setPreferencesFilename(preferencesPath); + p->load(); + p->init("autoconnect_previous", false); + p->init("general_first_run", true); + p->init("general_save_session", true); + p->init("general_save_attached", true); + p->init("general_doubleclick_attach", true); +#if defined(__linux__) && (defined(__arm__) || defined(__aarch64__)) + p->init("general_use_opengl", false); +#else + p->init("general_use_opengl", true); +#endif + p->init("general_use_animations", true); + p->init("font_scale", "1"); + p->init("general_theme", "Scopy"); + p->init("general_language", "en"); + p->init("show_grid", true); + p->init("show_graticule", false); + p->init("iiowidgets_use_lazy_loading", true); + p->init("general_plot_target_fps", "60"); + p->init("general_show_plot_fps", false); + p->init("general_use_native_dialogs", false); + p->init("general_additional_plugin_path", ""); + p->init("general_load_decoders", true); + p->init("general_doubleclick_ctrl_opens_menu", true); + p->init("general_check_online_version", false); + p->init("general_show_status_bar", true); + p->init("general_connect_to_multiple_devices", true); + p->init("general_scan_for_devices", true); + + connect(p, SIGNAL(preferenceChanged(QString, QVariant)), this, SLOT(handlePreferences(QString, QVariant))); + + Style::GetInstance()->setTheme(Preferences::GetInstance()->get("general_theme").toString()); + + QString theme = p->get("general_theme").toString(); + QString themeName = "scopy-" + theme; + QIcon::setThemeName(themeName); + QIcon::setThemeSearchPaths({":/gui/icons/" + themeName}); + qInfo(CAT_BENCHMARK) << "Init preferences took: " << timer.elapsed() << "ms"; +} + +void ScopyMainWindow::loadOpenGL() +{ + bool disablePref = false; + QOpenGLContext *ct = QOpenGLContext::currentContext(); + if(ct) { + QOpenGLFunctions *glFuncs = ct->functions(); + bool glCtxValid = ct->isValid(); + QString glVersion = QString((const char *)(glFuncs->glGetString(GL_VERSION))); + qInfo(CAT_BENCHMARK) << "GL_VENDOR " << reinterpret_cast(glFuncs->glGetString(GL_VENDOR)); + qInfo(CAT_BENCHMARK) << "GL_RENDERER " + << reinterpret_cast(glFuncs->glGetString(GL_RENDERER)); + qInfo(CAT_BENCHMARK) << "GL_VERSION " << glVersion; + qInfo(CAT_BENCHMARK) << "GL_EXTENSIONS " + << reinterpret_cast(glFuncs->glGetString(GL_EXTENSIONS)); + qInfo(CAT_BENCHMARK) << "QOpenGlContext valid: " << glCtxValid; + if(!glCtxValid || glVersion.compare("2.0.0", Qt::CaseInsensitive) < 0) { + disablePref = true; + } + } else { + qInfo(CAT_BENCHMARK) << "QOpenGlContext is invalid"; + disablePref = true; + } + + qInfo(CAT_BENCHMARK) << "OpenGL load status: " << !disablePref; + if(disablePref) { + Preferences::GetInstance()->set("general_use_opengl", false); + Preferences::GetInstance()->save(); + RestartDialog *restarter = new RestartDialog(this); + restarter->setDescription( + "Scopy uses OpenGL for high performance plot rendering. Valid OpenGL context (>v2.0.0) not " + "detected.\n" + "Restarting will set Scopy rendering mode to software. This option can be changed from the " + "Preferences " + "menu.\n" + "Please visit the Wiki " + "Analog page for troubleshooting."); + connect(restarter, &RestartDialog::restartButtonClicked, [=] { + ApplicationRestarter::triggerRestart(); + restarter->deleteLater(); + }); + QMetaObject::invokeMethod(restarter, &RestartDialog::showDialog, Qt::QueuedConnection); + } + + delete m_glLoader; + m_glLoader = nullptr; +} + +void ScopyMainWindow::loadPluginsFromRepository(PluginRepository *pr) +{ + + QElapsedTimer timer; + timer.start(); + // Check the local build plugins folder first + // Check if directory exists and it's not empty + QDir pathDir(scopy::config::localPluginFolderPath()); + + if(pathDir.exists() && pathDir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries).count() != 0) { + pr->init(scopy::config::localPluginFolderPath()); + } else { + pr->init(scopy::config::defaultPluginFolderPath()); + } + +#ifndef Q_OS_ANDROID + QString pluginAdditionalPath = Preferences::GetInstance()->get("general_additional_plugin_path").toString(); + if(!pluginAdditionalPath.isEmpty()) { + pr->init(pluginAdditionalPath); + } +#endif + + qInfo(CAT_BENCHMARK) << "Loading the plugins from the repository took: " << timer.elapsed() << "ms"; +} + +void ScopyMainWindow::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + Preferences *p = Preferences::GetInstance(); + if(p->get("general_use_opengl").toBool() && m_glLoader) { + loadOpenGL(); + } +} + +void ScopyMainWindow::handlePreferences(QString str, QVariant val) +{ + Preferences *p = Preferences::GetInstance(); + + if(str == "general_use_opengl") { + Q_EMIT p->restartRequired(); + } else if(str == "general_use_animations") { + AnimationManager::getInstance().toggleAnimations(val.toBool()); + } else if(str == "general_theme") { + Q_EMIT p->restartRequired(); + } else if(str == "font_scale") { + Q_EMIT p->restartRequired(); + } else if(str == "general_language") { + Q_EMIT p->restartRequired(); + } else if(str == "general_show_status_bar") { + StatusBarManager::GetInstance()->setEnabled(val.toBool()); + } else if(str == "plugins_use_debugger_v2") { + Q_EMIT p->restartRequired(); + } else if(str == "general_connect_to_multiple_devices") { + bool general_connect_to_multiple_devices = + Preferences::get("general_connect_to_multiple_devices").toBool(); + dm->setExclusive(!general_connect_to_multiple_devices); + } else if(str == "general_scan_for_devices") { + enableScanner(); + } +} + +void ScopyMainWindow::initPythonWIN32() +{ +#ifdef WIN32 + QString pythonhome; + QString pythonpath; + + pythonpath += QCoreApplication::applicationDirPath() + "\\" + PYTHON_VERSION + ";"; + pythonpath += QCoreApplication::applicationDirPath() + "\\" + PYTHON_VERSION + "\\plat-win;"; + pythonpath += QCoreApplication::applicationDirPath() + "\\" + PYTHON_VERSION + "\\lib-dynload;"; + pythonpath += QCoreApplication::applicationDirPath() + "\\" + PYTHON_VERSION + "\\site-packages;"; + QString scopypythonpath = qgetenv("SCOPY_PYTHONPATH"); + pythonpath += scopypythonpath; + +#ifdef SCOPY_DEV_MODE + pythonhome += QString(BUILD_PYTHON_LIBRARY_DIRS) + ";"; + pythonpath += QString(BUILD_PYTHON_LIBRARY_DIRS) + ";"; + pythonpath += QString(BUILD_PYTHON_LIBRARY_DIRS) + "\\plat-win;"; + pythonpath += QString(BUILD_PYTHON_LIBRARY_DIRS) + "\\lib-dynload;"; + pythonpath += QString(BUILD_PYTHON_LIBRARY_DIRS) + "\\site-packages;"; +#endif + + qputenv("PYTHONHOME", pythonhome.toLocal8Bit()); + qputenv("PYTHONPATH", pythonpath.toLocal8Bit()); + + qInfo(CAT_SCOPY) << "SCOPY_PYTHONPATH: " << scopypythonpath; + qInfo(CAT_SCOPY) << "PYTHONHOME: " << qgetenv("PYTHONHOME"); + qInfo(CAT_SCOPY) << "PYTHONPATH: " << qgetenv("PYTHONPATH"); +#endif +} + +void ScopyMainWindow::loadDecoders() +{ + QElapsedTimer timer; + timer.start(); +#if defined(WITH_SIGROK) && defined(WITH_PYTHON) +#if defined __APPLE__ + QString path = QCoreApplication::applicationDirPath() + "/decoders"; +#elif defined(__appimage__) + QString path = QCoreApplication::applicationDirPath() + "/../lib/decoders"; +#else + QString path = "decoders"; +#endif + + bool success = true; + static bool srd_loaded = false; + if(srd_loaded) { + srd_exit(); + } + + if(srd_init(path.toStdString().c_str()) != SRD_OK) { + qInfo(CAT_SCOPY) << "ERROR: libsigrokdecode init failed."; + success = false; + } else { + srd_loaded = true; + /* Load the protocol decoders */ + srd_decoder_load_all(); + auto decoder = srd_decoder_get_by_id("parallel"); + + if(decoder == nullptr) { + success = false; + qInfo(CAT_SCOPY) << "ERROR: libsigrokdecode load the protocol decoders failed."; + } + } + + if(!success) { + QMessageBox error(this); + error.setText( + tr("ERROR: There was a problem initializing libsigrokdecode. Some features may be missing")); + error.exec(); + } +#else + qInfo(CAT_SCOPY) << "Python or libsigrokdecode are disabled, can't load decoders"; +#endif + qInfo(CAT_BENCHMARK) << "Loading the decoders took: " << timer.elapsed() << "ms"; +} + +void ScopyMainWindow::initApi() +{ + api = new ScopyMainWindow_API(this); + ScopyJS *js = ScopyJS::GetInstance(); + api->setObjectName("scopy"); + js->registerApi(api); +} + +void ScopyMainWindow::addDeviceToUi(QString id, Device *d) +{ + m_toolMenuManager->addMenuItem(id, d->displayName(), d->toolList()); + hp->addDevice(id, d); +} + +void ScopyMainWindow::removeDeviceFromUi(QString id) +{ + m_toolMenuManager->removeMenuItem(id); + hp->removeDevice(id); +} + +void ScopyMainWindow::receiveVersionDocument(QJsonDocument document) +{ + QJsonValue scopyJson = document["scopy"]; + if(scopyJson.isNull()) { + qWarning(CAT_SCOPY) << "Could not find the entry \"scopy\" in the json document"; + return; + } + + QJsonValue scopyVersion = scopyJson["version"]; + if(scopyVersion.isNull()) { + qWarning(CAT_SCOPY) + << R"(Could not find the entry "version" in the "scopy" entry of the json document)"; + return; + } + + QVersionNumber currentScopyVersion = QVersionNumber::fromString(SCOPY_VERSION).normalized(); + QVersionNumber upstreamScopyVersion = + QVersionNumber::fromString(scopyVersion.toString().remove(0, 1)).normalized(); + + if(upstreamScopyVersion > currentScopyVersion) { + StatusBarManager::pushMessage( + "Your Scopy version of outdated. Please consider updating it. The newest version is " + + upstreamScopyVersion.toString(), + 10000); // 10 sec + } + + qInfo(CAT_SCOPY) << "The upstream scopy version is" << upstreamScopyVersion << "and the current one is" + << currentScopyVersion; +} + +#include "moc_scopymainwindow.cpp" diff --git a/core/src/scopymainwindow_api.cpp b/core/src/scopymainwindow_api.cpp new file mode 100644 index 0000000000..00449406c0 --- /dev/null +++ b/core/src/scopymainwindow_api.cpp @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "scopymainwindow_api.h" + +#include "qapplication.h" + +#include +using namespace scopy; + +Q_LOGGING_CATEGORY(CAT_SCOPY_API, "Scopy_API") + +ScopyMainWindow_API::ScopyMainWindow_API(ScopyMainWindow *w) + : ApiObject() + , m_w(w) +{} + +ScopyMainWindow_API::~ScopyMainWindow_API() {} + +void ScopyMainWindow_API::acceptLicense() +{ + if(m_w->license) { + Q_EMIT m_w->license->getContinueBtn()->clicked(); + } + + if(m_w->checkUpdate) { + Q_EMIT m_w->checkUpdate->setCheckVersion(false); + } +} + +QString ScopyMainWindow_API::addDevice(QString uri, QString cat, bool async) +{ + auto &&cp = ConnectionProvider::GetInstance(); + Connection *conn = cp->open(uri); + QString devID = ""; + if(conn) { + Q_ASSERT(m_w->dm != nullptr); + devID = m_w->dm->createDevice(cat, uri, async); + } else { + uri = "ip:" + uri; + conn = cp->open(uri); + if(conn) { + devID = m_w->dm->createDevice(cat, uri, async); + } else { + qWarning(CAT_SCOPY_API) << "No device available"; + } + } + return devID; +} + +QString ScopyMainWindow_API::addDevice(QString uri, QList plugins, QString cat, bool async) +{ + auto &&cp = ConnectionProvider::GetInstance(); + Connection *conn = cp->open(uri); + QString devID = ""; + if(conn) { + Q_ASSERT(m_w->dm != nullptr); + devID = m_w->dm->createDevice(cat, uri, async, plugins); + } else { + uri = "ip:" + uri; + conn = cp->open(uri); + if(conn) { + devID = m_w->dm->createDevice(cat, uri, async, plugins); + } else { + qWarning(CAT_SCOPY_API) << "No device available"; + } + } + return devID; +} + +Device *ScopyMainWindow_API::getDevice(int idx) +{ + Q_ASSERT(m_w->dm != nullptr); + Device *dev = nullptr; + QList mapKeys = m_w->dm->map.keys(); + std::sort(mapKeys.begin(), mapKeys.end(), sortByUUID); + if(!mapKeys.isEmpty() && idx < mapKeys.size()) { + dev = m_w->dm->map[mapKeys[idx]]; + } + return dev; +} + +bool ScopyMainWindow_API::removeDevice(QString uri, QString cat) +{ + auto &&cp = ConnectionProvider::GetInstance(); + Connection *conn = cp->open(uri); + Q_ASSERT(m_w->dm != nullptr); + bool devRemoved = false; + if(conn) { + m_w->dm->removeDevice(cat, uri); + devRemoved = true; + } else { + qWarning(CAT_SCOPY_API) << "No device found"; + } + return devRemoved; +} + +bool ScopyMainWindow_API::removeDevice(int idx) +{ + Device *dev = getDevice(idx); + bool devRemoved = false; + if(dev) { + m_w->dm->removeDevice(dev->category(), dev->param()); + devRemoved = true; + } else { + qWarning(CAT_SCOPY_API) << "No device found"; + } + return devRemoved; +} + +bool ScopyMainWindow_API::startScan(bool scanState) +{ + ScanButtonController *sbc = new ScanButtonController(m_w->scanCycle, m_w->hp->scanControlBtn(), this); + sbc->enableScan(scanState); + return scanState; +} + +QStringList ScopyMainWindow_API::getDevicesName() +{ + Q_ASSERT(m_w->dm != nullptr); + QStringList devicesName = m_w->dm->map.keys(); + return devicesName; +} + +bool ScopyMainWindow_API::connectDevice(int idx) +{ + Device *dev = getDevice(idx); + bool isConnected = false; + bool successfulConnection = false; + if(dev) { + isConnected = m_w->dm->connectedDev.contains(dev->id()); + if(!isConnected) { + dev->connectDev(); + successfulConnection = true; + } else { + qWarning(CAT_SCOPY_API) << "The device is already connected!"; + } + } else { + qWarning(CAT_SCOPY_API) << "The device is not available!"; + } + return successfulConnection; +} + +bool ScopyMainWindow_API::connectDevice(QString devID) +{ + Q_ASSERT(m_w->dm != nullptr); + Device *dev = m_w->dm->getDevice(devID); + bool successfulConnection = false; + if(dev) { + bool isConnected = m_w->dm->connectedDev.contains(dev->id()); + if(!isConnected) { + dev->connectDev(); + successfulConnection = true; + } else { + qWarning(CAT_SCOPY_API) << "The device is already connected!"; + } + } else { + qWarning(CAT_SCOPY_API) << "The device is not available!"; + } + return successfulConnection; +} + +bool ScopyMainWindow_API::disconnectDevice(QString devID) +{ + Q_ASSERT(m_w->dm != nullptr); + Device *dev = m_w->dm->getDevice(devID); + if(dev) { + dev->disconnectDev(); + } else { + qWarning(CAT_SCOPY_API) << "Device with id " << devID << " is not available!"; + return false; + } + return true; +} + +bool ScopyMainWindow_API::disconnectDevice() +{ + Q_ASSERT(m_w->dm != nullptr); + QString devID = ""; + if(!m_w->dm->connectedDev.isEmpty()) { + devID = m_w->dm->connectedDev.back(); + } + Device *dev = m_w->dm->getDevice(devID); + if(dev) { + dev->disconnectDev(); + } else { + qWarning(CAT_SCOPY_API) << "Device with id " << devID << " is not available!"; + return false; + } + return true; +} + +bool ScopyMainWindow_API::switchTool(QString devID, QString toolName) +{ + Q_ASSERT(m_w->dm != nullptr); + bool toolSwitched = false; + if(!m_w->dm->connectedDev.contains(devID)) { + qWarning(CAT_SCOPY_API) << "Device " << devID << " not connected"; + return toolSwitched; + } + Device *dev = m_w->dm->getDevice(devID); + if(dev) { + ToolMenuEntry *tool = ToolMenuEntry::findToolMenuEntryByName(dev->toolList(), toolName); + if(!tool) { + qWarning(CAT_SCOPY_API) << "Tool " << toolName << " doesn't exist for " << dev->displayName(); + } else { + Q_EMIT m_w->dm->requestTool(tool->uuid()); + toolSwitched = true; + } + } else { + qWarning(CAT_SCOPY_API) << "Device with id " << devID << " is not available!"; + } + return toolSwitched; +} + +bool ScopyMainWindow_API::switchTool(QString toolName) +{ + Q_ASSERT(m_w->dm != nullptr); + QString devID = ""; + bool toolSwitched = false; + if(!m_w->dm->connectedDev.isEmpty()) { + devID = m_w->dm->connectedDev.back(); + } else { + qWarning(CAT_SCOPY_API) << "No device connected"; + return toolSwitched; + } + Device *dev = m_w->dm->getDevice(devID); + if(dev) { + ToolMenuEntry *tool = ToolMenuEntry::findToolMenuEntryByName(dev->toolList(), toolName); + if(!tool) { + qWarning(CAT_SCOPY_API) << "Tool " << toolName << " doesn't exist for " << dev->displayName(); + } else { + Q_EMIT m_w->dm->requestTool(tool->uuid()); + toolSwitched = true; + } + } else { + qWarning(CAT_SCOPY_API) << "Device with id " << devID << " is not available!"; + } + return toolSwitched; +} + +void ScopyMainWindow_API::runScript(QString scriptPath, bool exitApp) +{ + QFile file(scriptPath); + if(!file.open(QFile::ReadOnly)) { + qCritical(CAT_SCOPY_API) << "Unable to open the script file: " << scriptPath; + return; + } + const QString scriptContent = getScriptContent(&file); + QJSValue val = ScopyJS::GetInstance()->engine()->evaluate(scriptContent, scriptPath); + int ret = EXIT_SUCCESS; + if(val.isError()) { + qWarning(CAT_SCOPY_API) << "Exception:" << val.toString(); + ret = EXIT_FAILURE; + } else if(!val.isUndefined()) { + qWarning(CAT_SCOPY_API) << val.toString(); + } + + qInfo(CAT_SCOPY_API) << "Script finished with status" << ret; + /* Exit application */ + if(exitApp) + qApp->exit(ret); +} + +void ScopyMainWindow_API::runScriptList(QStringList scriptPathList, bool exitApp) +{ + foreach(QString scriptPath, scriptPathList) { + runScript(scriptPath, false); + } + + if(exitApp) { + int ret = EXIT_SUCCESS; + qApp->exit(ret); + } +} + +const QString ScopyMainWindow_API::getScriptContent(QFile *file) +{ + QTextStream stream(file); + QString firstLine = stream.readLine(); + if(!firstLine.startsWith("#!")) + stream.seek(0); + + QString content = stream.readAll(); + file->close(); + return content; +} + +bool ScopyMainWindow_API::sortByUUID(const QString &k1, const QString &k2) +{ + return k1.split("_").last() < k2.split("_").last(); +} + +void ScopyMainWindow_API::exit() { qApp->exit(); } + +QStringList ScopyMainWindow_API::getTools() +{ + Q_ASSERT(m_w->dm != nullptr); + QString devID = ""; + QStringList resultList; + if(!m_w->dm->connectedDev.isEmpty()) { + devID = m_w->dm->connectedDev.back(); + } else { + qWarning(CAT_SCOPY_API) << "No device connected"; + return resultList; + } + Device *dev = m_w->dm->getDevice(devID); + if(dev) { + QList toolList = dev->toolList(); + for(int i = 0; i < toolList.size(); i++) { + resultList.append(toolList[i]->name()); + } + } else { + qWarning(CAT_SCOPY_API) << "Device with id " << devID << " is not available!"; + } + return resultList; +} + +QStringList ScopyMainWindow_API::getToolsForPlugin(QString plugin) +{ + Q_ASSERT(m_w->dm != nullptr); + QString devID = ""; + QStringList resultList; + if(!m_w->dm->connectedDev.isEmpty()) { + devID = m_w->dm->connectedDev.back(); + } else { + qWarning(CAT_SCOPY_API) << "No device connected"; + return resultList; + } + Device *dev = m_w->dm->getDevice(devID); + if(dev) { + QList toolList = dev->toolList(); + for(int i = 0; i < toolList.size(); i++) { + if(toolList[i]->pluginName() == plugin) { + resultList.append(toolList[i]->name()); + } + } + } else { + qWarning(CAT_SCOPY_API) << "Device with id " << devID << " is not available!"; + } + return resultList; +} + +QPair ScopyMainWindow_API::getPreference(QString prefName) +{ + Preferences *p = Preferences::GetInstance(); + QMap prefMap = p->getPreferences(); + if(prefName != NULL) { + return QPair(prefName, prefMap[prefName]); + } + return {}; +} + +QMap ScopyMainWindow_API::getPreferences() +{ + Preferences *p = Preferences::GetInstance(); + QMap prefMap = p->getPreferences(); + return prefMap; +} + +void ScopyMainWindow_API::setPreference(QString prefName, QVariant value) +{ + Preferences *p = Preferences::GetInstance(); + p->set(prefName, value); + if(prefName == "general_use_opengl") { + qWarning(CAT_SCOPY_API) << "Restart is required for the change to take place"; + + } else if(prefName == "general_theme") { + qWarning(CAT_SCOPY_API) << "Restart is required for the change to take place"; + + } else if(prefName == "font_scale") { + qWarning(CAT_SCOPY_API) << "Restart is required for the change to take place"; + + } else if(prefName == "general_language") { + qWarning(CAT_SCOPY_API) << "Restart is required for the change to take place"; + } +} + +void ScopyMainWindow_API::aboutPage() +{ + QString path = ":/about.html"; + QFile file(path); + + if(!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + qWarning(CAT_SCOPY_API) << "Could not open the file:" << path; + } + + QTextStream in(&file); + + while(!in.atEnd()) { + QString line = in.readLine(); + qInfo(CAT_SCOPY_API) << line; + } + + file.close(); +} + +QStringList ScopyMainWindow_API::getPlugins(int idx) +{ + Device *dev = getDevice(idx); + QStringList pluginList; + if(dev) { + pluginList = availablePlugins("", "", dev); + } else { + qWarning(CAT_SCOPY_API) << "Device not found"; + } + return pluginList; +} + +QStringList ScopyMainWindow_API::getPlugins(QString uri, QString cat) +{ + QStringList pluginList; + auto &&cp = ConnectionProvider::GetInstance(); + Connection *conn = cp->open(uri); + if(conn) { + pluginList = availablePlugins(uri, cat, nullptr); + } else { + qWarning(CAT_SCOPY_API) << "Device not found"; + } + return pluginList; +} + +QStringList ScopyMainWindow_API::availablePlugins(QString uri, QString cat, Device *dev) +{ + PluginRepository *pr = new PluginRepository(this); + m_w->loadPluginsFromRepository(pr); + PluginManager *pm = pr->getPluginManager(); + QList compatiblePlugins; + QStringList resultList; + if(!uri.isEmpty()) { + compatiblePlugins = pm->getCompatiblePlugins(uri, cat); + } else { + compatiblePlugins = pm->getCompatiblePlugins(dev->param(), dev->category()); + } + + for(int i = 0; i < compatiblePlugins.size(); i++) { + resultList.append(compatiblePlugins[i]->name()); + } + return resultList; +} + +bool ScopyMainWindow_API::getToolBtnState(QString tool) +{ + Q_ASSERT(m_w->dm != nullptr); + QString devID = ""; + if(!m_w->dm->connectedDev.isEmpty()) { + devID = m_w->dm->connectedDev.back(); + } else { + qWarning(CAT_SCOPY_API) << "No device connected"; + return false; + } + Device *dev = m_w->dm->getDevice(devID); + if(dev) { + QList toolList = dev->toolList(); + for(int i = 0; i < toolList.size(); i++) { + if(toolList[i]->name() == tool) { + return toolList[i]->runBtnVisible(); + } + } + } else { + qWarning(CAT_SCOPY_API) << "Device with id " << devID << " is not available!"; + } + return false; +} + +bool ScopyMainWindow_API::runTool(QString tool, bool flag) +{ + Q_ASSERT(m_w->dm != nullptr); + QString devID = ""; + bool toolStateChange = false; + if(!m_w->dm->connectedDev.isEmpty()) { + devID = m_w->dm->connectedDev.back(); + } else { + qWarning(CAT_SCOPY_API) << "No device connected"; + return toolStateChange; + } + Device *dev = m_w->dm->getDevice(devID); + if(dev) { + QList toolList = dev->toolList(); + for(int i = 0; i < toolList.size(); i++) { + if(toolList[i]->name() == tool) { + toolList[i]->setRunning(flag); + toolList[i]->setRunEnabled(flag); + toolStateChange = true; + } + } + } else { + qWarning(CAT_SCOPY_API) << "Device with id " << devID << " is not available!"; + } + return toolStateChange; +} + +bool ScopyMainWindow_API::loadSetup(QString filename, QString path) +{ + QString fullPath = path + "/" + filename; + bool setupLoaded = false; + if(QFile::exists(fullPath)) { + m_w->load(fullPath); + setupLoaded = true; + } else { + qWarning(CAT_SCOPY_API) << "File " << filename << " does not exist"; + } + return setupLoaded; +} + +bool ScopyMainWindow_API::saveSetup(QString filename, QString path) +{ + Q_ASSERT(m_w->dm != nullptr); + bool setupSaved = false; + if(!m_w->dm->connectedDev.isEmpty()) { + m_w->save(path + "/" + filename); + qInfo(CAT_SCOPY_API) << "Setup saved at: " << path; + setupSaved = true; + } else { + qWarning(CAT_SCOPY_API) << "Unable to save setup: No device connected"; + } + return setupSaved; +} +#include "moc_scopymainwindow_api.cpp" diff --git a/core/src/scopypreferencespage.cpp b/core/src/scopypreferencespage.cpp new file mode 100644 index 0000000000..c8bf42f718 --- /dev/null +++ b/core/src/scopypreferencespage.cpp @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "scopypreferencespage.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "gui/preferenceshelper.h" +#include +#include "application_restarter.h" +#include "smallOnOffSwitch.h" +#include +#include +#include +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_PREFERENCESPAGE, "ScopyPreferencesPage"); + +using namespace scopy; +ScopyPreferencesPage::ScopyPreferencesPage(QWidget *parent) + : QWidget(parent) + , tabWidget(new VerticalTabWidget(this)) + , layout(new QVBoxLayout(this)) +{ + initUI(); + initRestartWidget(); + + addHorizontalTab(buildGeneralPreferencesPage(), "General"); +} + +void ScopyPreferencesPage::initUI() +{ + layout->setMargin(0); + layout->setSpacing(0); + this->setLayout(layout); + layout->addWidget(tabWidget); + + Style::setBackgroundColor(tabWidget, json::theme::background_primary); +} + +void ScopyPreferencesPage::addHorizontalTab(QWidget *w, QString text) +{ + w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + QWidget *pane = new QWidget(); + Style::setBackgroundColor(pane, json::theme::background_subtle); + QHBoxLayout *lay = new QHBoxLayout(); + lay->setMargin(10); + pane->setLayout(lay); + + QScrollArea *scrollArea = new QScrollArea(); + scrollArea->setWidget(w); + scrollArea->setWidgetResizable(true); + scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + lay->addWidget(scrollArea); + Style::setBackgroundColor(scrollArea->viewport(), json::theme::background_subtle); + tabWidget->addTab(pane, text); +} + +void ScopyPreferencesPage::initSessionDevices() +{ + QMap prevSession; + QMap devicesMap = Preferences::get("autoconnect_devices").toMap(); + for(QMap::iterator it = devicesMap.begin(); it != devicesMap.end(); ++it) { + QStringList plugins = it.value().toString().split(";"); + prevSession[it.key()] = plugins; + } + updateSessionDevices(prevSession); +} + +void ScopyPreferencesPage::updateSessionDevices(QMap devices) +{ + int btnIdx = m_autoConnectWidget->contentLayout()->indexOf(m_devRefresh); + QStringList keys = devices.keys(); + for(const QString &uri : qAsConst(keys)) { + if(!m_connDevices.contains(uri)) { + QString prefId = uri + "_sticky"; + QCheckBox *devCb = new QCheckBox(uri, m_autoConnectWidget); + devCb->setObjectName(uri); + devCb->setChecked(Preferences::get(prefId).toBool()); + m_connDevices[uri] = devCb; + m_autoConnectWidget->contentLayout()->insertWidget(btnIdx, devCb); + connect(devCb, &QCheckBox::toggled, this, [=](bool en) { + if(en) { + DeviceAutoConnect::addDevice(uri, devices[uri]); + } else { + DeviceAutoConnect::removeDevice(uri); + } + Preferences::set(prefId, en); + }); + } + } +} + +ScopyPreferencesPage::~ScopyPreferencesPage() {} + +void ScopyPreferencesPage::initRestartWidget() +{ + restartWidget = new QWidget(); + QHBoxLayout *lay = new QHBoxLayout(); + lay->setSpacing(0); + lay->setMargin(10); + restartWidget->setLayout(lay); + restartWidget->setVisible(false); + QLabel *lab = new QLabel("An application restart is required for these settings to take effect. "); + QSpacerItem *space1 = new QSpacerItem(6, 20, QSizePolicy::Expanding, QSizePolicy::Fixed); + QSpacerItem *space2 = new QSpacerItem(6, 20, QSizePolicy::Preferred, QSizePolicy::Fixed); + QPushButton *btn = new QPushButton("Restart"); + Style::setStyle(btn, style::properties::button::basicButton, true, true); + StyleHelper::BackgroundWidget(restartWidget, "restartWidget"); + btn->setFixedWidth(100); + + lay->addWidget(btn); + lay->addSpacerItem(space2); + lay->addWidget(lab); + lay->addSpacerItem(space1); + layout->addWidget(restartWidget); + + connect(btn, &QPushButton::clicked, btn, []() { ApplicationRestarter::triggerRestart(); }); + connect(Preferences::GetInstance(), &Preferences::restartRequired, this, + [=]() { restartWidget->setVisible(true); }); +} + +QWidget *ScopyPreferencesPage::buildSaveSessionPreference() +{ + Preferences *p = Preferences::GetInstance(); + QWidget *w = new QWidget(this); + QHBoxLayout *lay = new QHBoxLayout(w); + lay->setMargin(0); + + lay->addWidget( + PreferencesHelper::addPreferenceCheckBox(p, "general_save_session", "Save/Load Scopy session", this)); + lay->addSpacerItem(new QSpacerItem(40, 40, QSizePolicy::Expanding, QSizePolicy::Fixed)); + lay->addWidget(new QLabel("Settings files location ", this)); + QPushButton *navigateBtn = new QPushButton("Open", this); + Style::setStyle(navigateBtn, style::properties::button::borderButton); + navigateBtn->setMaximumWidth(Style::getDimension(json::global::unit_5)); + connect(navigateBtn, &QPushButton::clicked, this, + [=]() { QDesktopServices::openUrl(scopy::config::settingsFolderPath()); }); + lay->addWidget(navigateBtn); + return w; +} + +void ScopyPreferencesPage::removeIniFiles(bool backup) +{ + QString dir = scopy::config::settingsFolderPath(); + QDir loc(dir); + QFileInfoList plugins = loc.entryInfoList(QDir::Files); + QStringList settingsFiles; + + for(const QFileInfo &p : plugins) { + if(p.suffix() == "ini") + settingsFiles.append(p.absoluteFilePath()); + } + qInfo(CAT_PREFERENCESPAGE) << "Removing ini files .. "; + for(auto &&file : settingsFiles) { + if(backup) { + QFile(file + ".bak").remove(); + QFile(file).rename(file + ".bak"); + qDebug(CAT_PREFERENCESPAGE) << "Renamed" << file << "to" << file << ".bak"; + } else { + QFile(file).remove(); + qDebug(CAT_PREFERENCESPAGE) << "Removed" << file; + } + } +} + +void ScopyPreferencesPage::resetScopyPreferences() +{ + Preferences *p = Preferences::GetInstance(); + removeIniFiles(); + p->clear(); + Q_EMIT Preferences::GetInstance()->restartRequired(); +} + +QWidget *ScopyPreferencesPage::buildResetScopyDefaultButton() +{ + QWidget *w = new QWidget(this); + QHBoxLayout *lay = new QHBoxLayout(w); + + QPushButton *resetBtn = new QPushButton("Reset", this); + Style::setStyle(resetBtn, style::properties::button::borderButton); + resetBtn->setMaximumWidth(Style::getDimension(json::global::unit_5)); + connect(resetBtn, &QPushButton::clicked, this, &ScopyPreferencesPage::resetScopyPreferences); + lay->addWidget(resetBtn); + lay->setMargin(0); + lay->addSpacerItem(new QSpacerItem(6, 40, QSizePolicy::Preferred, QSizePolicy::Fixed)); + lay->addWidget(new QLabel("Reset to settings and plugins to default")); + lay->addSpacerItem(new QSpacerItem(40, 40, QSizePolicy::Expanding, QSizePolicy::Fixed)); + + return w; +} + +QWidget *ScopyPreferencesPage::buildGeneralPreferencesPage() +{ + QWidget *page = new QWidget(this); + QVBoxLayout *lay = new QVBoxLayout(page); + Preferences *p = Preferences::GetInstance(); + TranslationsRepository *t = scopy::TranslationsRepository::GetInstance(); + + lay->setMargin(0); + lay->setSpacing(10); + page->setLayout(lay); + + // General preferences + MenuSectionWidget *generalWidget = new MenuSectionWidget(page); + MenuCollapseSection *generalSection = new MenuCollapseSection( + "General", MenuCollapseSection::MHCW_NONE, MenuCollapseSection::MHW_BASEWIDGET, generalWidget); + generalWidget->contentLayout()->setSpacing(10); + generalWidget->contentLayout()->addWidget(generalSection); + generalSection->contentLayout()->setSpacing(10); + lay->addWidget(generalWidget); + lay->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + generalSection->contentLayout()->addWidget(buildSaveSessionPreference()); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCheckBox( + p, "general_save_attached", "Save/Load tool attached state", generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCheckBox( + p, "general_doubleclick_attach", "Doubleclick to attach/detach tool", generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCheckBox( + p, "general_doubleclick_ctrl_opens_menu", "Doubleclick control buttons to open menu", generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCheckBox( + p, "general_use_opengl", "Enable OpenGL plotting", generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCheckBox( + p, "general_use_animations", "Enable menu animations", generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCheckBox( + p, "general_check_online_version", "Enable automatic online check for updates.", generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCheckBox( + p, "general_show_status_bar", "Enable the status bar for displaying important messages.", + generalSection)); + generalSection->contentLayout()->addWidget( + PreferencesHelper::addPreferenceCheckBox(p, "show_grid", "Show Grid", generalSection)); + generalSection->contentLayout()->addWidget( + PreferencesHelper::addPreferenceCheckBox(p, "show_graticule", "Show Graticule", generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCheckBox( + p, "iiowidgets_use_lazy_loading", "Use Lazy Loading", generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCheckBox( + p, "general_use_native_dialogs", "Use native dialogs", generalSection)); + QWidget *autoConnectCb = PreferencesHelper::addPreferenceCheckBox( + p, "autoconnect_previous", "Auto-connect to previous session", generalSection); + generalSection->contentLayout()->addWidget(autoConnectCb); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCombo( + p, "font_scale", "Font scale (EXPERIMENTAL)", {"1", "1.15", "1.3", "1.45", "1.6", "1.75", "1.9"}, + generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCombo( + p, "general_theme", "Theme", Style::GetInstance()->getThemeList(), generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCombo( + p, "general_language", "Language", t->getLanguages(), generalSection)); + generalSection->contentLayout()->addWidget( + PreferencesHelper::addPreferenceCheckBox(p, "general_connect_to_multiple_devices", + "Connect to multiple devices (EXPERIMENTAL)", generalSection)); + generalSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCheckBox( + p, "general_scan_for_devices", "Regularly scan for new devices", generalSection)); + + // Auto-connect + m_autoConnectWidget = new MenuSectionCollapseWidget("Session devices", MenuCollapseSection::MHCW_NONE, + MenuCollapseSection::MHW_COMPOSITEWIDGET, page); + MenuCollapseHeader *autoConnectHeader = + dynamic_cast(m_autoConnectWidget->collapseSection()->header()); + autoConnectHeader->headerWidget()->layout()->addWidget( + new QLabel("At each auto-connect session, it will try to connect to the checked devices")); + m_autoConnectWidget->contentLayout()->setSpacing(10); + lay->addWidget(m_autoConnectWidget); + lay->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + m_devRefresh = new QPushButton("Refresh", m_autoConnectWidget); + m_devRefresh->setMaximumWidth(Style::getDimension(json::global::unit_5)); + Style::setStyle(m_devRefresh, style::properties::button::basicButton); + m_autoConnectWidget->add(m_devRefresh); + + connect(m_devRefresh, &QPushButton::pressed, this, &ScopyPreferencesPage::refreshDevicesPressed); + + // Debug preferences + MenuSectionWidget *debugWidget = new MenuSectionWidget(page); + MenuCollapseSection *debugSection = new MenuCollapseSection("Debug", MenuCollapseSection::MHCW_NONE, + MenuCollapseSection::MHW_BASEWIDGET, debugWidget); + debugWidget->contentLayout()->setSpacing(10); + debugWidget->contentLayout()->addWidget(debugSection); + debugSection->contentLayout()->setSpacing(10); + lay->addWidget(debugWidget); + lay->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + debugSection->contentLayout()->addWidget( + PreferencesHelper::addPreferenceCheckBox(p, "general_show_plot_fps", "Show plot FPS", debugSection)); + debugSection->contentLayout()->addWidget(PreferencesHelper::addPreferenceCombo( + p, "general_plot_target_fps", "Plot target FPS", {"15", "20", "30", "60"}, debugSection)); + debugSection->contentLayout()->addWidget(buildResetScopyDefaultButton()); + + return page; +} + +#include "moc_scopypreferencespage.cpp" diff --git a/core/src/scopytitlemanager.cpp b/core/src/scopytitlemanager.cpp new file mode 100644 index 0000000000..8bad0ba2d9 --- /dev/null +++ b/core/src/scopytitlemanager.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "scopytitlemanager.h" + +#include +#include +#include + +#define TITLE_SEPARATOR " - " +Q_LOGGING_CATEGORY(CAT_SCOPYTITLEMANAGER, "ScopyTitleManager") + +using namespace scopy; + +ScopyTitleManager *ScopyTitleManager::pinstance_{nullptr}; + +ScopyTitleManager::ScopyTitleManager(QObject *parent) + : QObject(parent) +{ + qDebug(CAT_SCOPYTITLEMANAGER) << "ctor"; +} + +ScopyTitleManager::~ScopyTitleManager() { qDebug(CAT_SCOPYTITLEMANAGER) << "dtor"; } + +void ScopyTitleManager::buildTitle() +{ + QString result; + bool addSeparator = false; + + if(!m_title.isEmpty()) { + result = m_title; + addSeparator = true; + } + + if(!m_version.isEmpty()) { + if(addSeparator) { + result += TITLE_SEPARATOR; + } + + result += m_version; + addSeparator = true; + } + + if(!m_hash.isEmpty()) { + if(addSeparator) { + result += TITLE_SEPARATOR; + } + + result += m_hash; + addSeparator = true; + } + + if(!m_filename.isEmpty()) { + if(addSeparator) { + result += TITLE_SEPARATOR; + } + + result += m_filename; + addSeparator = true; + } + + m_currentTitle = result; + auto instance = ScopyTitleManager::GetInstance(); + if(instance->m_mainWindow) { + instance->m_mainWindow->setWindowTitle(instance->m_currentTitle); + qDebug(CAT_SCOPYTITLEMANAGER) << "Title was built: " << m_currentTitle; + } else { + qWarning(CAT_SCOPYTITLEMANAGER) << "Cannot set title, no mainWidget was specified"; + } +} + +ScopyTitleManager *ScopyTitleManager::GetInstance() +{ + if(pinstance_ == nullptr) { + pinstance_ = new ScopyTitleManager(QApplication::instance()); // singleton has the app as parent + } else { + qDebug(CAT_SCOPYTITLEMANAGER) << "Singleton instance already created."; + } + return pinstance_; +} + +void ScopyTitleManager::setApplicationName(QString title) +{ + auto instance = ScopyTitleManager::GetInstance(); + instance->m_title = title; + instance->buildTitle(); +} + +void ScopyTitleManager::clearApplicationName() +{ + auto instance = ScopyTitleManager::GetInstance(); + instance->m_title.clear(); + instance->buildTitle(); +} + +void ScopyTitleManager::setScopyVersion(QString version) +{ + auto instance = ScopyTitleManager::GetInstance(); + instance->m_version = version; + instance->buildTitle(); +} + +void ScopyTitleManager::clearScopyVersion() +{ + auto instance = ScopyTitleManager::GetInstance(); + instance->m_version.clear(); + instance->buildTitle(); +} + +void ScopyTitleManager::setGitHash(QString hash) +{ + auto instance = ScopyTitleManager::GetInstance(); + instance->m_hash = hash; + instance->buildTitle(); +} + +void ScopyTitleManager::clearGitHash() +{ + auto instance = ScopyTitleManager::GetInstance(); + instance->m_hash.clear(); + instance->buildTitle(); +} + +void ScopyTitleManager::setIniFileName(QString filename) +{ + auto instance = ScopyTitleManager::GetInstance(); + instance->m_filename = filename; + instance->buildTitle(); +} + +void ScopyTitleManager::clearIniFileName() +{ + auto instance = ScopyTitleManager::GetInstance(); + instance->m_filename.clear(); + instance->buildTitle(); +} + +void ScopyTitleManager::clearAll() +{ + auto instance = ScopyTitleManager::GetInstance(); + instance->m_title.clear(); + instance->m_version.clear(); + instance->m_hash.clear(); + instance->m_filename.clear(); +} + +QString ScopyTitleManager::getCurrentTitle() { return ScopyTitleManager::GetInstance()->m_currentTitle; } + +void ScopyTitleManager::setMainWindow(QWidget *window) +{ + ScopyTitleManager::GetInstance()->m_mainWindow = window; + qDebug(CAT_SCOPYTITLEMANAGER) << "Main window was set."; +} + +#include "moc_scopytitlemanager.cpp" diff --git a/core/src/toolmenu.cpp b/core/src/toolmenu.cpp new file mode 100644 index 0000000000..dff0b2d907 --- /dev/null +++ b/core/src/toolmenu.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "toolmenu.h" +#include +#include "gui/dynamicWidget.h" +#include + +using namespace scopy; + +ToolMenu::ToolMenu(QWidget *parent) + : QWidget(parent) + , m_btnGroup(new QButtonGroup(this)) +{ + m_uuid = 0; + QVBoxLayout *lay = new QVBoxLayout(); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); + + m_scroll = new QScrollArea(parent); + QWidget *wScroll = new QWidget(m_scroll); + + m_layScroll = new QVBoxLayout(); + m_layScroll->setMargin(0); + m_layScroll->setSpacing(20); + + wScroll->setLayout(m_layScroll); + m_scroll->setWidgetResizable(true); + m_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + m_scroll->setSizeAdjustPolicy(QAbstractScrollArea::SizeAdjustPolicy::AdjustToContents); + + m_scroll->setWidget(wScroll); + m_scroll->verticalScrollBar()->setVisible(false); + + lay->setMargin(0); + lay->setSpacing(10); + setLayout(lay); + + m_spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); + m_layScroll->addSpacerItem(m_spacer); + + lay->addWidget(m_scroll); + + connect(m_scroll->verticalScrollBar(), &QScrollBar::rangeChanged, this, &ToolMenu::onScrollRangeChanged); +} + +ToolMenu::~ToolMenu() {} + +void ToolMenu::add(QWidget *w) +{ + int spacerIndex = m_layScroll->indexOf(m_spacer); + m_layScroll->insertWidget(spacerIndex, w); + m_uuid++; +} + +void ToolMenu::add(int index, QString itemId, QWidget *w) +{ + m_widgetMap.insert(itemId, w); + if(index < 0) { + add(w); + } else { + add(index, w); + } +} + +void ToolMenu::add(int index, QWidget *w) +{ + m_layScroll->insertWidget(index, w); + m_uuid++; +} +void ToolMenu::remove(QWidget *w) +{ + m_widgetMap.remove(widgetName(w)); + m_layScroll->removeWidget(w); +} + +int ToolMenu::indexOf(QWidget *w) { return m_layScroll->indexOf(w); } + +void ToolMenu::colapseAll() +{ + for(QWidget *w : qAsConst(m_widgetMap)) { + Collapsable *c = dynamic_cast(w); + if(c != nullptr) { + c->setCollapsed(true); + } + } +} + +QButtonGroup *ToolMenu::btnGroup() const { return m_btnGroup; } + +QString ToolMenu::widgetName(QWidget *w) { return m_widgetMap.key(w, ""); } + +// Used to display the scrollbar when needed and to maintain its size in the scroll area when not needed. +// We chose this approach because for the Qt::ScrollBarAsNeeded policy the size of the scrollball cannot be retained +// with Util::retainWidgetSizeWhenHidden because the resizing of the scrollbar is done dynamically (withoud using the +// show/hide default functions). +void ToolMenu::onScrollRangeChanged(int min, int max) +{ + if(max > min) { + m_scroll->verticalScrollBar()->setVisible(true); + } else { + m_scroll->verticalScrollBar()->setVisible(false); + } +} + +#include "moc_toolmenu.cpp" diff --git a/core/src/toolmenuitem.cpp b/core/src/toolmenuitem.cpp new file mode 100644 index 0000000000..9c46a243d1 --- /dev/null +++ b/core/src/toolmenuitem.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "toolmenuitem.h" +#include "dynamicWidget.h" + +#include +#include +#include +#include +#include +#include +#include "gui/dynamicWidget.h" +#include "gui/utils.h" +#include "qdebug.h" +#include "style_properties.h" + +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_TOOLMENUITEM, "ToolMenuItem") + +using namespace scopy; + +ToolMenuItem::ToolMenuItem(QString uuid, QString name, QString icon, QWidget *parent) + : QPushButton(parent) + , m_uuid(uuid) + , m_name(name) + , m_icon(icon) +{ + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + QVBoxLayout *lay = new QVBoxLayout(this); + setLayout(lay); + setFixedHeight(Style::getDimension(json::global::unit_3)); + lay->setSpacing(0); + lay->setContentsMargins(0, 0, 0, 0); + + QWidget *toolOption = new QWidget(this); + toolOption->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + QHBoxLayout *toolLay = new QHBoxLayout(toolOption); + toolLay->setSpacing(0); + toolLay->setContentsMargins(0, 0, 0, 0); + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + m_toolRunBtn = new CustomPushButton(toolOption); + toolLay->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed)); + toolLay->addWidget(m_toolRunBtn); + + m_toolRunBtn->setMaximumWidth(Style::getDimension(json::global::unit_3)); + + setIcon(QIcon::fromTheme(m_icon)); + setCheckable(true); + setIconSize(QSize(Style::getDimension(json::global::unit_2_5), Style::getDimension(json::global::unit_2_5))); + + m_toolRunBtn->setCheckable(true); + m_toolRunBtn->setText(""); + + m_toolRunBtn->setFlat(true); + + lay->addWidget(toolOption); + + setAttribute(Qt::WA_StyledBackground, true); +#ifdef __ANDROID__ + setDynamicProperty(this, "allowHover", false); +#else + setStyleSheet("text-align:left;"); + Style::setStyle(m_toolRunBtn, style::properties::button::stopButton); + Style::setStyle(m_toolRunBtn, style::properties::widget::notInteractive); + Style::setStyle(this, style::properties::button::toolButton); + + enableDoubleClick(true); +#endif +} + +ToolMenuItem::~ToolMenuItem() {} + +QPushButton *ToolMenuItem::getToolRunBtn() const { return m_toolRunBtn; } + +void ToolMenuItem::enableDoubleClick(bool enable) +{ + if(enable) { + installEventFilter(this); + } else { + removeEventFilter(this); + removeEventFilter(this); + } +} + +bool ToolMenuItem::eventFilter(QObject *watched, QEvent *event) +{ + if(event->type() == QEvent::MouseButtonDblClick) { + QMouseEvent *mouseEvent = static_cast(event); + if(mouseEvent->button() == Qt::LeftButton) { + if(isEnabled()) { + Q_EMIT doubleclick(); + return true; + } + } + } + + return QObject::event(event); +} + +void ToolMenuItem::setName(QString str) +{ + m_name = str; + setText(m_name); +} + +void ToolMenuItem::setSelected(bool en) { setDynamicProperty(this, "selected", en); } + +void ToolMenuItem::setDisabled(bool disabled) { setDisabled(disabled); } + +void ToolMenuItem::updateItem() +{ + ToolMenuEntry *tme = dynamic_cast(QObject::sender()); + Q_ASSERT(tme); + QSignalBlocker sb(m_toolRunBtn); + setVisible(tme->visible()); + setEnabled(tme->enabled()); + setName(tme->name()); + m_toolRunBtn->setEnabled(tme->runEnabled()); + m_toolRunBtn->setEnabled(tme->runBtnVisible()); + m_toolRunBtn->setChecked(tme->running()); + qDebug(CAT_TOOLMENUITEM) << "updating toolmenuentry for " << tme->name() << " - " << tme->uuid(); +} + +void ToolMenuItem::enterEvent(QEvent *event) +{ +#ifndef __ANDROID__ + setDynamicProperty(this, "allowHover", true); + event->accept(); +#endif +} + +void ToolMenuItem::leaveEvent(QEvent *event) +{ +#ifndef __ANDROID__ + setDynamicProperty(this, "allowHover", false); + event->accept(); +#endif +} + +QString ToolMenuItem::getId() const { return m_uuid; } + +#include "moc_toolmenuitem.cpp" diff --git a/core/src/toolmenumanager.cpp b/core/src/toolmenumanager.cpp new file mode 100644 index 0000000000..3ada849ba9 --- /dev/null +++ b/core/src/toolmenumanager.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "toolmenumanager.h" +#include +#include +#include +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(CAT_TOOLMENUMANAGER, "ToolMenuManager") +using namespace scopy; + +ToolMenuManager::ToolMenuManager(ToolStack *ts, DetachedToolWindowManager *dtm, ToolMenu *toolMenu, QObject *parent) + : QObject(parent) + , m_ts(ts) + , m_dtm(dtm) + , m_toolMenu(toolMenu) +{} + +ToolMenuManager::~ToolMenuManager() {} + +void ToolMenuManager::addMenuItem(QString deviceId, QString devName, QList tools, int itemIndex) +{ + QString param; + if(!tools.isEmpty()) + param = tools.at(0)->param(); + MenuSectionCollapseWidget *devSection = createMenuSectionItem(devName, param); + QButtonGroup *menuBtnGroup = m_toolMenu->btnGroup(); + for(ToolMenuEntry *tme : tools) { + ToolMenuItem *toolMenuItem = createToolMenuItem(tme, devSection); + devSection->add(toolMenuItem); + menuBtnGroup->addButton(toolMenuItem); + connect(tme, &ToolMenuEntry::updateTool, this, &ToolMenuManager::updateTool); + connect(tme, &ToolMenuEntry::updateToolAttached, this, + [this, toolMenuItem](bool oldAttach) { updateToolAttached(oldAttach, toolMenuItem); }); + Q_EMIT tme->updateToolEntry(); + } + m_toolMenu->add(itemIndex, deviceId, devSection); + m_itemMap[deviceId] = devSection; + qDebug(CAT_TOOLMENUMANAGER) << "Menu item with id" << deviceId << "has been added"; +} + +void ToolMenuManager::removeMenuItem(QString deviceId) +{ + if(!m_itemMap.contains(deviceId)) { + qDebug(CAT_TOOLMENUMANAGER) << "No entry with id:" << deviceId; + return; + } + MenuSectionCollapseWidget *devSection = m_itemMap[deviceId]; + m_itemMap.remove(deviceId); + m_toolMenu->remove(devSection); + delete devSection; + devSection = nullptr; + qDebug(CAT_TOOLMENUMANAGER) << "Menu item with id" << deviceId << "has been removed"; +} + +void ToolMenuManager::changeToolListContents(QString deviceId, QList tools) +{ + if(!m_itemMap.contains(deviceId)) { + qDebug(CAT_TOOLMENUMANAGER) << "No entry with id:" << deviceId; + return; + } + for(ToolMenuEntry *tme : tools) { + tme->disconnect(this); + } + MenuSectionCollapseWidget *menuItem = m_itemMap[deviceId]; + QString devName = menuItem->collapseSection()->title(); + int itemIndex = m_toolMenu->indexOf(menuItem); + removeMenuItem(deviceId); + addMenuItem(deviceId, devName, tools, itemIndex); + showMenuItem(deviceId); +} + +void ToolMenuManager::showMenuItem(QString id) +{ + if(!m_connectedDev.contains(m_prevItem)) + hideMenuItem(m_prevItem); + if(!m_itemMap.contains(id)) { + // if the id is not a device id, it could be a tool id + Q_EMIT requestToolSelect(id); + return; + } + + m_itemMap[id]->show(); + m_prevItem = id; +} + +void ToolMenuManager::hideMenuItem(QString id) +{ + if(!m_itemMap.contains(id)) + return; + m_itemMap[id]->hide(); +} + +void ToolMenuManager::deviceConnected(QString id) +{ + m_connectedDev.append(id); + showMenuItem(id); +} + +void ToolMenuManager::deviceDisconnected(QString id) +{ + m_connectedDev.removeAll(id); + if(m_prevItem.compare(id) != 0) { + hideMenuItem(id); + } +} + +void ToolMenuManager::onDisplayNameChanged(QString id, QString devName) +{ + m_itemMap[id]->collapseSection()->setTitle(devName); +} + +void ToolMenuManager::updateTool(QWidget *old) +{ + + ToolMenuEntry *tme = dynamic_cast(QObject::sender()); + Q_ASSERT(tme); + QString id = tme->uuid(); + QWidget *tool = tme->tool(); + + if(old != nullptr) { // we had a widget + saveToolAttachedState(tme); + if(m_ts->contains(id)) { + m_ts->remove(id); + } + if(m_dtm->contains(id)) { + m_dtm->remove(id); + } + } + if(tool != nullptr) { // we have a new widget + if(tme->attached()) { + m_ts->add(id, tool); + } else { + m_dtm->add(id, tme); + } + loadToolAttachedState(tme); + } + qDebug(CAT_TOOLMENUMANAGER) << "updating tool for " << tme->name() << " - " << id; +} + +void ToolMenuManager::updateToolAttached(bool oldAttach, ToolMenuItem *toolMenuItem) +{ + ToolMenuEntry *tme = dynamic_cast(QObject::sender()); + Q_ASSERT(tme); + QWidget *tool = tme->tool(); + QString id = tme->uuid(); + + if(tme->attached()) { + // tool is detached, it will attach to the main window + if(m_dtm->contains(id)) { + m_dtm->remove(id); + } + m_ts->add(id, tool); + attachSuccesful(toolMenuItem); + } else { + // tool is attached, it will detach + if(m_ts->contains(id)) { + m_ts->remove(id); + } + m_dtm->add(id, tme); + detachSuccesful(toolMenuItem); + } + // by this time, the tool has changed it's state, either from attached to detached, or from detached to attached + saveToolAttachedState(tme); + showTool(toolMenuItem); + + // highlight the current tool from the main window + if(tme->attached()) { + // the selected tool just attached, so it will be at the top of the stack, therefore highlighted + if(toolMenuItem) { + toolMenuItem->setChecked(true); + toolMenuItem->setSelected(true); + } + } else { + // the top tool just detached, so we need to find the tool that is positioned at the new top of the + // stack. + if(toolMenuItem) { + toolMenuItem->toggle(); + } + } +} + +void ToolMenuManager::loadToolAttachedState(ToolMenuEntry *tme) +{ + Preferences *p = Preferences::GetInstance(); + QString prefId; + if(!p->get("general_save_attached").toBool()) + return; + + prefId = tme->id() + "_attached"; + p->init(prefId, tme->attached()); + bool attach = p->get(prefId).toBool(); + tme->setAttached(attach); +} + +void ToolMenuManager::saveToolAttachedState(ToolMenuEntry *tme) +{ + Preferences *p = Preferences::GetInstance(); + if(!p->get("general_save_attached").toBool()) + return; + QString prefId; + prefId = tme->id() + "_attached"; + bool attach = tme->attached(); + p->set(prefId, attach); +} + +void ToolMenuManager::detachSuccesful(ToolMenuItem *toolMenuItem) +{ + QButtonGroup *menuBtnGroup = m_toolMenu->btnGroup(); + if(toolMenuItem) { + toolMenuItem->setSelected(false); + menuBtnGroup->removeButton(toolMenuItem); + } +} + +void ToolMenuManager::attachSuccesful(ToolMenuItem *toolMenuItem) +{ + QButtonGroup *menuBtnGroup = m_toolMenu->btnGroup(); + if(toolMenuItem) { + menuBtnGroup->addButton(toolMenuItem); + } +} + +void ToolMenuManager::showTool(ToolMenuItem *toolMenuItem) +{ + if(toolMenuItem) { + toolMenuItem->setChecked(true); + } + Q_EMIT requestToolSelect(toolMenuItem->getId()); +} + +void ToolMenuManager::selectTool(ToolMenuItem *toolMenuItem, bool on) +{ + QButtonGroup *menuBtnGroup = m_toolMenu->btnGroup(); + if(menuBtnGroup->id(toolMenuItem) != -1) { + toolMenuItem->setSelected(on); + } +} + +void ToolMenuManager::setTmeAttached(ToolMenuEntry *tme) +{ + Preferences *p = Preferences::GetInstance(); + if(!p->get("general_doubleclick_attach").toBool()) + return; + tme->setAttached(!tme->attached()); +} + +MenuSectionCollapseWidget *ToolMenuManager::createMenuSectionItem(QString deviceName, QString uri) +{ + MenuSectionCollapseWidget *section = new MenuSectionCollapseWidget( + deviceName, MenuCollapseSection::MHCW_ARROW, MenuCollapseSection::MHW_COMPOSITEWIDGET, m_toolMenu); + section->contentLayout()->setSpacing(0); + section->menuSection()->layout()->setMargin(0); + section->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + MenuCollapseHeader *collapseHeader = dynamic_cast(section->collapseSection()->header()); + if(collapseHeader) { + CompositeHeaderWidget *chw = dynamic_cast(collapseHeader->headerWidget()); + if(chw) { + chw->add(new QLabel(uri, chw)); + chw->layout()->setContentsMargins(Style::getDimension(json::global::unit_1), 0, 0, 0); + } + Style::setStyle(collapseHeader, style::properties::widget::bottomBorder); + } + section->setCollapsed(false); + section->hide(); + return section; +} + +ToolMenuItem *ToolMenuManager::createToolMenuItem(ToolMenuEntry *tme, QWidget *parent) +{ + ToolMenuItem *toolMenuItem = new ToolMenuItem(tme->uuid(), tme->name(), tme->icon(), parent); + connect(toolMenuItem->getToolRunBtn(), &QPushButton::toggled, tme, &ToolMenuEntry::runToggled); + connect(toolMenuItem->getToolRunBtn(), &QPushButton::clicked, tme, &ToolMenuEntry::runClicked); + connect(toolMenuItem, &QPushButton::clicked, this, [=]() { Q_EMIT requestToolSelect(toolMenuItem->getId()); }); + connect(toolMenuItem, &QPushButton::toggled, this, + [this, toolMenuItem](bool on) { selectTool(toolMenuItem, on); }); + connect(toolMenuItem, &ToolMenuItem::doubleclick, this, [this, tme]() { setTmeAttached(tme); }); + connect(tme, &ToolMenuEntry::updateToolEntry, toolMenuItem, &ToolMenuItem::updateItem); + + return toolMenuItem; +} + +#include "moc_toolmenumanager.cpp" diff --git a/core/src/toolstack.cpp b/core/src/toolstack.cpp new file mode 100644 index 0000000000..0753f3290f --- /dev/null +++ b/core/src/toolstack.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "toolstack.h" + +#include +#include +#include + +using namespace scopy; + +Q_LOGGING_CATEGORY(CAT_TOOLSTACK, "ToolStack") + +ToolStack::ToolStack(QWidget *parent) + : MapStackedWidget(parent) +{} + +ToolStack::~ToolStack() {} + +#include "moc_toolstack.cpp" diff --git a/core/src/translationsrepository.cpp b/core/src/translationsrepository.cpp new file mode 100644 index 0000000000..eefa5d9b39 --- /dev/null +++ b/core/src/translationsrepository.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "translationsrepository.h" + +#include "common/scopyconfig.h" + +#include +#include +#include +#include + +#include + +Q_LOGGING_CATEGORY(CAT_TRANSLATIONREPOSITORY, "ScopyTranslations"); + +using namespace scopy; + +TranslationsRepository *TranslationsRepository::pinstance_{nullptr}; + +TranslationsRepository::TranslationsRepository(QObject *parent) + : QObject(parent) +{} + +TranslationsRepository::~TranslationsRepository() {} + +TranslationsRepository *TranslationsRepository::GetInstance() +{ + if(pinstance_ == nullptr) { + pinstance_ = new TranslationsRepository(QApplication::instance()); // singleton has the app as parent + } + return pinstance_; +} + +QString TranslationsRepository::getTranslationsPath() +{ + // Check the local plugins folder first + QDir pathDir(config::localTranslationFolderPath()); + if(pathDir.exists()) { + return config::localTranslationFolderPath(); + } + + return config::defaultTranslationFolderPath(); +} + +QStringList TranslationsRepository::getLanguages() +{ + QDir directory(TranslationsRepository::getTranslationsPath()); + QStringList languages = directory.entryList(QStringList() << "*.qm", QDir::Files).replaceInStrings(".qm", ""); + for(const QString &lang : qAsConst(languages)) { + if(lang.contains("_")) + languages.removeOne(lang); + } + + // no languages found + if(languages.empty()) { + languages.append("default"); + } + + return languages; +} + +void TranslationsRepository::loadTranslations(QString language) +{ + if(language == "default") { + qDebug(CAT_TRANSLATIONREPOSITORY) << "No languages loaded (default)"; + return; + } + + QList translatorList = QList(); + QDir directory(TranslationsRepository::getTranslationsPath()); + QFileInfoList languages = directory.entryInfoList(QStringList() << "*.qm", QDir::Files); + + for(const QFileInfo &lang : languages) { + if(lang.fileName().endsWith("_" + language + ".qm") || lang.fileName() == language + ".qm") { + translatorList.append(new QTranslator()); + translatorList.last()->load(lang.filePath()); + QApplication::installTranslator(translatorList.last()); + + qDebug(CAT_TRANSLATIONREPOSITORY) << "Loaded:" << lang.fileName(); + } + } +} + +#include "moc_translationsrepository.cpp" diff --git a/core/src/versioncheckmessage.cpp b/core/src/versioncheckmessage.cpp new file mode 100644 index 0000000000..bd4e5b1e94 --- /dev/null +++ b/core/src/versioncheckmessage.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "versioncheckmessage.h" + +#include +#include +#include + +using namespace scopy; +VersionCheckMessage::VersionCheckMessage(QWidget *parent) + : QWidget(parent) +{ + setLayout(new QHBoxLayout(this)); + layout()->setContentsMargins(0, 0, 0, 0); + auto textLabel = new QLabel( + QString("

Should Scopy check for online versions?    Yes    No

") + .replace("text_color", Style::getAttribute(json::theme::content_default)), + this); + connect(textLabel, &QLabel::linkActivated, this, [this](const QString &text) { + if(text == "yes") { + setCheckVersion(true); + } else if(text == "no") { + setCheckVersion(false); + } + + delete this; + }); + textLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse); + layout()->addWidget(textLabel); + + connect(this, &VersionCheckMessage::setCheckVersion, this, &VersionCheckMessage::saveCheckVersion); +} + +VersionCheckMessage::~VersionCheckMessage() {} + +void VersionCheckMessage::saveCheckVersion(bool allowed) { Preferences::set("general_check_online_version", allowed); } + +#include "moc_versioncheckmessage.cpp" diff --git a/core/test/CMakeLists.txt b/core/test/CMakeLists.txt new file mode 100644 index 0000000000..ca4cf9cd4e --- /dev/null +++ b/core/test/CMakeLists.txt @@ -0,0 +1,60 @@ +# +# Copyright (c) 2024 Analog Devices Inc. +# +# This file is part of Scopy +# (see https://www.github.com/analogdevicesinc/scopy). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +cmake_minimum_required(VERSION 3.5) + +include(ScopyTest) + +setup_scopy_tests(pluginmanager pluginrepository) + +# test_translationsrepository +if(ENABLE_TRANSLATION) + set(TEST_GENERATETRANSLATIONS ${PROJECT_NAME}_test_generatetranslations) + generate_translations() + qt_add_resources(TEST_TRANSLATIONS_REPOSITORY "${CMAKE_CURRENT_BINARY_DIR}/translations.qrc") + + if(ANDROID) + add_library(${TEST_GENERATETRANSLATIONS} SHARED ${TEST_TRANSLATIONS_REPOSITORY}) + else() + add_library(${TEST_GENERATETRANSLATIONS} ${TEST_TRANSLATIONS_REPOSITORY}) + endif() + + # move test translation files to core/tests + add_custom_command( + TARGET ${TEST_GENERATETRANSLATIONS} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/translations/test.qm + ${CMAKE_CURRENT_BINARY_DIR}/translations/test.qm + ) + add_custom_command( + TARGET ${TEST_GENERATETRANSLATIONS} POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove + ${CMAKE_BINARY_DIR}/translations/test.qm + ) + add_custom_command( + TARGET ${TEST_GENERATETRANSLATIONS} POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/translations/test_test.qm + ${CMAKE_CURRENT_BINARY_DIR}/translations/test_test.qm + ) + add_custom_command( + TARGET ${TEST_GENERATETRANSLATIONS} POST_BUILD COMMAND ${CMAKE_COMMAND} -E remove + ${CMAKE_BINARY_DIR}/translations/test_test.qm + ) + + setup_scopy_tests(translationsrepository) +endif() diff --git a/core/test/resources/translations.qrc b/core/test/resources/translations.qrc new file mode 100644 index 0000000000..94186dd8f9 --- /dev/null +++ b/core/test/resources/translations.qrc @@ -0,0 +1,5 @@ + + + @TRANSLATIONS@ + + diff --git a/core/test/resources/translations/test.ts b/core/test/resources/translations/test.ts new file mode 100644 index 0000000000..ab67af039b --- /dev/null +++ b/core/test/resources/translations/test.ts @@ -0,0 +1,11 @@ + + + + + TST_TranslationsRepository + + TEST1 + test1 + + + diff --git a/core/test/resources/translations/test_test.ts b/core/test/resources/translations/test_test.ts new file mode 100644 index 0000000000..406ef7a65c --- /dev/null +++ b/core/test/resources/translations/test_test.ts @@ -0,0 +1,11 @@ + + + + + TST_TranslationsRepository + + TEST2 + test2 + + + diff --git a/core/test/testPluginExcludeLower.json b/core/test/testPluginExcludeLower.json new file mode 100644 index 0000000000..e7ae803e11 --- /dev/null +++ b/core/test/testPluginExcludeLower.json @@ -0,0 +1,14 @@ +(R"plugin( +{ +"TestPlugin" : { + "priority":2, + "category" : ["test", "unittest"] +}, +"TestPluginIp" : { + "priority":1000, + "category" : ["test", "unittest"], + "exclude" : ["*", "!testplugin" ] +} +} +)plugin") + diff --git a/core/test/testPluginExcludeSpecificLower.json b/core/test/testPluginExcludeSpecificLower.json new file mode 100644 index 0000000000..cb2793e447 --- /dev/null +++ b/core/test/testPluginExcludeSpecificLower.json @@ -0,0 +1,14 @@ +(R"plugin( +{ +"TestPlugin" : { + "priority":2, + "category" : ["test", "unittest"] +}, +"TestPluginIp" : { + "priority":1000, + "category" : ["test", "unittest"], + "exclude" : ["testplugin" ] +} +} +)plugin") + diff --git a/core/test/testPluginExcludeSpecificUpper.json b/core/test/testPluginExcludeSpecificUpper.json new file mode 100644 index 0000000000..ef38bc6ded --- /dev/null +++ b/core/test/testPluginExcludeSpecificUpper.json @@ -0,0 +1,14 @@ +(R"plugin( +{ +"TestPlugin" : { + "priority":2, + "category" : ["test", "unittest"] +}, +"TestPluginIp" : { + "priority":1000, + "category" : ["test", "unittest"], + "exclude" : ["TESTPLUGIN" ] +} +} +)plugin") + diff --git a/core/test/testPluginExcludeUpper.json b/core/test/testPluginExcludeUpper.json new file mode 100644 index 0000000000..2b1404b37b --- /dev/null +++ b/core/test/testPluginExcludeUpper.json @@ -0,0 +1,14 @@ +(R"plugin( +{ +"TestPlugin" : { + "priority":2, + "category" : ["test", "unittest"] +}, +"TestPluginIp" : { + "priority":1000, + "category" : ["test", "unittest"], + "exclude" : ["*", "!TESTPLUGIN" ] +} +} +)plugin") + diff --git a/core/test/testplugin.json b/core/test/testplugin.json new file mode 100644 index 0000000000..3372baeaae --- /dev/null +++ b/core/test/testplugin.json @@ -0,0 +1,12 @@ +(R"plugin( +{ +"TestPlugin" : { + "priority":2, + "category" : ["test", "unittest"] +}, +"TestPluginIp" : { + "priority":1000, + "category" : ["test", "unittest"] +} +} +)plugin") diff --git a/core/test/testpluginexclude.json b/core/test/testpluginexclude.json new file mode 100644 index 0000000000..4ba6a42bc4 --- /dev/null +++ b/core/test/testpluginexclude.json @@ -0,0 +1,14 @@ +(R"plugin( +{ +"TestPlugin" : { + "priority":2, + "category" : ["test", "unittest"] +}, +"TestPluginIp" : { + "priority":1000, + "category" : ["test", "unittest"], + "exclude" : "*" +} +} +)plugin") + diff --git a/core/test/testpluginexclude2.json b/core/test/testpluginexclude2.json new file mode 100644 index 0000000000..8516f288d9 --- /dev/null +++ b/core/test/testpluginexclude2.json @@ -0,0 +1,14 @@ +(R"plugin( +{ +"TestPlugin" : { + "priority":2, + "category" : ["test", "unittest"] +}, +"TestPluginIp" : { + "priority":1000, + "category" : ["test", "unittest"], + "exclude" : ["*", "!TestPlugin" ] +} +} +)plugin") + diff --git a/core/test/tst_pluginmanager.cpp b/core/test/tst_pluginmanager.cpp new file mode 100644 index 0000000000..ffe4d6f414 --- /dev/null +++ b/core/test/tst_pluginmanager.cpp @@ -0,0 +1,377 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "core/pluginmanager.h" + +#include +#include +#include +#include +#include +#include + +using namespace scopy; + +class TST_PluginManager : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void libsFound(); + void loadLibs(); + void metadataOps(); + void exclusion(); + void exclusionSpecificLowercase(); + void exclusionSpecificUppercase(); + void exclusionExcept(); + void exclusionExceptUppercase(); + void exclusionExceptLowercase(); + +private: + void initFileList(); + QStringList libs; +}; + +#define NONPLUGIN_LIBRARY_LOCATION "../libscopycore.so" +#define PLUGIN_LOCATION "../../plugins/plugins" + +void TST_PluginManager::libsFound() +{ + initFileList(); + QVERIFY2(libs.count() > 0, "No libs not found"); +} + +void TST_PluginManager::loadLibs() +{ + PluginManager *p = new PluginManager(this); + p->add(libs); + QVERIFY2(p->count() > 0, "Load libs failed"); + + p->clear(); + QVERIFY2(p->count() == 0, "Clear libs failed"); + + for(const auto &lib : qAsConst(libs)) { + p->add(lib); + } + QVERIFY2(p->count() > 0, "Add 1-by-1 failed"); + + QList plugins; + QList sortedplugins; + plugins = p->getPlugins(""); + + QVERIFY2(plugins.count() == p->count(), "Plugin clone failed"); + + p->sort(); + sortedplugins = p->getPlugins(""); + for(auto p : qAsConst(plugins)) { + for(auto q : qAsConst(sortedplugins)) { + if(p == q) + QFAIL("duplicates found in sortedplugins vs plugins"); + } + } + + QVERIFY2(plugins.count() == sortedplugins.count(), "Subsequent call to get plugin gives different counts"); + for(int i = 1; i < sortedplugins.count(); i++) { + if(sortedplugins[i - 1]->metadata()["priority"].toInt() < + sortedplugins[i]->metadata()["priority"].toInt()) + QFAIL("Sort by priority failed"); + } + + QList usbPlugins = p->getCompatiblePlugins("usb:", "test"); + for(auto &&p : usbPlugins) { + QVERIFY2(p->param() == "usb:", "param not set to plugin"); + } + + QList ipPlugins = p->getCompatiblePlugins("ip:", "test"); + bool found = false; + QVERIFY2(ipPlugins.count() > 0, "No ip: plugins found"); + for(auto &&p : ipPlugins) { + if(p->name() == "TestPluginIp") + found = true; + } + QVERIFY2(found, "TestPluginIp not found"); + + p->add(NONPLUGIN_LIBRARY_LOCATION); + QVERIFY2(p->count() == plugins.count(), "Added nonplugin library to manager"); + + usbPlugins.clear(); + usbPlugins = p->getCompatiblePlugins("usb:", "test"); + for(auto &&p : usbPlugins) { + QVERIFY2(p->param() == "usb:", "param not set to plugin"); + } + + delete p; +} + +void TST_PluginManager::metadataOps() +{ + PluginManager *p = new PluginManager(this); + p->add(libs); + QVERIFY2(p->count() > 0, "Load libs failed"); + + QString json = QString( +#include "testplugin.json" + ); + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &err); + if(err.error != QJsonParseError::NoError) { + qCritical() << "JSON Parse error !" << err.errorString(); + qCritical() << json; + qCritical() << QString(" ").repeated(err.offset) + "^"; + } + QJsonObject obj = doc.object(); + p->clear(); + p->setMetadata(obj); + p->add(libs); + p->sort(); + QVERIFY2(p->count() > 0, "Load libs failed"); + + auto plugins = p->getPlugins("test"); + QVERIFY2(plugins.count() >= 2, "Exactly 2 unit tests not found"); + qDebug() << plugins[0]->name(); + + QVERIFY2(plugins[0]->name() == "TestPluginIp", "TestPluginIp is not loaded with highest priority"); + QVERIFY2(plugins[0]->metadata()["priority"] == 1000, "TestPluginIp priority not overridden"); +} + +void TST_PluginManager::exclusion() +{ + PluginManager *p = new PluginManager(this); + p->add(libs); + QVERIFY2(p->count() > 0, "Load libs failed"); + + QString json = QString( +#include "testpluginexclude.json" + ); + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &err); + if(err.error != QJsonParseError::NoError) { + qCritical() << "JSON Parse error !" << err.errorString(); + qCritical() << json; + qCritical() << QString(" ").repeated(err.offset) + "^"; + } + QJsonObject obj = doc.object(); + p->clear(); + p->setMetadata(obj); + p->add(libs); + p->sort(); + QVERIFY2(p->count() > 0, "Load libs failed"); + + auto plugins = p->getCompatiblePlugins("ip:", "unittest"); + QVERIFY2(plugins.count() == 2, "Only TestPluginIp plugin compatible compatible"); + qDebug() << plugins[0]->name(); + qDebug() << plugins[1]->name(); + + QVERIFY2(plugins[0]->name() == "TestPluginIp", "TestPlugin is the first plugin"); + QVERIFY2(plugins[0]->metadata()["exclude"] == "*", "TestPluginIp excludes everything"); + QVERIFY2(plugins[1]->name() == "TestPlugin", "TestPluginIp is the second plugin"); + + QVERIFY2(plugins[0]->enabled() == true, "TestPluginIp not enabled"); + QVERIFY2(plugins[1]->enabled() == false, "TestPlugin is enabled"); +} + +void TST_PluginManager::exclusionSpecificLowercase() +{ + PluginManager *p = new PluginManager(this); + p->add(libs); + QVERIFY2(p->count() > 0, "Load libs failed"); + + QString json = QString( +#include "testPluginExcludeSpecificLower.json" + ); + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &err); + if(err.error != QJsonParseError::NoError) { + qCritical() << "JSON Parse error !" << err.errorString(); + qCritical() << json; + qCritical() << QString(" ").repeated(err.offset) + "^"; + } + QJsonObject obj = doc.object(); + p->clear(); + p->setMetadata(obj); + p->add(libs); + p->sort(); + QVERIFY2(p->count() > 0, "Load libs failed"); + + auto plugins = p->getCompatiblePlugins("ip:", "unittest"); + QVERIFY2(plugins.count() == 2, "Exactly 1 unit tests not found"); + qDebug() << plugins[0]->name(); + + QVERIFY2(plugins[0]->name() == "TestPluginIp", "TestPluginIp is the first plugin"); + QVERIFY2(plugins[0]->metadata()["exclude"].toArray()[0] == "testplugin", "TestPluginIP "); + QVERIFY2(plugins[1]->name() == "TestPlugin", "TestPlugin is the second plugin"); + + QVERIFY2(plugins[0]->enabled() == true, "TestPluginIp not enabled"); + QVERIFY2(plugins[1]->enabled() == false, "TestPlugin is enabled"); +} + +void TST_PluginManager::exclusionSpecificUppercase() +{ + PluginManager *p = new PluginManager(this); + p->add(libs); + QVERIFY2(p->count() > 0, "Load libs failed"); + + QString json = QString( +#include "testPluginExcludeSpecificUpper.json" + ); + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &err); + if(err.error != QJsonParseError::NoError) { + qCritical() << "JSON Parse error !" << err.errorString(); + qCritical() << json; + qCritical() << QString(" ").repeated(err.offset) + "^"; + } + QJsonObject obj = doc.object(); + p->clear(); + p->setMetadata(obj); + p->add(libs); + p->sort(); + QVERIFY2(p->count() > 0, "Load libs failed"); + + auto plugins = p->getCompatiblePlugins("ip:", "unittest"); + QVERIFY2(plugins.count() == 2, "Exactly 1 unit tests not found"); + qDebug() << plugins[0]->name(); + + QVERIFY2(plugins[0]->name() == "TestPluginIp", "TestPluginIp is the first plugin"); + QVERIFY2(plugins[0]->metadata()["exclude"].toArray()[0] == "TESTPLUGIN", "TestPluginIP "); + QVERIFY2(plugins[1]->name() == "TestPlugin", "TestPlugin is the second plugin"); + + QVERIFY2(plugins[0]->enabled() == true, "TestPluginIp not enabled"); + QVERIFY2(plugins[1]->enabled() == false, "TestPlugin is enabled"); +} + +void TST_PluginManager::exclusionExcept() +{ + PluginManager *p = new PluginManager(this); + p->add(libs); + QVERIFY2(p->count() > 0, "Load libs failed"); + + QString json = QString( +#include "testpluginexclude2.json" + ); + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &err); + if(err.error != QJsonParseError::NoError) { + qCritical() << "JSON Parse error !" << err.errorString(); + qCritical() << json; + qCritical() << QString(" ").repeated(err.offset) + "^"; + } + QJsonObject obj = doc.object(); + p->clear(); + p->setMetadata(obj); + p->add(libs); + p->sort(); + QVERIFY2(p->count() > 0, "Load libs failed"); + + auto plugins = p->getCompatiblePlugins("ip:", "unittest"); + QVERIFY2(plugins.count() == 2, "Exactly 1 unit tests not found"); + qDebug() << plugins[0]->name(); + + QVERIFY2(plugins[0]->name() == "TestPluginIp", "TestPluginIp is not loaded"); + QVERIFY2(plugins[0]->metadata()["exclude"].toArray()[0] == "*" && + plugins[0]->metadata()["exclude"].toArray()[1] == "!TestPlugin", + "TestPluginIP "); + QVERIFY2(plugins[1]->name() == "TestPlugin", "Second TestPlugin is not loaded"); +} + +void TST_PluginManager::exclusionExceptUppercase() +{ + PluginManager *p = new PluginManager(this); + p->add(libs); + QVERIFY2(p->count() > 0, "Load libs failed"); + + QString json = QString( +#include "testPluginExcludeUpper.json" + ); + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &err); + if(err.error != QJsonParseError::NoError) { + qCritical() << "JSON Parse error !" << err.errorString(); + qCritical() << json; + qCritical() << QString(" ").repeated(err.offset) + "^"; + } + QJsonObject obj = doc.object(); + p->clear(); + p->setMetadata(obj); + p->add(libs); + p->sort(); + QVERIFY2(p->count() > 0, "Load libs failed"); + + auto plugins = p->getCompatiblePlugins("ip:", "unittest"); + QVERIFY2(plugins.count() == 2, "Exactly 1 unit tests not found"); + qDebug() << plugins[0]->name(); + + QVERIFY2(plugins[0]->name() == "TestPluginIp", "TestPluginIp is not loaded"); + QVERIFY2(plugins[0]->metadata()["exclude"].toArray()[0] == "*" && + plugins[0]->metadata()["exclude"].toArray()[1] == "!TESTPLUGIN", + "TestPluginIP "); + QVERIFY2(plugins[1]->name() == "TestPlugin", "Second TestPlugin is not loaded"); +} + +void TST_PluginManager::exclusionExceptLowercase() +{ + PluginManager *p = new PluginManager(this); + p->add(libs); + QVERIFY2(p->count() > 0, "Load libs failed"); + + QString json = QString( +#include "testPluginExcludeLower.json" + ); + QJsonParseError err; + QJsonDocument doc = QJsonDocument::fromJson(json.toUtf8(), &err); + if(err.error != QJsonParseError::NoError) { + qCritical() << "JSON Parse error !" << err.errorString(); + qCritical() << json; + qCritical() << QString(" ").repeated(err.offset) + "^"; + } + QJsonObject obj = doc.object(); + p->clear(); + p->setMetadata(obj); + p->add(libs); + p->sort(); + QVERIFY2(p->count() > 0, "Load libs failed"); + + auto plugins = p->getCompatiblePlugins("ip:", "unittest"); + QVERIFY2(plugins.count() == 2, "Exactly 1 unit tests not found"); + qDebug() << plugins[0]->name(); + + QVERIFY2(plugins[0]->name() == "TestPluginIp", "TestPluginIp is not loaded"); + QVERIFY2(plugins[0]->metadata()["exclude"].toArray()[0] == "*" && + plugins[0]->metadata()["exclude"].toArray()[1] == "!testplugin", + "TestPluginIP "); + QVERIFY2(plugins[1]->name() == "TestPlugin", "Second TestPlugin is not loaded"); +} + +void TST_PluginManager::initFileList() +{ + QDir directory(PLUGIN_LOCATION); + QStringList files = directory.entryList(); + libs.clear(); + for(const QString &file : files) { + if(QLibrary::isLibrary(file)) { + qDebug() << "Library: " << file; + libs.append(directory.absoluteFilePath(file)); + } + } +} + +QTEST_MAIN(TST_PluginManager) + +#include "tst_pluginmanager.moc" diff --git a/core/test/tst_pluginrepository.cpp b/core/test/tst_pluginrepository.cpp new file mode 100644 index 0000000000..5e704e004a --- /dev/null +++ b/core/test/tst_pluginrepository.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "core/pluginrepository.h" + +#include +#include +#include + +using namespace scopy; + +class TST_PluginRepository : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void loadLibs(); + +private: + QStringList libs; +}; + +#define NONPLUGIN_LIBRARY_LOCATION "../libscopycore.so" +#define PLUGIN_LOCATION "../../plugins/plugins" + +void TST_PluginRepository::loadLibs() +{ + PluginRepository *p = new PluginRepository(this); + PluginManager *pm = p->getPluginManager(); + QVERIFY(pm->metadata().isEmpty()); + p->init(PLUGIN_LOCATION); + // QVERIFY(!pm->metadata().isEmpty()); + + delete p; +} + +QTEST_MAIN(TST_PluginRepository) + +#include "tst_pluginrepository.moc" diff --git a/core/test/tst_translationsrepository.cpp b/core/test/tst_translationsrepository.cpp new file mode 100644 index 0000000000..7b1c03bb2c --- /dev/null +++ b/core/test/tst_translationsrepository.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024 Analog Devices Inc. + * + * This file is part of Scopy + * (see https://www.github.com/analogdevicesinc/scopy). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "core/translationsrepository.h" + +#include +#include +#include + +using namespace scopy; + +class TST_TranslationsRepository : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void checkPath(); + void checkGeneratedFiles(); + void loadTranslations(); + +private: + QStringList libs; +}; + +void TST_TranslationsRepository::checkPath() +{ + QDir pathDir(TranslationsRepository::GetInstance()->getTranslationsPath()); + + QVERIFY(pathDir.exists()); + QVERIFY(pathDir.entryList().contains("test.qm")); + QVERIFY(pathDir.entryList().contains("test_test.qm")); + qDebug() << "Found files:" << pathDir.entryList(); +} + +void TST_TranslationsRepository::checkGeneratedFiles() +{ + QStringList languages = TranslationsRepository::GetInstance()->getLanguages(); + qDebug() << "Found languages:" << languages; + + QVERIFY(languages.contains("test")); +} + +void TST_TranslationsRepository::loadTranslations() +{ + QVERIFY(tr("TEST1") == "TEST1"); + QVERIFY(tr("TEST2") == "TEST2"); + + TranslationsRepository::GetInstance()->loadTranslations("test"); + + // verifying if test.qm was applied + QVERIFY(tr("TEST1") == "test1"); + + // verifying if test_test.qm was applied + QVERIFY(tr("TEST2") == "test2"); +} + +QTEST_MAIN(TST_TranslationsRepository) + +#include "tst_translationsrepository.moc" diff --git a/core/ui/devicebrowser.ui b/core/ui/devicebrowser.ui new file mode 100644 index 0000000000..d4de72d3dc --- /dev/null +++ b/core/ui/devicebrowser.ui @@ -0,0 +1,262 @@ + + + DeviceBrowser + + + + 0 + 0 + 464 + 108 + + + + + 0 + 0 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 10 + + + 0 + + + 0 + + + 0 + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 9 + + + 9 + + + + + + 0 + 0 + + + + + 40 + 40 + + + + + 40 + 40 + + + + + + + + :/gui/icons/home.svg + + + + + 40 + 40 + + + + true + + + false + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + 9 + + + 9 + + + + + + 0 + 0 + + + + + 40 + 40 + + + + + 40 + 40 + + + + + + + + :/gui/icons/add.svg + + + + + 40 + 40 + + + + true + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + 0 + 0 + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + 0 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + diff --git a/core/ui/devicebutton.ui b/core/ui/devicebutton.ui new file mode 100644 index 0000000000..0bf899d55c --- /dev/null +++ b/core/ui/devicebutton.ui @@ -0,0 +1,279 @@ + + + DeviceButton + + + + 0 + 0 + 140 + 200 + + + + + 0 + 0 + + + + + 0 + 160 + + + + + 16777215 + 200 + + + + Form + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 140 + 120 + + + + + 140 + 160 + + + + QLabel { +qproperty-alignment: AlignCenter; +} + + + false + + + true + + + + 3 + + + 20 + + + 0 + + + 20 + + + 10 + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + false + + + false + + + Qt::AlignCenter + + + false + + + + + + + + 0 + 0 + + + + + 100 + 100 + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + 0 + 0 + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 0 + 5 + + + + + + + + + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 2 + + + + + 16777215 + 2 + + + + QFrame#line { + border: 2px solid transparent; +} + +QFrame#line[connecting=true] { + border: 2px solid yellow; +} + +QFrame#line[connected=true] { + border: 2px solid green; +} + +QFrame#line[failed=true] { + border: 2px solid red; +} + + + QFrame::Sunken + + + 2 + + + Qt::Horizontal + + + false + + + false + + + + + + + + + + + diff --git a/core/ui/devicepage.ui b/core/ui/devicepage.ui new file mode 100644 index 0000000000..bb76ef56da --- /dev/null +++ b/core/ui/devicepage.ui @@ -0,0 +1,101 @@ + + + DevicePage + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 5 + + + 20 + + + 5 + + + 20 + + + 5 + + + + + + + true + + + + + 0 + 0 + 398 + 288 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + diff --git a/core/ui/scopyhomeinfopage.ui b/core/ui/scopyhomeinfopage.ui new file mode 100644 index 0000000000..281410e853 --- /dev/null +++ b/core/ui/scopyhomeinfopage.ui @@ -0,0 +1,48 @@ + + + ScopyHomeInfoPage + + + + 0 + 0 + 400 + 300 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + qrc:/scopy_home.html + + + + true + + + + + + + + diff --git a/core/ui/scopyhomepage.ui b/core/ui/scopyhomepage.ui new file mode 100644 index 0000000000..6568bf75c2 --- /dev/null +++ b/core/ui/scopyhomepage.ui @@ -0,0 +1,200 @@ + + + ScopyHomePage + + + + 0 + 0 + 400 + 300 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 10 + + + 10 + + + 10 + + + 10 + + + 10 + + + + + + 1 + + + + + + 0 + 0 + + + + + + + + Qt::Orientation::Horizontal + + + QSizePolicy::Policy::Preferred + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + + QLayout::SizeConstraint::SetDefaultConstraint + + + 0 + + + 10 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Scan + + + + + + + + + + true + + + false + + + + + + + + 0 + 0 + + + + Scan + + + + + + + Qt::Orientation::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + + scopy::DeviceBrowser + QWidget +
devicebrowser.h
+ 1 +
+ + scopy::InfoPageStack + QStackedWidget +
infopagestack.h
+ 1 +
+ + scopy::SmallOnOffSwitch + QPushButton +
gui/smallOnOffSwitch.h
+
+
+ + +
diff --git a/core/ui/scopymainwindow.ui b/core/ui/scopymainwindow.ui new file mode 100644 index 0000000000..ab659bdfdf --- /dev/null +++ b/core/ui/scopymainwindow.ui @@ -0,0 +1,176 @@ + + + ScopyMainWindow + + + + 0 + 0 + 1280 + 720 + + + + + 0 + 0 + + + + + 1280 + 720 + + + + MainWindow + + + + + 0 + 0 + + + + + 1024 + 720 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 230 + 0 + + + + + 230 + 16777215 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + + + + + + scopy::ToolStack + QStackedWidget +
toolstack.h
+ 1 +
+ + scopy::MenuHAnim + QWidget +
gui/menu_anim.hpp
+ 1 +
+
+ + +
diff --git a/doc/Doxyfile_API.in b/doc/Doxyfile_API.in deleted file mode 100644 index aab0212dbe..0000000000 --- a/doc/Doxyfile_API.in +++ /dev/null @@ -1,1854 +0,0 @@ -DOXYFILE_ENCODING = UTF-8 -PROJECT_NAME = "Scopy Scripting Environment" -# PROJECT_NUMBER = @LIBIIO_VERSION_MAJOR@.@LIBIIO_VERSION_MINOR@ - -# Using the PROJECT_BRIEF tag one can provide an optional one line description -# for a project that appears at the top of each page and should give viewer a -# quick idea about the purpose of the project. Keep the description short. - -PROJECT_BRIEF = "Scripting library for Scopy" - -# With the PROJECT_LOGO tag one can specify an logo or icon that is included in -# the documentation. The maximum height of the logo should not exceed 55 pixels -# and the maximum width should not exceed 200 pixels. Doxygen will copy the logo -# to the output directory. - -PROJECT_LOGO = - -# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path -# into which the generated documentation will be written. If a relative path is -# entered, it will be relative to the location where doxygen was started. If -# left blank the current directory will be used. - -OUTPUT_DIRECTORY = . -CREATE_SUBDIRS = NO -OUTPUT_LANGUAGE = English - -# If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member -# descriptions after the members that are listed in the file and class -# documentation (similar to Javadoc). Set to NO to disable this. -# The default value is: YES. - -BRIEF_MEMBER_DESC = YES - -# If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief -# description of a member or function before the detailed description -# -# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the -# brief descriptions will be completely suppressed. -# The default value is: YES. - -REPEAT_BRIEF = YES - -# This tag implements a quasi-intelligent brief description abbreviator that is -# used to form the text in various listings. Each string in this list, if found -# as the leading text of the brief description, will be stripped from the text -# and the result, after processing the whole list, is used as the annotated -# text. Otherwise, the brief description is used as-is. If left blank, the -# following values are used ($name is automatically replaced with the name of -# the entity):The $name class, The $name widget, The $name file, is, provides, -# specifies, contains, represents, a, an and the. - -ABBREVIATE_BRIEF = - -# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then -# doxygen will generate a detailed section even if there is only a brief -# description. -# The default value is: NO. - -ALWAYS_DETAILED_SEC = NO - -# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all -# inherited members of a class in the documentation of that class as if those -# members were ordinary class members. Constructors, destructors and assignment -# operators of the base classes will not be shown. -# The default value is: NO. - -INLINE_INHERITED_MEMB = NO - -# If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path -# before files name in the file list and in the header files. If set to NO the -# shortest path that makes the file name unique will be used -# The default value is: YES. - -FULL_PATH_NAMES = YES - -# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. -# Stripping is only done if one of the specified strings matches the left-hand -# part of the path. The tag can be used to show relative paths in the file list. -# If left blank the directory from which doxygen is run is used as the path to -# strip. -# -# Note that you can specify absolute paths here, but also relative paths, which -# will be relative from the directory where doxygen is started. -# This tag requires that the tag FULL_PATH_NAMES is set to YES. - -STRIP_FROM_PATH = - -# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the -# path mentioned in the documentation of a class, which tells the reader which -# header file to include in order to use a class. If left blank only the name of -# the header file containing the class definition is used. Otherwise one should -# specify the list of include paths that are normally passed to the compiler -# using the -I flag. - -STRIP_FROM_INC_PATH = - -# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but -# less readable) file names. This can be useful is your file systems doesn't -# support long names like on DOS, Mac, or CD-ROM. -# The default value is: NO. - -SHORT_NAMES = NO - -# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the -# first line (until the first dot) of a Javadoc-style comment as the brief -# description. If set to NO, the Javadoc-style will behave just like regular Qt- -# style comments (thus requiring an explicit @brief command for a brief -# description.) -# The default value is: NO. - -JAVADOC_AUTOBRIEF = NO - -# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first -# line (until the first dot) of a Qt-style comment as the brief description. If -# set to NO, the Qt-style will behave just like regular Qt-style comments (thus -# requiring an explicit \brief command for a brief description.) -# The default value is: NO. - -QT_AUTOBRIEF = YES - -# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a -# multi-line C++ special comment block (i.e. a block of //! or /// comments) as -# a brief description. This used to be the default behavior. The new default is -# to treat a multi-line C++ comment block as a detailed description. Set this -# tag to YES if you prefer the old behavior instead. -# -# Note that setting this tag to YES also means that rational rose comments are -# not recognized any more. -# The default value is: NO. - -MULTILINE_CPP_IS_BRIEF = YES - -# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the -# documentation from any documented member that it re-implements. -# The default value is: YES. - -INHERIT_DOCS = YES - -# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a -# new page for each member. If set to NO, the documentation of a member will be -# part of the file/class/namespace that contains it. -# The default value is: NO. - -SEPARATE_MEMBER_PAGES = NO - -# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen -# uses this value to replace tabs by spaces in code fragments. -# Minimum value: 1, maximum value: 16, default value: 4. - -TAB_SIZE = 4 - -# This tag can be used to specify a number of aliases that act as commands in -# the documentation. An alias has the form: -# name=value -# For example adding -# "sideeffect=@par Side Effects:\n" -# will allow you to put the command \sideeffect (or @sideeffect) in the -# documentation, which will result in a user-defined paragraph with heading -# "Side Effects:". You can put \n's in the value part of an alias to insert -# newlines. - -ALIASES = - -# This tag can be used to specify a number of word-keyword mappings (TCL only). -# A mapping has the form "name=value". For example adding "class=itcl::class" -# will allow you to use the command class in the itcl::class meaning. - -TCL_SUBST = - -# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources -# only. Doxygen will then generate output that is more tailored for C. For -# instance, some of the names that are used will be different. The list of all -# members will be omitted, etc. -# The default value is: NO. - -OPTIMIZE_OUTPUT_FOR_C = NO - -# Doxygen selects the parser to use depending on the extension of the files it -# parses. With this tag you can assign which parser to use for a given -# extension. Doxygen has a built-in mapping, but you can override or extend it -# using this tag. The format is ext=language, where ext is a file extension, and -# language is one of the parsers supported by doxygen: IDL, Java, Javascript, -# C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make -# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C -# (default is Fortran), use: inc=Fortran f=C. -# -# Note For files without extension you can use no_extension as a placeholder. -# -# Note that for custom extensions you also need to set FILE_PATTERNS otherwise -# the files are not read by doxygen. - -EXTENSION_MAPPING = - -# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments -# according to the Markdown format, which allows for more readable -# documentation. See http://daringfireball.net/projects/markdown/ for details. -# The output of markdown processing is further processed by doxygen, so you can -# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in -# case of backward compatibilities issues. -# The default value is: YES. - -MARKDOWN_SUPPORT = YES - -# When enabled doxygen tries to link words that correspond to documented -# classes, or namespaces to their corresponding documentation. Such a link can -# be prevented in individual cases by by putting a % sign in front of the word -# or globally by setting AUTOLINK_SUPPORT to NO. -# The default value is: YES. - -AUTOLINK_SUPPORT = YES - -# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC -# tag is set to YES, then doxygen will reuse the documentation of the first -# member in the group (if any) for the other members of the group. By default -# all members of a group must be documented explicitly. -# The default value is: NO. - -DISTRIBUTE_GROUP_DOC = NO - -# Set the SUBGROUPING tag to YES to allow class member groups of the same type -# (for instance a group of public functions) to be put as a subgroup of that -# type (e.g. under the Public Functions section). Set it to NO to prevent -# subgrouping. Alternatively, this can be done per class using the -# \nosubgrouping command. -# The default value is: YES. - -SUBGROUPING = YES - -# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions -# are shown inside the group in which they are included (e.g. using \ingroup) -# instead of on a separate page (for HTML and Man pages) or section (for LaTeX -# and RTF). -# -# Note that this feature does not work in combination with -# SEPARATE_MEMBER_PAGES. -# The default value is: NO. - -INLINE_GROUPED_CLASSES = NO - -# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions -# with only public data fields or simple typedef fields will be shown inline in -# the documentation of the scope in which they are defined (i.e. file, -# namespace, or group documentation), provided this scope is documented. If set -# to NO, structs, classes, and unions are shown on a separate page (for HTML and -# Man pages) or section (for LaTeX and RTF). -# The default value is: NO. - -INLINE_SIMPLE_STRUCTS = NO - -# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or -# enum is documented as struct, union, or enum with the name of the typedef. So -# typedef struct TypeS {} TypeT, will appear in the documentation as a struct -# with name TypeT. When disabled the typedef will appear as a member of a file, -# namespace, or class. And the struct will be named TypeS. This can typically be -# useful for C code in case the coding convention dictates that all compound -# types are typedef'ed and only the typedef is referenced, never the tag name. -# The default value is: NO. - -TYPEDEF_HIDES_STRUCT = NO - -# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This -# cache is used to resolve symbols given their name and scope. Since this can be -# an expensive process and often the same symbol appears multiple times in the -# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small -# doxygen will become slower. If the cache is too large, memory is wasted. The -# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range -# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 -# symbols. At the end of a run doxygen will report the cache usage and suggest -# the optimal cache size from a speed point of view. -# Minimum value: 0, maximum value: 9, default value: 0. - -LOOKUP_CACHE_SIZE = 0 - -#--------------------------------------------------------------------------- -# Build related configuration options -#--------------------------------------------------------------------------- - -# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in -# documentation are documented, even if no documentation was available. Private -# class members and static file members will be hidden unless the -# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. -# Note: This will also disable the warnings about undocumented members that are -# normally produced when WARNINGS is set to YES. -# The default value is: NO. - -EXTRACT_ALL = NO - -# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will -# be included in the documentation. -# The default value is: NO. - -EXTRACT_PRIVATE = NO - -# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal -# scope will be included in the documentation. -# The default value is: NO. - -EXTRACT_PACKAGE = NO - -# If the EXTRACT_STATIC tag is set to YES all static members of a file will be -# included in the documentation. -# The default value is: NO. - -EXTRACT_STATIC = NO - -# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined -# locally in source files will be included in the documentation. If set to NO -# only classes defined in header files are included. Does not have any effect -# for Java sources. -# The default value is: YES. - -EXTRACT_LOCAL_CLASSES = NO - -# This flag is only useful for Objective-C code. When set to YES local methods, -# which are defined in the implementation section but not in the interface are -# included in the documentation. If set to NO only methods in the interface are -# included. -# The default value is: NO. - -EXTRACT_LOCAL_METHODS = NO - -# If this flag is set to YES, the members of anonymous namespaces will be -# extracted and appear in the documentation as a namespace called -# 'anonymous_namespace{file}', where file will be replaced with the base name of -# the file that contains the anonymous namespace. By default anonymous namespace -# are hidden. -# The default value is: NO. - -EXTRACT_ANON_NSPACES = NO - -# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all -# undocumented members inside documented classes or files. If set to NO these -# members will be included in the various overviews, but no documentation -# section is generated. This option has no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_MEMBERS = YES - -# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all -# undocumented classes that are normally visible in the class hierarchy. If set -# to NO these classes will be included in the various overviews. This option has -# no effect if EXTRACT_ALL is enabled. -# The default value is: NO. - -HIDE_UNDOC_CLASSES = YES - -# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend -# (class|struct|union) declarations. If set to NO these declarations will be -# included in the documentation. -# The default value is: NO. - -HIDE_FRIEND_COMPOUNDS = NO - -# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any -# documentation blocks found inside the body of a function. If set to NO these -# blocks will be appended to the function's detailed documentation block. -# The default value is: NO. - -HIDE_IN_BODY_DOCS = NO - -# The INTERNAL_DOCS tag determines if documentation that is typed after a -# \internal command is included. If the tag is set to NO then the documentation -# will be excluded. Set it to YES to include the internal documentation. -# The default value is: NO. - -INTERNAL_DOCS = NO - -# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file -# names in lower-case letters. If set to YES upper-case letters are also -# allowed. This is useful if you have classes or files whose names only differ -# in case and if your file system supports case sensitive file names. Windows -# and Mac users are advised to set this option to NO. -# The default value is: system dependent. - -CASE_SENSE_NAMES = YES - -# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with -# their full class and namespace scopes in the documentation. If set to YES the -# scope will be hidden. -# The default value is: NO. - -HIDE_SCOPE_NAMES = NO - -# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of -# the files that are included by a file in the documentation of that file. -# The default value is: YES. - -SHOW_INCLUDE_FILES = YES - -# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each -# grouped member an include statement to the documentation, telling the reader -# which file to include in order to use the member. -# The default value is: NO. - -SHOW_GROUPED_MEMB_INC = NO - -# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include -# files with double quotes in the documentation rather than with sharp brackets. -# The default value is: NO. - -FORCE_LOCAL_INCLUDES = NO - -# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the -# documentation for inline members. -# The default value is: YES. - -INLINE_INFO = YES - -# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the -# (detailed) documentation of file and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. -# The default value is: YES. - -SORT_MEMBER_DOCS = YES - -# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief -# descriptions of file, namespace and class members alphabetically by member -# name. If set to NO the members will appear in declaration order. Note that -# this will also influence the order of the classes in the class list. -# The default value is: NO. - -SORT_BRIEF_DOCS = NO - -# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the -# (brief and detailed) documentation of class members so that constructors and -# destructors are listed first. If set to NO the constructors will appear in the -# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. -# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief -# member documentation. -# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting -# detailed member documentation. -# The default value is: NO. - -SORT_MEMBERS_CTORS_1ST = NO - -# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy -# of group names into alphabetical order. If set to NO the group names will -# appear in their defined order. -# The default value is: NO. - -SORT_GROUP_NAMES = NO - -# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by -# fully-qualified names, including namespaces. If set to NO, the class list will -# be sorted only by class name, not including the namespace part. -# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. -# Note: This option applies only to the class list, not to the alphabetical -# list. -# The default value is: NO. - -SORT_BY_SCOPE_NAME = NO - -# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper -# type resolution of all parameters of a function it will reject a match between -# the prototype and the implementation of a member function even if there is -# only one candidate or it is obvious which candidate to choose by doing a -# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still -# accept a match between prototype and implementation in such cases. -# The default value is: NO. - -STRICT_PROTO_MATCHING = NO - -# The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the -# todo list. This list is created by putting \todo commands in the -# documentation. -# The default value is: YES. - -GENERATE_TODOLIST = YES - -# The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the -# test list. This list is created by putting \test commands in the -# documentation. -# The default value is: YES. - -GENERATE_TESTLIST = YES - -# The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug -# list. This list is created by putting \bug commands in the documentation. -# The default value is: YES. - -GENERATE_BUGLIST = YES - -# The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) -# the deprecated list. This list is created by putting \deprecated commands in -# the documentation. -# The default value is: YES. - -GENERATE_DEPRECATEDLIST= YES - -# The ENABLED_SECTIONS tag can be used to enable conditional documentation -# sections, marked by \if ... \endif and \cond -# ... \endcond blocks. - -ENABLED_SECTIONS = - -# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the -# initial value of a variable or macro / define can have for it to appear in the -# documentation. If the initializer consists of more lines than specified here -# it will be hidden. Use a value of 0 to hide initializers completely. The -# appearance of the value of individual variables and macros / defines can be -# controlled using \showinitializer or \hideinitializer command in the -# documentation regardless of this setting. -# Minimum value: 0, maximum value: 10000, default value: 30. - -MAX_INITIALIZER_LINES = 30 - -# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at -# the bottom of the documentation of classes and structs. If set to YES the list -# will mention the files that were used to generate the documentation. -# The default value is: YES. - -SHOW_USED_FILES = YES - -# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This -# will remove the Files entry from the Quick Index and from the Folder Tree View -# (if specified). -# The default value is: YES. - -SHOW_FILES = YES - -# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces -# page. This will remove the Namespaces entry from the Quick Index and from the -# Folder Tree View (if specified). -# The default value is: YES. - -SHOW_NAMESPACES = YES - -# The FILE_VERSION_FILTER tag can be used to specify a program or script that -# doxygen should invoke to get the current version for each file (typically from -# the version control system). Doxygen will invoke the program by executing (via -# popen()) the command command input-file, where command is the value of the -# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided -# by doxygen. Whatever the program writes to standard output is used as the file -# version. For an example see the documentation. - -FILE_VERSION_FILTER = - -# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed -# by doxygen. The layout file controls the global structure of the generated -# output files in an output format independent way. To create the layout file -# that represents doxygen's defaults, run doxygen with the -l option. You can -# optionally specify a file name after the option, if omitted DoxygenLayout.xml -# will be used as the name of the layout file. -# -# Note that if you run doxygen from a directory containing a file called -# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE -# tag is left empty. - -LAYOUT_FILE = - -# The CITE_BIB_FILES tag can be used to specify one or more bib files containing -# the reference definitions. This must be a list of .bib files. The .bib -# extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. -# For LaTeX the style of the bibliography can be controlled using -# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the -# search path. Do not use file names with spaces, bibtex cannot handle them. See -# also \cite for info how to create references. - -CITE_BIB_FILES = - -#--------------------------------------------------------------------------- -# Configuration options related to warning and progress messages -#--------------------------------------------------------------------------- - -# The QUIET tag can be used to turn on/off the messages that are generated to -# standard output by doxygen. If QUIET is set to YES this implies that the -# messages are off. -# The default value is: NO. - -QUIET = YES - -# The WARNINGS tag can be used to turn on/off the warning messages that are -# generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES -# this implies that the warnings are on. -# -# Tip: Turn warnings on while writing the documentation. -# The default value is: YES. - -WARNINGS = YES - -# If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate -# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag -# will automatically be disabled. -# The default value is: YES. - -WARN_IF_UNDOCUMENTED = YES - -# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for -# potential errors in the documentation, such as not documenting some parameters -# in a documented function, or documenting parameters that don't exist or using -# markup commands wrongly. -# The default value is: YES. - -WARN_IF_DOC_ERROR = YES - -# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that -# are documented, but have no documentation for their parameters or return -# value. If set to NO doxygen will only warn about wrong or incomplete parameter -# documentation, but not about the absence of documentation. -# The default value is: NO. - -WARN_NO_PARAMDOC = NO - -# The WARN_FORMAT tag determines the format of the warning messages that doxygen -# can produce. The string should contain the $file, $line, and $text tags, which -# will be replaced by the file and line number from which the warning originated -# and the warning text. Optionally the format may contain $version, which will -# be replaced by the version of the file (if it could be obtained via -# FILE_VERSION_FILTER) -# The default value is: $file:$line: $text. - -WARN_FORMAT = "$file:$line: $text" - -# The WARN_LOGFILE tag can be used to specify a file to which warning and error -# messages should be written. If left blank the output is written to standard -# error (stderr). - -WARN_LOGFILE = - -#--------------------------------------------------------------------------- -# Configuration options related to the input files -#--------------------------------------------------------------------------- - -# The INPUT tag is used to specify the files and/or directories that contain -# documented source files. You may enter file names like myfile.cpp or -# directories like /usr/src/myproject. Separate the files or directories with -# spaces. -# Note: If this tag is empty the current directory is searched. - -INPUT = @CMAKE_SOURCE_DIR@/src -#INPUT = /home/adi/scopy/src - -# This tag can be used to specify the character encoding of the source files -# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses -# libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: http://www.gnu.org/software/libiconv) for the list of -# possible encodings. -# The default value is: UTF-8. - -INPUT_ENCODING = UTF-8 - -# If the value of the INPUT tag contains directories, you can use the -# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank the -# following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, -# *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, -# *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, -# *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, -# *.qsf, *.as and *.js. - -FILE_PATTERNS = *_api.cpp, *_api.hpp - -# The RECURSIVE tag can be used to specify whether or not subdirectories should -# be searched for input files as well. -# The default value is: NO. - -RECURSIVE = NO - -# The EXCLUDE tag can be used to specify files and/or directories that should be -# excluded from the INPUT source files. This way you can easily exclude a -# subdirectory from a directory tree whose root is specified with the INPUT tag. -# -# Note that relative paths are relative to the directory from which doxygen is -# run. - -#EXCLUDE = @CMAKE_SOURCE_DIR@/iio-private.h @CMAKE_SOURCE_DIR@/debug.h - -# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or -# directories that are symbolic links (a Unix file system feature) are excluded -# from the input. -# The default value is: NO. - -EXCLUDE_SYMLINKS = NO - -# If the value of the INPUT tag contains directories, you can use the -# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude -# certain files from those directories. -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories for example use the pattern */test/* - -EXCLUDE_PATTERNS = - -# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names -# (namespaces, classes, functions, etc.) that should be excluded from the -# output. The symbol name can be a fully qualified name, a word, or if the -# wildcard * is used, a substring. Examples: ANamespace, AClass, -# AClass::ANamespace, ANamespace::*Test -# -# Note that the wildcards are matched against the file with absolute path, so to -# exclude all test directories use the pattern */test/* - -EXCLUDE_SYMBOLS = - -# The EXAMPLE_PATH tag can be used to specify one or more files or directories -# that contain example code fragments that are included (see the \include -# command). - -EXAMPLE_PATH = - -# If the value of the EXAMPLE_PATH tag contains directories, you can use the -# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and -# *.h) to filter out the source-files in the directories. If left blank all -# files are included. - -EXAMPLE_PATTERNS = - -# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be -# searched for input files to be used with the \include or \dontinclude commands -# irrespective of the value of the RECURSIVE tag. -# The default value is: NO. - -EXAMPLE_RECURSIVE = NO - -# The IMAGE_PATH tag can be used to specify one or more files or directories -# that contain images that are to be included in the documentation (see the -# \image command). - -IMAGE_PATH = - -# The INPUT_FILTER tag can be used to specify a program that doxygen should -# invoke to filter for each input file. Doxygen will invoke the filter program -# by executing (via popen()) the command: -# -# -# -# where is the value of the INPUT_FILTER tag, and is the -# name of an input file. Doxygen will then use the output that the filter -# program writes to standard output. If FILTER_PATTERNS is specified, this tag -# will be ignored. -# -# Note that the filter must not add or remove lines; it is applied before the -# code is scanned, but not when the output code is generated. If lines are added -# or removed, the anchors will not be placed correctly. - -INPUT_FILTER = - -# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern -# basis. Doxygen will compare the file name with each pattern and apply the -# filter if there is a match. The filters are a list of the form: pattern=filter -# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how -# filters are used. If the FILTER_PATTERNS tag is empty or if none of the -# patterns match the file name, INPUT_FILTER is applied. - -FILTER_PATTERNS = - -# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using -# INPUT_FILTER ) will also be used to filter the input files that are used for -# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). -# The default value is: NO. - -FILTER_SOURCE_FILES = NO - -# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file -# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and -# it is also possible to disable source filtering for a specific pattern using -# *.ext= (so without naming a filter). -# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. - -FILTER_SOURCE_PATTERNS = - -# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that -# is part of the input, its contents will be placed on the main page -# (index.html). This can be useful if you have a project on for instance GitHub -# and want to reuse the introduction page also for the doxygen output. - -USE_MDFILE_AS_MAINPAGE = - -#--------------------------------------------------------------------------- -# Configuration options related to source browsing -#--------------------------------------------------------------------------- - -# If the SOURCE_BROWSER tag is set to YES then a list of source files will be -# generated. Documented entities will be cross-referenced with these sources. -# -# Note: To get rid of all source code in the generated output, make sure that -# also VERBATIM_HEADERS is set to NO. -# The default value is: NO. - -SOURCE_BROWSER = NO - -# Setting the INLINE_SOURCES tag to YES will include the body of functions, -# classes and enums directly into the documentation. -# The default value is: NO. - -INLINE_SOURCES = NO - -# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any -# special comment blocks from generated source code fragments. Normal C, C++ and -# Fortran comments will always remain visible. -# The default value is: YES. - -STRIP_CODE_COMMENTS = YES - -# If the REFERENCED_BY_RELATION tag is set to YES then for each documented -# function all documented functions referencing it will be listed. -# The default value is: NO. - -REFERENCED_BY_RELATION = NO - -# If the REFERENCES_RELATION tag is set to YES then for each documented function -# all documented entities called/used by that function will be listed. -# The default value is: NO. - -REFERENCES_RELATION = NO - -# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set -# to YES, then the hyperlinks from functions in REFERENCES_RELATION and -# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will -# link to the documentation. -# The default value is: YES. - -REFERENCES_LINK_SOURCE = YES - -# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the -# source code will show a tooltip with additional information such as prototype, -# brief description and links to the definition and documentation. Since this -# will make the HTML file larger and loading of large files a bit slower, you -# can opt to disable this feature. -# The default value is: YES. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -SOURCE_TOOLTIPS = YES - -# If the USE_HTAGS tag is set to YES then the references to source code will -# point to the HTML generated by the htags(1) tool instead of doxygen built-in -# source browser. The htags tool is part of GNU's global source tagging system -# (see http://www.gnu.org/software/global/global.html). You will need version -# 4.8.6 or higher. -# -# To use it do the following: -# - Install the latest version of global -# - Enable SOURCE_BROWSER and USE_HTAGS in the config file -# - Make sure the INPUT points to the root of the source tree -# - Run doxygen as normal -# -# Doxygen will invoke htags (and that will in turn invoke gtags), so these -# tools must be available from the command line (i.e. in the search path). -# -# The result: instead of the source browser generated by doxygen, the links to -# source code will now point to the output of htags. -# The default value is: NO. -# This tag requires that the tag SOURCE_BROWSER is set to YES. - -USE_HTAGS = NO - -# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a -# verbatim copy of the header file for each class for which an include is -# specified. Set to NO to disable this. -# See also: Section \class. -# The default value is: YES. - -VERBATIM_HEADERS = YES - -#--------------------------------------------------------------------------- -# Configuration options related to the alphabetical class index -#--------------------------------------------------------------------------- - -# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all -# compounds will be generated. Enable this if the project contains a lot of -# classes, structs, unions or interfaces. -# The default value is: YES. - -ALPHABETICAL_INDEX = YES - -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 5 - -# In case all classes in a project start with a common prefix, all classes will -# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag -# can be used to specify a prefix (or a list of prefixes) that should be ignored -# while generating the index headers. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -IGNORE_PREFIX = - -#--------------------------------------------------------------------------- -# Configuration options related to the HTML output -#--------------------------------------------------------------------------- - -# If the GENERATE_HTML tag is set to YES doxygen will generate HTML output -# The default value is: YES. - -GENERATE_HTML = YES - -# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a -# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of -# it. -# The default directory is: html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_OUTPUT = html - -# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each -# generated HTML page (for example: .htm, .php, .asp). -# The default value is: .html. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FILE_EXTENSION = .html - -# The HTML_HEADER tag can be used to specify a user-defined HTML header file for -# each generated HTML page. If the tag is left blank doxygen will generate a -# standard header. -# -# To get valid HTML the header file that includes any scripts and style sheets -# that doxygen needs, which is dependent on the configuration options used (e.g. -# the setting GENERATE_TREEVIEW). It is highly recommended to start with a -# default header using -# doxygen -w html new_header.html new_footer.html new_stylesheet.css -# YourConfigFile -# and then modify the file new_header.html. See also section "Doxygen usage" -# for information on how to generate the default header that doxygen normally -# uses. -# Note: The header is subject to change so you typically have to regenerate the -# default header when upgrading to a newer version of doxygen. For a description -# of the possible markers and block names see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_HEADER = - -# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each -# generated HTML page. If the tag is left blank doxygen will generate a standard -# footer. See HTML_HEADER for more information on how to generate a default -# footer and what special commands can be used inside the footer. See also -# section "Doxygen usage" for information on how to generate the default footer -# that doxygen normally uses. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_FOOTER = - -# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style -# sheet that is used by each HTML page. It can be used to fine-tune the look of -# the HTML output. If left blank doxygen will generate a default style sheet. -# See also section "Doxygen usage" for information on how to generate the style -# sheet that doxygen normally uses. -# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as -# it is more robust and this tag (HTML_STYLESHEET) will in the future become -# obsolete. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_STYLESHEET = - -# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- -# defined cascading style sheet that is included after the standard style sheets -# created by doxygen. Using this option one can overrule certain style aspects. -# This is preferred over using HTML_STYLESHEET since it does not replace the -# standard style sheet and is therefor more robust against future updates. -# Doxygen will copy the style sheet file to the output directory. For an example -# see the documentation. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_STYLESHEET = - -# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or -# other source files which should be copied to the HTML output directory. Note -# that these files will be copied to the base HTML output directory. Use the -# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these -# files. In the HTML_STYLESHEET file, use the file name only. Also note that the -# files will be copied as-is; there are no commands or markers available. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_EXTRA_FILES = - -# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen -# will adjust the colors in the stylesheet and background images according to -# this color. Hue is specified as an angle on a colorwheel, see -# http://en.wikipedia.org/wiki/Hue for more information. For instance the value -# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 -# purple, and 360 is red again. -# Minimum value: 0, maximum value: 359, default value: 220. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_HUE = 220 - -# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors -# in the HTML output. For a value of 0 the output will use grayscales only. A -# value of 255 will produce the most vivid colors. -# Minimum value: 0, maximum value: 255, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_SAT = 100 - -# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the -# luminance component of the colors in the HTML output. Values below 100 -# gradually make the output lighter, whereas values above 100 make the output -# darker. The value divided by 100 is the actual gamma applied, so 80 represents -# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not -# change the gamma. -# Minimum value: 40, maximum value: 240, default value: 80. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_COLORSTYLE_GAMMA = 80 - -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to NO can help when comparing the output of multiple runs. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_TIMESTAMP = YES - -# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML -# documentation will contain sections that can be hidden and shown after the -# page has loaded. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_SECTIONS = NO - -# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries -# shown in the various tree structured indices initially; the user can expand -# and collapse entries dynamically later on. Doxygen will expand the tree to -# such a level that at most the specified number of entries are visible (unless -# a fully collapsed tree already exceeds this amount). So setting the number of -# entries 1 will produce a full collapsed tree by default. 0 is a special value -# representing an infinite number of entries and will result in a full expanded -# tree by default. -# Minimum value: 0, maximum value: 9999, default value: 100. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_INDEX_NUM_ENTRIES = 100 - -# If the GENERATE_DOCSET tag is set to YES, additional index files will be -# generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: http://developer.apple.com/tools/xcode/), introduced with -# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a -# Makefile in the HTML output directory. Running make will produce the docset in -# that directory and running make install will install the docset in -# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html -# for more information. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_DOCSET = NO - -# This tag determines the name of the docset feed. A documentation feed provides -# an umbrella under which multiple documentation sets from a single provider -# (such as a company or product suite) can be grouped. -# The default value is: Doxygen generated docs. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_FEEDNAME = "Doxygen generated docs" - -# This tag specifies a string that should uniquely identify the documentation -# set bundle. This should be a reverse domain-name style string, e.g. -# com.mycompany.MyDocSet. Doxygen will append .docset to the name. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_BUNDLE_ID = org.doxygen.Project - -# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify -# the documentation publisher. This should be a reverse domain-name style -# string, e.g. com.mycompany.MyDocSet.documentation. -# The default value is: org.doxygen.Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_ID = org.doxygen.Publisher - -# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. -# The default value is: Publisher. -# This tag requires that the tag GENERATE_DOCSET is set to YES. - -DOCSET_PUBLISHER_NAME = Publisher - -# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three -# additional HTML index files: index.hhp, index.hhc, and index.hhk. The -# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop -# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on -# Windows. -# -# The HTML Help Workshop contains a compiler that can convert all HTML output -# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML -# files are now used as the Windows 98 help format, and will replace the old -# Windows help format (.hlp) on all Windows platforms in the future. Compressed -# HTML files also contain an index, a table of contents, and you can search for -# words in the documentation. The HTML workshop also contains a viewer for -# compressed HTML files. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_HTMLHELP = NO - -# The CHM_FILE tag can be used to specify the file name of the resulting .chm -# file. You can add a path in front of the file if the result should not be -# written to the html output directory. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_FILE = - -# The HHC_LOCATION tag can be used to specify the location (absolute path -# including file name) of the HTML help compiler ( hhc.exe). If non-empty -# doxygen will try to run the HTML help compiler on the generated index.hhp. -# The file has to be specified with full path. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -HHC_LOCATION = - -# The GENERATE_CHI flag controls if a separate .chi index file is generated ( -# YES) or that it should be included in the master .chm file ( NO). -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -GENERATE_CHI = NO - -# The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) -# and project file content. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -CHM_INDEX_ENCODING = - -# The BINARY_TOC flag controls whether a binary table of contents is generated ( -# YES) or a normal table of contents ( NO) in the .chm file. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -BINARY_TOC = NO - -# The TOC_EXPAND flag can be set to YES to add extra items for group members to -# the table of contents of the HTML help documentation and to the tree view. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTMLHELP is set to YES. - -TOC_EXPAND = NO - -# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and -# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that -# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help -# (.qch) of the generated HTML documentation. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_QHP = NO - -# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify -# the file name of the resulting .qch file. The path specified is relative to -# the HTML output folder. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QCH_FILE = - -# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help -# Project output. For more information please see Qt Help Project / Namespace -# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_NAMESPACE = org.doxygen.Project - -# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt -# Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- -# folders). -# The default value is: doc. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_VIRTUAL_FOLDER = doc - -# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom -# filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_NAME = - -# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the -# custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- -# filters). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_CUST_FILTER_ATTRS = - -# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this -# project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHP_SECT_FILTER_ATTRS = - -# The QHG_LOCATION tag can be used to specify the location of Qt's -# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the -# generated .qhp file. -# This tag requires that the tag GENERATE_QHP is set to YES. - -QHG_LOCATION = - -# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be -# generated, together with the HTML files, they form an Eclipse help plugin. To -# install this plugin and make it available under the help contents menu in -# Eclipse, the contents of the directory containing the HTML and XML files needs -# to be copied into the plugins directory of eclipse. The name of the directory -# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. -# After copying Eclipse needs to be restarted before the help appears. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_ECLIPSEHELP = NO - -# A unique identifier for the Eclipse help plugin. When installing the plugin -# the directory name containing the HTML and XML files should also have this -# name. Each documentation set should have its own identifier. -# The default value is: org.doxygen.Project. -# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. - -ECLIPSE_DOC_ID = org.doxygen.Project - -# If you want full control over the layout of the generated HTML pages it might -# be necessary to disable the index and replace it with your own. The -# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top -# of each HTML page. A value of NO enables the index and the value YES disables -# it. Since the tabs in the index contain the same information as the navigation -# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -DISABLE_INDEX = NO - -# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index -# structure should be generated to display hierarchical information. If the tag -# value is set to YES, a side panel will be generated containing a tree-like -# index structure (just like the one that is generated for HTML Help). For this -# to work a browser that supports JavaScript, DHTML, CSS and frames is required -# (i.e. any modern browser). Windows users are probably better off using the -# HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can -# further fine-tune the look of the index. As an example, the default style -# sheet generated by doxygen has an example that shows how to put an image at -# the root of the tree instead of the PROJECT_NAME. Since the tree basically has -# the same information as the tab index, you could consider setting -# DISABLE_INDEX to YES when enabling this option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -GENERATE_TREEVIEW = NO - -# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that -# doxygen will group on one line in the generated HTML documentation. -# -# Note that a value of 0 will completely suppress the enum values from appearing -# in the overview section. -# Minimum value: 0, maximum value: 20, default value: 4. -# This tag requires that the tag GENERATE_HTML is set to YES. - -ENUM_VALUES_PER_LINE = 4 - -# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used -# to set the initial width (in pixels) of the frame in which the tree is shown. -# Minimum value: 0, maximum value: 1500, default value: 250. -# This tag requires that the tag GENERATE_HTML is set to YES. - -TREEVIEW_WIDTH = 250 - -# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to -# external symbols imported via tag files in a separate window. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -EXT_LINKS_IN_WINDOW = NO - -# Use this tag to change the font size of LaTeX formulas included as images in -# the HTML documentation. When you change the font size after a successful -# doxygen run you need to manually remove any form_*.png images from the HTML -# output directory to force them to be regenerated. -# Minimum value: 8, maximum value: 50, default value: 10. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_FONTSIZE = 10 - -# Use the FORMULA_TRANPARENT tag to determine whether or not the images -# generated for formulas are transparent PNGs. Transparent PNGs are not -# supported properly for IE 6.0, but are supported on all modern browsers. -# -# Note that when changing this option you need to delete any form_*.png files in -# the HTML output directory before the changes have effect. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -FORMULA_TRANSPARENT = YES - -# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# http://www.mathjax.org) which uses client side Javascript for the rendering -# instead of using prerendered bitmaps. Use this if you do not have LaTeX -# installed or if you want to formulas look prettier in the HTML output. When -# enabled you may also need to install MathJax separately and configure the path -# to it using the MATHJAX_RELPATH option. -# The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. - -USE_MATHJAX = NO - -# When MathJax is enabled you can set the default output format to be used for -# the MathJax output. See the MathJax site (see: -# http://docs.mathjax.org/en/latest/output.html) for more details. -# Possible values are: HTML-CSS (which is slower, but has the best -# compatibility), NativeMML (i.e. MathML) and SVG. -# The default value is: HTML-CSS. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_FORMAT = HTML-CSS - -# When MathJax is enabled you need to specify the location relative to the HTML -# output directory using the MATHJAX_RELPATH option. The destination directory -# should contain the MathJax.js script. For instance, if the mathjax directory -# is located at the same level as the HTML output directory, then -# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax -# Content Delivery Network so you can quickly see the result without installing -# MathJax. However, it is strongly recommended to install a local copy of -# MathJax from http://www.mathjax.org before deployment. -# The default value is: http://cdn.mathjax.org/mathjax/latest. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest - -# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax -# extension names that should be enabled during MathJax rendering. For example -# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_EXTENSIONS = - -# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces -# of code that will be used on startup of the MathJax code. See the MathJax site -# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an -# example see the documentation. -# This tag requires that the tag USE_MATHJAX is set to YES. - -MATHJAX_CODEFILE = - -# When the SEARCHENGINE tag is enabled doxygen will generate a search box for -# the HTML output. The underlying search engine uses javascript and DHTML and -# should work on any modern browser. Note that when using HTML help -# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) -# there is already a search function so this one should typically be disabled. -# For large projects the javascript based search engine can be slow, then -# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to -# search using the keyboard; to jump to the search box use + S -# (what the is depends on the OS and browser, but it is typically -# , /