diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..15316843 --- /dev/null +++ b/.clang-format @@ -0,0 +1,38 @@ +--- +BasedOnStyle: Mozilla +AccessModifierOffset: '-2' +AlignAfterOpenBracket: Align +AlignOperands: true +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowShortBlocksOnASingleLine: true +AllowShortFunctionsOnASingleLine: All +AlwaysBreakAfterReturnType: AllDefinitions +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: BeforeColon +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: true + SplitEmptyFunction: false +ColumnLimit: 79 +ConstructorInitializerAllOnOneLineOrOnePerLine: true +Cpp11BracedListStyle: true +IndentCaseLabels: false +IndentPPDirectives: AfterHash +NamespaceIndentation: All +SortIncludes: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeParens: ControlStatements +Standard: Cpp11 \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..2a417803 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,18 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.191.0/containers/cpp/.devcontainer/base.Dockerfile + +# [Choice] Debian / Ubuntu version: debian-11, debian-10, debian-9, ubuntu-20.04, ubuntu-18.04 +ARG VARIANT="ubuntu-20.04" +FROM mcr.microsoft.com/vscode/devcontainers/cpp:0-${VARIANT} + +# [Optional] Uncomment this section to install additional packages. +RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ + && apt-get -y install --no-install-recommends \ + locales +# Locales set-up +RUN echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen && \ + locale-gen en_US.UTF-8 && \ + /usr/sbin/update-locale LANG=en_US.UTF-8 + +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..ae586c01 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,35 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.191.0/containers/cpp +{ + "name": "C++", + "build": { + "dockerfile": "Dockerfile", + // Update 'VARIANT' to pick an Debian / Ubuntu OS version: debian-11, debian-10, debian-9, ubuntu-20.04, ubuntu-18.04 + "args": { "VARIANT": "ubuntu-20.04" } + }, + "runArgs": [ "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined"], + + // Set *default* container specific settings.json values on container create. + "settings": {}, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools", + "streetsidesoftware.code-spell-checker", + "xaver.clang-format" + ], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "bash scripts/install-dev-tools.sh", + + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode", + // Mount points in the container + "mounts": [ + "source=${localEnv:HOME}${localEnv:USERPROFILE},target=/host-home-folder,type=bind,consistency=cached" + ] +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..4cafaaaa --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +/build/* +.vscode +venv +__pycache__ \ No newline at end of file diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..c3ce3283 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,12 @@ +# Since git version 2.23, git-blame has a feature to ignore +# certain commits. +# +# This file contains a list of commits that are not likely what +# you are looking for in `git blame`. You can set this file as +# a default ignore file for blame by running the following +# command. +# +# $ git config blame.ignoreRevsFile .git-blame-ignore-revs + +# PR 98 : Apply formatting to ifm3d repo +35ce46f67db59e1505b1a7331b992c24ed87f93c \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md new file mode 100644 index 00000000..21469f78 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -0,0 +1,47 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior. For example: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Output/Screenshots** +If applicable, add console output and/or screenshots to help explain your problem. + +**Minimal Working Example** +If applicable/possible, please provide a [minimal working example](https://stackoverflow.com/help/minimal-reproducible-example) which demonstrates the problem and can be run standalone. + +``` +#include +int main() +{ + std::cout << "Hello World!" << std::endl; + return 0; +} +``` + +**Configuration/Environment (please complete the following information):** + - OS: [e.g. Ubuntu 18.04, Windows 10] + - ifm3d version: [e.g. 0.14.0] + - using ROS: [yes/no] + +**Camera Configuration** +Please include the output of `ifm3d dump` + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..3f927090 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Question + url: https://github.com/ifm/ifm3d/discussions + about: Please ask and answer questions here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..a8d72560 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: proposal +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/workflows/stale-issues.yml b/.github/workflows/stale-issues.yml new file mode 100644 index 00000000..a9f98e47 --- /dev/null +++ b/.github/workflows/stale-issues.yml @@ -0,0 +1,23 @@ +name: Close inactive issues +on: + schedule: + - cron: "30 1 * * *" + +jobs: + close-issues: + runs-on: ubuntu-latest + permissions: + issues: write + pull-requests: write + steps: + - uses: actions/stale@v5 + with: + days-before-issue-stale: 30 + days-before-issue-close: 14 + stale-issue-label: "stale" + stale-issue-message: "This issue is stale because it has been open for 30 days with no activity." + close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale." + days-before-pr-stale: -1 + days-before-pr-close: -1 + exempt-issue-labels: no-stale,in-progress + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..0c751864 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +/build/* +/docker/build/**/debs/* +.vscode +/examples/**/build +venv +__pycache__ +examples/o3r/oem/ssh/.* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..d72165f8 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,31 @@ +stages: + - version + - check + - build + - test + - sign + - tag + - release + - deploy + +variables: + SKIP_TEST: "false" + +include: + - local: .gitlab/rules.yml + - local: .gitlab/docker_config.yml + - local: .gitlab/update_version.yml + - local: .gitlab/check.yml + - local: .gitlab/build_windows.yml + - local: .gitlab/build_linux.yml + - local: .gitlab/test_windows.yml + - local: .gitlab/test_linux.yml + - local: .gitlab/sign_windows.yml + - local: .gitlab/tag.yml + - local: .gitlab/release.yml + - local: .gitlab/deploy.yml + - local: .gitlab/deploy_docs.yml + - local: .gitlab/deploy_python.yml + - local: .gitlab/deploy_docker.yml + - local: .gitlab/oss_compilance.yml + - local: .gitlab/deploy_github.yml diff --git a/.gitlab/build_linux.yml b/.gitlab/build_linux.yml new file mode 100644 index 00000000..6be15e60 --- /dev/null +++ b/.gitlab/build_linux.yml @@ -0,0 +1,144 @@ +build:linux: + stage: build + tags: + - shared_docker + variables: + BASE_IMAGE: ubuntu:18.04 + image: + name: gcr.io/kaniko-project/executor:v1.9.0-debug + entrypoint: [""] + script: + - !reference [.docker-config, script] + - echo "${KANIKO_PUSH_ARGS}" + - mkdir -p /kaniko/.docker + - echo "${DOCKER_CFG}" > /kaniko/.docker/config.json + - /kaniko/executor --context ${CI_PROJECT_DIR} + --dockerfile docker/ifm-robotics/docker/Dockerfile + ${KANIKO_BUILD_ARGS} + ${KANIKO_PUSH_ARGS} + ${DOCKER_BUILD_ARGS} + +# Docker +build:linux:docker: + stage: build + parallel: + matrix: + - RUNNER: shared_docker + BASE_IMAGE: ubuntu:20.04 + TAG_POSTFIX: -ubuntu-amd64 + - RUNNER: shared_docker_aarch64 + BASE_IMAGE: ubuntu:20.04 + TAG_POSTFIX: -ubuntu-arm64 + - RUNNER: shared_docker_aarch64 + BASE_IMAGE: nvcr.io/nvidia/l4t-base:r32.4.3 + TAG_POSTFIX: -l4t-arm64 + tags: + - ${RUNNER} + image: + name: gcr.io/kaniko-project/executor:v1.9.0-debug + entrypoint: [""] + script: + - !reference [.docker-config, script] + - mkdir -p /kaniko/.docker + - echo "${DOCKER_CFG}" > /kaniko/.docker/config.json + - /kaniko/executor --context ${CI_PROJECT_DIR} + --dockerfile docker/ifm-robotics/docker/Dockerfile + ${KANIKO_BUILD_ARGS} + ${KANIKO_PUSH_ARGS} + ${DOCKER_BUILD_ARGS} + rules: + - !reference [.tagged, rules] + - !reference [.nightly, rules] + +# Debs +build:linux:deb: + stage: build + parallel: + matrix: + - RUNNER: shared_docker_aarch64 + PLATFORM: linux/aarch64 + BASE_IMAGE: + - nvcr.io/nvidia/l4t-base:r32.4.3 + - ubuntu:22.04 + - ubuntu:20.04 + - ubuntu:18.04 + - RUNNER: shared_docker + PLATFORM: linux/amd64 + BASE_IMAGE: + - ubuntu:22.04 + - ubuntu:20.04 + - ubuntu:18.04 + tags: + - ${RUNNER} + image: + name: gcr.io/kaniko-project/executor:v1.9.0-debug + entrypoint: [""] + script: + - !reference [.docker-config, script] + - mkdir -p /kaniko/.docker + - echo "${DOCKER_CFG}" > /kaniko/.docker/config.json + - /kaniko/executor --context ${CI_PROJECT_DIR} + --dockerfile docker/ifm-robotics/deb/Dockerfile + --no-push + ${KANIKO_BUILD_ARGS} + ${DOCKER_BUILD_ARGS} + - cp /out/*.tar ${CI_PROJECT_DIR}/ + artifacts: + paths: + - ifm3d-*.tar + expire_in: 1 week + rules: + - !reference [.tagged, rules] + - !reference [.nightly, rules] + - !reference [.manual, rules] + +# Python wheels +build:linux:python_wheel: + image: ${IMAGE} + stage: build + parallel: + matrix: + - RUNNER: shared_docker + IMAGE: quay.io/pypa/manylinux2014_x86_64:latest + PYTHON_VERSION: ["cp38-cp38", "cp39-cp39", "cp310-cp310", "cp311-cp311"] + - RUNNER: shared_docker_aarch64 + IMAGE: quay.io/pypa/manylinux2014_aarch64:latest + PYTHON_VERSION: ["cp38-cp38", "cp39-cp39", "cp310-cp310", "cp311-cp311"] + tags: + - ${RUNNER} + script: + - | + "/opt/python/${PYTHON_VERSION}/bin/pip" wheel ${CI_PROJECT_DIR} --no-deps -w /tmp/wheelhouse/ + for whl in /tmp/wheelhouse/*.whl; do + auditwheel repair "$whl" -w /${CI_PROJECT_DIR}/wheelhouse/ + done + artifacts: + paths: + - wheelhouse/*.whl + expire_in: 1 week + rules: + - !reference [.tagged, rules] + - !reference [.nightly, rules] + - !reference [.manual, rules] + +# Docs +build:linux:docs: + stage: build + tags: + - shared_docker + image: + name: gcr.io/kaniko-project/executor:v1.9.0-debug + entrypoint: [""] + script: + - !reference [.docker-config, script] + - mkdir -p /kaniko/.docker + - echo "${DOCKER_CFG}" > /kaniko/.docker/config.json + - /kaniko/executor --context ${CI_PROJECT_DIR} + --dockerfile docker/ifm-robotics/doc/Dockerfile + --no-push + ${KANIKO_BUILD_ARGS} + ${DOCKER_BUILD_ARGS} + - mv /out ${CI_PROJECT_DIR}/docs + artifacts: + paths: + - docs diff --git a/.gitlab/build_windows.yml b/.gitlab/build_windows.yml new file mode 100644 index 00000000..83286f18 --- /dev/null +++ b/.gitlab/build_windows.yml @@ -0,0 +1,48 @@ +build:windows_2019: + stage: build + tags: + - shared_docker_windows + image: + name: ${DOCKER_IMAGE_WINDOWS_BUILDER} + entrypoint: ["cmd", "/S", "/C"] + script: + - 'cmake.exe -B build -G "Visual Studio 16 2019" -Ax64 -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=ON -DBUILD_MODULE_PCICCLIENT=ON -DCREATE_WIN_INSTALLER=ON .' + - cd build + - "cmake.exe --build . -j 8 --config Debug --target ALL_BUILD" + - "cmake.exe --build . -j 8 --config Release --target ALL_BUILD" + - 'cpack --config CPackConfig.cmake -C "Debug;Release"' + - "compress-archive -path './_CPack_Packages/win64/NSIS/ifm3d_windows_*/*' -destinationpath '../ifm3d_installer/ifm3d_windows_binaries.zip' -update -compressionlevel optimal" + artifacts: + name: "ifm3d_windows_installer" + paths: + - ifm3d_installer/*.exe + - ifm3d_installer/*.zip + +build:windows_2019:python_wheel: + stage: build + tags: + - shared_docker_windows + image: + name: ${DOCKER_IMAGE_WINDOWS_BUILDER} + entrypoint: ["cmd", "/S", "/C"] + parallel: + matrix: + - PYTHON_VERSION: + - "3.8.10" + - "3.9.13" + - "3.10.11" + - "3.11.3" + variables: + IFM3D_CMAKE_GENERATOR: "Visual Studio 16 2019" + CONFIG: Release + script: + - | + & C:/python/python.${env:PYTHON_VERSION}/tools/python.exe -m pip wheel ${CI_PROJECT_DIR} --no-deps -w ${CI_PROJECT_DIR}\wheelhouse/ + artifacts: + paths: + - wheelhouse/*.whl + expire_in: 1 week + rules: + - !reference [.tagged, rules] + - !reference [.nightly, rules] + - !reference [.manual, rules] diff --git a/.gitlab/check.yml b/.gitlab/check.yml new file mode 100644 index 00000000..dd90b333 --- /dev/null +++ b/.gitlab/check.yml @@ -0,0 +1,16 @@ +check:clang-format: + stage: check + tags: + - shared_docker + image: + name: gcr.io/kaniko-project/executor:v1.9.0-debug + entrypoint: [""] + script: + - !reference [.docker-config, script] + - echo "${KANIKO_PUSH_ARGS}" + - mkdir -p /kaniko/.docker + - echo "${DOCKER_CFG}" > /kaniko/.docker/config.json + - /kaniko/executor --context ${CI_PROJECT_DIR} + --dockerfile docker/ifm-robotics/check/Dockerfile + ${KANIKO_BUILD_ARGS} + --no-push diff --git a/.gitlab/deploy.yml b/.gitlab/deploy.yml new file mode 100644 index 00000000..714e5394 --- /dev/null +++ b/.gitlab/deploy.yml @@ -0,0 +1,8 @@ +deploy:start: + stage: deploy + image: alpine + tags: [shared_docker] + script: ["true"] + rules: + - !reference [.nightly-manual, rules] + - !reference [.tagged-manual, rules] diff --git a/.gitlab/deploy_docker.yml b/.gitlab/deploy_docker.yml new file mode 100644 index 00000000..99b42974 --- /dev/null +++ b/.gitlab/deploy_docker.yml @@ -0,0 +1,78 @@ +.docker_manifest: + stage: deploy + tags: + - shared_docker + image: toolhippie/manifest-tool:20210830 + before_script: + - !reference [.docker-config, script] + - mkdir -p $HOME/.docker + - echo "${DOCKER_CFG}" > $HOME/.docker/config.json + +.docker_deploy: + latest: + - | + for REGISTRY in $REGISTRIES; do + + TARGET=$(eval echo "\${${REGISTRY}_REPO}") + + manifest-tool push from-args \ + --target "${TARGET}:${DOCKER_TAG}-ubuntu" \ + --template "${TARGET}:${DOCKER_TAG}-ubuntu-ARCH" \ + --platforms "linux/arm64,linux/amd64" + + manifest-tool push from-args \ + --target "${TARGET}:${DOCKER_TAG}" \ + --template "${TARGET}:${DOCKER_TAG}-ubuntu-ARCH" \ + --platforms "linux/arm64,linux/amd64" + + manifest-tool push from-args \ + --target "${TARGET}:${DOCKER_TAG}-l4t" \ + --template "${TARGET}:${DOCKER_TAG}-l4t-ARCH" \ + --platforms "linux/arm64" + + done + stable: + - | + for REGISTRY in $REGISTRIES; do + + TARGET=$(eval echo "\${${REGISTRY}_REPO}") + + manifest-tool push from-args \ + --target "${TARGET}:stable-ubuntu" \ + --template "${TARGET}:${DOCKER_TAG}-ubuntu-ARCH" \ + --platforms "linux/arm64,linux/amd64" + + manifest-tool push from-args \ + --target "${TARGET}:stable" \ + --template "${TARGET}:${DOCKER_TAG}-ubuntu-ARCH" \ + --platforms "linux/arm64,linux/amd64" + + manifest-tool push from-args \ + --target "${TARGET}:stable-l4t" \ + --template "${TARGET}:${DOCKER_TAG}-l4t-ARCH" \ + --platforms "linux/arm64" + + done + +deploy:linux:docker_tagged: + extends: .docker_manifest + needs: + - job: deploy:start + artifacts: false + - job: build:linux:docker + artifacts: false + script: + - !reference [.docker_deploy, latest] + - !reference [.docker_deploy, stable] + rules: + - !reference [.tagged, rules] + +deploy:linux:docker_nightly: + extends: .docker_manifest + needs: + - job: build:linux:docker + artifacts: false + script: + - !reference [.docker_deploy, latest] + rules: + - !reference [.nightly, rules] diff --git a/.gitlab/deploy_docs.yml b/.gitlab/deploy_docs.yml new file mode 100644 index 00000000..5fa6a366 --- /dev/null +++ b/.gitlab/deploy_docs.yml @@ -0,0 +1,39 @@ +.docs: + stage: deploy + tags: + - shared_docker + image: bitnami/git + script: + - | + git config --global user.email "support.robotics@ifm.com" + git config --global user.name "ifm-csr" + git clone https://${DOCS_GIT_USERNAME}:${DOCS_GIT_PASSWORD}@${DOCS_GIT_REPO} repo + cd repo + rm -rf latest + cp -r ${CI_PROJECT_DIR}/docs/html latest + if [ "x${CI_COMMIT_TAG}" != "x" ]; then + cp -r ${CI_PROJECT_DIR}/docs/html "${CI_COMMIT_TAG}" + ln -sfn "${CI_COMMIT_TAG}/" stable + fi + git add -A + git commit -m "update docs" --allow-empty + git push + +deploy:linux:docs_tagged: + extends: .docs + needs: + - job: deploy:start + optional: true + artifacts: false + - job: build:linux:docs + artifacts: true + rules: + - !reference [.tagged, rules] + +deploy:linux:docs_nightly: + extends: .docs + needs: + - job: build:linux:docs + artifacts: true + rules: + - !reference [.nightly, rules] diff --git a/.gitlab/deploy_github.yml b/.gitlab/deploy_github.yml new file mode 100644 index 00000000..a9acdc23 --- /dev/null +++ b/.gitlab/deploy_github.yml @@ -0,0 +1,20 @@ +deploy:github: + variables: + GH_TOKEN: ${IFM_CSR_GH_RELEASE_TOKEN} + stage: deploy + image: ghcr.io/cicirello/pyaction + needs: + - job: release:github + artifacts: false + - job: deploy:start + artifacts: false + rules: + - !reference [.tagged, rules] + before_script: + - | + apt-get -y update + git config --global user.email "support.robotics@ifm.com" + git config --global user.name "ifm-csr" + script: + - export tag_value=$(cat VERSION | awk '{print $1}' FS="*") + - gh release edit $tag_value -R https://github.com/ifm/ifm3d.git --prerelease=false --latest \ No newline at end of file diff --git a/.gitlab/deploy_python.yml b/.gitlab/deploy_python.yml new file mode 100644 index 00000000..50b0035b --- /dev/null +++ b/.gitlab/deploy_python.yml @@ -0,0 +1,40 @@ +.python_wheels: + image: python:3 + stage: deploy + tags: + - shared_docker + script: + - | + pip install --no-cache-dir twine + twine upload --skip-existing --non-interactive wheelhouse/* + +deploy:python_wheels_tagged: + extends: .python_wheels + variables: + TWINE_USERNAME: ${PYTHON_REPO_USERNAME} + TWINE_PASSWORD: ${PYTHON_REPO_PASSWORD} + TWINE_REPOSITORY_URL: ${PYTHON_REPO_URL} + needs: + - job: deploy:start + artifacts: false + - job: build:windows_2019:python_wheel + artifacts: true + - job: build:linux:python_wheel + artifacts: true + rules: + - !reference [.tagged, rules] + +deploy:python_wheels_nightly: + extends: .python_wheels + variables: + TWINE_USERNAME: ${TEST_PYTHON_REPO_USERNAME} + TWINE_PASSWORD: ${TEST_PYTHON_REPO_PASSWORD} + TWINE_REPOSITORY_URL: ${TEST_PYTHON_REPO_URL} + needs: + - job: build:windows_2019:python_wheel + artifacts: true + - job: build:linux:python_wheel + artifacts: true + rules: + - !reference [.nightly, rules] + - !reference [.tagged, rules] diff --git a/.gitlab/docker_config.yml b/.gitlab/docker_config.yml new file mode 100644 index 00000000..1d488a95 --- /dev/null +++ b/.gitlab/docker_config.yml @@ -0,0 +1,27 @@ +.docker-config: + script: | + DOCKER_BUILD_ARGS="--build-arg BASE_IMAGE=${BASE_IMAGE} --build-arg PYTHON_VERSION=3.9" + + DOCKER_CFG="{\"auths\":{" + + DOCKER_CFG="${DOCKER_CFG}\"${DOCKERHUB_REGISTRY}\":{\"auth\":\"$(echo -n "${DOCKERHUB_USERNAME}:${DOCKERHUB_PASSWORD}" | base64)\"}," + DOCKER_CFG="${DOCKER_CFG}\"${GHCR_REGISTRY}\":{\"auth\":\"$(echo -n "${GHCR_USERNAME}:${GHCR_PASSWORD}" | base64)\"}" + + DOCKER_CFG="${DOCKER_CFG}}" + + DOCKER_TAG=dev + if [ "x${CI_COMMIT_TAG}" != "x" ]; then + DOCKER_TAG=${CI_COMMIT_TAG} + elif [ "${CI_COMMIT_BRANCH}" == "${CI_DEFAULT_BRANCH}" ]; then + DOCKER_TAG=latest + fi + DOCKER_CFG="${DOCKER_CFG} }" + + KANIKO_BUILD_ARGS="--reproducible --snapshotMode time --cache --cache-repo ${GHCR_REPO}/cache" + + KANIKO_PUSH_ARGS="\ + --destination ${DOCKERHUB_REPO}:${DOCKER_TAG}${TAG_POSTFIX}\ + --destination ${GHCR_REPO}:${DOCKER_TAG}${TAG_POSTFIX}\ + " + + REGISTRIES="DOCKERHUB GHCR" diff --git a/.gitlab/oss_compilance.yml b/.gitlab/oss_compilance.yml new file mode 100644 index 00000000..141605b5 --- /dev/null +++ b/.gitlab/oss_compilance.yml @@ -0,0 +1,28 @@ +# oss_compilance +deploy:oss_compilance: + stage: deploy + needs: + - job: deploy:start + artifacts: false + - job: deploy:linux:docker_tagged + artifacts: false + image: + name: nexus.ifm.com:20443/ifm-robotics/docker-oss-compliance + entrypoint: [""] + tags: + - shared_docker + cache: + - key: ifm3d-oss-compliance-release + paths: + - $CI_PROJECT_DIR/cache + script: + - !reference [.docker-config, script] + - mkdir -p $CI_PROJECT_DIR/oss-compliance + - cd /app + - python generate_html.py --cache $CI_PROJECT_DIR/cache --out $CI_PROJECT_DIR/oss-compliance ghcr.io/ifm/ifm3d:$DOCKER_TAG ifm3d + artifacts: + paths: + - $CI_PROJECT_DIR/oss-compliance + expire_in: 1 week + rules: + - !reference [.tagged, rules] diff --git a/.gitlab/release.yml b/.gitlab/release.yml new file mode 100644 index 00000000..28b4f8e1 --- /dev/null +++ b/.gitlab/release.yml @@ -0,0 +1,23 @@ +release:github: + variables: + GH_TOKEN: ${IFM_CSR_GH_RELEASE_TOKEN} + stage: release + image: ghcr.io/cicirello/pyaction + needs: + - job: build:linux:deb + artifacts: true + - job: sign:windows_2019 + artifacts: true + rules: + - !reference [.tagged, rules] + script: + - python3 scripts/release/get_changelog_between_versions.py + - export tag_value=$(cat VERSION | awk '{print $1}' FS="*") + - | + while ! curl -sSfI https://github.com/ifm/ifm3d/releases/tag/$tag_value > /dev/null; do + sleep 1 + done + - gh --version + - gh release create $tag_value -R https://github.com/ifm/ifm3d.git --prerelease -F CHANGELOG --title $tag_value --verify-tag + - gh release upload $tag_value -R https://github.com/ifm/ifm3d.git ifm3d-*.tar + - gh release upload $tag_value -R https://github.com/ifm/ifm3d.git ifm3d_installer/ifm3d_windows_*.exe \ No newline at end of file diff --git a/.gitlab/rules.yml b/.gitlab/rules.yml new file mode 100644 index 00000000..d21338fe --- /dev/null +++ b/.gitlab/rules.yml @@ -0,0 +1,32 @@ +.nightly: + rules: + - if: $CI_PIPELINE_SOURCE == "schedule" + when: on_success + +.nightly-manual: + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + when: never + - if: $CI_PIPELINE_SOURCE == "schedule" + when: manual + allow_failure: true + +.tagged: + rules: + - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/' + when: on_success + +.tagged-manual: + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + when: never + - if: '$CI_COMMIT_TAG =~ /^v\d+\.\d+\.\d+$/' + when: manual + allow_failure: true + +.manual: + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + when: never + - when: manual + allow_failure: true diff --git a/.gitlab/sign_windows.yml b/.gitlab/sign_windows.yml new file mode 100644 index 00000000..c50f57ad --- /dev/null +++ b/.gitlab/sign_windows.yml @@ -0,0 +1,12 @@ +sign:windows_2019: + extends: .sign:ev + stage: sign + needs: + - job: build:windows_2019 + artifacts: true + variables: + SIGN_FILE: $CI_PROJECT_DIR/ifm3d_installer/ifm3d_windows_*.exe + +include: + - project: "ifm-public/standard/gitlab-ci-yml-templates" + file: "templates/service-codeSigning/public_signing.yml" diff --git a/.gitlab/tag.yml b/.gitlab/tag.yml new file mode 100644 index 00000000..10e26f1a --- /dev/null +++ b/.gitlab/tag.yml @@ -0,0 +1,22 @@ +release:tag: + stage: tag + image: python:3 + tags: [shared_docker] + + before_script: + - | + apt-get -y update + apt-get -y install git + git config --global user.email "support.robotics@ifm.com" + git config --global user.name "ifm-csr" + script: + - python3 scripts/release/get_changelog_between_versions.py + - git remote set-url origin https://"ifm-csr":$RELEASE_ACCESS_TOKEN@$CI_SERVER_HOST/$CI_PROJECT_PATH.git + - export tag_value=$(cat VERSION | awk '{print $1}' FS="*") + - git tag -a $tag_value -F CHANGELOG + - git push origin $tag_value + + rules: + - if: ($CI_PIPELINE_SOURCE == "push") && ($CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH) && ($CI_COMMIT_MESSAGE =~ '/Release\/v\d+\.\d+\.\d+/') + when: always + - when: never \ No newline at end of file diff --git a/.gitlab/test_linux.yml b/.gitlab/test_linux.yml new file mode 100644 index 00000000..6377160d --- /dev/null +++ b/.gitlab/test_linux.yml @@ -0,0 +1,19 @@ +test:linux: + stage: test + tags: + - shared_docker + variables: + BASE_IMAGE: ubuntu:18.04 + image: + name: gcr.io/kaniko-project/executor:v1.9.0-debug + entrypoint: [""] + script: + - !reference [.docker-config, script] + - echo "${KANIKO_PUSH_ARGS}" + - mkdir -p /kaniko/.docker + - echo "${DOCKER_CFG}" > /kaniko/.docker/config.json + - /kaniko/executor --context ${CI_PROJECT_DIR} + --dockerfile docker/ifm-robotics/test/Dockerfile + ${KANIKO_BUILD_ARGS} + --no-push + diff --git a/.gitlab/test_windows.yml b/.gitlab/test_windows.yml new file mode 100644 index 00000000..48a8ee21 --- /dev/null +++ b/.gitlab/test_windows.yml @@ -0,0 +1,15 @@ +# Run unit test +test:windows_2019: + stage: test + tags: + - shared_docker_windows + image: + name: ${DOCKER_IMAGE_WINDOWS_BUILDER} + entrypoint: ["cmd", "/S", "/C"] + variables: + GIT_CLEAN_FLAGS: none + script: + - cmake.exe -B build -G "Visual Studio 16 2019" -Ax64 -DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS=ON -DBUILD_MODULE_PCICCLIENT=ON -DBUILD_TESTS=ON -DGTEST_CMAKE_DIR=C:/googletest -Dgtest_force_shared_crt=TRUE -DCMAKE_INSTALL_PREFIX=$PWD/build/install .' + - cd build + - cmake.exe --build . -j 8 --config Release --clean-first --target check + diff --git a/.gitlab/update_version.yml b/.gitlab/update_version.yml new file mode 100644 index 00000000..06b9a05c --- /dev/null +++ b/.gitlab/update_version.yml @@ -0,0 +1,28 @@ +variables: + VERSION: + description: "Release version in format vx.x.x" + +version:update_version: + stage: version + image: python:3 + tags: [shared_docker] + + before_script: + - | + apt-get -y update + apt-get -y install git + git config --global user.email "support.robotics@ifm.com" + git config --global user.name "ifm-csr" + + script: + - | + python3 scripts/release/update_version.py --version=${VERSION} + git checkout -B Release/${VERSION} + git commit -a -m "update document for release-${VERSION}" + git remote set-url origin https://"ifm-csr":$RELEASE_ACCESS_TOKEN@$CI_SERVER_HOST/$CI_PROJECT_PATH.git + git push -f -o merge_request.create -o merge_request.target=$CI_DEFAULT_BRANCH -o merge_request.assign="17" -o merge_request.assign="15" -o merge_request.assign="70" -o merge_request.approvals_before_merge=1 origin Release/${VERSION}:Release/${VERSION} + rules: + - if: $VERSION != "" + when: always + #- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH + diff --git a/CMakeLists.txt b/CMakeLists.txt index 11ec27f0..bbcaaf41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,123 +1,255 @@ -project(IFM3D CXX) -cmake_minimum_required(VERSION 2.8.3) -set(GIT_PROJECT_NAME "ifm3d") +cmake_minimum_required(VERSION 3.11) +cmake_policy(SET CMP0048 NEW) + +# Some generic settings +include(cmake/StandardProjectSettings.cmake) # Make our cmake functions accessible -set(CMAKE_MODULE_PATH - ${IFM3D_SOURCE_DIR}/cmake/modules - ${CMAKE_MODULE_PATH}) +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/modules) + +set(ROOT_PROJECT_NAME IFM3D) +set(PROJECT_NAME ${ROOT_PROJECT_NAME}) +find_program(GIT_EXECUTABLE git) +include(GetVersionFromGitTag) + +# Create aliases in order to avoid ${${..}} syntax in other files +set(ROOT_PROJECT_VERSION ${${ROOT_PROJECT_NAME}_VERSION}) +set(ROOT_PROJECT_VERSION_MAJOR ${${ROOT_PROJECT_NAME}_VERSION_MAJOR}) +set(ROOT_PROJECT_VERSION_MINOR ${${ROOT_PROJECT_NAME}_VERSION_MINOR}) +set(ROOT_PROJECT_VERSION_PATCH ${${ROOT_PROJECT_NAME}_VERSION_PATCH}) +set(ROOT_PROJECT_VERSION_TWEAK "${${ROOT_PROJECT_NAME}_VERSION_TWEAK}") +set(ROOT_PROJECT_VERSION_AHEAD ${${ROOT_PROJECT_NAME}_VERSION_AHEAD}) +set(ROOT_PROJECT_VERSION_GIT_SHA ${${ROOT_PROJECT_NAME}_VERSION_GIT_SHA}) +set(ROOT_PROJECT_VERSION_FULL_IN_CASE_OF_PATCHES ${${ROOT_PROJECT_NAME}_VERSION_FULL_IN_CASE_OF_PATCHES}) +set(ROOT_PROJECT_VERSION_META ${${ROOT_PROJECT_NAME}_VERSION_META}) +set(ROOT_PROJECT_VERSION_SIMPLE "${ROOT_PROJECT_VERSION_MAJOR}.${ROOT_PROJECT_VERSION_MINOR}.${ROOT_PROJECT_VERSION_PATCH}") +project(${ROOT_PROJECT_NAME} LANGUAGES C CXX VERSION ${ROOT_PROJECT_VERSION_SIMPLE}) + +set(GIT_PROJECT_NAME "ifm3d") + +# Some generic settings +include(cmake/StandardProjectSettings.cmake) # Force an out-of-source build of the code include(MacroOutOfSourceBuild) macro_ensure_out_of_source_build( "Please build ${PROJECT_NAME} out-of-source") -# Library version -include(ifm3d_version) - # Conditionally turn on/off parts of the build (global-level) -option(BUILD_TESTS "Build unit tests" ON) +option(BUILD_TESTS "Build unit tests" OFF) option(BUILD_MODULE_FRAMEGRABBER "Build the framegrabber module" ON) -option(BUILD_MODULE_IMAGE "Build the image module" ON) +option(BUILD_MODULE_DESERIALIZE "Build the deserialize module" ON) +option(BUILD_MODULE_SWUPDATER "Build the swupdater module" ON) +option(BUILD_MODULE_PCICCLIENT "Build the pcicclient module" OFF) option(BUILD_MODULE_TOOLS "Build the tools module" ON) +option(BUILD_MODULE_PYBIND11 "Build the python bindings module" OFF) option(BUILD_SDK_PKG "Build install packages for development purposes" ON) +option(BUILD_SHARED_LIBS "Build modules as shared libraries" ON) +option(BUILD_EXAMPLES "Build the examples" OFF) +option(BUILD_DOC "Build documentation" OFF) +option(BUILD_IN_DEPS "download and build dependencies" ON) +option(CREATE_WIN_INSTALLER "Create windows installer" OFF) +option(CREATE_PYTHON_STUBS "Create the Python stubs" OFF) # Installation root -set(CPACK_SET_DESTDIR true) +if(NOT WIN32) + set(CPACK_SET_DESTDIR true) +endif() set(CMAKE_INSTALL_PREFIX "/usr" CACHE STRING "CMake install prefix") set(CPACK_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX}) +#add postfix d to all target binaries for Debug build +set(CMAKE_DEBUG_POSTFIX d) + +# Installation paths +set(_lib lib CACHE STRING "Basename of the library-directory") +set(_bin bin CACHE STRING "Basename of the bin-directory") +set(_include include CACHE STRING "Basename of the include-directory") + # Where to find GTest set(GTEST_CMAKE_DIR "/usr/src/gtest" CACHE STRING "GTest cmake project dir") # Global compiler flags -set(CMAKE_BUILD_TYPE Release) # Release or Debug -set(CMAKE_CXX_EXTENSIONS OFF) # OFF -> -std=c++11, ON -> -std=gnu++11 -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_EXTENSIONS OFF) # OFF -> -std=c++17, ON -> -std=gnu++17 +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED true) -set(CMAKE_CXX_FLAGS - "${CMAKE_CXX_FLAGS} -Wno-literal-suffix -s -std=c++11") +if(WIN32) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) + add_definitions(-DBOOST_ALL_NO_LIB) +endif(WIN32) + +# create the cmake-package files +if(BUILD_SDK_PKG) + include(CMakePackageConfigHelpers) + + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/ifm3d-config-version.cmake + VERSION ${IFM3D_VERSION} + COMPATIBILITY AnyNewerVersion + ) + + set(CONFIG_PACKAGE_LOCATION ${_lib}/cmake/ifm3d-${IFM3D_VERSION}) +endif() + +################################################ +## Start deferring work to the submodules +################################################ # Bootstrap gtest if(BUILD_TESTS) - add_subdirectory(${GTEST_CMAKE_DIR} gtest_bin) + add_subdirectory(${GTEST_CMAKE_DIR} gtest_bin EXCLUDE_FROM_ALL) + add_definitions(-DGTEST_LINKED_AS_SHARED_LIBRARY=1) add_custom_target(check) + #var + if(WIN32) + set(TEST_WRAPPER_EXT "bat" ) + else() + set(TEST_WRAPPER_EXT "sh" ) + endif() + set(TEST_WRAPPER_IN "test_wrapper.${TEST_WRAPPER_EXT}.in" ) + set(TEST_WRAPPER "test_wrapper.${TEST_WRAPPER_EXT}" ) endif() +# Include third-party depencency libs +if(CREATE_WIN_INSTALLER) + add_subdirectory(third-party EXCLUDE_FROM_ALL) + + # Copy DLLs and libs to CMAKE_INSTALL_PREFIX + install(SCRIPT ${CMAKE_SOURCE_DIR}/cmake/windows_installer/cp_dll_script.cmake) + + #Include cpack.cmake script + include(${CMAKE_SOURCE_DIR}/cmake/windows_installer/cpack.cmake) +else() + add_subdirectory(third-party) +endif(CREATE_WIN_INSTALLER) + # Build sub-modules -add_subdirectory(modules/camera) +add_subdirectory(modules/common) +list(APPEND DOXYGEN_MODULES "common") + +add_subdirectory(modules/device) +list(APPEND DOXYGEN_MODULES "device") + if(BUILD_MODULE_FRAMEGRABBER) add_definitions(-DBUILD_MODULE_FRAMEGRABBER=ON) add_subdirectory(modules/framegrabber) + list(APPEND DOXYGEN_MODULES "framegrabber") endif() -if(BUILD_MODULE_IMAGE) - # `image` relies on `framegrabber`, in the event the user - # explicitly disabled the `framegrabber` but not `image` - # we are going to let the compiler convey the message (for now) instead of - # doing some implicit CMake stuff that will be hard to maintain and - # confuse the end-user. - add_subdirectory(modules/image) + +if(BUILD_MODULE_SWUPDATER) + add_definitions(-DBUILD_MODULE_SWUPDATER=ON) + add_subdirectory(modules/swupdater) + list(APPEND DOXYGEN_MODULES "swupdater") +endif() + +if(BUILD_MODULE_PCICCLIENT) + add_subdirectory(modules/pcicclient) + list(APPEND DOXYGEN_MODULES "pcicclient") endif() + +if(BUILD_MODULE_PYBIND11) + add_definitions(-DBUILD_MODULE_PYBIND11) + add_subdirectory(modules/pybind11) +endif() + +if(BUILD_MODULE_DESERIALIZE) + add_definitions(-DBUILD_MODULE_DESERIALIZE=ON) + add_subdirectory(modules/deserialize) + list(APPEND DOXYGEN_MODULES "deserialize") +endif() + +if(BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + if(BUILD_MODULE_TOOLS) add_subdirectory(modules/tools) endif() +################################################ +## Sub-modules are done, finish up the cmake package +################################################ +if(BUILD_SDK_PKG) + configure_package_config_file( + ${CMAKE_CURRENT_LIST_DIR}/cmake/modules/ifm3d-config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/ifm3d-config.cmake + INSTALL_DESTINATION ${CONFIG_PACKAGE_LOCATION} + ) + + install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/ifm3d-config-version.cmake + ${CMAKE_CURRENT_BINARY_DIR}/ifm3d-config.cmake + DESTINATION ${CONFIG_PACKAGE_LOCATION} + COMPONENT common + ) +endif() + +################################################# +# Documentation +################################################# +if(BUILD_DOC) + add_subdirectory(doc) +endif() ################################################# # Packaging stuff - for now, we build debs only ################################################# -file(COPY ${IFM3D_SOURCE_DIR}/cmake/utils/ifm3d-dpkg-deps.py.in - DESTINATION ${IFM3D_BINARY_DIR} - FILE_PERMISSIONS OWNER_READ - OWNER_WRITE - OWNER_EXECUTE - GROUP_READ - GROUP_EXECUTE - WORLD_READ - WORLD_EXECUTE - ) -configure_file( - ${IFM3D_BINARY_DIR}/ifm3d-dpkg-deps.py.in - ${IFM3D_BINARY_DIR}/ifm3d-dpkg-deps.py - @ONLY - ) - -# Turn on component-based installation -set(CPACK_DEB_COMPONENT_INSTALL ON) -set(CPACK_GENERATOR "DEB") - -# Package architecture -find_program(DPKG_CMD dpkg) -if(NOT CMAKE_CROSSCOMPILING) - if(NOT DPKG_CMD) - message(STATUS - "Cannot find dpkg in your path, default to ${CMAKE_SYSTEM_PROCESSOR}.") - set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}") +if(NOT WIN32) + file(COPY ${IFM3D_SOURCE_DIR}/cmake/utils/ifm3d-dpkg-deps.py.in + DESTINATION ${IFM3D_BINARY_DIR} + FILE_PERMISSIONS OWNER_READ + OWNER_WRITE + OWNER_EXECUTE + GROUP_READ + GROUP_EXECUTE + WORLD_READ + WORLD_EXECUTE + ) + configure_file( + ${IFM3D_BINARY_DIR}/ifm3d-dpkg-deps.py.in + ${IFM3D_BINARY_DIR}/ifm3d-dpkg-deps.py + @ONLY + ) + + # Turn on component-based installation + set(CPACK_DEB_COMPONENT_INSTALL ON) + set(CPACK_GENERATOR "DEB") + get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) + list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified") + + # Package architecture + find_program(DPKG_CMD dpkg) + if(NOT CMAKE_CROSSCOMPILING) + if(NOT DPKG_CMD) + message(STATUS + "Cannot find dpkg in your path, default to ${CMAKE_SYSTEM_PROCESSOR}.") + set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}") + else() + execute_process(COMMAND "${DPKG_CMD}" --print-architecture + OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + endif(NOT DPKG_CMD) else() - execute_process(COMMAND "${DPKG_CMD}" --print-architecture - OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - endif(NOT DPKG_CMD) -else() - # big assumption here - set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "armhf") -endif() + # big assumption here + set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "armhf") + endif() + + set(CPACK_DEBIAN_PACKAGE_SECTION Libraries) + set(CPACK_DEBIAN_PACKAGE_NAME ${GIT_PROJECT_NAME}) + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "C++ sensor interface to ifm 3D cameras") + set(CPACK_PACKAGE_VENDOR "ifm") + set(CPACK_PACKAGE_CONTACT "support.robotics@ifm.com") + set(CPACK_PACKAGE_VERSION_MAJOR ${IFM3D_VERSION_MAJOR}) + set(CPACK_PACKAGE_VERSION_MINOR ${IFM3D_VERSION_MINOR}) + set(CPACK_PACKAGE_VERSION_PATCH ${IFM3D_VERSION_PATCH}) + set(CPACK_PACKAGE_FILE_NAME + "${GIT_PROJECT_NAME}_${IFM3D_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") + + # dynamically create dependencies of the various modules + #set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) # <-- Good idea, doesn't work for us + add_custom_target(repackage + COMMAND ${IFM3D_BINARY_DIR}/ifm3d-dpkg-deps.py ${IFM3D_BINARY_DIR}/*.deb + ) -set(CPACK_DEBIAN_PACKAGE_SECTION Libraries) -set(CPACK_DEBIAN_PACKAGE_NAME ${GIT_PROJECT_NAME}) -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "C++ sensor interface to ifm 3D cameras") -set(CPACK_PACKAGE_VENDOR "Love Park Robotics, LLC") -set(CPACK_PACKAGE_CONTACT "Tom Panzarella ") -set(CPACK_PACKAGE_VERSION_MAJOR ${IFM3D_VERSION_MAJOR}) -set(CPACK_PACKAGE_VERSION_MINOR ${IFM3D_VERSION_MINOR}) -set(CPACK_PACKAGE_VERSION_PATCH ${IFM3D_VERSION_PATCH}) -set(CPACK_PACKAGE_FILE_NAME - "${GIT_PROJECT_NAME}_${IFM3D_VERSION_STRING}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") - -# dynamically create dependencies of the various modules -#set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) # <-- Good idea, doesn't work for us -add_custom_target(repackage - COMMAND ${IFM3D_BINARY_DIR}/ifm3d-dpkg-deps.py ${IFM3D_BINARY_DIR}/*.deb - ) - -include(CPack) + include(CPack) +endif(NOT WIN32) diff --git a/ChangeLog.md b/ChangeLog.md index 0109d88b..f709179f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,481 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Changed +- Updated Cxxopts library to version 3.1.1 and used through FetchContent + +### Added +- Add support for O3D3xx and O3X devices + +## 1.3.3 - 2023-06-22 + +### Fixed +- Fixed the error reporting through onError callback + +## 1.3.2 - 2023-06-16 +### Added +- Add env variable IFM3D_SWUPDATE_CURL_TIMEOUT for curl transaction timeout during swupdate +- Add Project description and links to PYPI page +- Add an example on how to use the deserializer module + +### Fixed +- Release versions wrongly marked as +dirty + +### Changed +- Replaced glog with ifm3d::Logger, see the migration guide for details + +## 1.3.1 - 2023-06-16 [YANKED] error during release - use 1.3.2 + +## 1.3.0 - 2023-06-16 [YANKED] error during release - use 1.3.2 + +## 1.2.6 - 2023-04-05 +### Fixes +- Fix state of the FrameGrabber not being reset correctly after Stop() causing receive failures on subsequent Start()s + +### Added +- Generate and distribute stubs with python wheel package +- Add MAC address in ifm3d discover command output + +## 1.2.5 - 2023-04-03 +### Fixes +- Fixed a crash in FrameGrabber when the p command is called multiple times +- Fixed potential crash when waiting for the future returned by FrameGrabber::WaitForFrame() + +## 1.2.4 - 2023-03-28 +### Changes +- Increase network timeout for the O3R::Set() call to 10 seconds + +### Fixes +- Fixed potential crash during device discovery +- Fixed possible Segmentation fault when calling FrameGrabber::Stop() directly after FrameGrabber::Start() +- Fixed typo in RGBInfoV1, TOFInfoV4 and TOFInfoV3: extrisic_optic_to_user -> extrinsic_optic_to_user + +## 1.2.3 - 2023-03-17 +### Fixes +- Fixed Segmentation fault when assigning a AsyncNotification or AsyncError Handler while the FrameGrabber is not running +- Added Missing alias for ifm3dpy.Error to ifm3dpy.device.Error +- Fix device discovery requiring manual firewall rule on windows + +### Added +- CI job for release of ifm3d + +## 1.2.2 - 2023-03-06 +### Added +- Added Frame::FrameCount to access the frame counter value +- Added API to enable disable masking in FrameGrabber + +### Changed +- Split the python bindings into submodules, see the migration guide for details. *Warning:* this requires an update of existing codebases. +- Convert SWUpdater python bindings naming to snake_case, see the migration guide for details. *Warning:* this requires an update of existing codebases. +- `FrameGrabber::Start` now returns a future resolving once the FrameGrabber is ready to receive Frames + +### Fixes +- Fix a bug that could cause the update process to fail with a hash mismatch error on certain network configurations +- Fix a bug preventing the FrameGrabber from being `Start`ed after it was previously `Stop`ped +- Fix a typo in the `transform_cell_center_to_user` variable (previously called `transfor_cell_center_to_user`). +- Add missing python binding for `O3R::ResolveConfig` +- Fix cmake targets for deserialize module + +## 1.2.1 - 2023-02-09 +### Fixes +- Fix a bug that could result in an endless loop when receiving PCIC tickets +- Fix a crash when receiving pcic data without any chunks + +## 1.2.0 - 2023-02-03 + +### Changed +- Upgraded nlohmann::json to version 3.11.2 +- Removed json from the global namespace and moved nlohmann::json to the ifm3d::json namespace + +### Fixes +- Missing python bindings for `O3R::Port` and `O3R::Ports` +- CONFIDENCE_IMAGE image is not requested automatically anymore unless it's required for generating the requested chunks + +### Added +- `O3R::RebootToRecovery` to reboot supported O3R devices into recovery mode +- Support for O3R recovery based updates +- Add size() interface in ifm3d::Buffer +- Add Deserialize module + - Add struct TOFInfoV3 + - Add struct TOFInfoV4 + - Add struct RGBInfoV1 + - Add struct ODSInfoV1 + - Add struct ODSOccupancyGridV1 + +## 1.1.1 - 2022-12-09 +### Fixes +- Data grabbing [issue](https://github.com/ifm/ifm3d/issues/377) with ifm3dpy-v1.1.0 + +## 1.1.0 - 2022-12-02 +### Added +- Change ```FrameGrabber::Stop``` to non blocking call and now returns ```std::future``` +- Add onError callback for error reporting in streaming mode of Framegrabber +- Support for retrieving O3R Diagnostics over XMLRPC +- Support for Python 3.11 +- Support for Ubuntu 22.04 +- Python binding: FrameGrabber.sw_trigger() + +### Fixes +- XYZ_IMAGE coordinates were actually ZXY instead of XYZ, this has been corrected. + +## 1.0.1 - 2022-10-14 +### Added +- Auto closure of stale issue on GitHub +- Support for ODS schema +- Asynchronous notification support + +### Fixes +- ifm3d and ifm3dpy documentation update +- Confidence buffer available for user +- Reflectivity buffer support in schema +- Distance noise buffer in float format +- Exception if buffer is not available in frame +- Extrinsic values +- Linux .deb file now depends on local installed libs + +## 1.0.0 - 2022-09-08 +### Added +- Example update showing usage of fg API +- Visibility attribute added for ifm3d API +- Playground example for cmake users +- Python API renamed as per C++ changes +- CameraO3D, CameraO3R, CameraO3X renamed as O3D,O3R,O3X respectively +- Camera is renamed as LegacyDevice +- CameraBase is renamed as Device +- image_id is renamed as buffer_id +- Image is renamed as Buffer +- Camera module name changed to Device +- Example list update +- Windows installer support for ifm3d +- ifm3dpy documentation update +- Software trigger support for the FrameGrabber module +- Software trigger Error Reporting on execution failure +- Async Error support for FrameGrabber module +- Schema support for FrameGrabber module +- OSS compliance added +- Windows build instruction update +- Major architecture changes in the FrameGrabber module +- Swupdater support for ifm3dpy +- File(.swu) streaming for updating firmware through swupdater +- Multiple timestamp support in ifm3dpy +- O3R support for intrinsic calibration model type 2 (Fisheye Distortion Model) + +### Fixed +- Error code from 7 digit to 6 digit for Lib error codes +- Discover app for O3R devices +- ifm3d::tools compatibility for O3R devices + + +## 0.93.0 - 2022-02-17 +### Added +- Document for schema +- Document for O3X parameters +- Added functionality to get timestamp at which data is send over ethernet +- Support for Distance noise image for O3X Devices +- Support for latest O3X firmware (1.1.190) +- New Parameters for O3X device: AbsDistStraylightThreshold, EnableStraylightCorrection, EnableNoiseEstimation, + CompensateAmbientLightDrift, DistNoiseThreshold, EnableNoiseEstimation, RelAmpStraylightThreshold +- Added the O3X FW 1.1.166 to the compatibility list +- Added compatibility list to swupdate document +- error_t::message() function to retrieve details about exceptions +- custom python exception type: ifm3dpy.Error +- Added timeout option in swupdate command of tools + +### Fixed +- O3R FW detection + +### Changed +- The conversion of the camera frame is now a compile time option (Use `-DUSE_LEGACY_COORDINATES=ON` to keep the old behaviour) +- O3RCamera::Port & O3RCamera::Ports methods to get information (pcic port & type) of connected ports +- O3RCamera::ResolveConfig convencience method to access specific parts of the configuration + +## 0.92.0 - 2021-10-22 +### Added +- HTTP 407 Proxy authentication required error detection +- Python 3.10 builds +- Docker image on DockerHub: ifmrobotics/ifm3d +- Docker image on GHCR: ghcr.io/ifm/ifm3d:latest +- Basic usage tutorials + +### Fixed +- O3R broken XYZImage +- O3R getInit() method +- Use the correct base image for arm64 based containers + +### Changed +- Docker images now build ifm3d in Release mode + +## 0.91.0 - 2021-10-05 + +### Added +- O3R specific methods + +## 0.90.2 - 2021-09-16 + +- stlImage module (Image container based on STL) +- Removed copying of the tools header +- Example to upload docker container to O3R + +## 0.90.1 - 2021-08-17 + +### Added +- Basic c++ tutorials +- Support for the new JSON based XML-RPC interface +- Support for 2D image data +- ifm3dpy_viewer python example +- Generate version based on last tag and commits since + +### Changed +- Split Camera implementation into multiple classes +- IsO3D/IsO3X/IsO3R replaced by WhoAmI/AmI functions + +## 0.90.0 - 2021-01-18 + +### Added + +- Basic O3R support +- Support for the compressed image format introduced for O3R +- Initial IPv4 Discovery in the ifm3d command line tool + +### Removed + +- Hardcoded compiler Flags for Linux +- Copy of the header files during CMake build + +## Changes between ifm3d 0.18.0 and 0.20.0 [Unreleased] +* Added clang format support for formatting +* Changed License Headers to SPDX format +* Embedded third-party libs asio and cxxopt + * Removed boost from dependency list +* Support user defined port for camera, fg, swupdater module + * This enable ifm3d to connect to devices behind NAT router +* Added example for NTP to command line usage +* Added build jobs in github actions + * Windows VS 2019 + * Ubuntu 20.04 +* Bugfixes + * #284 ifm3d Compiling error at swupdater app with VS2019 and Windows + * #283 Imported target "ifm3d::image" includes non-existent path "/usr/include/opencv" + +## Changes between ifm3d 0.17.0 and 0.18.0 +* Support for latest O3D3XX firmware (1.30.5309) +* Support for latest O3X firmware (1.0.156) +* Deprecated ROS-specific apt repositories +* Deprecated python2 support +* Support for Ubuntu 20.04 Focal Fossa +* Packaged and released ifm3d as a Snap +* Added support for Ubuntu ARM64 +* Added support for L4T (Linux for Tegra) JetPack 4.3 and 4.4 +* Improved Windows build instructions +* Created GitHub Actions CI workflows +* Bugfixes: + * #190 - Added missing include for Windows build + +## Changes between ifm3d 0.16.0 and 0.17.0 +* Reverted changes in 0.16.0 (FrameGrabberUdp -- No viable path to UDP + implementation in F/W) +* Bugfixes + * Issue with libcurl usage on 32bit targets + * Corrected minimum firmware version required for inverse intrinsics + * Corrected handling of spurious wakes in FrameGrabber + * Fixed ComputeCartesian python unit test to properly blank out invalid + pixels + * Changed `build` Dockerfiles to use pip for numpy/pytest + * Fixed race condition in PCICClient unit tests + * Changed setup.py to honor the environment variables per the Windows installation instructions + * Updated installation documentation for Windows + +## Changes between ifm3d 0.15.1 and 0.16.0 +* Created new `framegrabberudp` module for consuming data over UDP interface + +## Changes between ifm3d 0.15.0 and 0.15.1 +* Minor updates to allow for cross-compiling ifm3d for the O3D3XX +* PCIC timeout issue fixed + +## Changes between ifm3d 0.14.1 and 0.15.0 +* Added Interface for getting json_model from O3D3xx devices. + +## Changes between ifm3d 0.14.0 and 0.14.1 +* Fixes to how timeouts are handled in `swupdate` module +* Updated embedded JSON library to + [3.6.1](https://github.com/nlohmann/json/releases/tag/v3.6.1), + single-header. + +## Changes between ifm3d 0.13.0 and 0.14.0 +* New module: swupdater -- utilities for updating camera firmware + * Ported functionality from `swupdate` command into its own library for + programmatic consumption. + * Updated certain semantics of the `swupdate` command in the `tools` + module to match those of the other ifm3d `tools` commands + * Updated command line switch naming to match other ifm3d tools: + * `check` subcommand now invoked by `-c` or `--check` + * `reboot` subcommand now invoked by `-r` or `--reboot` + * `file` subcommand will now test for recovery and automatically + reboot the device into recovery as needed. +* Disabled framegrabber's InverseIntrinsicParamSchema test due to suspected + false failures. Test case will be investigated and re-opened in a future + release. +* Fixed issues with unit test scripts on Windows +* Fixed Windows build documentation + * Added `BUILD_SHARED_LIBS` definition to `glog` to address issues with + logging to STDERR in Windows binaries + * Parameterized the CMake generator for easier building when multiple + versions of MSVC are installed concurrently + +## Changes between ifm3d 0.12.0 and 0.13.0 +* Honor semantics of CMake's BUILD_SHARED_LIBS flag (ON by default). Setting + to off will build and link against ifm3d modules as static libraries. +* New module: pybind11 -- Python bindings for the the C++ API + +## Changes between ifm3d 0.11.2 and 0.12.0 + +* Fixes to build infrastructure in support of windows unit tests +* Added support to retrieve the inverse intrinsic parameters from O3D3xx + cameras + +## Changes between ifm3d 0.11.1 and 0.11.2 + +* Bugfix for #111, moved a log message in framegrabber to IFM3D_PROTO_DEBUG to + keep noise level low when running an O3X for extended periods of time. +* Changed flagging bad pixels to always be `0` regardless of data type. Users + could always consult the confidence image themselves and discriminate between + a true `0` (not possible) and a bad pixel which they could then transform to + `nan` or whatever other sentinel makes sense for their application. + +## Changes between ifm3d 0.11.0 and 0.11.1 + +* Bugfix for #103 ``header is not in the correct format`` when ``make check`` is + executed against FW 1.6.2114 +* Bugifx for #107 Allows OpenCV module headers to be included in more than one + translation unit thus avoiding violation of ODR. +* The `image` and `opencv` modules now flags bad pixels at the driver-level + +## Changes between ifm3d 0.10.0 and 0.11.0 + +* Added a `jitter` subcommand to ifm3d +* Added support to retrieve the intrinsic parameters from O3D3xx cameras + +## Changes between ifm3d 0.9.3 and 0.10.0 + +* Adds support for setting the `IFM3D_SESSION_ID` environment variable for + establishing edit sessions with the camera using a known ID. +* Sessions are now explicitly cancellable if the session ID is known. +* Some session management optimizations in `FromJSON` which should result in + incremental speedups in importing JSON configurations to the camera. + + +## Changes between ifm3d 0.9.2 and 0.9.3 + +* Added build instructions how to switch between Release and Debug + for Windows builds +* Added Troubleshoot guide +* Added Opencv module build instruction for windows +* Added minimum MSVC version requirement +* Added prerequisite packages list for building ifm3d +* Changed warning message in framegrabber from `WARNING` to `IFM3D_TRACE` + severity level +* Updated swupdate command with --check (recovery mode check) and -r (reboot to + productive mode) + +## Changes between ifm3d 0.9.1 and 0.9.2 + +* Added Support for the Ubuntu 18.04 +* Added glog support in the cmake config files + +## Changes between ifm3d 0.9.0 and 0.9.1 + +* Removed some additional Boost dependencies + +## Changes between ifm3d 0.8.3 and 0.9.0 + +* Version number handling is now done in the cmake `project` command in the + top-level CMakeLists.txt file +* Dropped support for Ubuntu 14.04 +* Increased cmake requirements to 3.5 +* Increaded compiler requirements to C++14 +* Moved `examples` module into new project + [ifm3d-examples](https://github.com/lovepark/ifm3d-examples) +* Moved `viewer` sub-command out of the ifm3d project. This is to decrease the + PCL dependencies (see Issue #42). A new project will be created in support of + this viewer application: + [ifm3d-pcl-viewer](https://github.com/lovepark/ifm3d-pcl-viewer) +* Updated JSON parsing library to 3.1.2 +* By default, pcicclient module is now `OFF`. +* Pixel-parsing framework has been significantly refactored. Sub-system + specific docs for image container implementers have been provided in the + `doc` folder. +* Updated the `ImageBuffer` to conform to the new pixel-publishing + architecture. +* Initial implementation of an OpenCV-only (i.e., no PCL) image container. This + is the `opencv` module of the `ifm3d` project. +* Added a `passwd` subcommand to `ifm3d` + +## Changes between ifm3d 0.8.2 and 0.8.3 + +* Fixed a cmake regression regarding -std=c++11 flags passed to the compiler; + surfaces on old versions of cmake, i.e., in Ubuntu 14.04 + +## Changes between ifm3d 0.8.1 and 0.8.2 + +* Patch to windows build +* Better semver parsing of camera firmware + +## Changes between ifm3d 0.8.0 and 0.8.1 + +* Reverted Windows build changes due to how it broke packaging on Linux + +## Changes between ifm3d 0.7.0 and 0.8.0 + +* Illumination temperature is registered to frame data + +## Changes between ifm3d 0.6.0 and 0.7.0 + +* Added timestamping of image buffers +* Added support for setting/getting time on O3D cameras +* Added support for setting temporary application parameters. Please note, that + if the device does not support this, it may "fail silently", so, a + closed-loop check by the user is recommended. + +## Changes between ifm3d 0.5.0 and 0.6.0 + +* Added the pcic client feature from `libo3d3xx` +* Added the ability to dump on-camera tracelogs including an interface to this + capability via the `trace` subcommand to the `ifm3d` command-line tool. + +## Changes between ifm3d 0.4.0 and 0.5.0 + +* Added `swupdate` subcommand in the tools module +* Added image module support to Windows build + +## Changes between ifm3d 0.3.3 and 0.4.0 + +* Added modules/tools/contrib with bash completions for ifm3d + +## Changes between ifm3d 0.3.2 and 0.3.3 + +* Windows build support (should have been a bump to 0.4.0) + +## Changes between ifm3d 0.3.1 and 0.3.2 + +* CMake build scripts now look for opencv in tools module since the image + buffer header includes an opencv header + +## Changes between ifm3d 0.3.0 and 0.3.1 + +* Fixed regression on 14.04 - no compiler support for std::put_time (#3) + +## Changes between ifm3d 0.2.0 and 0.3.0 + +* Support for NTP (on O3X) +* Added simple viewer sub-command to the `ifm3d` command-line program. This + viewer will render the point cloud and color each pixel with the normalized + amplitude value registered to that point. + ## Changes between ifm3d 0.1.0 and 0.2.0 * Added software trigger support to O3X diff --git a/LICENSE-thirdparty b/LICENSE-thirdparty new file mode 100644 index 00000000..bc8edb14 --- /dev/null +++ b/LICENSE-thirdparty @@ -0,0 +1,887 @@ +ifm3d library is redistributed within ifm3dpy package. +This license applies to ifm3d binary in the this directory + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +----------------------------------------------------------------------------- +json is redistributed within all ifm3d and ifm3dpy packages. +This license applies to json binary in the directory modules/camera/include/ifm3d/contrib + +JSON for Modern C++ is licensed under the MIT License +: + +Copyright (c) 2013-2017 Niels Lohmann + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +----------------------------------------------------------------------------- +asio is redistributed within all ifm3d and ifm3dpy packages. +This license applies to asio binary in the directory third-party/. + + Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------------------------------- +cxxopts is redistributed within all ifm3d and ifm3dpy packages. +This license applies to cxxopts binary in the directory third-party/. + +Copyright (c) 2014 Jarryd Beck + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +----------------------------------------------------------------------------------- +websocketpp is redistributed within all ifm3d and ifm3dpy packages. +This license applies to websocketpp binary in the directory third-party/. + +Main Library: + +Copyright (c) 2014, Peter Thorson. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the WebSocket++ Project nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Bundled Libraries: + +****** Base 64 Library (base64/base64.hpp) ****** +base64.hpp is a repackaging of the base64.cpp and base64.h files into a +single header suitable for use as a header only library. This conversion was +done by Peter Thorson (webmaster@zaphoyd.com) in 2012. All modifications to +the code are redistributed under the same license as the original, which is +listed below. + +base64.cpp and base64.h + +Copyright (C) 2004-2008 René Nyffenegger + +This source code is provided 'as-is', without any express or implied +warranty. In no event will the author be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + +3. This notice may not be removed or altered from any source distribution. + +René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +****** SHA1 Library (sha1/sha1.hpp) ****** +sha1.hpp is a repackaging of the sha1.cpp and sha1.h files from the shallsha1 +library (http://code.google.com/p/smallsha1/) into a single header suitable for +use as a header only library. This conversion was done by Peter Thorson +(webmaster@zaphoyd.com) in 2013. All modifications to the code are redistributed +under the same license as the original, which is listed below. + + Copyright (c) 2011, Micael Hildenborg + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Micael Hildenborg nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +****** MD5 Library (common/md5.hpp) ****** +md5.hpp is a reformulation of the md5.h and md5.c code from +http://www.opensource.apple.com/source/cups/cups-59/cups/md5.c to allow it to +function as a component of a header only library. This conversion was done by +Peter Thorson (webmaster@zaphoyd.com) in 2012 for the WebSocket++ project. The +changes are released under the same license as the original (listed below) + +Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +L. Peter Deutsch +ghost@aladdin.com + +****** UTF8 Validation logic (utf8_validation.hpp) ****** +utf8_validation.hpp is adapted from code originally written by Bjoern Hoehrmann +. See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for +details. + +The original license: + +Copyright (c) 2008-2009 Bjoern Hoehrmann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- +curl is redistributes within all ifm3d and ifm3dpy packages. +This license applies to curl binary used with ifm3d and ifm3dpy binaries. + +Open Source Initiative OSI - The MIT License + +http://www.opensource.org/licenses/mit-license.php + +Copyright (c) 2010-2013 Brian Cavalier and John Hann + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------------------------------- +xmlrpc-c is redistributes within all ifm3d and ifm3dpy packages. +This license applies to xmlrpc-c binary used with ifm3d and ifm3dpy binaries. + +The copyright owners of this package license the public to copy it +(and do other things with it which are controlled by copyright law) +under a few simple conditions. + +Each source file describes the copyright license for that particular +file. This file summarizes the licenses for your convenience. + +All the code written specifically for Xmlrpc-c, which is most +of the code, and the aggregation, is licensed under the +XML-RPC FOR C/C++ license shown below. + +Some of the code was written for another purpose and copied into +Xmlrpc-c. Its copyright owners license the code under a different +license: + +The Expat Licence applies to the contents of the directory lib/expat, +the ABYSS Web Server License applies to the contents of the directory +lib/abyss and parts of the file src/xmlrpc_abyss.c. + +The Python 1.5.2 license applies to parts of the file +src/xmlrpc_base64.c. + +And as for the tools/ directory, you'll have to examine the licenses +on your own. + +These same licenses have been offered throughout Xmlrpc-c's history. + + + XML-RPC For C/C++ License + ------------------------- + +Copyright (C) 2001 by First Peer, Inc. All rights reserved. +Copyright (C) 2001 by Eric Kidd. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + Expat License + ------------- + +Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + ABYSS Web Server License + ------------------------ + +Copyright (C) 2000 by Moez Mahfoudh . All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + + + + Python 1.5.2 License + -------------------- + +Copyright 1991, 1992, 1993, 1994 by Stichting Mathematisch Centrum, +Amsterdam, The Netherlands. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the names of Stichting Mathematisch +Centrum or CWI or Corporation for National Research Initiatives or +CNRI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +While CWI is the initial source for this software, a modified version +is made available by the Corporation for National Research Initiatives +(CNRI) at the Internet address ftp://ftp.python.org. + +STICHTING MATHEMATISCH CENTRUM AND CNRI DISCLAIM ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH +CENTRUM OR CNRI BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL +DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. + +--------------------------------------------------------------------------------- +Glog is redistributes within all ifm3d and ifm3dpy packages. +This license applies to Glog binary used with ifm3d and ifm3dpy binaries. + +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--------------------------------------------------------------------------------------- +pybind11 is redistributes within ifm3dpy packages. +This license applies to pybind binary used with ifm3dpy binaries. + +Copyright (c) 2016 Wenzel Jakob , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of +external contributions to this project including patches, pull requests, etc. +------------------------------------------------------------------------------------------- +opencv is redistributes within ifm3d packages. +This license applies to opencv binary used with ifm3d binaries. + +By downloading, copying, installing or using the software you agree to this license. +If you do not agree to this license, do not download, install, +copy or use the software. + + + License Agreement + For Open Source Computer Vision Library + (3-clause BSD License) + +Copyright (C) 2000-2020, Intel Corporation, all rights reserved. +Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. +Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved. +Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. +Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved. +Copyright (C) 2015-2016, Itseez Inc., all rights reserved. +Copyright (C) 2019-2020, Xperience AI, all rights reserved. +Third party copyrights are property of their respective owners. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the names of the copyright holders nor the names of the contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +This software is provided by the copyright holders and contributors "as is" and +any express or implied warranties, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose are disclaimed. +In no event shall copyright holders or contributors be liable for any direct, +indirect, incidental, special, exemplary, or consequential damages +(including, but not limited to, procurement of substitute goods or services; +loss of use, data, or profits; or business interruption) however caused +and on any theory of liability, whether in contract, strict liability, +or tort (including negligence or otherwise) arising in any way out of +the use of this software, even if advised of the possibility of such damage. +---------------------------------------------------------------------------------- \ No newline at end of file diff --git a/LICENSE.MIT b/LICENSE.MIT deleted file mode 100644 index c4ce40d1..00000000 --- a/LICENSE.MIT +++ /dev/null @@ -1,22 +0,0 @@ -JSON for Modern C++ is licensed under the MIT License -: - -Copyright (c) 2013-2017 Niels Lohmann - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 732759e6..b11a52fb 100644 --- a/README.md +++ b/README.md @@ -1,379 +1,68 @@ -ifm3d -===== -Library and utilities for working with ifm pmd-based 3D ToF Cameras. +# ifm3d Overview +Library and utilities for working with ifm pmd-based 3D ToF Perception devices. Compatible with the O3R, O3D and O3X platforms. +This library is available in python, c++, and has wrappers for ROS and ROS2. +[C++ API Reference](https://ifm.github.io/ifm3d-docs/html/cpp_api/annotated.html) +[Python API Reference](https://ifm.github.io/ifm3d-docs/html/_autosummary/ifm3dpy.html#module-ifm3dpy) +Comprehensive documentation is available on [ifm3d.com](https://ifm3d.com/). -Software Compatibility Matrix ------------------------------ - - - - - - - - - - - - - - - - - - - - - - -
ifm3d versionO3D Firmware VersionO3X Firmware VersionUbuntu Linux VersionNotes
0.1.01.6.21140.1.416.04Initial (beta) release
0.2.01.6.21140.1.16, 0.1.2014.04, 16.04Software triggering (O3X), support for Ubuntu 14.04
-**NOTE**: Our *officially supported platform* is Ubuntu Linux. However, other - operating systems will likely be acceptable, especially other Debian-based - Linuxes. We also note that, WRT Ubuntu Linux, our current plan is to support - the two most recent LTS releases. So, for example, as of this writing, we - currently support 16.04 (preferred) and 14.04. Once 18.04 is released, we - will drop support for 14.04 and only be supporting 18.04 and 16.04. If you - are using this library embedded in a product, you should plan accordingly. +![Build (Ubuntu)](https://github.com/ifm/ifm3d/workflows/Build%20(Ubuntu)/badge.svg?branch=master) +![Build (Windows)](https://github.com/ifm/ifm3d/workflows/Build%20(Windows)/badge.svg?branch=master) -Organization of the Software ----------------------------- -The ifm3d software is organized into modules, they are: - - - - - - - - - - - - - - - - - - - - - - -
Module NameDescription
CameraProvides an implementation of the XMLRPC protocol for configuring the - camera and PMD imager settings
FramegrabberProvides an implementation of the PCIC protocol for streaming pixel - data and triggered image acquisition.
ImageProvides a bridge from raw camera bytes to OpenCV and PCL image - encodings.
ToolsProvides the ifm3d command line tool for manipulating and introspecting - the hardware interactively. It is also suitable for usage within shell scripts.
- -Installing the Software ------------------------ - -### Build Dependencies - -Building the software from source, requires the following pre-requisites -installed on your machine: - -* [Boost](http://www.boost.org) -* [Gtest](https://github.com/google/googletest) -* [Glog](https://github.com/google/glog) -* [libxmlrpc](http://xmlrpc-c.sourceforge.net/) -* [CMake](http://www.cmake.org) -* [OpenCV](http://opencv.org) -* [PCL](http://pointclouds.org) - -Additionally, if you plan to build the debian packages and have the -dependencies computed for you dynamically (see the note below on the -`repackage` target), you will also need: - -* [Python 2.7](https://www.python.org/) -* [readelf](https://www.gnu.org/software/binutils/) (Part of the `binutils` package) -* [dpkg](https://help.ubuntu.com/lts/serverguide/dpkg.html) - -We note that, if you are running on a supported Linux, all of these packages -are available through the offical debian repositories and should be a simple -`apt-get` away from being installed on your machine. - -### Building From Source - -Building the software follows the usual cmake idiom of: - -``` -$ mkdir build -$ cd build -$ cmake -DCMAKE_INSTALL_PREFIX=/usr .. -$ make -$ make check -$ sudo make install -``` - -Alternatively, if you are on a supported Linux platform (see above), the -preferred method of building and installing the software is: - -``` -$ mkdir build -$ cd build -$ cmake -DCMAKE_INSTALL_PREFIX=/usr .. -$ make -$ make check -$ make package -$ make repackage -$ sudo dpkg -i ifm3d_0.2.0_amd64-camera.deb -$ sudo dpkg -i ifm3d_0.2.0_amd64-framegrabber.deb -$ sudo dpkg -i ifm3d_0.2.0_amd64-image.deb -$ sudo dpkg -i ifm3d_0.2.0_amd64-tools.deb -``` -(The version number embedded in the deb file will be dependent upon which -version of the `ifm3d` software you are building) - -A few important notes when building from source: - -* For the `make check` step, you will need to have your camera plugged in. The - cameras settings will get mutated by this process, so, you are encouraged to - back up your configuration if you'd like to later restore your camera to its - pre-testing state. You are also encouraged to test against the camera (or - cameras) you plan to use. I.e., O3D, O3X, etc. Please note that testing - against the O3X will fail unless you have at least version 0.1.16 of the ifm - firmware installed on the device. - -* Many `ifm3d` users ultimately plan to use this library along with its - associated [ROS wrapper](https://github.com/lovepark/ifm3d-ros). If this is - the case, you need to be sure that the version of OpenCV that you link to in - both `ifm3d` and `ifm3d-ros` are consistent. To give you some control over - that, the build process allows you to explicitly call out which version of - OpenCV you wish to use. For example, if you are on 14.04 and using ROS - Indigo, your `cmake` line above should look something like: `$ cmake - -DCMAKE_INSTALL_PREFIX=/usr -DFORCE_OPENCV2=ON ..`. Similarly, if you are on - 16.04 and ROS Kinetic, your `cmake` line above should look something like: `$ cmake - -DCMAKE_INSTALL_PREFIX=/usr -DFORCE_OPENCV3=ON ..` - -* Experienced users may be puzzled by the `repackage` step. If you are simply - building for your local machine, you can skip it (albeit, with minimal - risk). This step is used to dynamically compute the debian dependencies for - the particular module. Due to how we are partitioning out the software, this - approach is necessary vs. the more traditional - `CPACK_DEBIAN_PACKAGE_SHLIBDEPS` wrapper around `dpkg-shlibdeps`. We - basically created [a version of that tool](cmake/utils/ifm3d-dpkg-deps.py.in) - that exploits *a-priori* information about the `ifm3d` environment to - properly compute the debian dependencies. If you are building debs on a build - machine to be distributed out to various runtime computers, you will - certainly want to exectue the `repackage` target so that you are ensured the - runtime machines have the proper dependency chain in place. - - -Basic Library Usage -------------------- -A set of example programs for using the library are forthcoming. In the -meantime, please refer to -[the image module unit tests](modules/image/test/ifm3d-image-tests.cpp) for -concrete examples of library usage. We think you will find those instructive -and will enable you to get started quickly with the software. - - -Configuring Your Camera ------------------------ -(For exemplary purposes, we assume an O3X camera) - -The central command-line tool provided with the library is the -appropriately-named binary program `ifm3d`. - -To view your current camera settings, you can run the following command: -``` -$ ifm3d dump -{ - "ifm3d": { - "Apps": [ - { - "Description": "", - "Id": "1299148885", - "Imager": { - "ExposureTime": "1000", - "FrameRate": "5", - "MaxAllowedFrameRate": "12.5", - "MinimumAmplitude": "42", - "SpatialFilter": {}, - "SpatialFilterType": "0", - "SymmetryThreshold": "0.4", - "TemporalFilter": {}, - "TemporalFilterType": "0", - "Type": "1FRQ_1EXP_0GRAY" - }, - "Index": "1", - "Name": "", - "OutputAmplitudeImage": "true", - "OutputConfidenceImage": "true", - "OutputDistanceImage": "true", - "OutputGrayscaleImage": "false", - "OutputXYZImage": "true", - "TriggerMode": "1", - "Type": "Camera" - } - ], - "Device": { - "ArticleNumber": "", - "ArticleStatus": "??", - "Description": "", - "DeviceType": "1:512", - "IPAddressConfig": "0", - "ImageTimestampReference": "1489579229", - "Name": "New sensor", - "OperatingMode": "0", - "PasswordActivated": "false", - "SessionTimeout": "30", - "UpTime": "1.59972222222222" - }, - "Net": { - "MACAddress": "00:02:01:40:54:09", - "NetworkSpeed": "0", - "StaticIPv4Address": "192.168.0.69", - "StaticIPv4Gateway": "192.168.0.201", - "StaticIPv4SubNetMask": "255.255.255.0", - "UseDHCP": "false" - }, - "_": { - "Date": "Tue Mar 28 21:16:07 2017", - "HWInfo": { - "MACAddress": "00:02:01:40:54:09", - "Mainboard": "#!03_M100_B01_12345678_008025483", - "MiraSerial": "Not implemented" - }, - "SWVersion": { - "Algorithm_Version": "0.1.3", - "Calibration_Device": "00:02:01:40:54:09", - "Calibration_Version": "0.0.1", - "ELDK": "GOLDENEYE_YOCTO_HARDFP-273-06d9c894636352a6c93711c7284d02b0c794a527", - "IFM_Software": "0.1.4", - "Linux": "Linux version 3.14.34-rt31-yocto-standard-00016-g5121435-dirty (jenkins@dettlx152) (gcc version 4.9.2 (GCC) ) #1 SMP PREEMPT RT Tue Mar 14 08:40:14 CET 2017", - "Main_Application": "0.4.986" - }, - "ifm3d_version": 100 - } - } -} -``` +| 3D cloud | Distance | RGB | +| -- | -- | -- | +| ![3D cloud of a stack of boxes](xyz.png) | ![Distance image of a stack of boxes](distance.png) | ![RGB image of a stack of boxes](jpeg.png) | +## Released Versions -Redirecting the above serialized json to a file would allow you to edit camera -parameters. Let's say you saved that data to a file called `o3x.json` and made -some modifications. To commit those changes to the hardware, you would: +⚠️ Note that the `main` branch is generally in a work in progress state and you probably want to use a +tagged [release version](https://github.com/ifm/ifm3d/releases) for production. - $ ifm3d config < o3x.json +### Current Revision -You could also just edit a single parameter quite easily from the command -line. Referring to our dump above where the camera is set to stream data at 5 -Hz, if we wanted to step the frame rate up to 10 Hz, you could: +The table below show the compatibility between the current ifm3d version and the firmware versions for the O3X, O3D and O3R devices. Other combinations than the ones listed below might work but have not been thoroughly tested. - $ echo '{"Apps":[{"Index":"1","Imager":{"FrameRate":"10"}}]}' | ifm3d config +| ifm3d version | Supported O3D Firmware Version | Supported O3X Firmware Version | Supported O3R Firmware Version | Supported Ubuntu Linux Version | Notes | +| ------------- | ------------------------------ | ------------------------------ | -------------------- | ------------------------------ | ----- | +| 1.2.6 | Not supported, please use ifm3d 0.20.x | Not tested, please use ifm3d 0.20.x | 1.0.14 | 20.04, 22.04 | See the changelog and the migrating guide. | -We can see that our setting took affect by dumping the camera configuration -again. This time, we *grep* our output using the handy json filtering tool -[jq](https://stedolan.github.io/jq): +> Note that a full compatibility matrix is available [here](ifm3d/doc/sphinx/content/swcompat:ifm3d%20Software%20Compatibility%20Matrix) for older versions. +## Organization of the Software -``` -$ ifm3d dump | jq .ifm3d.Apps[0].Imager.FrameRate -"10" -``` - -You can see what else is available via the `ifm3d` tool by running: - -``` -$ ifm3d --help -ifm3d: version=0.1.0 -usage: ifm3d [] [] - -global options: - -h [ --help ] Produce this help message and exit - --ip arg (=192.168.0.69) IP address of the sensor - --xmlrpc-port arg (=80) XMLRPC port of the sensor - --password arg Password for establishing an edit-session with the - sensor - - -These are common commands used in various situations: - - cp Create a new application on the sensor, - bootstrapped from a copy of an existing one. - - config Configure sensor settings from a JSON description of - the desired sensor state. See also `dump'. - - dump Serialize the sensor state to JSON. - - export Export an application or whole sensor configuration - into a format compatible with ifm Vision Assistant. - - hz Compute the actual frequency at which the FrameGrabber - is running. - - import Import an application or whole sensor configuration - that is compatible with ifm Vision Assistant's export - format. - - ls Lists the applications currently installed on - the sensor. - - reboot Reboot the sensor, potentially into recovery - mode. Recovery mode is useful for putting the - sensor into a state where it can be flashed - with new firmware. - - reset Reset the sensor to factory defaults. - - rm Deletes an application from the sensor. - - schema Construct and analyze image acquisition schema masks. - - -For bug reports, please see: -https://github.com/lovepark/ifm3d/issues -``` - -We also note that every sub-command also accepts the `--help` argument. For -example: +The ifm3d software is organized into modules, they are: +| Module name | Description | +| ----------- | ----------- | +| `device` | Provides an implementation of the XMLRPC protocol for configuring the camera and pmd imager settings. | +| `framegrabber` | Provides an implementation of the PCIC protocol for streaming pixel data and triggered image acquisition.| +| `swupdater` | Provides utilities for managing the SWUpdate subsystem of the camera. | +| `pcicclient` | Direct access to PCIC to, for example, actuate digital IO.| +| `tools` | Provides the ifm3d command line tool for manipulating and introspecting the hardware interactively. It is also suitable for usage within shell scripts to, for example, manage fleets of cameras.| +| `pybind11` | Provides python bindings through pybind11 to the native C++ API. Supports all general camera functionality as well as a zero-copy interface to image data, exposed as NumPy arrays. | +| `deserialize` | Provides definitions and functions for deserializing structs sent over PCIC. | + +## Installation instructions +Please refer to the corresponding section on [ifm3d.com](https://ifm3d.com/). + +## Supported docker containers +Docker containers are available for the ifm3d library, both on [ghcr](https://github.com/orgs/ifm/packages?repo_name=ifm3d) and on the [dockerhub](https://hub.docker.com/r/ifmrobotics/ifm3d). +You can pull them with: +```bash +docker pull ghcr.io/ifm/ifm3d:stable ``` -$ ifm3d reboot --help -usage: ifm3d [] reboot [] - -global options: - -h [ --help ] Produce this help message and exit - --ip arg (=192.168.0.69) IP address of the sensor - --xmlrpc-port arg (=80) XMLRPC port of the sensor - --password arg Password for establishing an edit-session with the - sensor - -reboot options: - -r [ --recovery ] Reboot into recovery mode +OR +```bash +docker pull ifmrobotics/ifm3d:stable ``` +Note that we provide 2 tags, *stable* always points to the latest tagged version, and *latest* is built nightly with the latest changes on the o3r/main-next branch. The *latest* tag is typically a work in progress. +For more details on the available containers, see [here](ifm3d/doc/sphinx/content/installation_instructions/install_docker:Docker%20dev%20container). +For more details on docker and the O3R platform see [here](documentation/O3R/Docker/README:Docker%20on%20O3R). -For viewing the point cloud data, currently, our only supported visualization -tool is [rviz](http://wiki.ros.org/rviz). To utilize it, you will need to -install our [ifm3d ROS bindings](https://github.com/lovepark/ifm3d-ros) and -follow the instructions from there. - - -Known Issues, Bugs, and our TODO list -------------------------------------- -We greatly appreciate your choice to use our sensor interface software. As of -this writing, this software is very new. However, it is informed by many years -of developing and supporting -[libo3d3xx](https://github.com/lovepark/libo3d3xx), the de-facto C++ sensor -interface to the ifm O3D. We are committed to evolving this software package -into the production quality code that has made libo3d3xx the basis of many -commercial products with the additional benefit of providing a hardware -abstraction layer over top of *all* ifm pmd-based 3D ToF cameras. - -We appreciate your feedback as you use the code. We will be tracking things -(including our TODO list) on the -[Github Issue Tracker](https://github.com/lovepark/ifm3d/issues). +## Report a bug and check the known issues -LICENSE -------- -Please see the file called [LICENSE](LICENSE). +Please see the [Github Issue Tracker](https://github.com/ifm/ifm3d/issues), or contact `support.robotics@ifm.com`. +## LICENSE -AUTHORS -------- -Tom Panzarella +Please see the [LICENSE](LICENSE) file. diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..103e4a53 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +v1.3.3*v1.3.3*1*3*3 \ No newline at end of file diff --git a/clang_format.py b/clang_format.py new file mode 100644 index 00000000..490fb635 --- /dev/null +++ b/clang_format.py @@ -0,0 +1,125 @@ +#!/usr/bin/python3 + +# SPDX-License-Identifier: Apache-2.0 +# Copyright (C) ifm electronic gmbh +# +# THE PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. + +# Provides functions to format C/C++ files using clang-format +import shutil +import re +import subprocess +import os +import multiprocessing +import functools +import sys +assert sys.version_info > (3, 5), "python 3.5 or more required" + +max_parallel_process = 10 + +# The bare Minimum clang-format version +minimum_required_clang_format_version = "10.0.0" + + +# Extension and folders to be excludes from formatting +apply_extensions = (".cxx", ".cpp", ".c", ".hxx", ".hh", ".cc", ".hpp", ".h") +include_folders = ["modules", "examples"] +exclude_folders = ["modules/device/include/ifm3d/contrib"] + +#making path accorinding to OS +exclude_folders = list(map(lambda rel_path : os.path.normcase(os.path.join("", os.path.relpath(rel_path))), exclude_folders)) + +def format_file(clangf_exe, file, dry_run=False): + args = [clangf_exe, "-i", "-style=file"] + + if dry_run: + args.append("--dry-run") + args.append("--Werror") + + return subprocess.call([*args, file]) == 0 + + +def clang_format(clangf_exe, dry_run=False): + """ + Function runs the clang formatter on the files + with extension mention in 'apply_extensions' and + in the folders mentioned in 'include_folders' + + Return: + (bool) True if all files are formatted else + False. + """ + filelist = [] + # walk all the folders from the current wotking directory + for dir in include_folders: + for root, dirs, files in os.walk(os.path.join(os.getcwd(), dir)): + if os.path.relpath(root, os.getcwd()) in exclude_folders: + files[:] = [] + dirs[:] = [] + + # loop over files + for file in files: + # check for file extension + if file.endswith(apply_extensions): + filelist.append(os.path.join(root, file)) + + with multiprocessing.Pool(max_parallel_process) as p: + try: + return all(p.map(functools.partial(format_file, clangf_exe, dry_run=dry_run), filelist)) + except Exception as e: + print("Error with formatting file", e) + + return False + + +def check_clang_format_version(clangf_exe): + """ + Function checks the version of the clang-formatter + available with the host system + """ + try: + _minimum_version_num = int( + minimum_required_clang_format_version.replace('.', '')) + if shutil.which(clangf_exe): + ret = os.popen("{} --version".format(clangf_exe)).read() + tokens = re.search("^(\w+-\w+) (\w+) ([0-9.]+).*", ret) + if tokens.group(1) == "clang-format" and tokens.group(2) == "version": + _current_version_num = int(tokens.group(3).replace(".", "")) + if _current_version_num >= _minimum_version_num: + return True + else: + return False + else: + return False + else: + return False + except: + return False + + +# entry point +if __name__ == "__main__": + # build a list of possible clang-format versions + clang_executables = ['clang-format'] + for i in range(10, 99): + clang_executables.append("clang-format-{}".format(i)) + + # check if the appropriate one is installed + clangf_exe = None + for i in clang_executables: + if check_clang_format_version(i): + clangf_exe = i + break + # bail out in case no clang-format was found + if clangf_exe is None: + print("Please install clang-format version {}".format(minimum_required_clang_format_version)) + quit(1) + + dry_run = "check" in sys.argv + + # do the formatting + ok = clang_format(clangf_exe, dry_run=dry_run) + if ok: + print(f"{'Formatting' if not dry_run else 'Checking'} done!") + + quit(0 if ok else 1) diff --git a/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake new file mode 100644 index 00000000..1ee6f5ae --- /dev/null +++ b/cmake/StandardProjectSettings.cmake @@ -0,0 +1,34 @@ +# Set a default build type if none was specified +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") + set(CMAKE_BUILD_TYPE + RelWithDebInfo + CACHE STRING "Choose the type of build." FORCE) + # Set the possible values of build type for cmake-gui, ccmake + set_property( + CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS + "Debug" + "Release" + "MinSizeRel" + "RelWithDebInfo") +endif() + +# Generate compile_commands.json to make it easier to work with clang based tools +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +option(ENABLE_IPO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)" OFF) + +if(ENABLE_IPO) + include(CheckIPOSupported) + check_ipo_supported( + RESULT + result + OUTPUT + output) + if(result) + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + else() + message(SEND_ERROR "IPO is not supported: ${output}") + endif() +endif() diff --git a/cmake/modules/GetVersionFromGitTag.cmake b/cmake/modules/GetVersionFromGitTag.cmake new file mode 100644 index 00000000..70182660 --- /dev/null +++ b/cmake/modules/GetVersionFromGitTag.cmake @@ -0,0 +1,160 @@ +# +# This cmake module sets the project version and partial version +# variables by analysing the git tag and commit history. It expects git +# tags defined with semantic versioning 2.0.0 (http://semver.org/). +# +# The module expects the PROJECT_NAME variable to be set, and recognizes +# the GIT_FOUND, GIT_EXECUTABLE and VERSION_UPDATE_FROM_GIT variables. +# If Git is found and VERSION_UPDATE_FROM_GIT is set to boolean TRUE, +# the project version will be updated using information fetched from the +# most recent git tag and commit. Otherwise, the module will try to read +# a VERSION file containing the full and partial versions. The module +# will update this file each time the project version is updated. +# +# Once done, this module will define the following variables: +# +# ${PROJECT_NAME}_VERSION_STRING - Version string without metadata +# such as "v2.0.0" or "v1.2.41-beta.1". This should correspond to the +# most recent git tag. +# ${PROJECT_NAME}_VERSION_STRING_FULL - Version string with metadata +# such as "v2.0.0+3.a23fbc" or "v1.3.1-alpha.2+4.9c4fd1" +# ${PROJECT_NAME}_VERSION - Same as ${PROJECT_NAME}_VERSION_STRING, +# without the preceding 'v', e.g. "2.0.0" or "1.2.41-beta.1" +# ${PROJECT_NAME}_VERSION_FULL_IN_CASE_OF_PATCHES - Version string with metadata in case of patches +# without preceding 'v', e.g. "1.3.1" or "1.3.1-alpha.2" or "1.3.1-alpha.2+4.9c4fd1" +# ${PROJECT_NAME}_VERSION_MAJOR - Major version integer (e.g. 2 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_MINOR - Minor version integer (e.g. 3 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_PATCH - Patch version integer (e.g. 1 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_TWEAK - Tweak version string (e.g. "RC.2" in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_AHEAD - How many commits ahead of last tag (e.g. 21 in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_GIT_SHA - The git sha1 of the most recent commit (e.g. the "ef12c8" in v2.3.1-RC.2+21.ef12c8) +# ${PROJECT_NAME}_VERSION_META - metadata in case of patches +# +# This module is public domain, use it as it fits you best. +# +# Author: Nuno Fachada + +# Check if git is found... +if (GIT_EXECUTABLE AND EXISTS ${CMAKE_SOURCE_DIR}/.git) + + # Get last tag from git + execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0 --tags + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE) + + #How many commits since last tag + execute_process(COMMAND ${GIT_EXECUTABLE} rev-list ${${PROJECT_NAME}_VERSION_STRING}..HEAD --count + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_AHEAD + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Get current commit SHA from git + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_VERSION_GIT_SHA + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Get partial versions into a list + string(REGEX MATCHALL "-.*$|[0-9]+" ${PROJECT_NAME}_PARTIAL_VERSION_LIST + ${${PROJECT_NAME}_VERSION_STRING}) + + # Set the version numbers + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 0 ${PROJECT_NAME}_VERSION_MAJOR) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 1 ${PROJECT_NAME}_VERSION_MINOR) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST + 2 ${PROJECT_NAME}_VERSION_PATCH) + + # The tweak part is optional, so check if the list contains it + list(LENGTH ${PROJECT_NAME}_PARTIAL_VERSION_LIST + ${PROJECT_NAME}_PARTIAL_VERSION_LIST_LEN) + if (${PROJECT_NAME}_PARTIAL_VERSION_LIST_LEN GREATER 3) + list(GET ${PROJECT_NAME}_PARTIAL_VERSION_LIST 3 ${PROJECT_NAME}_VERSION_TWEAK) + endif() + + # Unset the list + unset(${PROJECT_NAME}_PARTIAL_VERSION_LIST) + + # Set full project version string + set(${PROJECT_NAME}_VERSION_STRING_FULL + ${${PROJECT_NAME}_VERSION_STRING}+${${PROJECT_NAME}_VERSION_AHEAD}.${${PROJECT_NAME}_VERSION_GIT_SHA}) + + # Check wether working tree is clean + execute_process(COMMAND ${GIT_EXECUTABLE} status --porcelain + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE ${PROJECT_NAME}_WORKING_DIR_CLEAN) + + if(NOT ${PROJECT_NAME}_WORKING_DIR_CLEAN STREQUAL "") + SET(${PROJECT_NAME}_VERSION_META "+dirty") + MESSAGE(WARNING "Workdir not clean, marking version as dirty:\n${${PROJECT_NAME}_WORKING_DIR_CLEAN}") + endif() + + # Unset working dir clean + unset(${PROJECT_NAME}_WORKING_DIR_CLEAN) + + # Save version to file (which will be used when Git is not available + # or VERSION_UPDATE_FROM_GIT is disabled) + file(WRITE ${CMAKE_BINARY_DIR}/VERSION ${${PROJECT_NAME}_VERSION_STRING_FULL} + "*" ${${PROJECT_NAME}_VERSION_STRING} + "*" ${${PROJECT_NAME}_VERSION_MAJOR} + "*" ${${PROJECT_NAME}_VERSION_MINOR} + "*" ${${PROJECT_NAME}_VERSION_PATCH} + "*" ${${PROJECT_NAME}_VERSION_TWEAK} + "*" ${${PROJECT_NAME}_VERSION_AHEAD} + "*" ${${PROJECT_NAME}_VERSION_GIT_SHA}) + +else() + + # Git not available, get version from file + file(STRINGS ${CMAKE_SOURCE_DIR}/VERSION ${PROJECT_NAME}_VERSION_LIST) + string(REPLACE "*" ";" ${PROJECT_NAME}_VERSION_LIST ${${PROJECT_NAME}_VERSION_LIST}) + + # Get Length of the version elements + list(LENGTH ${PROJECT_NAME}_VERSION_LIST VERSION_ELEMENT_LENGTH) + # Set partial versions + if(0 LESS ${VERSION_ELEMENT_LENGTH}) + list(GET ${PROJECT_NAME}_VERSION_LIST 0 ${PROJECT_NAME}_VERSION_STRING_FULL) + endif() + if(1 LESS ${VERSION_ELEMENT_LENGTH}) + list(GET ${PROJECT_NAME}_VERSION_LIST 1 ${PROJECT_NAME}_VERSION_STRING) + endif() + if(2 LESS ${VERSION_ELEMENT_LENGTH}) + list(GET ${PROJECT_NAME}_VERSION_LIST 2 ${PROJECT_NAME}_VERSION_MAJOR) + endif() + if(3 LESS ${VERSION_ELEMENT_LENGTH}) + list(GET ${PROJECT_NAME}_VERSION_LIST 3 ${PROJECT_NAME}_VERSION_MINOR) + endif() + if(4 LESS ${VERSION_ELEMENT_LENGTH}) + list(GET ${PROJECT_NAME}_VERSION_LIST 4 ${PROJECT_NAME}_VERSION_PATCH) + endif() + if(5 LESS ${VERSION_ELEMENT_LENGTH}) + list(GET ${PROJECT_NAME}_VERSION_LIST 5 ${PROJECT_NAME}_VERSION_TWEAK) + endif() + if(6 LESS ${VERSION_ELEMENT_LENGTH}) + list(GET ${PROJECT_NAME}_VERSION_LIST 6 ${PROJECT_NAME}_VERSION_AHEAD) + endif() + if(7 LESS ${VERSION_ELEMENT_LENGTH}) + list(GET ${PROJECT_NAME}_VERSION_LIST 7 ${PROJECT_NAME}_VERSION_GIT_SHA) + endif() + + set(${PROJECT_NAME}_VERSION_META "+snapshot") + +endif() + + +# Set project version (without the preceding 'v') +set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}) +if (${PROJECT_NAME}_VERSION_TWEAK) + set(${PROJECT_NAME}_VERSION ${${PROJECT_NAME}_VERSION}-${${PROJECT_NAME}_VERSION_TWEAK}) +endif() + +# Set project version full in case of patches (without the preceding 'v') +set(${PROJECT_NAME}_VERSION_FULL_IN_CASE_OF_PATCHES ${${PROJECT_NAME}_VERSION}) +if (${PROJECT_NAME}_VERSION_AHEAD GREATER 0 AND NOT DEFINED ${PROJECT_NAME}_VERSION_META) + # Add ahead and git sha + set(${PROJECT_NAME}_VERSION_FULL_IN_CASE_OF_PATCHES + ${${PROJECT_NAME}_VERSION_FULL_IN_CASE_OF_PATCHES}+${${PROJECT_NAME}_VERSION_AHEAD}.${${PROJECT_NAME}_VERSION_GIT_SHA}) + set(${PROJECT_NAME}_VERSION_META +${${PROJECT_NAME}_VERSION_AHEAD}.${${PROJECT_NAME}_VERSION_GIT_SHA}) +endif() \ No newline at end of file diff --git a/cmake/modules/ifm3d-config.cmake.in b/cmake/modules/ifm3d-config.cmake.in new file mode 100644 index 00000000..6e42f6c9 --- /dev/null +++ b/cmake/modules/ifm3d-config.cmake.in @@ -0,0 +1,26 @@ +# 'commons' component is required +if(NOT TARGET ifm3d::common) + include("${CMAKE_CURRENT_LIST_DIR}/ifm3d-common-targets.cmake") +endif() + +if(NOT TARGET ifm3d::common) + set(ifm3d_FOUND False) + message(FATAL_ERROR + "ifm3d installation is broken, common targets not found!" + ) +else() + message(STATUS "ifm3d found component: common") + foreach(_comp ${ifm3d_FIND_COMPONENTS}) + if(NOT "${_comp}" MATCHES "common") + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/ifm3d-${_comp}-targets.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/ifm3d-${_comp}-targets.cmake") + message(STATUS "ifm3d found component: ${_comp}") + else() + if(ifm3d_FIND_REQUIRED_${_comp}) + set(ifm3d_FOUND False) + message(WARNING "ifm3d could not find component: ${_comp}") + endif() + endif() + endif() + endforeach() +endif() diff --git a/cmake/modules/ifm3d_version.cmake b/cmake/modules/ifm3d_version.cmake deleted file mode 100644 index 490802bd..00000000 --- a/cmake/modules/ifm3d_version.cmake +++ /dev/null @@ -1,9 +0,0 @@ -################################################ -## This is the single place to manage API -## version numbers -################################################ -set(IFM3D_VERSION_MAJOR 0) -set(IFM3D_VERSION_MINOR 2) -set(IFM3D_VERSION_PATCH 0) -set(IFM3D_VERSION_STRING - "${IFM3D_VERSION_MAJOR}.${IFM3D_VERSION_MINOR}.${IFM3D_VERSION_PATCH}") diff --git a/cmake/toolchains/o3d303.cmake b/cmake/toolchains/o3d303.cmake new file mode 100644 index 00000000..e73b42f1 --- /dev/null +++ b/cmake/toolchains/o3d303.cmake @@ -0,0 +1,22 @@ +# +# Toolchain for cross-building imf3d for the O3D3xx camera. +# +# NOTE: For this file to work, you must first: +# source /opt/poky/1.8.1/environment-setup-armv7ahf-vfp-neon-poky-linux-gnueabi +# + +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSROOT /opt/poky/1.8.1/sysroots/armv7ahf-vfp-neon-poky-linux-gnueabi) +set(SYSROOT_CONTRIB /opt/poky/contrib) +set(CMAKE_FIND_ROOT_PATH ${SYSROOT_CONTRIB} ${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) + +set(CMAKE_LIBRARY_PATH /lib + /usr/lib + /usr/lib/arm-linux-gnueabihf) + +set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "armhf") diff --git a/cmake/utils/ifm3d-dpkg-deps.py.in b/cmake/utils/ifm3d-dpkg-deps.py.in index 6a02a8b3..43c1ee9c 100644 --- a/cmake/utils/ifm3d-dpkg-deps.py.in +++ b/cmake/utils/ifm3d-dpkg-deps.py.in @@ -1,7 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # -*- python -*- # +# Copyright (C) 2020 ifm electronic, gmbh # Copyright (C) 2017 Love Park Robotics, LLC # # Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,7 +38,7 @@ import subprocess import sys import tempfile -VERSION = "@IFM3D_VERSION_STRING@" +VERSION = "@IFM3D_VERSION@" class DpkgDeps(object): """Auto-computes the dependencies for a debian package and repackages it @@ -68,7 +69,8 @@ class DpkgDeps(object): self.so_deps_ = self.generate_so_deps() self.deb_deps_ = self.generate_deb_deps() - self.update_control_file() + if len(self.deb_deps_) > 0: + self.update_control_file() self.repackage_deb() print("Removing: %s" % self.deb_file_in_) @@ -96,7 +98,7 @@ class DpkgDeps(object): file.write("Depends: ") n = len(self.deb_deps_.keys()) i = 0 - for deb, version in self.deb_deps_.iteritems(): + for deb, version in self.deb_deps_.items(): if "ifm3d" in deb: file.write("%s (= %s)" % (deb, version)) else: @@ -128,12 +130,21 @@ class DpkgDeps(object): debs["ifm3d-camera"] = VERSION elif "framegrabber" in so: debs["ifm3d-framegrabber"] = VERSION + elif "swupdater" in so: + debs["ifm3d-swupdater"] = VERSION elif "image" in so: debs["ifm3d-image"] = VERSION elif "tools" in so: debs["ifm3d-tools"] = VERSION + elif "opencv" in so: + debs["ifm3d-opencv"] = VERSION else: - out = subprocess.check_output(["dpkg", "-S", so]) + try: + # Starting sometime between 18.04 and 20.04, /lib is a + # symlink to /usr/lib so must check both places + out = subprocess.check_output(["dpkg", "-S", os.path.realpath(so)]).decode('utf-8') + except subprocess.CalledProcessError: + out = subprocess.check_output(["dpkg", "-S", so]).decode('utf-8') for line in out.split("\n"): parts = line.split(":") deb = parts[0] @@ -145,7 +156,7 @@ class DpkgDeps(object): if "ifm3d" in deb: continue - out = subprocess.check_output(["dpkg", "-s", deb]) + out = subprocess.check_output(["dpkg", "-s", deb]).decode('utf-8') for line in out.split("\n"): if line.startswith("Version"): m = re.search(r"Version:(.*)", line) @@ -168,14 +179,34 @@ class DpkgDeps(object): print("Found the following .so files: %s" % str(sos)) so_deps = {} + # This gives us the base name of the .so for so in sos: - out = subprocess.check_output(["readelf", "-d", so]) + out = subprocess.check_output(["readelf", "-d", so]).decode('utf-8') for line in out.split("\n"): if "NEEDED" in line: m = re.search(r"\[(.*)\]", line) if m is not None: so_deps[m.group(1)] = 1 + print(".so deps *prior* to full-path resolution:") + print(so_deps) + # now, resolve the full path to the .so + for so in sos: + out = subprocess.check_output(["ldd", so]).decode('utf-8') + for line in out.split("\n"): + parts = line.split() + if len(parts) <= 1: + continue + elif len(parts) < 3: + so_deps.pop(parts[0], None) + else: + if ((parts[0] in so_deps) and + ("ifm3d" not in parts[0])): + so_deps[parts[2]] = so_deps.pop(parts[0]) + + print(".so deps *after* to full-path resolution:") + print(so_deps) + retval = so_deps.keys() print("Found the following .so deps: %s" % retval) return retval diff --git a/cmake/windows_installer/.cpack_ignore b/cmake/windows_installer/.cpack_ignore new file mode 100644 index 00000000..ab402052 --- /dev/null +++ b/cmake/windows_installer/.cpack_ignore @@ -0,0 +1,11 @@ +\\.git/ +\\.git* +\\.vscode/ +\\.mypy_cache/ +\\.archive/ + +build*/ +doc/ + +.editorconfig +.gdbinit \ No newline at end of file diff --git a/cmake/windows_installer/cp_dll_script.cmake b/cmake/windows_installer/cp_dll_script.cmake new file mode 100644 index 00000000..13e16057 --- /dev/null +++ b/cmake/windows_installer/cp_dll_script.cmake @@ -0,0 +1,7 @@ +# Find third party DLLS from _deps +file(GLOB_RECURSE deps_DLLs + "_deps/*.dll" +) + +# Copy third party DLLs to CMAKE_INSTALL_PREFIX bin directory +file(COPY ${deps_DLLs} DESTINATION ${CMAKE_INSTALL_PREFIX}/bin/) diff --git a/cmake/windows_installer/cpack.cmake b/cmake/windows_installer/cpack.cmake new file mode 100644 index 00000000..34c40116 --- /dev/null +++ b/cmake/windows_installer/cpack.cmake @@ -0,0 +1,41 @@ +set(CPACK_GENERATOR NSIS) + +set(CPACK_PACKAGE_NAME "ifm3d") +set(CPACK_PACKAGE_VENDOR "ifm") +set(CPACK_PACKAGE_CONTACT "support.robotics@ifm.com") +set(CPACK_PACKAGE_VERSION ${ROOT_PROJECT_VERSION}) +set(CPACK_PACKAGE_VERSION_MAJOR ${ROOT_PROJECT_VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${ROOT_PROJECT_VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${ROOT_PROJECT_VERSION_PATCH}) +set(CPACK_PACKAGE_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/cmake/windows_installer\\\\ifm_logo.ico") + +string(TOLOWER ${CMAKE_SYSTEM_NAME} _sys) +string(TOLOWER ${PROJECT_NAME} _project_lower) +set(CPACK_SOURCE_GENERATOR ZIP) +set(CPACK_PACKAGE_FILE_NAME "${_project_lower}_${_sys}_${PROJECT_VERSION}") +set(CPACK_SOURCE_PACKAGE_FILE_NAME "${_project_lower}_${PROJECT_VERSION}") + +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") +set(CPACK_RESOURCE_FILE_README "${CMAKE_SOURCE_DIR}/README.md") + +set(CPACK_OUTPUT_FILE_PREFIX "${CMAKE_SOURCE_DIR}/ifm3d_installer") + +set(CPACK_NSIS_MODIFY_PATH ON) +set(CPACK_NSIS_DISPLAY_NAME "ifm3d") +set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\ifm3d.exe") +set(CPACK_NSIS_HELP_LINK "https://github.com/ifm") +set(CPACK_NSIS_URL_INFO_ABOUT "http://www.ifm.com/") +set(CPACK_NSIS_CONTACT "support.robotics@ifm.com") +set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/cmake/windows_installer\\\\ifm_logo.ico") +set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}/cmake/windows_installer\\\\ifm_logo.ico") + +# not .gitignore as its regex syntax is distinct +file(READ ${CMAKE_CURRENT_LIST_DIR}/.cpack_ignore _cpack_ignore) +string(REGEX REPLACE "\n" ";" _cpack_ignore ${_cpack_ignore}) +set(CPACK_SOURCE_IGNORE_FILES "${_cpack_ignore}") + +install(FILES ${CPACK_RESOURCE_FILE_README} ${CPACK_RESOURCE_FILE_LICENSE} + DESTINATION share/docs/${PROJECT_NAME}) + +include(CPack) \ No newline at end of file diff --git a/cmake/windows_installer/ifm_logo.ico b/cmake/windows_installer/ifm_logo.ico new file mode 100644 index 00000000..3c63c632 Binary files /dev/null and b/cmake/windows_installer/ifm_logo.ico differ diff --git a/distance.png b/distance.png new file mode 100644 index 00000000..4617daf1 Binary files /dev/null and b/distance.png differ diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt new file mode 100644 index 00000000..fc636d12 --- /dev/null +++ b/doc/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(doxygen) +add_subdirectory(sphinx) + +add_custom_target(docs DEPENDS ifm3d_doxygen ifm3d_sphinx) \ No newline at end of file diff --git a/doc/O3D_confidence.md b/doc/O3D_confidence.md new file mode 100644 index 00000000..bd515ae9 --- /dev/null +++ b/doc/O3D_confidence.md @@ -0,0 +1,13 @@ +# Confidence bits of the O3D explained + +The values in the confidence matrix contain information about the status of the corresponding ToF pixel. A confidence value has 8 bits. + +|Bit| Name | Meaning when bit is set | Contributes to Pixel Validity | +|---|------|-------------------------|-------------------------------| +|0 | **Pixel is invalid** | To determine if a pixel is valid or not, only this bit needs to be checked. The distance values, as well as the Cartesian coordinates of invalid pixels are set to 0. The reason for invalidity is encoded in some of the other bits below. | yes | +|1| **Pixel is saturated** | Pixel amplitude is saturated. | yes | +|2| **Bad A-B symmetry** | The A-B symmetry vale of the four phase measurement is above threshold. Remark: This symmetry value is used to detect motion artifacts. Noise (e.g. due to strong ambient light or very short integration times) or PMD interference may also contribute. | yes | +|3| **Low amplitude** | The amplitude value is below minimum amplitude threshold. | yes | +|[5, 4]| **Exposure time indicator** | The two bits indicate which exposure time was used in a multiple exposure measurement. 00 = unused. 01 = shortest exposure time (only used in 3 exposure mode). 10 = middle exposure time in 3 exposure mode, short exposure in double exposure mode. 11 = longest exposure time (always 1 in single exposure mode) | no | +|6| **Clipped Pixel** | Clipping box on 3D data. If clipping is active this bit indicates that the pixel coordinates are outside the defined volume. | yes | +|7| **Suspect/defective Pixel** | This pixel has been marked as "suspect" or "defective" and values have been replaced by interpolated values from the surroundings. | no | diff --git a/doc/O3X_rectExample_annotated.pdf b/doc/O3X_rectExample_annotated.pdf new file mode 100644 index 00000000..3cb39025 Binary files /dev/null and b/doc/O3X_rectExample_annotated.pdf differ diff --git a/doc/compute_cartesian/O3D_Cartesian_Computation.ipynb b/doc/compute_cartesian/O3D_Cartesian_Computation.ipynb new file mode 100644 index 00000000..fa67c25b --- /dev/null +++ b/doc/compute_cartesian/O3D_Cartesian_Computation.ipynb @@ -0,0 +1,172 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import ifm3dpy as ifm3d" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Grab one time-correlated frame off the camera with all data\n", + "# (i.e., distance image, unit vectors,\n", + "# on-board computed point cloud for ground truth comparisions, etc.)\n", + "cam = ifm3d.Camera()\n", + "fg = ifm3d.FrameGrabber(cam, 0xFFFF)\n", + "im = ifm3d.ImageBuffer()\n", + "assert(fg.wait_for_frame(im, 1000))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Let's look at the distance image just to validate the data\n", + "# are there\n", + "plt.imshow(im.distance_image())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def compute_cartesian(im):\n", + " #\n", + " # Given an ifm3d ImageBuffer `im`\n", + " #\n", + " extr = im.extrinsics()\n", + " tx = extr[0]\n", + " ty = extr[1]\n", + " tz = extr[2]\n", + "\n", + " uvec = im.unit_vectors()\n", + " ex = uvec[:,:,0]\n", + " ey = uvec[:,:,1]\n", + " ez = uvec[:,:,2]\n", + "\n", + " rdis = im.distance_image()\n", + " # cast to float\n", + " rdis_f = rdis.astype(np.float32)\n", + " if (rdis.dtype == np.float32):\n", + " # if the distance values from the camera were float,\n", + " # assume they were meters and so convert to mm\n", + " # for comparisions\n", + " rdis_f *= 1000.\n", + " \n", + " # Cartesian values in optical frame\n", + " x_ = ex * rdis_f + tx\n", + " y_ = ey * rdis_f + ty\n", + " z_ = ez * rdis_f + tz\n", + " \n", + " # Account for bad pixels\n", + " mask = rdis == 0\n", + " x_[mask] = 0\n", + " y_[mask] = 0\n", + " z_[mask] = 0\n", + "\n", + " # convert to camera coord frame and also cast back to\n", + " # int16 (the ground-truth data are int16 and mm)\n", + " x = z_.astype(np.int16)\n", + " y = -(x_.astype(np.int16))\n", + " z = -(y_.astype(np.int16))\n", + " \n", + " return np.dstack((x, y, z))" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, 1, 1)" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# this is our off-board point cloud computation\n", + "cloud_computed = compute_cartesian(im)\n", + "\n", + "# this is what was computed on the camera - I'm assuming O3D here,\n", + "# if using O3X, you may have to type cast from float to in and convert\n", + "# from meters to mm.\n", + "cloud_onboard = im.xyz_image()\n", + "\n", + "# Let's look at the max deviation in each dimension (mm)\n", + "x_diff = np.abs(cloud_computed[:,:,0] - cloud_onboard[:,:,0])\n", + "y_diff = np.abs(cloud_computed[:,:,1] - cloud_onboard[:,:,1])\n", + "z_diff = np.abs(cloud_computed[:,:,2] - cloud_onboard[:,:,2])\n", + "\n", + "(np.amax(x_diff), np.amax(y_diff), np.amax(z_diff)) # units are mm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.6.8" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/doc/cross_compiling.md b/doc/cross_compiling.md new file mode 100644 index 00000000..0113a3f2 --- /dev/null +++ b/doc/cross_compiling.md @@ -0,0 +1,37 @@ +# Cross-compiling ifm3d + +One benefit of the CMake build system is the possibility to cross compile your code. To demonstrate this with ifm3d we use a Toolchain created with OpenEmbedded as a showcase + + +# Installing a Toolchain + +Depending on your Toolchain provider this step might look different + +``` +$ chmod +x poky-glibc-i686-vantage-image-armv7ahf-vfp-neon-toolchain-qte-1.8.1.sh +$ ./poky-glibc-i686-vantage-image-armv7ahf-vfp-neon-toolchain-qte-1.8.1.sh +``` + +# Using the Toolchain + +``` +$ git clone https://github.com/ifm/ifm3d.git +$ cd ifm3d/ +$ mkdir build +$ source /opt/poky/1.8.1/environment-setup-armv7ahf-vfp-neon-poky-linux-gnueabi +$ cmake -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/o3d303.cmake -DCMAKE_INSTALL_PREFIX=../../oem-partition/usr .. +``` +The step ``source /opt/poky/1.8.1/environment-setup-armv7ahf-vfp-neon-poky-linux-gnueabi`` is necessary for the OpenEmbedded Toolchain to set all the needed Environment variables. + +The variable ``CMAKE_INSTALL_PREFIX`` may get handy when you plan to install the ifm3d library and tools to a specific location for an OEM deployment for example. +To run the build you may have to adjust the argument of ``-j`` depending on the number of CPU cores available in your system + +``` +$ make -j8 +$ make install +``` + +# Caution + +Both the provided Toolchain file [cmake/toolchains/o3d303.cmake](/cmake/toolchains/o3d303.cmake) and the instructions above are examples for the OEM workflow. Both maybe needs adjustments if you are planning to use a different type of set-up + diff --git a/doc/doxygen/CMakeLists.txt b/doc/doxygen/CMakeLists.txt new file mode 100644 index 00000000..4ddd5bcc --- /dev/null +++ b/doc/doxygen/CMakeLists.txt @@ -0,0 +1,27 @@ +################################################# +# Documentation (Doxygen) +################################################# +find_package(Doxygen) + +if (DOXYGEN_FOUND) + foreach(DOXYGEN_MODULE IN LISTS DOXYGEN_MODULES) + string(APPEND DOXYGEN_INPUTS "${PROJECT_SOURCE_DIR}/modules/${DOXYGEN_MODULE}/include ") + endforeach(DOXYGEN_MODULE) + + # set input and output files + set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile.out) + + # request to configure the file + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + + add_custom_target(ifm3d_doxygen + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/html/doxygen + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/latex/cpp_api + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM) +else (DOXYGEN_FOUND) + message("Doxygen need to be installed to generate the doxygen documentation") +endif (DOXYGEN_FOUND) \ No newline at end of file diff --git a/doc/doxygen/Doxyfile.in b/doc/doxygen/Doxyfile.in new file mode 100644 index 00000000..e05e5e5e --- /dev/null +++ b/doc/doxygen/Doxyfile.in @@ -0,0 +1,2579 @@ +# Doxyfile 1.8.17 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "ifm3d" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# 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 = + +# With the PROJECT_LOGO tag one can specify a logo or an 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 = @CMAKE_CURRENT_BINARY_DIR@ + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# 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 = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# 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 = @DOXYGEN_INPUTS@ + +# 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 = @DOXYGEN_INPUTS@ + +# 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 JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = 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 = NO + +# 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 = NO + +# 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 (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +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 + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = 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, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files), VHDL, tcl. 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 https://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 the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# 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 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 you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_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 + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = 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_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = 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 = YES + +# This flag is only useful for Objective-C code. If 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 = NO + +# 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 = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# 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 +# (including Cygwin) ands 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 HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= 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 = NO + +# 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 = @CMAKE_CURRENT_SOURCE_DIR@/DoxygenLayout.xml + +# 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 https://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. 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 = NO + +# 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. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = 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. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = @DOXYGEN_INPUTS@ + +# 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: https://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. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# 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 (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.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 \ + *.doc \ + *.txt \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice + +# 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 = YES + +# 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 = + +# 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 = */detail/* + +# 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 = nlohmann + +# 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. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +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. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +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 +# entity 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 https://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 configuration 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 + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files +# were built. This is equivalent to specifying the "-p" option to a clang tool, +# such as clang-check. These options will then be passed to the parser. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# 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/cpp_api + +# 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 additional user-defined +# cascading style sheets that are 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 therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = @CMAKE_CURRENT_SOURCE_DIR@/doxygen-awesome.css @CMAKE_CURRENT_SOURCE_DIR@/doxygen-awesome-sidebar-only.css + +# 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 style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://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 YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = 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: https://developer.apple.com/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 https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.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: https://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. Furthermore it +# enables the Previous and Next buttons. +# 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: https://doc.qt.io/archives/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: https://doc.qt.io/archives/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: https://doc.qt.io/archives/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: https://doc.qt.io/archives/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: +# https://doc.qt.io/archives/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 style sheets (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 = YES + +# 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 + +# If 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_TRANSPARENT 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 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered 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 https://www.mathjax.org before deployment. +# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ + +# 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 +# , /