mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-06-18 22:41:29 +08:00
Compare commits
21 Commits
pr-s-eepro
...
pr-remove-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a83e43cfd5 | ||
|
|
0e31dd560d | ||
|
|
ae47d69f50 | ||
|
|
eed4457d71 | ||
|
|
a0e42f2032 | ||
|
|
0f15eea283 | ||
|
|
9eddd0cdbc | ||
|
|
5d5d9e399b | ||
|
|
a3ad956394 | ||
|
|
c72a11fe9f | ||
|
|
fc53da51fa | ||
|
|
a49cffb09f | ||
|
|
8552465408 | ||
|
|
100d9c97fb | ||
|
|
5db3060c2a | ||
|
|
9e93fd753e | ||
|
|
e8c19a2006 | ||
|
|
1777d6bcd2 | ||
|
|
9adda29da2 | ||
|
|
e34cb8ccb5 | ||
|
|
2557a7441c |
45
.claude/settings.local.json
Normal file
45
.claude/settings.local.json
Normal file
@ -0,0 +1,45 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git -C /Users/rroche/Work/Dronecode/PX4-Autopilot-ci-orchestrator commit -S -s -m \"$\\(cat <<''EOF''\nci-orchestrator: cache emscripten SDK in failsafe-sim job\n\nCache the _emscripten_sdk directory keyed on version 4.0.15 so\nsubsequent runs skip the git clone and install steps \\(~30s savings\non cache hit\\). The build step sources emsdk_env.sh unchanged.\nEOF\n\\)\")",
|
||||
"WebFetch(domain:gist.github.com)",
|
||||
"Bash(git -C /Users/rroche/Work/Dronecode/PX4-Autopilot-ci-orchestrator commit -S -s -m \"$\\(cat <<''EOF''\nci-orchestrator: upgrade SITL and ROS integration runners to 8cpu\n\nROS Integration Tests \\(31m\\) is the critical path bottleneck,\nfollowed by SITL Tests tailsitter \\(20m\\). Both spend significant\ntime compiling dependencies \\(xrce-dds, ROS2 libs, gazebo\\). Upgrade\nfrom 4cpu to 8cpu to parallelize compilation and reduce wall-clock.\nEOF\n\\)\")",
|
||||
"Bash(git -C /Users/rroche/Work/Dronecode/PX4-Autopilot-ci-orchestrator commit -S -s -m \"$\\(cat <<''EOF''\nci-orchestrator: fix flash analysis comment alignment\n\nRemove extra indentation from code fences and bloaty output inside\nthe PR comment body so the column alignment renders correctly in\nGitHub markdown.\nEOF\n\\)\")",
|
||||
"Bash(docker run:*)",
|
||||
"Bash(docker rm:*)",
|
||||
"Bash(git -C /Users/rroche/Work/Dronecode/PX4-Autopilot-ci-orchestrator commit -S -s -m \"$\\(cat <<''EOF''\nci-orchestrator: increase SITL test speed factor to 20x\n\nBump simulation speed factor from 10x to 20x for MAVSDK SITL tests.\nWith the 8cpu runners this should be sustainable and roughly halve\nthe 18min test execution phase.\nEOF\n\\)\")",
|
||||
"Bash(docker exec:*)",
|
||||
"Bash(git -C /Users/rroche/Work/Dronecode/PX4-Autopilot-ci-orchestrator commit -S -s -m \"$\\(cat <<''EOF''\nci-orchestrator: build Gazebo plugins in cache seed job\n\nAdd Gazebo Classic plugin build to the build-sitl cache seed job so\ndownstream SITL test jobs get ccache hits for both PX4 firmware and\nGazebo objects. Bump ccache max_size from 250M to 400M to fit both.\nEOF\n\\)\")",
|
||||
"Bash(git -C /Users/rroche/Work/Dronecode/PX4-Autopilot-ci-orchestrator commit -S -s -m \"$\\(cat <<''EOF''\ndocs: update CI orchestrator docs for 4-tier architecture and caching\n\nUpdate documentation to reflect recent changes:\n- 5-tier to 4-tier restructure \\(old T3 merged into T2\\)\n- Add comprehensive caching strategy section covering ccache key\n patterns, scopes, sizes, and the cache seed pattern\n- Document Emscripten SDK cache for failsafe-sim\n- Update runner types \\(8cpu for SITL/ROS, 16cpu for clang-tidy\\)\n- SITL tests now run at 20x speed factor\n- Remove CI summary job references\n- Update tier numbering throughout troubleshooting and best practices\nEOF\n\\)\")",
|
||||
"Bash(docker build:*)",
|
||||
"WebFetch(domain:raw.githubusercontent.com)",
|
||||
"Bash(docker system prune:*)",
|
||||
"WebSearch",
|
||||
"WebFetch(domain:hub.docker.com)",
|
||||
"WebFetch(domain:docs.px4.io)",
|
||||
"WebFetch(domain:github.com)",
|
||||
"Bash(find:*)",
|
||||
"Bash(docker search:*)",
|
||||
"Bash(docker images:*)",
|
||||
"Bash(gh pr create --draft --title \"packaging: add PX4 SITL .deb package\" --body \"$\\(cat <<''EOF''\nSingle .deb package for PX4 SITL with Gazebo Harmonic resources. Installs to /opt/px4-sitl with a px4-sitl wrapper script symlinked to /usr/bin.\n\nChanges:\n- cmake/package.cmake: CPack config for px4-sitl .deb with minimal Gazebo runtime deps\n- platforms/posix/CMakeLists.txt: install targets for Gazebo models, worlds, plugins\n- Tools/packaging/px4-sitl.sh: launcher script \\(XDG dirs, gz_env.sh generation, dartsim symlink fix\\)\n- Tools/packaging/postinst, postrm: create/remove /usr/bin/px4-sitl symlink\n- .github/workflows/build_deb_package.yml: CI to build and validate the .deb\n- docs/en/packaging/px4_sitl_deb.md: usage and build docs\n\nTested with PX4_SIM_MODEL=gz_x500 and sihsim_quadx in Ubuntu 24.04 container.\nEOF\n\\)\")",
|
||||
"Bash(gh gist create:*)",
|
||||
"Bash(git commit -S -s -m \"$\\(cat <<''EOF''\npackaging: exclude legacy install rules from .deb builds\n\nGate legacy install rules behind if\\(NOT DPKG_PROGRAM\\) so they only\nrun for tarball/ROS workflows. Gate .deb install rules behind\nif\\(DPKG_PROGRAM\\) so they only run when building .deb packages.\n\nWithout this, both rule sets execute during .deb builds, triplicating\nfiles and pulling in the entire source tree \\(integrationtests, launch,\nTools, CMakeLists.txt, gazebo-classic models\\) into the package.\nEOF\n\\)\")",
|
||||
"Bash(gh issue create:*)",
|
||||
"Bash(git commit -S -s --amend -m \"$\\(cat <<''EOF''\npackaging: exclude legacy install rules from .deb builds\n\nMove find_program\\(DPKG_PROGRAM dpkg\\) into platforms/posix/CMakeLists.txt\nso it is available before the install rules are processed \\(package.cmake\nruns later\\). Gate both the top-level posix install block and the\nSITL-specific legacy install rules behind if\\(NOT DPKG_PROGRAM\\) so they\nonly run for tarball/ROS workflows.\n\nGate .deb install rules behind if\\(DPKG_PROGRAM\\) so they only run when\nbuilding .deb packages. Mark Gazebo resource installs as OPTIONAL so\nCPack does not fail when the gz submodule is not checked out.\n\nWithout this, both rule sets execute during .deb builds, triplicating\nfiles and pulling in the entire source tree \\(integrationtests, launch,\nTools, CMakeLists.txt, gazebo-classic models\\) into the package.\n\nSigned-off-by: Ramon Roche <mrpollo@gmail.com>\nEOF\n\\)\")",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(kill:*)",
|
||||
"WebFetch(domain:cmake.org)",
|
||||
"Bash(gh run watch:*)",
|
||||
"Bash(gh pr list:*)",
|
||||
"Bash(gh pr checks:*)",
|
||||
"WebFetch(domain:discuss.px4.io)",
|
||||
"Bash(gh issue list:*)",
|
||||
"Bash(pkill:*)",
|
||||
"WebFetch(domain:runs-on.com)",
|
||||
"Bash(gh release:*)",
|
||||
"Bash(docker manifest:*)",
|
||||
"Bash(gh pr:*)",
|
||||
"Bash(GIT_EDITOR=true git cherry-pick --continue)"
|
||||
]
|
||||
}
|
||||
}
|
||||
6
.github/actions/build-deb/action.yml
vendored
6
.github/actions/build-deb/action.yml
vendored
@ -20,7 +20,7 @@ runs:
|
||||
steps:
|
||||
- name: Restore ccache
|
||||
id: ccache-restore
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ inputs.ccache-key-prefix }}-${{ github.ref_name }}-${{ github.sha }}
|
||||
@ -52,7 +52,7 @@ runs:
|
||||
run: ccache -s
|
||||
|
||||
- name: Save ccache
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
if: always()
|
||||
with:
|
||||
path: ~/.ccache
|
||||
@ -108,7 +108,7 @@ runs:
|
||||
echo "PASS: gazebo package validation successful"
|
||||
|
||||
- name: Upload .deb artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ inputs.artifact-name }}
|
||||
path: build/px4_sitl_${{ inputs.target }}/*.deb
|
||||
|
||||
2
.github/actions/save-ccache/action.yml
vendored
2
.github/actions/save-ccache/action.yml
vendored
@ -16,7 +16,7 @@ runs:
|
||||
|
||||
- name: Cache - Save ccache
|
||||
if: always()
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ inputs.cache-primary-key }}
|
||||
|
||||
2
.github/actions/setup-ccache/action.yml
vendored
2
.github/actions/setup-ccache/action.yml
vendored
@ -33,7 +33,7 @@ runs:
|
||||
|
||||
- name: Cache - Restore ccache
|
||||
id: restore
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ inputs.cache-key-prefix }}-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
||||
17
.github/workflows/build_all_targets.yml
vendored
17
.github/workflows/build_all_targets.yml
vendored
@ -72,10 +72,10 @@ jobs:
|
||||
timestamp: ${{ steps.set-timestamp.outputs.timestamp }}
|
||||
branchname: ${{ steps.set-branch.outputs.branchname }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Cache Python pip
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ hashFiles('**./Tools/setup/requirements.txt') }}
|
||||
@ -131,11 +131,10 @@ jobs:
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Git ownership workaround
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
# ccache key breakdown:
|
||||
@ -144,7 +143,7 @@ jobs:
|
||||
# ccache-<linux>-<x64>-<nuttx-0>-
|
||||
- name: Cache Restore from Key
|
||||
id: cc_restore
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ format('ccache-{0}-{1}-{2}', runner.os, matrix.runner, matrix.group) }}
|
||||
@ -176,7 +175,7 @@ jobs:
|
||||
./Tools/ci/package_build_artifacts.sh
|
||||
|
||||
- name: Upload Build Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: px4_${{matrix.group}}_build_artifacts
|
||||
path: artifacts/
|
||||
@ -189,7 +188,7 @@ jobs:
|
||||
|
||||
- name: Cache Save
|
||||
if: always()
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ steps.cc_restore.outputs.cache-primary-key }}
|
||||
@ -211,7 +210,7 @@ jobs:
|
||||
uploadlocation: ${{ steps.upload-location.outputs.uploadlocation }}
|
||||
steps:
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: artifacts/
|
||||
merge-multiple: true
|
||||
|
||||
18
.github/workflows/build_deb_package.yml
vendored
18
.github/workflows/build_deb_package.yml
vendored
@ -40,11 +40,10 @@ jobs:
|
||||
should_push: ${{ steps.push.outputs.should_push }}
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-tags: true
|
||||
submodules: false
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Set PX4 version
|
||||
id: version
|
||||
@ -93,7 +92,7 @@ jobs:
|
||||
apt-get update && apt-get install -y git
|
||||
git config --global --add safe.directory $(realpath .)
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
@ -103,7 +102,7 @@ jobs:
|
||||
run: ./Tools/ci/use_aws_apt_mirror.sh
|
||||
|
||||
- name: Cache apt packages
|
||||
uses: actions/cache@v4
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: /var/cache/apt/archives
|
||||
key: apt-${{ matrix.target }}-${{ matrix.codename }}-${{ matrix.arch }}-${{ hashFiles('Tools/setup/ubuntu.sh') }}
|
||||
@ -136,13 +135,12 @@ jobs:
|
||||
- { image: gazebo, repo: px4-sitl-gazebo, target: default, arch: arm64, runner: arm64, platform: "linux/arm64", dockerfile: Dockerfile.gazebo }
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: false
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Download Noble .deb artifact
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: px4-sitl-debs-${{ matrix.target }}-noble-${{ matrix.arch }}
|
||||
path: docker-context
|
||||
@ -157,13 +155,13 @@ jobs:
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
uses: docker/setup-buildx-action@v4
|
||||
with:
|
||||
driver: docker-container
|
||||
platforms: ${{ matrix.platform }}
|
||||
|
||||
- name: Build and push container image
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@v7
|
||||
with:
|
||||
context: docker-context
|
||||
file: Tools/packaging/${{ matrix.dockerfile }}
|
||||
|
||||
85
.github/workflows/checks.yml
vendored
85
.github/workflows/checks.yml
vendored
@ -12,51 +12,88 @@ on:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
gate_checks:
|
||||
name: Gate Checks [${{ matrix.check }}]
|
||||
runs-on: [runs-on,runner=2cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
container:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
fail-fast: true
|
||||
matrix:
|
||||
check: [
|
||||
"check_format",
|
||||
"check_newlines",
|
||||
"tests",
|
||||
"tests_coverage",
|
||||
"px4_fmu-v2_default stack_check",
|
||||
"validate_module_configs",
|
||||
"shellcheck_all",
|
||||
"NO_NINJA_BUILD=1 px4_fmu-v5_default",
|
||||
"NO_NINJA_BUILD=1 px4_sitl_default",
|
||||
"px4_sitl_allyes",
|
||||
"module_documentation",
|
||||
]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-depth: 1
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- name: Building [${{ matrix.check }}]
|
||||
env:
|
||||
PX4_SBOM_DISABLE: 1
|
||||
run: |
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
git config --system --add safe.directory '*'
|
||||
make ${{ matrix.check }}
|
||||
run: make ${{ matrix.check }}
|
||||
|
||||
- name: Uploading Coverage to Codecov.io
|
||||
if: contains(matrix.check, 'coverage')
|
||||
uses: codecov/codecov-action@v1
|
||||
tests:
|
||||
name: Unit Tests
|
||||
runs-on: [runs-on,runner=8cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
container:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
GIT_COMMITTER_EMAIL: bot@px4.io
|
||||
GIT_COMMITTER_NAME: PX4BuildBot
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: unittests
|
||||
file: coverage/lcov.info
|
||||
fetch-depth: 1
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- uses: ./.github/actions/setup-ccache
|
||||
id: ccache
|
||||
with:
|
||||
cache-key-prefix: ccache-sitl
|
||||
max-size: 300M
|
||||
|
||||
- name: Build and run unit tests
|
||||
env:
|
||||
PX4_SBOM_DISABLE: 1
|
||||
run: make tests
|
||||
|
||||
- uses: ./.github/actions/save-ccache
|
||||
if: always()
|
||||
with:
|
||||
cache-primary-key: ${{ steps.ccache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Auto-update EKF change indication baselines
|
||||
if: github.event_name == 'push'
|
||||
uses: stefanzweifel/git-auto-commit-action@v7
|
||||
with:
|
||||
file_pattern: 'src/modules/ekf2/test/change_indication/*.csv'
|
||||
commit_user_name: ${{ env.GIT_COMMITTER_NAME }}
|
||||
commit_user_email: ${{ env.GIT_COMMITTER_EMAIL }}
|
||||
commit_message: |
|
||||
[AUTO COMMIT] update EKF change indication
|
||||
|
||||
See .github/workflows/checks.yml for more details
|
||||
|
||||
- name: Check for EKF functional changes
|
||||
run: git diff --exit-code
|
||||
working-directory: src/modules/ekf2/test/change_indication
|
||||
|
||||
128
.github/workflows/clang-tidy.yml
vendored
128
.github/workflows/clang-tidy.yml
vendored
@ -21,15 +21,15 @@ jobs:
|
||||
runs-on: [runs-on, runner=16cpu-linux-x64, "run-id=${{ github.run_id }}", "extras=s3-cache"]
|
||||
container:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
outputs:
|
||||
has_findings: ${{ steps.clang_tidy.outputs.has_findings }}
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
@ -37,7 +37,7 @@ jobs:
|
||||
id: ccache
|
||||
with:
|
||||
cache-key-prefix: ccache-clang-tidy
|
||||
max-size: 120M
|
||||
max-size: 150M
|
||||
|
||||
- name: Build - px4_sitl_default (Clang)
|
||||
run: make -j16 px4_sitl_default-clang
|
||||
@ -47,96 +47,50 @@ jobs:
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" != "pull_request" ]; then
|
||||
make -j$(nproc) clang-tidy
|
||||
echo "has_findings=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
output=$(python3 Tools/ci/run-clang-tidy-pr.py origin/${{ github.base_ref }} 2>&1)
|
||||
exit_code=$?
|
||||
echo "$output"
|
||||
# Helper prints this message on both early-exit paths
|
||||
# (no changed C++ files, or no matching TUs in the compile DB)
|
||||
if echo "$output" | grep -q "skipping clang-tidy"; then
|
||||
echo "has_findings=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "has_findings=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
exit $exit_code
|
||||
python3 Tools/ci/run-clang-tidy-pr.py origin/${{ github.base_ref }}
|
||||
fi
|
||||
|
||||
- name: Upload compile_commands.json
|
||||
# On PRs, also produce a `pr-review` artifact for the PR Review Poster
|
||||
# workflow to consume. clang-tidy-diff-18 emits a unified fixes.yml that
|
||||
# the producer script translates into line-anchored review comments.
|
||||
# Running this inside the same container as the build means there is no
|
||||
# workspace-path rewriting and no cross-runner artifact handoff.
|
||||
- name: Export clang-tidy fixes for PR review
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@v4
|
||||
run: |
|
||||
mkdir -p pr-review
|
||||
git diff -U0 origin/${{ github.base_ref }}...HEAD \
|
||||
| clang-tidy-diff-18.py -p1 \
|
||||
-path build/px4_sitl_default-clang \
|
||||
-export-fixes pr-review/fixes.yml \
|
||||
-j0 || true
|
||||
|
||||
- name: Build pr-review artifact
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
python3 Tools/ci/clang-tidy-fixes-to-review.py \
|
||||
--fixes pr-review/fixes.yml \
|
||||
--repo-root "$GITHUB_WORKSPACE" \
|
||||
--repo "$GITHUB_REPOSITORY" \
|
||||
--pr-number "${{ github.event.pull_request.number }}" \
|
||||
--commit-sha "${{ github.event.pull_request.head.sha }}" \
|
||||
--out-dir pr-review \
|
||||
--event REQUEST_CHANGES
|
||||
|
||||
- name: Upload pr-review artifact
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: compile-commands
|
||||
path: build/px4_sitl_default-clang/compile_commands.json
|
||||
name: pr-review
|
||||
path: |
|
||||
pr-review/manifest.json
|
||||
pr-review/comments.json
|
||||
retention-days: 1
|
||||
|
||||
- uses: ./.github/actions/save-ccache
|
||||
if: always()
|
||||
with:
|
||||
cache-primary-key: ${{ steps.ccache.outputs.cache-primary-key }}
|
||||
|
||||
post_clang_tidy_comments:
|
||||
name: Clang-Tidy PR Annotations
|
||||
needs: [clang_tidy]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
if: >-
|
||||
always()
|
||||
&& github.event.pull_request
|
||||
&& github.event.pull_request.head.repo.full_name == github.repository
|
||||
&& (needs.clang_tidy.result == 'success' || needs.clang_tidy.result == 'failure')
|
||||
&& needs.clang_tidy.outputs.has_findings == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install clang-tools (for clang-tidy-diff)
|
||||
run: sudo apt-get install -y clang-tools
|
||||
|
||||
- name: Download compile_commands.json
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: compile-commands
|
||||
path: build/px4_sitl_default-clang
|
||||
|
||||
- name: Run clang-tidy-diff and export fixes
|
||||
run: |
|
||||
# WHY WE REWRITE compile_commands.json PATHS
|
||||
#
|
||||
# The clang_tidy job runs on a RunsOn/AWS runner where the workspace
|
||||
# root is "/__w/PX4-Autopilot/PX4-Autopilot". All absolute paths baked
|
||||
# into compile_commands.json (the "file" and "directory" fields, and
|
||||
# every -I include path in "command") use that prefix.
|
||||
#
|
||||
# This annotation job runs on a GitHub-hosted runner where the
|
||||
# workspace root is "/home/runner/work/PX4-Autopilot/PX4-Autopilot".
|
||||
# When clang-tidy-diff invokes clang-tidy, it reads the "directory"
|
||||
# field from compile_commands.json and calls chdir() on it. Since
|
||||
# the AWS-style path does not exist here, clang-tidy crashes with:
|
||||
# LLVM ERROR: Cannot chdir into "/__w/.../build/px4_sitl_default-clang"
|
||||
# and silently produces an empty fixes.yml, so no annotations are posted.
|
||||
#
|
||||
# Fix: rewrite all occurrences of the AWS workspace prefix to the
|
||||
# current runner workspace ($GITHUB_WORKSPACE) before invoking
|
||||
# clang-tidy-diff. Safe because compile_commands.json is a local
|
||||
# scratch file pulled from the artifact; no source file is modified.
|
||||
sed -i "s|/__w/PX4-Autopilot/PX4-Autopilot|${GITHUB_WORKSPACE}|g" \
|
||||
build/px4_sitl_default-clang/compile_commands.json
|
||||
|
||||
mkdir -p clang-tidy-result
|
||||
git diff -U0 origin/${{ github.base_ref }}...HEAD \
|
||||
| clang-tidy-diff-18.py -p1 \
|
||||
-path build/px4_sitl_default-clang \
|
||||
-export-fixes clang-tidy-result/fixes.yml \
|
||||
-j0 || true
|
||||
|
||||
- name: Annotate PR with clang-tidy findings
|
||||
uses: platisd/clang-tidy-pr-comments@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
clang_tidy_fixes: clang-tidy-result/fixes.yml
|
||||
request_changes: true
|
||||
suggestions_per_comment: 10
|
||||
|
||||
6
.github/workflows/commit_checks.yml
vendored
6
.github/workflows/commit_checks.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
sparse-checkout: Tools/ci
|
||||
fetch-depth: 1
|
||||
@ -56,7 +56,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
sparse-checkout: Tools/ci
|
||||
fetch-depth: 1
|
||||
@ -102,7 +102,7 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
if: env.IS_FORK == 'false'
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
sparse-checkout: Tools/ci
|
||||
fetch-depth: 1
|
||||
|
||||
72
.github/workflows/compile_macos.yml
vendored
72
.github/workflows/compile_macos.yml
vendored
@ -19,49 +19,49 @@ concurrency:
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
config: [
|
||||
px4_fmu-v5_default,
|
||||
px4_sitl
|
||||
]
|
||||
steps:
|
||||
- name: install Python 3.10
|
||||
uses: actions/setup-python@v5
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Cache - Restore Homebrew Packages
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/Library/Caches/Homebrew/downloads
|
||||
key: macos-homebrew-${{ runner.arch }}-${{ hashFiles('Tools/setup/macos.sh') }}
|
||||
restore-keys: |
|
||||
macos-homebrew-${{ runner.arch }}-
|
||||
|
||||
- name: Cache - Restore pip Packages
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: ~/Library/Caches/pip
|
||||
key: macos-pip-${{ runner.arch }}-${{ hashFiles('Tools/setup/requirements.txt') }}
|
||||
restore-keys: |
|
||||
macos-pip-${{ runner.arch }}-
|
||||
|
||||
- name: setup
|
||||
run: |
|
||||
./Tools/setup/macos.sh
|
||||
run: ./Tools/setup/macos.sh
|
||||
|
||||
- name: Prepare ccache timestamp
|
||||
id: ccache_cache_timestamp
|
||||
shell: cmake -P {0}
|
||||
run: |
|
||||
string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC)
|
||||
file(APPEND "$ENV{GITHUB_OUTPUT}" "timestamp=${current_date}\n")
|
||||
- name: ccache cache files
|
||||
uses: actions/cache@v4
|
||||
- uses: ./.github/actions/setup-ccache
|
||||
id: ccache
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: macos_${{matrix.config}}-ccache-${{steps.ccache_cache_timestamp.outputs.timestamp}}
|
||||
restore-keys: macos_${{matrix.config}}-ccache-
|
||||
- name: setup ccache
|
||||
run: |
|
||||
mkdir -p ~/.ccache
|
||||
echo "base_dir = ${GITHUB_WORKSPACE}" > ~/.ccache/ccache.conf
|
||||
echo "compression = true" >> ~/.ccache/ccache.conf
|
||||
echo "compression_level = 6" >> ~/.ccache/ccache.conf
|
||||
echo "max_size = 40M" >> ~/.ccache/ccache.conf
|
||||
echo "hash_dir = false" >> ~/.ccache/ccache.conf
|
||||
ccache -s
|
||||
ccache -z
|
||||
cache-key-prefix: ccache-macos
|
||||
max-size: 200M
|
||||
|
||||
- name: make ${{matrix.config}}
|
||||
run: |
|
||||
ccache -z
|
||||
make ${{matrix.config}}
|
||||
ccache -s
|
||||
- name: Build px4_sitl
|
||||
run: make px4_sitl
|
||||
|
||||
- name: Cache - Stats after px4_sitl
|
||||
run: ccache -s
|
||||
|
||||
- name: Build px4_fmu-v5_default
|
||||
run: make px4_fmu-v5_default
|
||||
|
||||
- uses: ./.github/actions/save-ccache
|
||||
if: always()
|
||||
with:
|
||||
cache-primary-key: ${{ steps.ccache.outputs.cache-primary-key }}
|
||||
|
||||
27
.github/workflows/compile_ubuntu.yml
vendored
27
.github/workflows/compile_ubuntu.yml
vendored
@ -29,12 +29,13 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: ['ubuntu:22.04', 'ubuntu:24.04']
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,"image=ubuntu24-full-x64","run-id=${{ github.run_id }}",spot=false]
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,"image=ubuntu24-full-x64","run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
container:
|
||||
image: ${{ matrix.version }}
|
||||
volumes:
|
||||
- /github/workspace:/github/workspace
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
|
||||
- name: Fix git in container
|
||||
run: |
|
||||
@ -47,15 +48,25 @@ jobs:
|
||||
apt update && apt install git -y
|
||||
git config --global --add safe.directory $(realpath .)
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Use AWS regional apt mirror
|
||||
if: startsWith(runner.name, 'runs-on--')
|
||||
run: ./Tools/ci/use_aws_apt_mirror.sh
|
||||
|
||||
- name: Install Deps, Build, and Make Quick Check
|
||||
run: |
|
||||
# we need to install dependencies and build on the same step
|
||||
# given the stateless nature of docker images
|
||||
./Tools/setup/ubuntu.sh
|
||||
make quick_check
|
||||
- name: Install Deps
|
||||
run: ./Tools/setup/ubuntu.sh
|
||||
|
||||
- uses: ./.github/actions/setup-ccache
|
||||
id: ccache
|
||||
with:
|
||||
cache-key-prefix: ccache-ubuntu-${{ matrix.version }}
|
||||
max-size: 200M
|
||||
|
||||
- name: Make Quick Check
|
||||
run: make quick_check
|
||||
|
||||
- uses: ./.github/actions/save-ccache
|
||||
if: always()
|
||||
with:
|
||||
cache-primary-key: ${{ steps.ccache.outputs.cache-primary-key }}
|
||||
|
||||
15
.github/workflows/dev_container.yml
vendored
15
.github/workflows/dev_container.yml
vendored
@ -51,12 +51,11 @@ jobs:
|
||||
meta_labels: ${{ steps.meta.outputs.labels }}
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.inputs.build_ref || github.ref }}
|
||||
fetch-tags: true
|
||||
submodules: false
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
# If manual dispatch, take the user‐provided input
|
||||
- name: Set PX4 Tag Version
|
||||
@ -96,12 +95,11 @@ jobs:
|
||||
runs-on: [runs-on,"runner=4cpu-linux-${{ matrix.runner }}","image=ubuntu24-full-${{ matrix.runner }}","run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.inputs.build_ref || github.ref }}
|
||||
fetch-tags: true
|
||||
submodules: false
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
@ -153,12 +151,11 @@ jobs:
|
||||
(startsWith(github.ref, 'refs/tags/') || (github.event_name == 'workflow_dispatch' && github.event.inputs.deploy_to_registry == 'true'))
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.inputs.build_ref || github.ref }}
|
||||
fetch-tags: true
|
||||
submodules: false
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v4
|
||||
|
||||
53
.github/workflows/docs-orchestrator.yml
vendored
53
.github/workflows/docs-orchestrator.yml
vendored
@ -46,8 +46,8 @@ jobs:
|
||||
source_changed: ${{ steps.changes.outputs.source }}
|
||||
docs_changed: ${{ steps.changes.outputs.docs }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dorny/paths-filter@v3
|
||||
- uses: actions/checkout@v6
|
||||
- uses: dorny/paths-filter@v4
|
||||
id: changes
|
||||
with:
|
||||
filters: |
|
||||
@ -72,20 +72,17 @@ jobs:
|
||||
container:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Git ownership workaround
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- name: Cache Restore - ccache
|
||||
id: cache-ccache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-docs-metadata-${{ github.sha }}
|
||||
@ -104,7 +101,7 @@ jobs:
|
||||
CCACHE_DIR: ~/.ccache
|
||||
|
||||
- name: Cache Save - ccache
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
if: always()
|
||||
with:
|
||||
path: ~/.ccache
|
||||
@ -116,7 +113,7 @@ jobs:
|
||||
CCACHE_DIR: ~/.ccache
|
||||
|
||||
- name: Upload metadata artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: pr-metadata
|
||||
path: docs/
|
||||
@ -134,10 +131,10 @@ jobs:
|
||||
container:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- uses: runs-on/action@v2
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
@ -148,7 +145,7 @@ jobs:
|
||||
|
||||
- name: Cache Restore - ccache
|
||||
id: cache-ccache
|
||||
uses: actions/cache/restore@v4
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-docs-metadata-${{ github.sha }}
|
||||
@ -167,7 +164,7 @@ jobs:
|
||||
CCACHE_DIR: ~/.ccache
|
||||
|
||||
- name: Cache Save - ccache
|
||||
uses: actions/cache/save@v4
|
||||
uses: actions/cache/save@v5
|
||||
if: always()
|
||||
with:
|
||||
path: ~/.ccache
|
||||
@ -216,21 +213,21 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Download metadata artifact
|
||||
if: needs.pr-metadata-regen.result == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: pr-metadata
|
||||
path: docs/
|
||||
|
||||
- name: Get changed doc files
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@v46.0.5
|
||||
uses: tj-actions/changed-files@v47
|
||||
with:
|
||||
json: true
|
||||
write_output_files: true
|
||||
@ -247,7 +244,7 @@ jobs:
|
||||
cat ./logs/prFiles.json
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
@ -301,14 +298,14 @@ jobs:
|
||||
|
||||
- name: Upload pr-comment artifact
|
||||
if: steps.prepare-pr-comment.outputs.prepared == 'true'
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: pr-comment
|
||||
path: pr-comment/
|
||||
retention-days: 1
|
||||
|
||||
- name: Upload link check results
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: link-check-results
|
||||
path: logs/
|
||||
@ -332,16 +329,14 @@ jobs:
|
||||
branchname: ${{ steps.set-branch.outputs.branchname }}
|
||||
releaseversion: ${{ steps.set-version.outputs.releaseversion }}
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Download metadata artifact (PR)
|
||||
if: github.event_name == 'pull_request' && needs.pr-metadata-regen.result == 'success'
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: pr-metadata
|
||||
path: docs/
|
||||
@ -365,7 +360,7 @@ jobs:
|
||||
echo "releaseversion=$version" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
@ -384,7 +379,7 @@ jobs:
|
||||
npm run docs:sitemap
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: px4_docs_build
|
||||
path: docs/.vitepress/dist/
|
||||
@ -406,7 +401,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: px4_docs_build
|
||||
path: ~/_book
|
||||
|
||||
2
.github/workflows/docs_crowdin_download.yml
vendored
2
.github/workflows/docs_crowdin_download.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
||||
lc: [ko, uk, zh-CN] # Target languages https://developer.crowdin.com/language-codes/
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: Debug Environment Variables
|
||||
run: |
|
||||
echo "CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_DOCS_PROJECT_ID }}"
|
||||
|
||||
2
.github/workflows/docs_crowdin_upload.yml
vendored
2
.github/workflows/docs_crowdin_upload.yml
vendored
@ -24,7 +24,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
- name: crowdin push
|
||||
uses: crowdin/github-action@v2
|
||||
with:
|
||||
|
||||
11
.github/workflows/docs_deploy.yml
vendored
11
.github/workflows/docs_deploy.yml
vendored
@ -22,12 +22,11 @@ jobs:
|
||||
build:
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",extras=s3-cache,spot=false]
|
||||
steps:
|
||||
- uses: runs-on/action@v1
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
@ -46,7 +45,7 @@ jobs:
|
||||
|
||||
- name: Upload artifact
|
||||
if: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged) || github.event_name == 'workflow_dispatch' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: px4_docs_build
|
||||
path: docs/.vitepress/dist/
|
||||
@ -59,7 +58,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Download Artifact
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: px4_docs_build
|
||||
path: ~/_book
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
name: EKF Change Indicator
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
# If two events are triggered within a short time in the same PR, cancel the run of the oldest event
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
unit_tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: main test
|
||||
run: |
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
git config --system --add safe.directory '*'
|
||||
make tests TESTFILTER=EKF
|
||||
|
||||
- name: Check if there is a functional change
|
||||
run: git diff --exit-code
|
||||
working-directory: src/modules/ekf2/test/change_indication
|
||||
@ -1,54 +0,0 @@
|
||||
name: EKF Update Change Indicator
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
jobs:
|
||||
unit_tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
|
||||
env:
|
||||
GIT_COMMITTER_EMAIL: bot@px4.io
|
||||
GIT_COMMITTER_NAME: PX4BuildBot
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: main test
|
||||
run: |
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
git config --system --add safe.directory '*'
|
||||
make tests TESTFILTER=EKF
|
||||
|
||||
- name: Check if there exists diff and save result in variable
|
||||
id: diff-check
|
||||
working-directory: src/modules/ekf2/test/change_indication
|
||||
run: |
|
||||
if git diff --quiet; then
|
||||
echo "CHANGE_INDICATED=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "CHANGE_INDICATED=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
- name: auto-commit any changes to change indication
|
||||
if: steps.diff-check.outputs.CHANGE_INDICATED == 'true'
|
||||
uses: stefanzweifel/git-auto-commit-action@v4
|
||||
with:
|
||||
file_pattern: 'src/modules/ekf2/test/change_indication/*.csv'
|
||||
commit_user_name: ${{ env.GIT_COMMITTER_NAME }}
|
||||
commit_user_email: ${{ env.GIT_COMMITTER_EMAIL }}
|
||||
commit_message: |
|
||||
[AUTO COMMIT] update change indication
|
||||
|
||||
See .github/workflows/ekf_update_change_indicator.yml for more details
|
||||
|
||||
- name: if there is a functional change, fail check
|
||||
if: steps.diff-check.outputs.CHANGE_INDICATED == 'true'
|
||||
run: exit 1
|
||||
22
.github/workflows/failsafe_sim.yml
vendored
22
.github/workflows/failsafe_sim.yml
vendored
@ -18,7 +18,7 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@ -32,19 +32,27 @@ jobs:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
options: --privileged --ulimit core=-1 --security-opt seccomp=unconfined
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- name: Install Node v20.18.0
|
||||
uses: actions/setup-node@v4
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 20.18.0
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- name: Cache - Restore Emscripten SDK
|
||||
id: cache-emsdk
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Git ownership workaround
|
||||
run: git config --system --add safe.directory '*'
|
||||
path: _emscripten_sdk
|
||||
key: emsdk-4.0.15
|
||||
|
||||
- name: Install empscripten
|
||||
if: steps.cache-emsdk.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone https://github.com/emscripten-core/emsdk.git _emscripten_sdk
|
||||
cd _emscripten_sdk
|
||||
|
||||
83
.github/workflows/flash_analysis.yml
vendored
83
.github/workflows/flash_analysis.yml
vendored
@ -24,7 +24,7 @@ env:
|
||||
jobs:
|
||||
analyze_flash:
|
||||
name: Analyzing ${{ matrix.target }}
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false]
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
container:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
strategy:
|
||||
@ -36,25 +36,58 @@ jobs:
|
||||
px4_fmu-v6x-bloaty-output: ${{ steps.gen-output.outputs.px4_fmu-v6x-bloaty-output }}
|
||||
px4_fmu-v6x-bloaty-summary-map: ${{ steps.gen-output.outputs.px4_fmu-v6x-bloaty-summary-map }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Git ownership workaround
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- name: Cache - Restore ccache (current)
|
||||
id: cache_current
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-flash-${{ matrix.target }}-current-${{ github.ref_name }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
ccache-flash-${{ matrix.target }}-current-${{ github.ref_name }}-
|
||||
ccache-flash-${{ matrix.target }}-current-
|
||||
|
||||
- name: Cache - Configure ccache
|
||||
run: |
|
||||
mkdir -p ~/.ccache
|
||||
echo "base_dir = ${GITHUB_WORKSPACE}" > ~/.ccache/ccache.conf
|
||||
echo "compression = true" >> ~/.ccache/ccache.conf
|
||||
echo "compression_level = 6" >> ~/.ccache/ccache.conf
|
||||
echo "max_size = 200M" >> ~/.ccache/ccache.conf
|
||||
echo "hash_dir = false" >> ~/.ccache/ccache.conf
|
||||
echo "compiler_check = content" >> ~/.ccache/ccache.conf
|
||||
ccache -s
|
||||
ccache -z
|
||||
|
||||
- name: Build Target
|
||||
run: make ${{ matrix.target }}_flash-analysis
|
||||
|
||||
- name: Store the ELF with the change
|
||||
run: cp ./build/**/*.elf ./with-change.elf
|
||||
|
||||
- name: Cache - Stats after Current Build
|
||||
run: ccache -s
|
||||
|
||||
- name: Cache - Save ccache (current)
|
||||
if: always()
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ steps.cache_current.outputs.cache-primary-key }}
|
||||
|
||||
- name: Clean previous build
|
||||
run: |
|
||||
make clean
|
||||
make distclean
|
||||
make submodulesclean
|
||||
ccache -C
|
||||
|
||||
- name: If it's a PR checkout the base branch
|
||||
if: ${{ github.event.pull_request }}
|
||||
@ -68,12 +101,34 @@ jobs:
|
||||
- name: Update submodules
|
||||
run: make submodulesupdate
|
||||
|
||||
- name: Cache - Restore ccache (baseline)
|
||||
id: cache_baseline
|
||||
uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-flash-${{ matrix.target }}-baseline-${{ github.sha }}
|
||||
restore-keys: |
|
||||
ccache-flash-${{ matrix.target }}-baseline-
|
||||
|
||||
- name: Cache - Reset ccache stats
|
||||
run: ccache -z
|
||||
|
||||
- name: Build
|
||||
run: make ${{ matrix.target }}_flash-analysis
|
||||
|
||||
- name: Store the ELF before the change
|
||||
run: cp ./build/**/*.elf ./before-change.elf
|
||||
|
||||
- name: Cache - Stats after Baseline Build
|
||||
run: ccache -s
|
||||
|
||||
- name: Cache - Save ccache (baseline)
|
||||
if: always()
|
||||
uses: actions/cache/save@v5
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ${{ steps.cache_baseline.outputs.cache-primary-key }}
|
||||
|
||||
- name: bloaty-action
|
||||
uses: PX4/bloaty-action@v1.0.0
|
||||
id: bloaty-step
|
||||
@ -105,7 +160,7 @@ jobs:
|
||||
if: github.event.pull_request
|
||||
steps:
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v3
|
||||
uses: peter-evans/find-comment@v4
|
||||
id: fc
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
@ -138,19 +193,19 @@ jobs:
|
||||
<!-- pr-comment-poster:flash-analysis -->
|
||||
## 🔎 FLASH Analysis
|
||||
<details>
|
||||
<summary>px4_fmu-v5x [Total VM Diff: ${{ env.V5X-SUMMARY-MAP-ABS }} byte (${{ env.V5X-SUMMARY-MAP-PERC}} %)]</summary>
|
||||
<summary>px4_fmu-v5x [Total VM Diff: ${{ env.V5X-SUMMARY-MAP-ABS }} byte (${{ env.V5X-SUMMARY-MAP-PERC}} %)]</summary>
|
||||
|
||||
```
|
||||
${{ needs.analyze_flash.outputs.px4_fmu-v5x-bloaty-output }}
|
||||
```
|
||||
```
|
||||
${{ needs.analyze_flash.outputs.px4_fmu-v5x-bloaty-output }}
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>px4_fmu-v6x [Total VM Diff: ${{ env.V6X-SUMMARY-MAP-ABS }} byte (${{ env.V6X-SUMMARY-MAP-PERC }} %)]</summary>
|
||||
<summary>px4_fmu-v6x [Total VM Diff: ${{ env.V6X-SUMMARY-MAP-ABS }} byte (${{ env.V6X-SUMMARY-MAP-PERC }} %)]</summary>
|
||||
|
||||
```
|
||||
${{ needs.analyze_flash.outputs.px4_fmu-v6x-bloaty-output }}
|
||||
```
|
||||
```
|
||||
${{ needs.analyze_flash.outputs.px4_fmu-v6x-bloaty-output }}
|
||||
```
|
||||
</details>
|
||||
|
||||
**Updated: _${{ steps.bt.outputs.timestamp }}_**
|
||||
@ -163,7 +218,7 @@ jobs:
|
||||
env.V5X-SUMMARY-MAP-ABS <= fromJSON(env.MIN_FLASH_NEG_DIFF_FOR_COMMENT) ||
|
||||
env.V6X-SUMMARY-MAP-ABS >= fromJSON(env.MIN_FLASH_POS_DIFF_FOR_COMMENT) ||
|
||||
env.V6X-SUMMARY-MAP-ABS <= fromJSON(env.MIN_FLASH_NEG_DIFF_FOR_COMMENT)
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: pr-comment
|
||||
path: pr-comment/
|
||||
|
||||
29
.github/workflows/fuzzing.yml
vendored
29
.github/workflows/fuzzing.yml
vendored
@ -12,19 +12,24 @@ env:
|
||||
|
||||
jobs:
|
||||
Fuzzing:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
container:
|
||||
image: px4io/px4-dev:v1.16.0-rc2-4-gb67c65bfe6
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
steps:
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
apt update && apt install -y clang
|
||||
- uses: runs-on/action@v2
|
||||
|
||||
- name: Fix git in Container
|
||||
run: |
|
||||
git config --global --add safe.directory $(realpath .)
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- uses: ./.github/actions/setup-ccache
|
||||
id: ccache
|
||||
with:
|
||||
cache-key-prefix: ccache-sitl
|
||||
max-size: 300M
|
||||
|
||||
- name: Build and Run Fuzz Tests
|
||||
run: |
|
||||
@ -38,7 +43,11 @@ jobs:
|
||||
./Tools/ci/run_fuzz_tests.sh $fuzz_binary 15m
|
||||
done
|
||||
|
||||
# Create a github issue in case of a failure
|
||||
- uses: ./.github/actions/save-ccache
|
||||
if: always()
|
||||
with:
|
||||
cache-primary-key: ${{ steps.ccache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Create Issue
|
||||
if: ${{ failure() }}
|
||||
uses: JasonEtco/create-an-issue@v2
|
||||
|
||||
22
.github/workflows/itcm_check.yml
vendored
22
.github/workflows/itcm_check.yml
vendored
@ -22,7 +22,7 @@ concurrency:
|
||||
jobs:
|
||||
check_itcm:
|
||||
name: Checking ${{ matrix.target }}
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false]
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
container:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
strategy:
|
||||
@ -46,14 +46,19 @@ jobs:
|
||||
boards/nxp/mr-tropic/nuttx-config/scripts/itcm_functions_includes.ld
|
||||
boards/nxp/mr-tropic/nuttx-config/scripts/itcm_static_functions.ld
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Git ownership workaround
|
||||
fetch-depth: 1
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- uses: ./.github/actions/setup-ccache
|
||||
id: ccache
|
||||
with:
|
||||
cache-key-prefix: ccache-itcm-${{ matrix.target }}
|
||||
max-size: 200M
|
||||
|
||||
- name: Build Target
|
||||
run: make ${{ matrix.target }}
|
||||
|
||||
@ -65,3 +70,8 @@ jobs:
|
||||
|
||||
- name: Execute the itcm-check
|
||||
run: python3 Tools/itcm_check.py --elf-file built.elf --script-files ${{ matrix.scripts }}
|
||||
|
||||
- uses: ./.github/actions/save-ccache
|
||||
if: always()
|
||||
with:
|
||||
cache-primary-key: ${{ steps.ccache.outputs.cache-primary-key }}
|
||||
|
||||
2
.github/workflows/label.yml
vendored
2
.github/workflows/label.yml
vendored
@ -16,6 +16,6 @@ jobs:
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
- uses: actions/labeler@v6
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
45
.github/workflows/mavros_mission_tests.yml
vendored
45
.github/workflows/mavros_mission_tests.yml
vendored
@ -1,45 +0,0 @@
|
||||
name: MAVROS Mission Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build SITL and Run Tests (inside old ROS container)
|
||||
run: |
|
||||
docker run --rm \
|
||||
-v "${GITHUB_WORKSPACE}:/workspace" \
|
||||
-w /workspace \
|
||||
px4io/px4-dev-ros-melodic:2021-09-08 \
|
||||
bash -c '
|
||||
git config --global --add safe.directory /workspace
|
||||
PX4_SBOM_DISABLE=1 make px4_sitl_default
|
||||
PX4_SBOM_DISABLE=1 make px4_sitl_default sitl_gazebo-classic
|
||||
./test/rostest_px4_run.sh \
|
||||
mavros_posix_test_mission.test \
|
||||
mission:=MC_mission_box \
|
||||
vehicle:=iris
|
||||
'
|
||||
44
.github/workflows/mavros_offboard_tests.yml
vendored
44
.github/workflows/mavros_offboard_tests.yml
vendored
@ -1,44 +0,0 @@
|
||||
name: MAVROS Offboard Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build SITL and Run Tests (inside old ROS container)
|
||||
run: |
|
||||
docker run --rm \
|
||||
-v "${GITHUB_WORKSPACE}:/workspace" \
|
||||
-w /workspace \
|
||||
px4io/px4-dev-ros-melodic:2021-09-08 \
|
||||
bash -c '
|
||||
git config --global --add safe.directory /workspace
|
||||
PX4_SBOM_DISABLE=1 make px4_sitl_default
|
||||
PX4_SBOM_DISABLE=1 make px4_sitl_default sitl_gazebo-classic
|
||||
./test/rostest_px4_run.sh \
|
||||
mavros_posix_tests_offboard_posctl.test \
|
||||
vehicle:=iris
|
||||
'
|
||||
73
.github/workflows/mavros_tests.yml
vendored
Normal file
73
.github/workflows/mavros_tests.yml
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
name: MAVROS Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: "MAVROS ${{ matrix.config.name }}"
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
permissions:
|
||||
contents: read
|
||||
container:
|
||||
image: px4io/px4-dev-ros-noetic:2021-09-08
|
||||
env:
|
||||
PX4_SBOM_DISABLE: 1
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- {name: "Mission", test_file: "mavros_posix_test_mission.test", params: "mission:=MC_mission_box vehicle:=iris"}
|
||||
- {name: "Offboard", test_file: "mavros_posix_tests_offboard_posctl.test", params: "vehicle:=iris"}
|
||||
steps:
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- name: Setup - Install Python Test Dependencies
|
||||
run: pip3 install -r Tools/setup/requirements.txt
|
||||
|
||||
- uses: ./.github/actions/setup-ccache
|
||||
id: ccache
|
||||
with:
|
||||
cache-key-prefix: ccache-sitl-gazebo-classic
|
||||
max-size: 350M
|
||||
|
||||
- uses: ./.github/actions/build-gazebo-sitl
|
||||
|
||||
- name: Test - MAVROS ${{ matrix.config.name }}
|
||||
run: |
|
||||
./test/rostest_px4_run.sh \
|
||||
${{ matrix.config.test_file }} \
|
||||
${{ matrix.config.params }}
|
||||
timeout-minutes: 10
|
||||
|
||||
- uses: ./.github/actions/save-ccache
|
||||
if: always()
|
||||
with:
|
||||
cache-primary-key: ${{ steps.ccache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Upload - Failed Test Logs
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: failed-mavros-${{ matrix.config.name }}-logs.zip
|
||||
path: |
|
||||
logs/**/**/**/*.log
|
||||
logs/**/**/**/*.ulg
|
||||
46
.github/workflows/nuttx_env_config.yml
vendored
46
.github/workflows/nuttx_env_config.yml
vendored
@ -1,46 +0,0 @@
|
||||
name: Nuttx Target with extra env config
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container:
|
||||
image: ghcr.io/px4/px4-dev:v1.17.0-rc2
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- px4_fmu-v5_default
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build PX4 and Run Test [${{ matrix.config }}]
|
||||
run: |
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
git config --system --add safe.directory '*'
|
||||
export PX4_EXTRA_NUTTX_CONFIG='CONFIG_NSH_LOGIN_PASSWORD="test";CONFIG_NSH_CONSOLE_LOGIN=y'
|
||||
echo "PX4_EXTRA_NUTTX_CONFIG: $PX4_EXTRA_NUTTX_CONFIG"
|
||||
|
||||
make ${{ matrix.config }} nuttx_context
|
||||
|
||||
echo "Check that the config option is set"
|
||||
grep CONFIG_NSH_LOGIN_PASSWORD build/${{ matrix.config }}/NuttX/nuttx/.config
|
||||
26
.github/workflows/pr-comment-poster.yml
vendored
26
.github/workflows/pr-comment-poster.yml
vendored
@ -40,12 +40,13 @@ name: PR Comment Poster
|
||||
# manifest and body itself, NOT copied fork-controlled content into
|
||||
# them. Producer workflows are responsible for that.
|
||||
#
|
||||
# 6. `actions/checkout@v4` below uses NO ref (so it pulls the base branch,
|
||||
# 6. `actions/checkout@v6` below uses NO ref (so it pulls the base branch,
|
||||
# the default-branch commit this workflow file was loaded from) AND uses
|
||||
# sparse-checkout to materialize ONLY Tools/ci/pr-comment-poster.py. The
|
||||
# rest of the repo never touches the workspace. This is safe: the only
|
||||
# file the job executes is a base-repo Python script that was reviewed
|
||||
# through normal code review, never anything from the PR.
|
||||
# sparse-checkout to materialize ONLY Tools/ci/pr-comment-poster.py and
|
||||
# its stdlib-only helper module Tools/ci/_github_helpers.py. The rest of
|
||||
# the repo never touches the workspace. This is safe: the only files the
|
||||
# job executes are base-repo Python scripts that were reviewed through
|
||||
# normal code review, never anything from the PR.
|
||||
#
|
||||
# ==============================================================================
|
||||
# ARTIFACT CONTRACT
|
||||
@ -91,22 +92,29 @@ jobs:
|
||||
post:
|
||||
name: Post PR Comment
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.workflow_run.conclusion != 'cancelled'
|
||||
# Only run for pull_request producer runs. Push-to-main and other
|
||||
# non-PR triggers would have no comment to post, and silently no-oping
|
||||
# inside the script made it look like the poster was broken. Gating at
|
||||
# the job level surfaces those as a clean "Skipped" in the UI instead.
|
||||
if: >-
|
||||
github.event.workflow_run.conclusion != 'cancelled'
|
||||
&& github.event.workflow_run.event == 'pull_request'
|
||||
steps:
|
||||
# Checkout runs first so the poster script is available AND so that
|
||||
# actions/checkout@v4's default clean step does not delete the artifact
|
||||
# actions/checkout@v6's default clean step does not delete the artifact
|
||||
# zip that the next step writes into the workspace. Sparse-checkout
|
||||
# restricts the materialized tree to just the poster script.
|
||||
- name: Checkout poster script only
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
sparse-checkout: |
|
||||
Tools/ci/pr-comment-poster.py
|
||||
Tools/ci/_github_helpers.py
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Download pr-comment artifact
|
||||
id: download
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
|
||||
177
.github/workflows/pr-review-poster.yml
vendored
Normal file
177
.github/workflows/pr-review-poster.yml
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
name: PR Review Poster
|
||||
|
||||
# Generic PR review-comment poster. Sibling of "PR Comment Poster": that
|
||||
# workflow posts sticky issue-style comments, this one posts line-anchored
|
||||
# review comments on the "Files changed" tab. Any analysis workflow that
|
||||
# wants to flag specific lines can produce a `pr-review` artifact and this
|
||||
# workflow will dismiss any stale matching review and post a fresh one.
|
||||
# Designed so analysis jobs running on untrusted fork PRs can still get
|
||||
# their inline annotations posted back to the PR.
|
||||
#
|
||||
# ==============================================================================
|
||||
# SECURITY INVARIANTS
|
||||
# ==============================================================================
|
||||
# This workflow runs on `workflow_run` which means it runs in the BASE REPO
|
||||
# context with a WRITE token, even when the triggering PR comes from a fork.
|
||||
# That is the entire reason it exists, and also the reason it is a loaded
|
||||
# footgun. Anyone modifying this file MUST preserve the following invariants:
|
||||
#
|
||||
# 1. NEVER check out PR code. No `actions/checkout` with a ref. No git clone
|
||||
# of a fork branch. No execution of scripts from the downloaded artifact.
|
||||
# The ONLY things read from the artifact are `manifest.json` and
|
||||
# `comments.json`, and both are treated as opaque data (JSON parsed by
|
||||
# the poster script and the comment fields posted via the GitHub API).
|
||||
#
|
||||
# 2. `pr_number` is validated to be a positive integer before use.
|
||||
# `marker` is validated to be printable ASCII only before use.
|
||||
# `commit_sha` is validated to be 40 lowercase hex characters.
|
||||
# `event` is validated against an allowlist of `COMMENT` and
|
||||
# `REQUEST_CHANGES`. `APPROVE` is intentionally forbidden so a bot
|
||||
# cannot approve a pull request. Validation happens inside
|
||||
# Tools/ci/pr-review-poster.py which is checked out from the base
|
||||
# branch, not from the artifact.
|
||||
#
|
||||
# 3. Comment bodies and the optional summary are passed to the GitHub API
|
||||
# as JSON fields, never interpolated into a shell command string.
|
||||
#
|
||||
# 4. This workflow file lives on the default branch. `workflow_run` only
|
||||
# loads workflow files from the default branch, so a fork cannot modify
|
||||
# THIS file as part of a PR. The fork CAN cause this workflow to fire
|
||||
# by triggering a producer workflow that uploads a `pr-review`
|
||||
# artifact. That is intended.
|
||||
#
|
||||
# 5. The artifact-name filter (`pr-review`) is the only gate on which
|
||||
# workflow runs get processed. Any workflow in this repo that uploads
|
||||
# an artifact named `pr-review` is trusted to have written the
|
||||
# manifest and comments itself, NOT copied fork-controlled content
|
||||
# into them. Producer workflows are responsible for that.
|
||||
#
|
||||
# 6. `actions/checkout@v6` below uses NO ref (so it pulls the base branch,
|
||||
# the default-branch commit this workflow file was loaded from) AND
|
||||
# uses sparse-checkout to materialize ONLY
|
||||
# Tools/ci/pr-review-poster.py and its stdlib-only helper module
|
||||
# Tools/ci/_github_helpers.py. The rest of the repo never touches the
|
||||
# workspace. This is safe: the only files the job executes are
|
||||
# base-repo Python scripts that were reviewed through normal code
|
||||
# review, never anything from the PR.
|
||||
#
|
||||
# 7. Stale-review dismissal is restricted to reviews whose AUTHOR is
|
||||
# `github-actions[bot]` AND whose body contains the producer's
|
||||
# marker. A fork PR cannot impersonate the bot login, and cannot
|
||||
# inject the marker into a human reviewer's body without API
|
||||
# access. Both filters together prevent the poster from ever
|
||||
# dismissing a human review.
|
||||
#
|
||||
# ==============================================================================
|
||||
# ARTIFACT CONTRACT
|
||||
# ==============================================================================
|
||||
# Producers upload an artifact named exactly `pr-review` containing:
|
||||
#
|
||||
# manifest.json:
|
||||
# {
|
||||
# "pr_number": 12345, // required, int > 0
|
||||
# "marker": "<!-- pr-review-poster:clang-tidy -->", // required, printable ASCII
|
||||
# "event": "REQUEST_CHANGES", // required, "COMMENT" | "REQUEST_CHANGES"
|
||||
# "commit_sha": "0123456789abcdef0123456789abcdef01234567", // required, 40 hex chars
|
||||
# "summary": "Optional review summary text" // optional
|
||||
# }
|
||||
#
|
||||
# comments.json: JSON array of line-anchored review comment objects:
|
||||
# [
|
||||
# {"path": "src/foo.cpp", "line": 42, "side": "RIGHT", "body": "..."},
|
||||
# {"path": "src/bar.hpp", "start_line": 10, "line": 15,
|
||||
# "side": "RIGHT", "start_side": "RIGHT", "body": "..."}
|
||||
# ]
|
||||
#
|
||||
# The `marker` string is used to find an existing matching review to
|
||||
# dismiss before posting a new one. It MUST be unique per producer (e.g.
|
||||
# include the producer name).
|
||||
#
|
||||
# Producers MUST write `pr_number` and `commit_sha` from their own
|
||||
# workflow context (`github.event.pull_request.number` and
|
||||
# `github.event.pull_request.head.sha`) and MUST NOT read either from any
|
||||
# fork-controlled source.
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
# Producers that may upload a `pr-review` artifact. When a new
|
||||
# producer is wired up, add its workflow name here. Runs of workflows
|
||||
# not in this list will never trigger the poster. Every run of a
|
||||
# listed workflow will trigger the poster, which will no-op if no
|
||||
# `pr-review` artifact exists.
|
||||
workflows:
|
||||
- "Static Analysis"
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
post:
|
||||
name: Post PR Review
|
||||
runs-on: ubuntu-latest
|
||||
# Only run for pull_request producer runs. Push-to-main and other
|
||||
# non-PR triggers have no review to post, so gating at the job level
|
||||
# surfaces those as a clean "Skipped" in the UI instead of a
|
||||
# silent no-op buried inside the script.
|
||||
if: >-
|
||||
github.event.workflow_run.conclusion != 'cancelled'
|
||||
&& github.event.workflow_run.event == 'pull_request'
|
||||
steps:
|
||||
# Checkout runs first so the poster scripts are available AND so
|
||||
# that actions/checkout@v6's default clean step does not delete the
|
||||
# artifact zip that the next step writes into the workspace.
|
||||
# Sparse-checkout restricts the materialized tree to just the
|
||||
# poster script and its stdlib helper module.
|
||||
- name: Checkout poster script only
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
sparse-checkout: |
|
||||
Tools/ci/pr-review-poster.py
|
||||
Tools/ci/_github_helpers.py
|
||||
sparse-checkout-cone-mode: false
|
||||
|
||||
- name: Download pr-review artifact
|
||||
id: download
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: context.payload.workflow_run.id,
|
||||
});
|
||||
const match = artifacts.data.artifacts.find(a => a.name === 'pr-review');
|
||||
if (!match) {
|
||||
core.info('No pr-review artifact on this run; nothing to post.');
|
||||
core.setOutput('found', 'false');
|
||||
return;
|
||||
}
|
||||
const download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: match.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
const fs = require('fs');
|
||||
fs.writeFileSync('pr-review.zip', Buffer.from(download.data));
|
||||
core.setOutput('found', 'true');
|
||||
|
||||
- name: Unpack artifact
|
||||
if: steps.download.outputs.found == 'true'
|
||||
run: |
|
||||
mkdir -p pr-review
|
||||
unzip -q pr-review.zip -d pr-review
|
||||
|
||||
- name: Validate artifact
|
||||
if: steps.download.outputs.found == 'true'
|
||||
run: python3 Tools/ci/pr-review-poster.py validate pr-review
|
||||
|
||||
- name: Post PR review
|
||||
if: steps.download.outputs.found == 'true'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: python3 Tools/ci/pr-review-poster.py post pr-review
|
||||
19
.github/workflows/python_checks.yml
vendored
19
.github/workflows/python_checks.yml
vendored
@ -14,20 +14,23 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: [runs-on,runner=1cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}"]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install Python3
|
||||
run: sudo apt-get install python3 python3-setuptools python3-pip -y
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install tools
|
||||
run: python3 -m pip install mypy types-requests flake8 --break-system-packages
|
||||
run: pip install mypy types-requests flake8
|
||||
|
||||
- name: Check MAVSDK test scripts with mypy
|
||||
run: $HOME/.local/bin/mypy --strict test/mavsdk_tests/*.py
|
||||
run: mypy --strict test/mavsdk_tests/*.py
|
||||
|
||||
- name: Check MAVSDK test scripts with flake8
|
||||
run: $HOME/.local/bin/flake8 test/mavsdk_tests/*.py
|
||||
run: flake8 test/mavsdk_tests/*.py
|
||||
|
||||
81
.github/workflows/ros_integration_tests.yml
vendored
81
.github/workflows/ros_integration_tests.yml
vendored
@ -23,16 +23,18 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu22-full-x64,"run-id=${{ github.run_id }}",spot=false]
|
||||
runs-on: [runs-on,runner=8cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
container:
|
||||
image: px4io/px4-dev-ros2-galactic:2021-09-08
|
||||
options: --privileged --ulimit core=-1 --security-opt seccomp=unconfined
|
||||
env:
|
||||
PX4_SBOM_DISABLE: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Git Ownership Workaround
|
||||
fetch-depth: 1
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- name: Update ROS Keys
|
||||
@ -45,30 +47,21 @@ jobs:
|
||||
run: |
|
||||
apt update && apt install -y gazebo11 libgazebo11-dev gstreamer1.0-plugins-bad gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly libgstreamer-plugins-base1.0-dev
|
||||
|
||||
- name: Prepare ccache timestamp
|
||||
id: ccache_cache_timestamp
|
||||
shell: cmake -P {0}
|
||||
run: |
|
||||
string(TIMESTAMP current_date "%Y-%m-%d-%H;%M;%S" UTC)
|
||||
message("::set-output name=timestamp::${current_date}")
|
||||
- name: ccache cache files
|
||||
uses: actions/cache@v4
|
||||
- uses: ./.github/actions/setup-ccache
|
||||
id: ccache
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ros_integration_tests-${{matrix.config.build_type}}-ccache-${{steps.ccache_cache_timestamp.outputs.timestamp}}
|
||||
restore-keys: ros_integration_tests-${{matrix.config.build_type}}-ccache-
|
||||
- name: setup ccache
|
||||
run: |
|
||||
mkdir -p ~/.ccache
|
||||
echo "base_dir = ${GITHUB_WORKSPACE}" > ~/.ccache/ccache.conf
|
||||
echo "compression = true" >> ~/.ccache/ccache.conf
|
||||
echo "compression_level = 6" >> ~/.ccache/ccache.conf
|
||||
echo "max_size = 300M" >> ~/.ccache/ccache.conf
|
||||
echo "hash_dir = false" >> ~/.ccache/ccache.conf
|
||||
ccache -s
|
||||
ccache -z
|
||||
cache-key-prefix: ccache-ros-integration
|
||||
max-size: 400M
|
||||
|
||||
- name: Get and build micro-xrce-dds-agent
|
||||
- name: Cache - Restore Micro-XRCE-DDS Agent
|
||||
id: cache-xrce-agent
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: /opt/Micro-XRCE-DDS-Agent
|
||||
key: xrce-agent-v2.2.1-fastdds-2.8.2-galactic-2021-09-08
|
||||
|
||||
- name: Build - Micro-XRCE-DDS Agent (v2.2.1)
|
||||
if: steps.cache-xrce-agent.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cd /opt
|
||||
git clone --recursive https://github.com/eProsima/Micro-XRCE-DDS-Agent.git
|
||||
@ -79,10 +72,18 @@ jobs:
|
||||
cd build
|
||||
cmake ..
|
||||
make -j2
|
||||
- name: ccache post-run micro-xrce-dds-agent
|
||||
run: ccache -s
|
||||
|
||||
- name: Get and build the ros2 interface library
|
||||
- name: Cache - Restore PX4 ROS 2 Interface Library Workspace
|
||||
id: cache-px4-ros2-ws
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: /opt/px4_ws
|
||||
# Bump 'v1' when the cached workspace layout changes in a way
|
||||
# that is not captured by the message/service hash below.
|
||||
key: px4-ros2-ws-v1-galactic-2021-09-08-${{ hashFiles('msg/*.msg', 'msg/versioned/*.msg', 'srv/*.srv') }}
|
||||
|
||||
- name: Build - PX4 ROS 2 Interface Library
|
||||
if: steps.cache-px4-ros2-ws.outputs.cache-hit != 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
PX4_DIR="$(pwd)"
|
||||
@ -108,19 +109,8 @@ jobs:
|
||||
"${PX4_DIR}/Tools/copy_to_ros_ws.sh" "$(pwd)"
|
||||
rm -rf src/translation_node src/px4_msgs_old
|
||||
colcon build --symlink-install
|
||||
- name: ccache post-run ros workspace
|
||||
run: ccache -s
|
||||
|
||||
- name: Build PX4
|
||||
env:
|
||||
PX4_SBOM_DISABLE: 1
|
||||
run: make px4_sitl_default
|
||||
- name: ccache post-run px4/firmware
|
||||
run: ccache -s
|
||||
- name: Build SITL Gazebo
|
||||
run: make px4_sitl_default sitl_gazebo-classic
|
||||
- name: ccache post-run sitl_gazebo-classic
|
||||
run: ccache -s
|
||||
- uses: ./.github/actions/build-gazebo-sitl
|
||||
|
||||
- name: Core dump settings
|
||||
run: |
|
||||
@ -135,9 +125,14 @@ jobs:
|
||||
test/ros_test_runner.py --verbose --model iris --force-color
|
||||
timeout-minutes: 45
|
||||
|
||||
- uses: ./.github/actions/save-ccache
|
||||
if: always()
|
||||
with:
|
||||
cache-primary-key: ${{ steps.ccache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Upload failed logs
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: failed-logs.zip
|
||||
path: |
|
||||
|
||||
45
.github/workflows/ros_translation_node.yml
vendored
45
.github/workflows/ros_translation_node.yml
vendored
@ -10,6 +10,9 @@ on:
|
||||
- '**'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@ -20,8 +23,8 @@ concurrency:
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
name: Build and test
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false]
|
||||
name: Build and test [${{ matrix.config.ros_version }}]
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@ -29,33 +32,43 @@ jobs:
|
||||
- {ros_version: "humble", ubuntu: "jammy"}
|
||||
- {ros_version: "jazzy", ubuntu: "noble"}
|
||||
container:
|
||||
image: rostooling/setup-ros-docker:ubuntu-${{ matrix.config.ubuntu }}-latest
|
||||
image: ros:${{ matrix.config.ros_version }}-ros-base-${{ matrix.config.ubuntu }}
|
||||
steps:
|
||||
- name: Setup ROS 2 (${{ matrix.config.ros_version }})
|
||||
uses: ros-tooling/setup-ros@v0.7
|
||||
with:
|
||||
required-ros-distributions: ${{ matrix.config.ros_version }}
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
# Workaround for https://github.com/actions/runner/issues/2033
|
||||
- name: ownership workaround
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- uses: ./.github/actions/setup-ccache
|
||||
id: ccache
|
||||
with:
|
||||
cache-key-prefix: ccache-ros-translation-${{ matrix.config.ros_version }}
|
||||
max-size: 150M
|
||||
base-dir: /ros_ws
|
||||
install-ccache: 'true'
|
||||
|
||||
- name: Check .msg file versioning
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
./Tools/ci/check_msg_versioning.sh ${{ github.event.pull_request.base.sha }} ${{github.event.pull_request.head.sha}}
|
||||
|
||||
- name: Build and test
|
||||
- name: Build - Translation Node
|
||||
run: |
|
||||
ros_ws=/ros_ws
|
||||
mkdir -p $ros_ws/src
|
||||
./Tools/copy_to_ros_ws.sh $ros_ws
|
||||
cd $ros_ws
|
||||
source /opt/ros/${{ matrix.config.ros_version }}/setup.sh
|
||||
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release --symlink-install --event-handlers=console_cohesion+
|
||||
source ./install/setup.sh
|
||||
./build/translation_node/translation_node_unit_tests
|
||||
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_C_COMPILER_LAUNCHER=ccache --symlink-install --event-handlers=console_cohesion+
|
||||
|
||||
- name: Test - Translation Node Unit Tests
|
||||
run: |
|
||||
source /ros_ws/install/setup.sh
|
||||
/ros_ws/build/translation_node/translation_node_unit_tests
|
||||
|
||||
- uses: ./.github/actions/save-ccache
|
||||
if: always()
|
||||
with:
|
||||
cache-primary-key: ${{ steps.ccache.outputs.cache-primary-key }}
|
||||
|
||||
2
.github/workflows/sbom_license_check.yml
vendored
2
.github/workflows/sbom_license_check.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
submodules: false
|
||||
|
||||
4
.github/workflows/sbom_monthly_audit.yml
vendored
4
.github/workflows/sbom_monthly_audit.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.branch || github.ref }}
|
||||
fetch-depth: 1
|
||||
@ -51,7 +51,7 @@ jobs:
|
||||
|
||||
- name: Create issue if problems found
|
||||
if: steps.check.outputs.has_issues == 'true'
|
||||
uses: actions/github-script@v7
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
|
||||
69
.github/workflows/sitl_tests.yml
vendored
69
.github/workflows/sitl_tests.yml
vendored
@ -24,7 +24,7 @@ concurrency:
|
||||
jobs:
|
||||
build:
|
||||
name: Testing PX4 ${{ matrix.config.model }}
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu22-full-x64,"run-id=${{ github.run_id }}",spot=false]
|
||||
runs-on: [runs-on,runner=8cpu-linux-x64,image=ubuntu24-full-x64,"run-id=${{ github.run_id }}",spot=false,extras=s3-cache]
|
||||
container:
|
||||
image: px4io/px4-dev-simulation-focal:2021-09-08
|
||||
options: --privileged --ulimit core=-1 --security-opt seccomp=unconfined
|
||||
@ -37,53 +37,25 @@ jobs:
|
||||
# transitions). Re-enable once the test infrastructure is stabilized.
|
||||
# - {model: "tailsitter" , latitude: "29.660316", longitude: "-82.316658", altitude: "30", build_type: "RelWithDebInfo" } # Florida
|
||||
# - {model: "standard_vtol", latitude: "47.397742", longitude: "8.545594", altitude: "488", build_type: "Coverage" } # Zurich
|
||||
env:
|
||||
PX4_CMAKE_BUILD_TYPE: ${{ matrix.config.build_type }}
|
||||
PX4_SBOM_DISABLE: 1
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: runs-on/action@v2
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Git Ownership Workaround
|
||||
fetch-depth: 1
|
||||
- name: Configure Git Safe Directory
|
||||
run: git config --system --add safe.directory '*'
|
||||
|
||||
- id: set-timestamp
|
||||
name: Set timestamp for cache
|
||||
run: echo "::set-output name=timestamp::$(date +"%Y%m%d%H%M%S")"
|
||||
|
||||
- name: Cache Key Config
|
||||
uses: actions/cache@v4
|
||||
- uses: ./.github/actions/setup-ccache
|
||||
id: ccache
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: sitl-ccache-${{ steps.set-timestamp.outputs.timestamp }}
|
||||
restore-keys: sitl-ccache-${{ steps.set-timestamp.outputs.timestamp }}
|
||||
cache-key-prefix: ccache-sitl-gazebo-classic
|
||||
max-size: 350M
|
||||
|
||||
- name: Cache Conf Config
|
||||
run: |
|
||||
mkdir -p ~/.ccache
|
||||
echo "base_dir = ${GITHUB_WORKSPACE}" > ~/.ccache/ccache.conf
|
||||
echo "compression = true" >> ~/.ccache/ccache.conf
|
||||
echo "compression_level = 6" >> ~/.ccache/ccache.conf
|
||||
echo "max_size = 120M" >> ~/.ccache/ccache.conf
|
||||
echo "hash_dir = false" >> ~/.ccache/ccache.conf
|
||||
ccache -s
|
||||
ccache -z
|
||||
|
||||
- name: Build PX4
|
||||
env:
|
||||
PX4_CMAKE_BUILD_TYPE: ${{matrix.config.build_type}}
|
||||
PX4_SBOM_DISABLE: 1
|
||||
run: make px4_sitl_default
|
||||
|
||||
- name: Cache Post-Run [px4_sitl_default]
|
||||
run: ccache -s
|
||||
|
||||
- name: Build SITL Gazebo
|
||||
env:
|
||||
PX4_CMAKE_BUILD_TYPE: ${{matrix.config.build_type}}
|
||||
run: make px4_sitl_default sitl_gazebo-classic
|
||||
|
||||
- name: Cache Post-Run [sitl_gazebo-classic]
|
||||
run: ccache -s
|
||||
- uses: ./.github/actions/build-gazebo-sitl
|
||||
|
||||
- name: Download MAVSDK
|
||||
run: wget "https://github.com/mavlink/MAVSDK/releases/download/v$(cat test/mavsdk_tests/MAVSDK_VERSION)/libmavsdk-dev_$(cat test/mavsdk_tests/MAVSDK_VERSION)_ubuntu20.04_amd64.deb"
|
||||
@ -96,19 +68,19 @@ jobs:
|
||||
PX4_HOME_LAT: ${{matrix.config.latitude}}
|
||||
PX4_HOME_LON: ${{matrix.config.longitude}}
|
||||
PX4_HOME_ALT: ${{matrix.config.altitude}}
|
||||
PX4_CMAKE_BUILD_TYPE: ${{matrix.config.build_type}}
|
||||
run: |
|
||||
export
|
||||
ulimit -a
|
||||
|
||||
- name: Build PX4 / MAVSDK tests
|
||||
env:
|
||||
PX4_CMAKE_BUILD_TYPE: ${{matrix.config.build_type}}
|
||||
DONT_RUN: 1
|
||||
run: make px4_sitl_default sitl_gazebo-classic mavsdk_tests
|
||||
|
||||
- name: Cache Post-Run [px4_sitl_default sitl_gazebo-classic mavsdk_tests]
|
||||
run: ccache -s
|
||||
- uses: ./.github/actions/save-ccache
|
||||
if: always()
|
||||
with:
|
||||
cache-primary-key: ${{ steps.ccache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Core Dump Settings
|
||||
run: |
|
||||
@ -120,13 +92,12 @@ jobs:
|
||||
PX4_HOME_LAT: ${{matrix.config.latitude}}
|
||||
PX4_HOME_LON: ${{matrix.config.longitude}}
|
||||
PX4_HOME_ALT: ${{matrix.config.altitude}}
|
||||
PX4_CMAKE_BUILD_TYPE: ${{matrix.config.build_type}}
|
||||
run: test/mavsdk_tests/mavsdk_test_runner.py --speed-factor 10 --abort-early --model ${{matrix.config.model}} test/mavsdk_tests/configs/sitl.json --verbose --force-color
|
||||
timeout-minutes: 45
|
||||
|
||||
- name: Upload failed logs
|
||||
if: failure()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: failed-${{matrix.config.model}}-logs.zip
|
||||
path: |
|
||||
@ -140,7 +111,7 @@ jobs:
|
||||
|
||||
- name: Upload PX4 coredump
|
||||
if: failure() && ${{ hashFiles('px4.core') != '' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: coredump
|
||||
path: px4.core
|
||||
@ -157,7 +128,7 @@ jobs:
|
||||
|
||||
- name: Upload Coverage Information to Codecov
|
||||
if: contains(matrix.config.build_type, 'Coverage')
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v6
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
flags: mavsdk
|
||||
|
||||
3
.github/workflows/stale.yml
vendored
3
.github/workflows/stale.yml
vendored
@ -2,6 +2,7 @@ name: 'Handle stale issues and PRs'
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 1 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
@ -9,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
operations-per-run: 250
|
||||
operations-per-run: 1500
|
||||
days-before-stale: 90
|
||||
days-before-close: 30
|
||||
stale-issue-label: 'stale'
|
||||
|
||||
2
.github/workflows/sync_to_px4_msgs.yml
vendored
2
.github/workflows/sync_to_px4_msgs.yml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: [runs-on,runner=4cpu-linux-x64,image=ubuntu22-full-x64,"run-id=${{ github.run_id }}",spot=false]
|
||||
steps:
|
||||
- name: Checkout PX4 repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup git credentials
|
||||
run: |
|
||||
|
||||
@ -22,7 +22,7 @@ jobs:
|
||||
TAG_NAME: ${{ github.event_name == 'workflow_dispatch' && inputs.tag_name || github.ref_name }}
|
||||
steps:
|
||||
- name: Checkout PX4 repo
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
@ -31,7 +31,7 @@ Reviewers help maintain PX4 across the project without ownership of a specific c
|
||||
|
||||
| Name | GitHub | Chat | email
|
||||
|------|--------|------|----------------------
|
||||
| _No reviewers yet._ | | |
|
||||
| Onur Ozkan | [@onur-ozkan](https://github.com/onur-ozkan) | onur_ozkan0126 | <onur@orkavian.com>
|
||||
|
||||
|
||||
**Documentation Maintainers**
|
||||
|
||||
12
README.md
12
README.md
@ -70,7 +70,17 @@ PX4 is an open-source autopilot stack for drones and unmanned vehicles. It suppo
|
||||
|
||||
<sub>…and many more: helicopters, autogyros, airships, submarines, boats, and other experimental platforms. These frames have basic support but are not part of the regular flight-test program. See the <a href="https://docs.px4.io/main/en/airframes/airframe_reference.html">full airframe reference</a>.</sub>
|
||||
|
||||
## Quick Start
|
||||
## Try PX4
|
||||
|
||||
Run PX4 in simulation with a single command. No build tools, no dependencies beyond Docker:
|
||||
|
||||
```bash
|
||||
docker run --rm -it -p 14550:14550/udp px4io/px4-sitl:latest
|
||||
```
|
||||
|
||||
Open [QGroundControl](https://qgroundcontrol.com) and fly. See [PX4 Simulation Quickstart](../dev_setup/px4_simulation_quickstart.md) for more options.
|
||||
|
||||
## Build from Source
|
||||
|
||||
```bash
|
||||
git clone https://github.com/PX4/PX4-Autopilot.git --recursive
|
||||
|
||||
172
Tools/ci/_github_helpers.py
Normal file
172
Tools/ci/_github_helpers.py
Normal file
@ -0,0 +1,172 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Shared GitHub REST helpers for PX4 CI scripts.
|
||||
|
||||
This module is imported by the PR poster scripts under Tools/ci/. It is
|
||||
NOT an executable entry point; do not run it directly.
|
||||
|
||||
Provides:
|
||||
- fail(msg) terminates the caller with a clear error
|
||||
- GitHubClient(token) thin stdlib-only GitHub REST client with
|
||||
single-request and paginated helpers
|
||||
|
||||
Python stdlib only. No third-party dependencies.
|
||||
|
||||
History: extracted from Tools/ci/pr-comment-poster.py so that
|
||||
pr-comment-poster.py and pr-review-poster.py share the same HTTP plumbing
|
||||
without duplicating ~100 lines of request/pagination/error-handling code.
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import typing
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
|
||||
GITHUB_API = 'https://api.github.com'
|
||||
DEFAULT_USER_AGENT = 'px4-ci'
|
||||
API_VERSION = '2022-11-28'
|
||||
|
||||
|
||||
def fail(msg: str) -> typing.NoReturn:
|
||||
"""Print an error to stderr and exit with status 1.
|
||||
|
||||
Annotated NoReturn so static checkers understand control does not
|
||||
continue past a fail() call.
|
||||
"""
|
||||
print('error: {}'.format(msg), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _parse_next_link(link_header):
|
||||
"""Return the URL for rel="next" from an RFC 5988 Link header, or None.
|
||||
|
||||
The Link header is comma-separated entries of the form:
|
||||
<https://...?page=2>; rel="next", <https://...?page=5>; rel="last"
|
||||
We walk each entry and return the URL of the one whose rel attribute is
|
||||
"next". Accept single-quoted rel values for robustness even though
|
||||
GitHub always emits double quotes.
|
||||
"""
|
||||
if not link_header:
|
||||
return None
|
||||
for part in link_header.split(','):
|
||||
segs = part.strip().split(';')
|
||||
if len(segs) < 2:
|
||||
continue
|
||||
url_seg = segs[0].strip()
|
||||
if not (url_seg.startswith('<') and url_seg.endswith('>')):
|
||||
continue
|
||||
url = url_seg[1:-1]
|
||||
for attr in segs[1:]:
|
||||
attr = attr.strip()
|
||||
if attr == 'rel="next"' or attr == "rel='next'":
|
||||
return url
|
||||
return None
|
||||
|
||||
|
||||
class GitHubClient:
|
||||
"""Minimal GitHub REST client backed by the Python stdlib.
|
||||
|
||||
Each instance holds a token and a user-agent so callers do not have to
|
||||
thread them through every call. Methods return parsed JSON (or None for
|
||||
empty responses) and raise RuntimeError with the server response body on
|
||||
HTTP errors, so CI logs show what the API actually objected to.
|
||||
|
||||
Usage:
|
||||
client = GitHubClient(token, user_agent='px4-pr-comment-poster')
|
||||
body, headers = client.request('GET', 'repos/{o}/{r}/pulls/123')
|
||||
for item in client.paginated('repos/{o}/{r}/pulls/123/reviews'):
|
||||
...
|
||||
"""
|
||||
|
||||
def __init__(self, token, user_agent=DEFAULT_USER_AGENT):
|
||||
if not token:
|
||||
raise ValueError('GitHub token is required')
|
||||
self._token = token
|
||||
self._user_agent = user_agent
|
||||
|
||||
def request(self, method, path_or_url, json_body=None):
|
||||
"""GET/POST/PATCH/PUT/DELETE a single API path or absolute URL.
|
||||
|
||||
`path_or_url` may be either a relative API path (e.g.
|
||||
"repos/PX4/PX4-Autopilot/pulls/123") or an absolute URL such as the
|
||||
next-page URL returned from paginated results. Relative paths are
|
||||
prefixed with the GitHub API base.
|
||||
|
||||
Returns (parsed_json_or_none, headers_dict). Raises RuntimeError
|
||||
on HTTP or transport errors.
|
||||
"""
|
||||
url = self._resolve(path_or_url)
|
||||
return self._do_request(method, url, json_body)
|
||||
|
||||
def paginated(self, path, per_page=100):
|
||||
"""GET a path and follow rel="next" Link headers.
|
||||
|
||||
Yields items from each page's JSON array. Bumps per_page to 100
|
||||
(GitHub's max) so large result sets take fewer round-trips.
|
||||
Raises RuntimeError if any page response is not a JSON array.
|
||||
"""
|
||||
url = self._resolve(path)
|
||||
sep = '&' if '?' in url else '?'
|
||||
url = '{}{}per_page={}'.format(url, sep, per_page)
|
||||
while url is not None:
|
||||
body, headers = self._do_request('GET', url, None)
|
||||
if body is None:
|
||||
return
|
||||
if not isinstance(body, list):
|
||||
raise RuntimeError(
|
||||
'expected JSON array from {}, got {}'.format(
|
||||
url, type(body).__name__))
|
||||
for item in body:
|
||||
yield item
|
||||
url = _parse_next_link(headers.get('Link'))
|
||||
|
||||
def _resolve(self, path_or_url):
|
||||
if path_or_url.startswith('http://') or path_or_url.startswith('https://'):
|
||||
return path_or_url
|
||||
return '{}/{}'.format(GITHUB_API.rstrip('/'), path_or_url.lstrip('/'))
|
||||
|
||||
def _do_request(self, method, url, json_body):
|
||||
data = None
|
||||
headers = {
|
||||
'Authorization': 'Bearer {}'.format(self._token),
|
||||
'Accept': 'application/vnd.github+json',
|
||||
# Pin the API version so GitHub deprecations don't silently
|
||||
# change the response shape under us.
|
||||
'X-GitHub-Api-Version': API_VERSION,
|
||||
'User-Agent': self._user_agent,
|
||||
}
|
||||
if json_body is not None:
|
||||
data = json.dumps(json_body).encode('utf-8')
|
||||
headers['Content-Type'] = 'application/json; charset=utf-8'
|
||||
|
||||
req = urllib.request.Request(
|
||||
url, data=data, method=method, headers=headers)
|
||||
try:
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
raw = resp.read()
|
||||
# HTTPMessage is case-insensitive on lookup but its items()
|
||||
# preserves the original case. GitHub sends "Link" with a
|
||||
# capital L, which is what _parse_next_link expects.
|
||||
resp_headers = dict(resp.headers.items())
|
||||
if not raw:
|
||||
return None, resp_headers
|
||||
return json.loads(raw.decode('utf-8')), resp_headers
|
||||
except urllib.error.HTTPError as e:
|
||||
# GitHub error bodies are JSON with a "message" field and often
|
||||
# a "documentation_url". Dump the raw body into the exception so
|
||||
# the CI log shows exactly what the API objected to. A bare
|
||||
# "HTTP 422" tells us nothing useful.
|
||||
try:
|
||||
err_body = e.read().decode('utf-8', errors='replace')
|
||||
except Exception:
|
||||
err_body = '(no body)'
|
||||
raise RuntimeError(
|
||||
'GitHub API {} {} failed: HTTP {} {}\n{}'.format(
|
||||
method, url, e.code, e.reason, err_body))
|
||||
except urllib.error.URLError as e:
|
||||
# Network layer failure (DNS, TLS, connection reset). No HTTP
|
||||
# response to parse; just surface the transport reason.
|
||||
raise RuntimeError(
|
||||
'GitHub API {} {} failed: {}'.format(method, url, e.reason))
|
||||
539
Tools/ci/clang-tidy-fixes-to-review.py
Normal file
539
Tools/ci/clang-tidy-fixes-to-review.py
Normal file
@ -0,0 +1,539 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# clang-tidy-fixes-to-review.py
|
||||
#
|
||||
# Producer-side helper that converts a clang-tidy fixes.yml file into a
|
||||
# pr-review artifact (manifest.json + comments.json) suitable for
|
||||
# Tools/ci/pr-review-poster.py.
|
||||
#
|
||||
# This script runs inside the clang-tidy job's px4-dev container so it can
|
||||
# read the source tree directly and look up byte offsets in the original
|
||||
# files. The output it writes is a fully-baked array of review comments;
|
||||
# the poster never reads source files or fixes.yml.
|
||||
#
|
||||
# ----------------------------------------------------------------------------
|
||||
# ATTRIBUTION
|
||||
# ----------------------------------------------------------------------------
|
||||
# This script reuses the diagnostic-to-review-comment translation logic
|
||||
# from platisd/clang-tidy-pr-comments. The original work is:
|
||||
#
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2021 Dimitris Platis
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Adapted parts:
|
||||
# - get_diff_line_ranges_per_file() and its inner change_to_line_range()
|
||||
# - generate_review_comments() and its nested helpers
|
||||
# (get_line_by_offset, validate_warning_applicability,
|
||||
# calculate_replacements_diff, markdown, markdown_url,
|
||||
# diagnostic_name_visual, generate_single_comment)
|
||||
# - reorder_diagnostics()
|
||||
#
|
||||
# Removed parts (handled by Tools/ci/pr-review-poster.py instead):
|
||||
# - post_review_comments / dismiss_change_requests / resolve_conversations
|
||||
# - the original argparse main and the requests-based HTTP layer
|
||||
#
|
||||
# Adaptation notes:
|
||||
# - The HTTP layer is rewritten on top of Tools/ci/_github_helpers.py so
|
||||
# this script does not depend on the third-party `requests` package.
|
||||
# - Conversation resolution (the GraphQL path) is intentionally dropped
|
||||
# for v1; revisit if it turns out to be missed.
|
||||
# - Clang-Tidy 8 upconvert is preserved verbatim.
|
||||
#
|
||||
# ----------------------------------------------------------------------------
|
||||
# Bounded assumptions (documented for future maintainers):
|
||||
# - Source files are UTF-8 (we read them as latin_1, matching clang-tidy's
|
||||
# own byte-offset model, and the offsets we surface are line counts)
|
||||
# - Source files use LF line endings
|
||||
# - Malformed entries in fixes.yml are skipped with a warning rather than
|
||||
# crashing the job
|
||||
#
|
||||
# Dependencies: pyyaml + Tools/ci/_github_helpers.py.
|
||||
# pyyaml is preinstalled in the px4-dev container; this script is intended
|
||||
# to run there, not on bare ubuntu-latest.
|
||||
"""Convert a clang-tidy fixes.yml into a pr-review artifact."""
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
import json
|
||||
import os
|
||||
import posixpath
|
||||
import re
|
||||
import sys
|
||||
import urllib.parse
|
||||
|
||||
import yaml
|
||||
|
||||
import _github_helpers
|
||||
from _github_helpers import fail as _fail
|
||||
|
||||
|
||||
# Markers used inside the per-comment body to call out severity. Plain
|
||||
# strings rather than emojis to keep the file emoji-free per project
|
||||
# preferences; the rendered Markdown is unaffected.
|
||||
SINGLE_COMMENT_MARKERS = {
|
||||
'Error': '**[error]**',
|
||||
'Warning': '**[warning]**',
|
||||
'Remark': '**[remark]**',
|
||||
'fallback': '**[note]**',
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Diff-range parsing (adapted from platisd)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def get_diff_line_ranges_per_file(pr_files):
|
||||
"""Return a dict mapping each PR file path to a list of line ranges
|
||||
(the +new-side hunks) parsed from its patch."""
|
||||
|
||||
def change_to_line_range(change):
|
||||
split_change = change.split(',')
|
||||
start = int(split_change[0])
|
||||
size = int(split_change[1]) if len(split_change) > 1 else 1
|
||||
return range(start, start + size)
|
||||
|
||||
result = {}
|
||||
for pr_file in pr_files:
|
||||
# Removed binary files etc. have no patch section.
|
||||
if 'patch' not in pr_file:
|
||||
continue
|
||||
file_name = pr_file['filename']
|
||||
# Match lines like '@@ -101,8 +102,11 @@'
|
||||
git_line_tags = re.findall(
|
||||
r'^@@ -.*? +.*? @@', pr_file['patch'], re.MULTILINE)
|
||||
changes = [
|
||||
tag.replace('@@', '').strip().split()[1].replace('+', '')
|
||||
for tag in git_line_tags
|
||||
]
|
||||
result[file_name] = [
|
||||
change_to_line_range(change) for change in changes
|
||||
]
|
||||
return result
|
||||
|
||||
|
||||
def fetch_pull_request_files(client, repo, pr_number):
|
||||
"""Yield file metadata objects for each file modified by the PR."""
|
||||
path = 'repos/{}/pulls/{}/files'.format(repo, pr_number)
|
||||
for entry in client.paginated(path):
|
||||
yield entry
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Diagnostic ordering (adapted from platisd)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def reorder_diagnostics(diags):
|
||||
"""Return diagnostics ordered Error -> Warning -> Remark -> other."""
|
||||
errors = [d for d in diags if d.get('Level') == 'Error']
|
||||
warnings = [d for d in diags if d.get('Level') == 'Warning']
|
||||
remarks = [d for d in diags if d.get('Level') == 'Remark']
|
||||
others = [
|
||||
d for d in diags
|
||||
if d.get('Level') not in {'Error', 'Warning', 'Remark'}
|
||||
]
|
||||
if others:
|
||||
print(
|
||||
'warning: some fixes have an unexpected Level (not Error, '
|
||||
'Warning, or Remark)', file=sys.stderr)
|
||||
return errors + warnings + remarks + others
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Comment generation (adapted from platisd)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def generate_review_comments(clang_tidy_fixes, repository_root,
|
||||
diff_line_ranges_per_file,
|
||||
single_comment_markers):
|
||||
"""Yield review comment dicts for each clang-tidy diagnostic that
|
||||
intersects the PR diff."""
|
||||
|
||||
def get_line_by_offset(file_path, offset):
|
||||
# Clang-Tidy doesn't support multibyte encodings and measures
|
||||
# offsets in bytes; latin_1 makes byte offsets and string offsets
|
||||
# equivalent.
|
||||
with open(repository_root + file_path, encoding='latin_1') as fh:
|
||||
source = fh.read()
|
||||
return source[:offset].count('\n') + 1
|
||||
|
||||
def validate_warning_applicability(file_path, start_line_num, end_line_num):
|
||||
assert end_line_num >= start_line_num
|
||||
for line_range in diff_line_ranges_per_file[file_path]:
|
||||
assert line_range.step == 1
|
||||
if (line_range.start <= start_line_num
|
||||
and end_line_num < line_range.stop):
|
||||
return True
|
||||
return False
|
||||
|
||||
def calculate_replacements_diff(file_path, replacements):
|
||||
# Apply replacements in reverse order so subsequent offsets do not
|
||||
# shift.
|
||||
replacements.sort(key=lambda item: (-item['Offset']))
|
||||
with open(repository_root + file_path, encoding='latin_1') as fh:
|
||||
source = fh.read()
|
||||
changed = source
|
||||
for replacement in replacements:
|
||||
changed = (
|
||||
changed[:replacement['Offset']]
|
||||
+ replacement['ReplacementText']
|
||||
+ changed[replacement['Offset'] + replacement['Length']:]
|
||||
)
|
||||
return difflib.Differ().compare(
|
||||
source.splitlines(keepends=True),
|
||||
changed.splitlines(keepends=True),
|
||||
)
|
||||
|
||||
def markdown(s):
|
||||
md_chars = '\\`*_{}[]<>()#+-.!|'
|
||||
|
||||
def escape_chars(s):
|
||||
for ch in md_chars:
|
||||
s = s.replace(ch, '\\' + ch)
|
||||
return s
|
||||
|
||||
def unescape_chars(s):
|
||||
for ch in md_chars:
|
||||
s = s.replace('\\' + ch, ch)
|
||||
return s
|
||||
|
||||
s = escape_chars(s)
|
||||
s = re.sub(
|
||||
"'([^']*)'",
|
||||
lambda m: '`` ' + unescape_chars(m.group(1)) + ' ``',
|
||||
s,
|
||||
)
|
||||
return s
|
||||
|
||||
def markdown_url(label, url):
|
||||
return '[{}]({})'.format(label, url)
|
||||
|
||||
def diagnostic_name_visual(diagnostic_name):
|
||||
visual = '**{}**'.format(markdown(diagnostic_name))
|
||||
try:
|
||||
first_dash_idx = diagnostic_name.index('-')
|
||||
except ValueError:
|
||||
return visual
|
||||
namespace = urllib.parse.quote_plus(diagnostic_name[:first_dash_idx])
|
||||
check_name = urllib.parse.quote_plus(
|
||||
diagnostic_name[first_dash_idx + 1:])
|
||||
return markdown_url(
|
||||
visual,
|
||||
'https://clang.llvm.org/extra/clang-tidy/checks/{}/{}.html'.format(
|
||||
namespace, check_name),
|
||||
)
|
||||
|
||||
def generate_single_comment(file_path, start_line_num, end_line_num,
|
||||
name, message, single_comment_marker,
|
||||
replacement_text=None):
|
||||
result = {
|
||||
'path': file_path,
|
||||
'line': end_line_num,
|
||||
'side': 'RIGHT',
|
||||
'body': '{} {} {}\n{}'.format(
|
||||
single_comment_marker,
|
||||
diagnostic_name_visual(name),
|
||||
single_comment_marker,
|
||||
markdown(message),
|
||||
),
|
||||
}
|
||||
if start_line_num != end_line_num:
|
||||
result['start_line'] = start_line_num
|
||||
result['start_side'] = 'RIGHT'
|
||||
if replacement_text is not None:
|
||||
if not replacement_text or replacement_text[-1] != '\n':
|
||||
replacement_text += '\n'
|
||||
result['body'] += '\n```suggestion\n{}```'.format(replacement_text)
|
||||
return result
|
||||
|
||||
for diag in clang_tidy_fixes['Diagnostics']:
|
||||
# Upconvert clang-tidy 8 format to 9+
|
||||
if 'DiagnosticMessage' not in diag:
|
||||
diag['DiagnosticMessage'] = {
|
||||
'FileOffset': diag.get('FileOffset'),
|
||||
'FilePath': diag.get('FilePath'),
|
||||
'Message': diag.get('Message'),
|
||||
'Replacements': diag.get('Replacements', []),
|
||||
}
|
||||
|
||||
diag_message = diag['DiagnosticMessage']
|
||||
diag_message['FilePath'] = posixpath.normpath(
|
||||
(diag_message.get('FilePath') or '').replace(repository_root, ''))
|
||||
for replacement in diag_message.get('Replacements') or []:
|
||||
replacement['FilePath'] = posixpath.normpath(
|
||||
replacement['FilePath'].replace(repository_root, ''))
|
||||
|
||||
diag_name = diag.get('DiagnosticName', '<unknown>')
|
||||
diag_message_msg = diag_message.get('Message', '')
|
||||
level = diag.get('Level', 'Warning')
|
||||
single_comment_marker = single_comment_markers.get(
|
||||
level, single_comment_markers['fallback'])
|
||||
|
||||
replacements = diag_message.get('Replacements') or []
|
||||
if not replacements:
|
||||
file_path = diag_message['FilePath']
|
||||
offset = diag_message.get('FileOffset')
|
||||
if offset is None:
|
||||
print('warning: skipping {!r}: missing FileOffset'.format(
|
||||
diag_name), file=sys.stderr)
|
||||
continue
|
||||
if file_path not in diff_line_ranges_per_file:
|
||||
print(
|
||||
"'{}' for {} does not apply to the files changed in "
|
||||
'this PR'.format(diag_name, file_path))
|
||||
continue
|
||||
try:
|
||||
line_num = get_line_by_offset(file_path, offset)
|
||||
except (OSError, ValueError) as e:
|
||||
print('warning: skipping {!r} on {}: {}'.format(
|
||||
diag_name, file_path, e), file=sys.stderr)
|
||||
continue
|
||||
|
||||
print("Processing '{}' at line {} of {}...".format(
|
||||
diag_name, line_num, file_path))
|
||||
if validate_warning_applicability(file_path, line_num, line_num):
|
||||
yield generate_single_comment(
|
||||
file_path,
|
||||
line_num,
|
||||
line_num,
|
||||
diag_name,
|
||||
diag_message_msg,
|
||||
single_comment_marker=single_comment_marker,
|
||||
)
|
||||
else:
|
||||
print('This warning does not apply to the lines changed '
|
||||
'in this PR')
|
||||
else:
|
||||
for file_path in {item['FilePath'] for item in replacements}:
|
||||
if file_path not in diff_line_ranges_per_file:
|
||||
print(
|
||||
"'{}' for {} does not apply to the files changed "
|
||||
'in this PR'.format(diag_name, file_path))
|
||||
continue
|
||||
|
||||
line_num = 1
|
||||
start_line_num = None
|
||||
end_line_num = None
|
||||
replacement_text = None
|
||||
|
||||
try:
|
||||
diff_iter = calculate_replacements_diff(
|
||||
file_path,
|
||||
[r for r in replacements if r['FilePath'] == file_path],
|
||||
)
|
||||
except (OSError, ValueError) as e:
|
||||
print('warning: skipping {!r} on {}: {}'.format(
|
||||
diag_name, file_path, e), file=sys.stderr)
|
||||
continue
|
||||
|
||||
for line in diff_iter:
|
||||
# Comment line, ignore.
|
||||
if line.startswith('? '):
|
||||
continue
|
||||
# A '-' line is the start or continuation of a region
|
||||
# to replace.
|
||||
if line.startswith('- '):
|
||||
if start_line_num is None:
|
||||
start_line_num = line_num
|
||||
end_line_num = line_num
|
||||
else:
|
||||
end_line_num = line_num
|
||||
if replacement_text is None:
|
||||
replacement_text = ''
|
||||
line_num += 1
|
||||
# A '+' line is part of the replacement text.
|
||||
elif line.startswith('+ '):
|
||||
if replacement_text is None:
|
||||
replacement_text = line[2:]
|
||||
else:
|
||||
replacement_text += line[2:]
|
||||
# A context line marks the end of a replacement region.
|
||||
elif line.startswith(' '):
|
||||
if replacement_text is not None:
|
||||
if start_line_num is None:
|
||||
# Pure addition: synthesize a one-line
|
||||
# range and append the context line to
|
||||
# the replacement.
|
||||
start_line_num = line_num
|
||||
end_line_num = line_num
|
||||
replacement_text += line[2:]
|
||||
|
||||
print("Processing '{}' at lines {}-{} of {}...".format(
|
||||
diag_name, start_line_num, end_line_num, file_path))
|
||||
|
||||
if validate_warning_applicability(
|
||||
file_path, start_line_num, end_line_num):
|
||||
yield generate_single_comment(
|
||||
file_path,
|
||||
start_line_num,
|
||||
end_line_num,
|
||||
diag_name,
|
||||
diag_message_msg,
|
||||
single_comment_marker=single_comment_marker,
|
||||
replacement_text=replacement_text,
|
||||
)
|
||||
else:
|
||||
print(
|
||||
'This warning does not apply to the '
|
||||
'lines changed in this PR')
|
||||
|
||||
start_line_num = None
|
||||
end_line_num = None
|
||||
replacement_text = None
|
||||
|
||||
line_num += 1
|
||||
else:
|
||||
# Unknown difflib prefix; skip rather than abort.
|
||||
print('warning: unexpected diff prefix {!r}; '
|
||||
'skipping diagnostic'.format(line[:2]),
|
||||
file=sys.stderr)
|
||||
break
|
||||
|
||||
# End of file with a pending replacement region.
|
||||
if replacement_text is not None and start_line_num is not None:
|
||||
print("Processing '{}' at lines {}-{} of {}...".format(
|
||||
diag_name, start_line_num, end_line_num, file_path))
|
||||
if validate_warning_applicability(
|
||||
file_path, start_line_num, end_line_num):
|
||||
yield generate_single_comment(
|
||||
file_path,
|
||||
start_line_num,
|
||||
end_line_num,
|
||||
diag_name,
|
||||
diag_message_msg,
|
||||
single_comment_marker=single_comment_marker,
|
||||
replacement_text=replacement_text,
|
||||
)
|
||||
else:
|
||||
print('This warning does not apply to the lines '
|
||||
'changed in this PR')
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Entry point
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Convert a clang-tidy fixes.yml into a pr-review '
|
||||
'artifact (manifest.json + comments.json).',
|
||||
)
|
||||
parser.add_argument('--fixes', required=True,
|
||||
help='Path to fixes.yml from clang-tidy')
|
||||
parser.add_argument('--repo-root', required=True,
|
||||
help='Path to the repository root containing the '
|
||||
'source files referenced by fixes.yml')
|
||||
parser.add_argument('--repo', required=True,
|
||||
help='owner/name of the repository')
|
||||
parser.add_argument('--pr-number', required=True, type=int,
|
||||
help='Pull request number')
|
||||
parser.add_argument('--commit-sha', required=True,
|
||||
help='40-char hex commit SHA the review will pin to')
|
||||
parser.add_argument('--out-dir', required=True,
|
||||
help='Directory to write manifest.json and '
|
||||
'comments.json')
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
default='<!-- pr-review-poster:clang-tidy -->',
|
||||
help='Marker string embedded in the review body so the poster '
|
||||
'can find and dismiss stale runs')
|
||||
parser.add_argument(
|
||||
'--event',
|
||||
default='REQUEST_CHANGES',
|
||||
choices=('COMMENT', 'REQUEST_CHANGES'),
|
||||
help='GitHub review event type')
|
||||
parser.add_argument(
|
||||
'--summary', default='',
|
||||
help='Optional review summary text appended to the review body')
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
if args.pr_number <= 0:
|
||||
_fail('--pr-number must be > 0')
|
||||
if not re.match(r'^[0-9a-f]{40}$', args.commit_sha):
|
||||
_fail('--commit-sha must be a 40-char lowercase hex string')
|
||||
|
||||
token = os.environ.get('GITHUB_TOKEN')
|
||||
if not token:
|
||||
_fail('GITHUB_TOKEN is not set')
|
||||
|
||||
# Normalize the repo root with a trailing slash so the platisd-style
|
||||
# str.replace() trick still strips it cleanly.
|
||||
repo_root = args.repo_root
|
||||
if not repo_root.endswith(os.sep):
|
||||
repo_root = repo_root + os.sep
|
||||
|
||||
os.makedirs(args.out_dir, exist_ok=True)
|
||||
|
||||
client = _github_helpers.GitHubClient(token, user_agent='px4-clang-tidy-fixes-to-review')
|
||||
|
||||
print('Fetching PR file list from GitHub...')
|
||||
pr_files = list(fetch_pull_request_files(client, args.repo, args.pr_number))
|
||||
diff_line_ranges_per_file = get_diff_line_ranges_per_file(pr_files)
|
||||
|
||||
print('Loading clang-tidy fixes from {}...'.format(args.fixes))
|
||||
if not os.path.isfile(args.fixes):
|
||||
# No fixes file means clang-tidy ran cleanly. Emit an empty
|
||||
# comments.json so the poster can short-circuit.
|
||||
comments = []
|
||||
else:
|
||||
with open(args.fixes, encoding='utf-8') as fh:
|
||||
clang_tidy_fixes = yaml.safe_load(fh)
|
||||
if (not clang_tidy_fixes
|
||||
or 'Diagnostics' not in clang_tidy_fixes
|
||||
or not clang_tidy_fixes['Diagnostics']):
|
||||
comments = []
|
||||
else:
|
||||
clang_tidy_fixes['Diagnostics'] = reorder_diagnostics(
|
||||
clang_tidy_fixes['Diagnostics'])
|
||||
comments = list(generate_review_comments(
|
||||
clang_tidy_fixes,
|
||||
repo_root,
|
||||
diff_line_ranges_per_file,
|
||||
single_comment_markers=SINGLE_COMMENT_MARKERS,
|
||||
))
|
||||
|
||||
print('Generated {} review comment(s)'.format(len(comments)))
|
||||
|
||||
manifest = {
|
||||
'pr_number': args.pr_number,
|
||||
'marker': args.marker,
|
||||
'event': args.event,
|
||||
'commit_sha': args.commit_sha,
|
||||
}
|
||||
if args.summary:
|
||||
manifest['summary'] = args.summary
|
||||
|
||||
manifest_path = os.path.join(args.out_dir, 'manifest.json')
|
||||
comments_path = os.path.join(args.out_dir, 'comments.json')
|
||||
with open(manifest_path, 'w', encoding='utf-8') as fh:
|
||||
json.dump(manifest, fh, indent=2)
|
||||
fh.write('\n')
|
||||
with open(comments_path, 'w', encoding='utf-8') as fh:
|
||||
json.dump(comments, fh, indent=2)
|
||||
fh.write('\n')
|
||||
|
||||
print('Wrote {} and {}'.format(manifest_path, comments_path))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@ -37,9 +37,9 @@ import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import typing
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
import _github_helpers
|
||||
from _github_helpers import fail as _fail
|
||||
|
||||
|
||||
# GitHub hard limit is 65535 bytes. Cap well under to leave headroom for
|
||||
@ -53,7 +53,6 @@ MARKER_MAX_LEN = 200
|
||||
|
||||
ACCEPTED_MODES = ('upsert',)
|
||||
|
||||
GITHUB_API = 'https://api.github.com'
|
||||
USER_AGENT = 'px4-pr-comment-poster'
|
||||
|
||||
|
||||
@ -61,11 +60,6 @@ USER_AGENT = 'px4-pr-comment-poster'
|
||||
# Validation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _fail(msg: str) -> typing.NoReturn:
|
||||
print('error: {}'.format(msg), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _is_printable_ascii(s):
|
||||
# Space (0x20) through tilde (0x7E) inclusive.
|
||||
return all(0x20 <= ord(ch) <= 0x7E for ch in s)
|
||||
@ -161,121 +155,11 @@ def validate_manifest(directory):
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GitHub HTTP helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _github_request(method, url, token, json_body=None):
|
||||
"""Perform a single GitHub REST request.
|
||||
|
||||
Returns a tuple (parsed_json_or_none, headers_dict). Raises RuntimeError
|
||||
with the server response body on HTTP errors so CI logs show what
|
||||
GitHub complained about.
|
||||
"""
|
||||
data = None
|
||||
headers = {
|
||||
'Authorization': 'Bearer {}'.format(token),
|
||||
'Accept': 'application/vnd.github+json',
|
||||
# Pin the API version so GitHub deprecations don't silently change
|
||||
# the response shape under us.
|
||||
'X-GitHub-Api-Version': '2022-11-28',
|
||||
'User-Agent': USER_AGENT,
|
||||
}
|
||||
if json_body is not None:
|
||||
data = json.dumps(json_body).encode('utf-8')
|
||||
headers['Content-Type'] = 'application/json; charset=utf-8'
|
||||
|
||||
req = urllib.request.Request(url, data=data, method=method, headers=headers)
|
||||
try:
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
raw = resp.read()
|
||||
# HTTPMessage is case-insensitive on lookup but its items() preserves
|
||||
# the original case. GitHub sends "Link" with a capital L, which is
|
||||
# what _parse_next_link expects.
|
||||
resp_headers = dict(resp.headers.items())
|
||||
if not raw:
|
||||
return None, resp_headers
|
||||
return json.loads(raw.decode('utf-8')), resp_headers
|
||||
except urllib.error.HTTPError as e:
|
||||
# GitHub error bodies are JSON with a "message" field and often a
|
||||
# "documentation_url". Dump the raw body into the exception so the CI
|
||||
# log shows exactly what the API objected to. A bare "HTTP 422"
|
||||
# tells us nothing useful.
|
||||
try:
|
||||
err_body = e.read().decode('utf-8', errors='replace')
|
||||
except Exception:
|
||||
err_body = '(no body)'
|
||||
raise RuntimeError(
|
||||
'GitHub API {} {} failed: HTTP {} {}\n{}'.format(
|
||||
method, url, e.code, e.reason, err_body))
|
||||
except urllib.error.URLError as e:
|
||||
# Network layer failure (DNS, TLS, connection reset). No HTTP response
|
||||
# to parse; just surface the transport reason.
|
||||
raise RuntimeError(
|
||||
'GitHub API {} {} failed: {}'.format(method, url, e.reason))
|
||||
|
||||
|
||||
def _parse_next_link(link_header):
|
||||
"""Return the URL for rel="next" from an RFC 5988 Link header, or None.
|
||||
|
||||
The Link header is comma-separated entries of the form:
|
||||
<https://...?page=2>; rel="next", <https://...?page=5>; rel="last"
|
||||
We walk each entry and return the URL of the one whose rel attribute is
|
||||
"next". Accept single-quoted rel values for robustness even though GitHub
|
||||
always emits double quotes.
|
||||
"""
|
||||
if not link_header:
|
||||
return None
|
||||
for part in link_header.split(','):
|
||||
segs = part.strip().split(';')
|
||||
if len(segs) < 2:
|
||||
continue
|
||||
url_seg = segs[0].strip()
|
||||
if not (url_seg.startswith('<') and url_seg.endswith('>')):
|
||||
continue
|
||||
url = url_seg[1:-1]
|
||||
for attr in segs[1:]:
|
||||
attr = attr.strip()
|
||||
if attr == 'rel="next"' or attr == "rel='next'":
|
||||
return url
|
||||
return None
|
||||
|
||||
|
||||
def github_api(method, path, token, json_body=None):
|
||||
"""GET/POST/PATCH a single GitHub API path. Non-paginated."""
|
||||
url = '{}/{}'.format(GITHUB_API.rstrip('/'), path.lstrip('/'))
|
||||
body, _ = _github_request(method, url, token, json_body=json_body)
|
||||
return body
|
||||
|
||||
|
||||
def github_api_paginated(path, token):
|
||||
"""GET a GitHub API path and follow rel="next" Link headers.
|
||||
|
||||
Yields items from each page's JSON array.
|
||||
"""
|
||||
url = '{}/{}'.format(GITHUB_API.rstrip('/'), path.lstrip('/'))
|
||||
# GitHub defaults to per_page=30. Bump to 100 (the max) so a PR with a
|
||||
# few hundred comments fetches in 3 or 4 round-trips instead of 10+.
|
||||
sep = '&' if '?' in url else '?'
|
||||
url = '{}{}per_page=100'.format(url, sep)
|
||||
while url is not None:
|
||||
body, headers = _github_request('GET', url, token)
|
||||
if body is None:
|
||||
return
|
||||
if not isinstance(body, list):
|
||||
raise RuntimeError(
|
||||
'expected JSON array from {}, got {}'.format(
|
||||
url, type(body).__name__))
|
||||
for item in body:
|
||||
yield item
|
||||
url = _parse_next_link(headers.get('Link'))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Comment upsert
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def find_existing_comment_id(token, repo, pr_number, marker):
|
||||
def find_existing_comment_id(client, repo, pr_number, marker):
|
||||
"""Return the id of the first PR comment whose body contains marker, or None.
|
||||
|
||||
PR comments are issue comments in GitHub's data model, so we hit
|
||||
@ -286,7 +170,7 @@ def find_existing_comment_id(token, repo, pr_number, marker):
|
||||
appear in user-written prose.
|
||||
"""
|
||||
path = 'repos/{}/issues/{}/comments'.format(repo, pr_number)
|
||||
for comment in github_api_paginated(path, token):
|
||||
for comment in client.paginated(path):
|
||||
body = comment.get('body') or ''
|
||||
if marker in body:
|
||||
return comment.get('id')
|
||||
@ -308,24 +192,22 @@ def build_final_body(body, marker):
|
||||
return '{}\n\n{}\n'.format(body.rstrip('\n'), marker)
|
||||
|
||||
|
||||
def upsert_comment(token, repo, pr_number, marker, body):
|
||||
def upsert_comment(client, repo, pr_number, marker, body):
|
||||
final_body = build_final_body(body, marker)
|
||||
existing_id = find_existing_comment_id(token, repo, pr_number, marker)
|
||||
existing_id = find_existing_comment_id(client, repo, pr_number, marker)
|
||||
|
||||
if existing_id is not None:
|
||||
print('Updating comment {} on PR #{}'.format(existing_id, pr_number))
|
||||
github_api(
|
||||
client.request(
|
||||
'PATCH',
|
||||
'repos/{}/issues/comments/{}'.format(repo, existing_id),
|
||||
token,
|
||||
json_body={'body': final_body},
|
||||
)
|
||||
else:
|
||||
print('Creating new comment on PR #{}'.format(pr_number))
|
||||
github_api(
|
||||
client.request(
|
||||
'POST',
|
||||
'repos/{}/issues/{}/comments'.format(repo, pr_number),
|
||||
token,
|
||||
json_body={'body': final_body},
|
||||
)
|
||||
|
||||
@ -364,8 +246,9 @@ def cmd_post(args):
|
||||
_fail('GITHUB_REPOSITORY must be "owner/name", got {!r}'.format(repo))
|
||||
|
||||
try:
|
||||
client = _github_helpers.GitHubClient(token, user_agent=USER_AGENT)
|
||||
upsert_comment(
|
||||
token=token,
|
||||
client=client,
|
||||
repo=repo,
|
||||
pr_number=result['pr_number'],
|
||||
marker=result['marker'],
|
||||
|
||||
468
Tools/ci/pr-review-poster.py
Normal file
468
Tools/ci/pr-review-poster.py
Normal file
@ -0,0 +1,468 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
PR review-comment poster for analysis workflows.
|
||||
|
||||
Sibling of Tools/ci/pr-comment-poster.py. Where pr-comment-poster.py posts
|
||||
sticky issue-style PR comments, this script posts line-anchored review
|
||||
comments on the "Files changed" tab. Use it for tools like clang-tidy that
|
||||
want to flag specific lines instead of (or in addition to) a rollup
|
||||
comment.
|
||||
|
||||
This script is invoked from the `PR Review Poster` workflow which runs on
|
||||
`workflow_run` in the base repository context. It consumes a `pr-review`
|
||||
artifact produced by an upstream analysis job and posts a fresh PR review
|
||||
via the GitHub REST API, dismissing any stale review the same producer
|
||||
left on a previous run.
|
||||
|
||||
Artifact contract (directory passed on the command line):
|
||||
|
||||
manifest.json
|
||||
{
|
||||
"pr_number": 12345, (required, int > 0)
|
||||
"marker": "<!-- pr-review-poster:clang-tidy -->", (required, printable ASCII)
|
||||
"event": "REQUEST_CHANGES", (required, "COMMENT" | "REQUEST_CHANGES")
|
||||
"commit_sha": "0123456789abcdef0123456789abcdef01234567",(required, 40 hex chars)
|
||||
"summary": "Optional review body text" (optional)
|
||||
}
|
||||
|
||||
comments.json
|
||||
JSON array of line-anchored review comment objects:
|
||||
[
|
||||
{"path": "src/foo.cpp", "line": 42, "side": "RIGHT",
|
||||
"body": "..."},
|
||||
{"path": "src/bar.hpp", "start_line": 10, "line": 15,
|
||||
"side": "RIGHT", "start_side": "RIGHT", "body": "..."}
|
||||
]
|
||||
|
||||
Note: an `APPROVE` event is intentionally NOT supported. Bots should never
|
||||
approve a pull request.
|
||||
|
||||
Security: this script is run in a write-token context from a workflow that
|
||||
MUST NOT check out PR code. Both manifest.json and comments.json are
|
||||
treated as opaque data. The marker is validated to printable ASCII only
|
||||
before use, and only reviews authored by github-actions[bot] whose body
|
||||
contains the marker can be dismissed (a fork cannot spoof either).
|
||||
|
||||
Subcommands:
|
||||
|
||||
validate <dir> Validate that <dir> contains a conforming manifest +
|
||||
comments file.
|
||||
post <dir> Validate, then dismiss any stale matching review and
|
||||
post a new review on the target PR. Requires env
|
||||
GITHUB_TOKEN and GITHUB_REPOSITORY.
|
||||
|
||||
Python stdlib only. No third-party dependencies.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
import _github_helpers
|
||||
from _github_helpers import fail as _fail
|
||||
|
||||
|
||||
USER_AGENT = 'px4-pr-review-poster'
|
||||
|
||||
# Marker length bounds. 1..200 is plenty for an HTML comment tag such as
|
||||
# "<!-- pr-review-poster:clang-tidy -->".
|
||||
MARKER_MIN_LEN = 1
|
||||
MARKER_MAX_LEN = 200
|
||||
|
||||
# Cap per-comment body size well under GitHub's hard limit so we leave
|
||||
# headroom for the wrapping JSON envelope. Empirically GitHub allows ~65535
|
||||
# bytes per review comment body; 60000 is a safe ceiling.
|
||||
MAX_COMMENT_BODY_BYTES = 60000
|
||||
|
||||
# Cap on number of comments per single review POST. platisd uses 10. The
|
||||
# value matters because GitHub's review-creation endpoint has a payload
|
||||
# size limit and review comments occasionally trip an abuse-detection
|
||||
# threshold when posted in very large batches. Smaller chunks also let us
|
||||
# spread the work across multiple reviews so a single bad entry only
|
||||
# fails its own chunk.
|
||||
COMMENTS_PER_REVIEW = 10
|
||||
|
||||
# Sleep between successive review POSTs to stay clear of GitHub's
|
||||
# secondary rate limits. platisd uses 10s; 5s is enough for our volume
|
||||
# and cuts user-visible latency.
|
||||
SLEEP_BETWEEN_CHUNKS_SECONDS = 5
|
||||
|
||||
ACCEPTED_EVENTS = ('COMMENT', 'REQUEST_CHANGES')
|
||||
ACCEPTED_SIDES = ('LEFT', 'RIGHT')
|
||||
COMMIT_SHA_RE = re.compile(r'^[0-9a-f]{40}$')
|
||||
|
||||
# The login GitHub assigns to the built-in actions token. Used to filter
|
||||
# the list of existing reviews so we never touch a human reviewer's review.
|
||||
BOT_LOGIN = 'github-actions[bot]'
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Validation
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _is_printable_ascii(s):
|
||||
return all(0x20 <= ord(ch) <= 0x7E for ch in s)
|
||||
|
||||
|
||||
def validate_marker(marker):
|
||||
"""Validate the marker string. See pr-comment-poster.py for rationale."""
|
||||
if not isinstance(marker, str):
|
||||
_fail('marker must be a string')
|
||||
n = len(marker)
|
||||
if n < MARKER_MIN_LEN or n > MARKER_MAX_LEN:
|
||||
_fail('marker length out of range ({}..{}): {}'.format(
|
||||
MARKER_MIN_LEN, MARKER_MAX_LEN, n))
|
||||
if not _is_printable_ascii(marker):
|
||||
_fail('marker contains non-printable or non-ASCII character')
|
||||
|
||||
|
||||
def _validate_comment_entry(idx, entry):
|
||||
"""Validate a single review-comment entry. Raises via _fail on error."""
|
||||
if not isinstance(entry, dict):
|
||||
_fail('comments[{}]: must be an object'.format(idx))
|
||||
|
||||
path = entry.get('path')
|
||||
if not isinstance(path, str) or not path:
|
||||
_fail('comments[{}].path: required non-empty string'.format(idx))
|
||||
|
||||
line = entry.get('line')
|
||||
if not isinstance(line, int) or isinstance(line, bool) or line <= 0:
|
||||
_fail('comments[{}].line: required positive integer'.format(idx))
|
||||
|
||||
side = entry.get('side', 'RIGHT')
|
||||
if side not in ACCEPTED_SIDES:
|
||||
_fail('comments[{}].side: must be one of {} (got {!r})'.format(
|
||||
idx, ', '.join(ACCEPTED_SIDES), side))
|
||||
|
||||
if 'start_line' in entry:
|
||||
start_line = entry['start_line']
|
||||
if (not isinstance(start_line, int)
|
||||
or isinstance(start_line, bool)
|
||||
or start_line <= 0):
|
||||
_fail('comments[{}].start_line: must be positive integer'.format(idx))
|
||||
if start_line >= line:
|
||||
_fail('comments[{}].start_line ({}) must be < line ({})'.format(
|
||||
idx, start_line, line))
|
||||
start_side = entry.get('start_side', side)
|
||||
if start_side not in ACCEPTED_SIDES:
|
||||
_fail('comments[{}].start_side: must be one of {}'.format(
|
||||
idx, ', '.join(ACCEPTED_SIDES)))
|
||||
|
||||
body = entry.get('body')
|
||||
if not isinstance(body, str) or not body:
|
||||
_fail('comments[{}].body: required non-empty string'.format(idx))
|
||||
body_bytes = len(body.encode('utf-8'))
|
||||
if body_bytes > MAX_COMMENT_BODY_BYTES:
|
||||
_fail('comments[{}].body too large: {} bytes (max {})'.format(
|
||||
idx, body_bytes, MAX_COMMENT_BODY_BYTES))
|
||||
|
||||
|
||||
def validate_manifest(directory):
|
||||
"""Validate <directory>/manifest.json and <directory>/comments.json.
|
||||
|
||||
Returns a dict with keys: pr_number, marker, event, commit_sha,
|
||||
summary, comments (list of validated comment dicts).
|
||||
"""
|
||||
manifest_path = os.path.join(directory, 'manifest.json')
|
||||
comments_path = os.path.join(directory, 'comments.json')
|
||||
|
||||
if not os.path.isfile(manifest_path):
|
||||
_fail('manifest.json missing at {}'.format(manifest_path))
|
||||
if not os.path.isfile(comments_path):
|
||||
_fail('comments.json missing at {}'.format(comments_path))
|
||||
|
||||
try:
|
||||
with open(manifest_path, 'r', encoding='utf-8') as f:
|
||||
manifest = json.load(f)
|
||||
except (OSError, json.JSONDecodeError) as e:
|
||||
_fail('manifest.json is not valid JSON: {}'.format(e))
|
||||
|
||||
if not isinstance(manifest, dict):
|
||||
_fail('manifest.json must be a JSON object')
|
||||
|
||||
pr_number = manifest.get('pr_number')
|
||||
if not isinstance(pr_number, int) or isinstance(pr_number, bool):
|
||||
_fail('pr_number must be an integer')
|
||||
if pr_number <= 0:
|
||||
_fail('pr_number must be > 0 (got {})'.format(pr_number))
|
||||
|
||||
marker = manifest.get('marker')
|
||||
validate_marker(marker)
|
||||
|
||||
event = manifest.get('event')
|
||||
if event not in ACCEPTED_EVENTS:
|
||||
_fail('event must be one of {} (got {!r}). APPROVE is intentionally '
|
||||
'forbidden.'.format(', '.join(ACCEPTED_EVENTS), event))
|
||||
|
||||
commit_sha = manifest.get('commit_sha')
|
||||
if not isinstance(commit_sha, str) or not COMMIT_SHA_RE.match(commit_sha):
|
||||
_fail('commit_sha must be a 40-character lowercase hex string')
|
||||
|
||||
summary = manifest.get('summary', '')
|
||||
if summary is None:
|
||||
summary = ''
|
||||
if not isinstance(summary, str):
|
||||
_fail('summary must be a string if present')
|
||||
|
||||
try:
|
||||
with open(comments_path, 'r', encoding='utf-8') as f:
|
||||
comments = json.load(f)
|
||||
except (OSError, json.JSONDecodeError) as e:
|
||||
_fail('comments.json is not valid JSON: {}'.format(e))
|
||||
|
||||
if not isinstance(comments, list):
|
||||
_fail('comments.json must be a JSON array')
|
||||
|
||||
for idx, entry in enumerate(comments):
|
||||
_validate_comment_entry(idx, entry)
|
||||
|
||||
return {
|
||||
'pr_number': pr_number,
|
||||
'marker': marker,
|
||||
'event': event,
|
||||
'commit_sha': commit_sha,
|
||||
'summary': summary,
|
||||
'comments': comments,
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Stale-review dismissal
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def find_stale_reviews(client, repo, pr_number, marker):
|
||||
"""Yield (id, state) for each existing review owned by the bot AND
|
||||
whose body contains the marker.
|
||||
|
||||
Filtering on BOTH author == github-actions[bot] AND marker-in-body is
|
||||
the security invariant: a fork PR cannot impersonate the bot login,
|
||||
and a fork PR also cannot inject the marker into a human reviewer's
|
||||
body without API access.
|
||||
"""
|
||||
path = 'repos/{}/pulls/{}/reviews'.format(repo, pr_number)
|
||||
for review in client.paginated(path):
|
||||
user = review.get('user') or {}
|
||||
if user.get('login') != BOT_LOGIN:
|
||||
continue
|
||||
body = review.get('body') or ''
|
||||
if marker not in body:
|
||||
continue
|
||||
yield review.get('id'), review.get('state')
|
||||
|
||||
|
||||
def dismiss_stale_reviews(client, repo, pr_number, marker):
|
||||
"""Dismiss (or, for PENDING reviews, delete) every stale matching review."""
|
||||
dismissal_message = 'Superseded by a newer run'
|
||||
for review_id, state in find_stale_reviews(client, repo, pr_number, marker):
|
||||
if review_id is None:
|
||||
continue
|
||||
if state == 'DISMISSED':
|
||||
# Already inert; nothing to do.
|
||||
continue
|
||||
if state == 'PENDING':
|
||||
# PENDING reviews cannot be dismissed; they must be deleted.
|
||||
print('Deleting pending stale review {}'.format(review_id))
|
||||
try:
|
||||
client.request(
|
||||
'DELETE',
|
||||
'repos/{}/pulls/{}/reviews/{}'.format(
|
||||
repo, pr_number, review_id))
|
||||
except RuntimeError as e:
|
||||
# Don't abort the run on dismissal failure: the new review
|
||||
# will still be posted.
|
||||
print('warning: failed to delete pending review {}: {}'.format(
|
||||
review_id, e), file=sys.stderr)
|
||||
continue
|
||||
print('Dismissing stale review {} (state={})'.format(review_id, state))
|
||||
try:
|
||||
client.request(
|
||||
'PUT',
|
||||
'repos/{}/pulls/{}/reviews/{}/dismissals'.format(
|
||||
repo, pr_number, review_id),
|
||||
json_body={
|
||||
'message': dismissal_message,
|
||||
'event': 'DISMISS',
|
||||
},
|
||||
)
|
||||
except RuntimeError as e:
|
||||
print('warning: failed to dismiss review {}: {}'.format(
|
||||
review_id, e), file=sys.stderr)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Review posting
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _chunk(lst, n):
|
||||
"""Yield successive n-sized slices of lst."""
|
||||
for i in range(0, len(lst), n):
|
||||
yield lst[i:i + n]
|
||||
|
||||
|
||||
def _build_review_body(marker, summary, chunk_index, chunk_total):
|
||||
"""Construct the review body text.
|
||||
|
||||
Always begins with the marker (so future runs can find and dismiss
|
||||
this review). Appends a chunk index when the comment set is split
|
||||
across multiple reviews, and the producer-supplied summary if any.
|
||||
"""
|
||||
parts = [marker]
|
||||
if chunk_total > 1:
|
||||
parts.append('({}/{})'.format(chunk_index + 1, chunk_total))
|
||||
if summary:
|
||||
parts.append('')
|
||||
parts.append(summary)
|
||||
return '\n'.join(parts)
|
||||
|
||||
|
||||
def _comment_to_api(entry):
|
||||
"""Project a validated comment dict to the GitHub API shape."""
|
||||
api = {
|
||||
'path': entry['path'],
|
||||
'line': entry['line'],
|
||||
'side': entry.get('side', 'RIGHT'),
|
||||
'body': entry['body'],
|
||||
}
|
||||
if 'start_line' in entry:
|
||||
api['start_line'] = entry['start_line']
|
||||
api['start_side'] = entry.get('start_side', api['side'])
|
||||
return api
|
||||
|
||||
|
||||
def post_review(client, repo, pr_number, marker, event, commit_sha, summary,
|
||||
comments):
|
||||
"""Post one or more reviews containing the validated comments.
|
||||
|
||||
Comments are split into COMMENTS_PER_REVIEW-sized chunks. Each chunk
|
||||
becomes its own review POST. A failed chunk logs a warning and the
|
||||
loop continues to the next chunk.
|
||||
"""
|
||||
chunks = list(_chunk(comments, COMMENTS_PER_REVIEW))
|
||||
total = len(chunks)
|
||||
if total == 0:
|
||||
print('No comments to post; skipping review creation.')
|
||||
return
|
||||
|
||||
posted_any = False
|
||||
for idx, chunk in enumerate(chunks):
|
||||
if idx > 0:
|
||||
time.sleep(SLEEP_BETWEEN_CHUNKS_SECONDS)
|
||||
body = _build_review_body(marker, summary, idx, total)
|
||||
payload = {
|
||||
'commit_id': commit_sha,
|
||||
'body': body,
|
||||
'event': event,
|
||||
'comments': [_comment_to_api(c) for c in chunk],
|
||||
}
|
||||
print('Posting review chunk {}/{} with {} comment(s)'.format(
|
||||
idx + 1, total, len(chunk)))
|
||||
try:
|
||||
client.request(
|
||||
'POST',
|
||||
'repos/{}/pulls/{}/reviews'.format(repo, pr_number),
|
||||
json_body=payload,
|
||||
)
|
||||
posted_any = True
|
||||
except RuntimeError as e:
|
||||
# Most common cause is HTTP 422: a comment refers to a line
|
||||
# GitHub does not consider part of the diff. Skip the bad
|
||||
# chunk and keep going so other findings still get posted.
|
||||
print('warning: review chunk {}/{} failed: {}'.format(
|
||||
idx + 1, total, e), file=sys.stderr)
|
||||
|
||||
if not posted_any:
|
||||
# If every single chunk failed, surface that as a hard error so
|
||||
# the workflow turns red and a human looks at it.
|
||||
_fail('all review chunks failed to post; see warnings above')
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Entry points
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def cmd_validate(args):
|
||||
result = validate_manifest(args.directory)
|
||||
print(('ok: pr_number={} marker_len={} event={} commit_sha={} '
|
||||
'comments={} summary_len={}').format(
|
||||
result['pr_number'],
|
||||
len(result['marker']),
|
||||
result['event'],
|
||||
result['commit_sha'],
|
||||
len(result['comments']),
|
||||
len(result['summary']),
|
||||
))
|
||||
return 0
|
||||
|
||||
|
||||
def cmd_post(args):
|
||||
result = validate_manifest(args.directory)
|
||||
|
||||
# Empty comment lists short-circuit silently. A producer that ran but
|
||||
# found nothing to flag should not generate noise on the PR.
|
||||
if len(result['comments']) == 0:
|
||||
print('No comments in artifact; nothing to post.')
|
||||
return 0
|
||||
|
||||
token = os.environ.get('GITHUB_TOKEN')
|
||||
if not token:
|
||||
_fail('GITHUB_TOKEN is not set')
|
||||
repo = os.environ.get('GITHUB_REPOSITORY')
|
||||
if not repo:
|
||||
_fail('GITHUB_REPOSITORY is not set (expected "owner/name")')
|
||||
if '/' not in repo:
|
||||
_fail('GITHUB_REPOSITORY must be "owner/name", got {!r}'.format(repo))
|
||||
|
||||
try:
|
||||
client = _github_helpers.GitHubClient(token, user_agent=USER_AGENT)
|
||||
dismiss_stale_reviews(
|
||||
client=client,
|
||||
repo=repo,
|
||||
pr_number=result['pr_number'],
|
||||
marker=result['marker'],
|
||||
)
|
||||
post_review(
|
||||
client=client,
|
||||
repo=repo,
|
||||
pr_number=result['pr_number'],
|
||||
marker=result['marker'],
|
||||
event=result['event'],
|
||||
commit_sha=result['commit_sha'],
|
||||
summary=result['summary'],
|
||||
comments=result['comments'],
|
||||
)
|
||||
except RuntimeError as e:
|
||||
_fail(str(e))
|
||||
return 0
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Validate and post line-anchored PR review comments '
|
||||
'from CI artifacts.',
|
||||
)
|
||||
sub = parser.add_subparsers(dest='command', required=True)
|
||||
|
||||
p_validate = sub.add_parser(
|
||||
'validate',
|
||||
help='Validate manifest.json and comments.json in the given directory.',
|
||||
)
|
||||
p_validate.add_argument('directory')
|
||||
p_validate.set_defaults(func=cmd_validate)
|
||||
|
||||
p_post = sub.add_parser(
|
||||
'post',
|
||||
help='Validate, then dismiss any stale review and post a new one. '
|
||||
'Requires env GITHUB_TOKEN and GITHUB_REPOSITORY.',
|
||||
)
|
||||
p_post.add_argument('directory')
|
||||
p_post.set_defaults(func=cmd_post)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
return args.func(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
@ -10,6 +10,7 @@ CONFIG_BOARD_SERIAL_EXT2="/dev/ttyS3"
|
||||
CONFIG_DRIVERS_ADC_ADS1115=y
|
||||
CONFIG_DRIVERS_ADC_BOARD_ADC=y
|
||||
CONFIG_DRIVERS_BAROMETER_BMP388=y
|
||||
CONFIG_DRIVERS_BAROMETER_DPS310=y
|
||||
CONFIG_DRIVERS_BAROMETER_INVENSENSE_ICP201XX=y
|
||||
CONFIG_DRIVERS_BAROMETER_MS5611=y
|
||||
CONFIG_DRIVERS_CAMERA_CAPTURE=y
|
||||
@ -47,6 +48,7 @@ CONFIG_MODULES_CAMERA_FEEDBACK=y
|
||||
CONFIG_MODULES_COMMANDER=y
|
||||
CONFIG_MODULES_CONTROL_ALLOCATOR=y
|
||||
CONFIG_MODULES_DATAMAN=y
|
||||
CONFIG_NUM_MISSION_ITMES_SUPPORTED=1000
|
||||
CONFIG_MODULES_EKF2=y
|
||||
CONFIG_MODULES_ESC_BATTERY=y
|
||||
CONFIG_MODULES_EVENTS=y
|
||||
@ -73,7 +75,6 @@ CONFIG_MODULES_MC_POS_CONTROL=y
|
||||
CONFIG_MODULES_MC_RATE_CONTROL=y
|
||||
CONFIG_MODULES_NAVIGATOR=y
|
||||
CONFIG_MODE_NAVIGATOR_VTOL_TAKEOFF=y
|
||||
CONFIG_NUM_MISSION_ITMES_SUPPORTED=1000
|
||||
CONFIG_MODULES_RC_UPDATE=y
|
||||
CONFIG_MODULES_SENSORS=y
|
||||
CONFIG_MODULES_TEMPERATURE_COMPENSATION=y
|
||||
@ -101,3 +102,4 @@ CONFIG_SYSTEMCMDS_TUNE_CONTROL=y
|
||||
CONFIG_SYSTEMCMDS_UORB=y
|
||||
CONFIG_SYSTEMCMDS_VER=y
|
||||
CONFIG_SYSTEMCMDS_WORK_QUEUE=y
|
||||
CONFIG_ARCH_CHIP_STM32H7=y
|
||||
|
||||
@ -83,10 +83,12 @@ ist8310 -X -b 1 -R 10 start
|
||||
if param compare SENS_INT_BARO_EN 1
|
||||
then
|
||||
icp201xx -I -a 0x64 start
|
||||
dps310 -I start
|
||||
fi
|
||||
|
||||
#external baro
|
||||
icp201xx -X start
|
||||
dps310 -X start
|
||||
|
||||
unset INA_CONFIGURED
|
||||
unset HAVE_PM2
|
||||
|
||||
@ -202,9 +202,6 @@ extern void stm32_spiinitialize(void);
|
||||
|
||||
extern void board_peripheral_reset(int ms);
|
||||
|
||||
/* Initialise the FRAM MTD. */
|
||||
extern void board_configure_fram(void);
|
||||
|
||||
#include <px4_platform_common/board_common.h>
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
@ -55,12 +55,10 @@
|
||||
|
||||
#include <nuttx/config.h>
|
||||
#include <nuttx/board.h>
|
||||
#include <nuttx/i2c/i2c_master.h>
|
||||
#include <nuttx/spi/spi.h>
|
||||
#include <nuttx/sdio.h>
|
||||
#include <nuttx/mmcsd.h>
|
||||
#include <nuttx/analog/adc.h>
|
||||
#include <nuttx/mtd/mtd.h>
|
||||
#include <nuttx/mm/gran.h>
|
||||
#include <chip.h>
|
||||
#include <stm32_uart.h>
|
||||
@ -72,14 +70,10 @@
|
||||
#include <systemlib/px4_macros.h>
|
||||
#include <px4_arch/io_timer.h>
|
||||
#include <px4_platform_common/init.h>
|
||||
#include <px4_platform_common/micro_hal.h>
|
||||
#include <px4_platform_common/px4_manifest.h>
|
||||
#include <px4_platform_common/px4_mtd.h>
|
||||
#include <px4_platform/gpio.h>
|
||||
#include <px4_platform/board_determine_hw_info.h>
|
||||
#include <px4_platform/board_dma_alloc.h>
|
||||
#include <px4_platform/board_hw_eeprom_rev_ver.h>
|
||||
#include <px4_platform/gpio.h>
|
||||
#include <lib/crc/crc.h>
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-Processor Definitions
|
||||
@ -164,80 +158,6 @@ stm32_boardinitialize(void)
|
||||
px4_gpio_init(gpio, arraySize(gpio));
|
||||
}
|
||||
|
||||
#if !defined(BOOTLOADER)
|
||||
/****************************************************************************
|
||||
* Name: eeprom_read_and_check_mft
|
||||
*
|
||||
* Description:
|
||||
* Read an mtd_mft_v0_t from the EEPROM at the given byte offset and
|
||||
* validate its CRC. Returns true if the record has a recognised version
|
||||
* field and a matching CRC16.
|
||||
****************************************************************************/
|
||||
|
||||
static bool eeprom_read_and_check_mft(struct i2c_master_s *i2c, uint16_t address)
|
||||
{
|
||||
uint8_t addr_write[2] = { (uint8_t)(address >> 8), (uint8_t)(address & 0xFF) };
|
||||
mtd_mft_v0_t mft = {};
|
||||
struct i2c_msg_s msgs[2] = {
|
||||
{ .frequency = 400000, .addr = 0x50, .flags = 0, .buffer = addr_write, .length = sizeof(addr_write) },
|
||||
{ .frequency = 400000, .addr = 0x50, .flags = I2C_M_READ, .buffer = (uint8_t *) &mft, .length = sizeof(mft) },
|
||||
};
|
||||
|
||||
int retries = 5;
|
||||
|
||||
while (I2C_TRANSFER(i2c, msgs, 2) != OK) {
|
||||
if (--retries == 0) {
|
||||
syslog(LOG_WARNING, "[boot] EEPROM I2C comm failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mft.version.id != MTD_MFT_v0) { return false; }
|
||||
|
||||
uint16_t computed = crc16_signature(CRC16_INITIAL, sizeof(mft) - sizeof(mft.crc), (const uint8_t *)&mft);
|
||||
return computed == mft.crc;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: detect_layout_is_fram
|
||||
*
|
||||
* Description:
|
||||
* Determine which EEPROM layout is present by reading MTD_MFT_VER from
|
||||
* the two possible byte offsets and validating its CRC.
|
||||
****************************************************************************/
|
||||
|
||||
static bool detect_layout_is_fram(void)
|
||||
{
|
||||
struct i2c_master_s *i2c = px4_i2cbus_initialize(4);
|
||||
bool ret;
|
||||
|
||||
if (!i2c) {
|
||||
syslog(LOG_WARNING, "[boot] EEPROM I2C init failed, defaulting to FRAM layout\n");
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* FRAM-variant: MTD_MFT_VER after MTD_CALDATA (224 blocks x 32 B). */
|
||||
if (eeprom_read_and_check_mft(i2c, 224u * 32u)) {
|
||||
ret = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* EEPROM-only: MTD_MFT_VER is the first partition (0 blocks x 32 B)*/
|
||||
if (eeprom_read_and_check_mft(i2c, 0u)) {
|
||||
ret = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Neither offset contains a valid record, default to FRAM layout. */
|
||||
ret = true;
|
||||
|
||||
out:
|
||||
px4_i2cbus_uninitialize(i2c);
|
||||
return ret;
|
||||
}
|
||||
#endif /* !defined(BOOTLOADER) */
|
||||
|
||||
/****************************************************************************
|
||||
* Name: board_app_initialize
|
||||
*
|
||||
@ -267,19 +187,15 @@ __EXPORT int board_app_initialize(uintptr_t arg)
|
||||
{
|
||||
#if !defined(BOOTLOADER)
|
||||
/* Need hrt running before using the ADC */
|
||||
|
||||
px4_platform_init();
|
||||
|
||||
/* First SPI init. HW version is not enabled yet, so only always-enabled
|
||||
* buses/devices can be used. */
|
||||
// Use the default HW_VER_REV(0x0,0x0) for Ramtron
|
||||
|
||||
stm32_spiinitialize();
|
||||
|
||||
bool fram_available = detect_layout_is_fram();
|
||||
|
||||
if (fram_available) {
|
||||
board_configure_fram();
|
||||
}
|
||||
|
||||
/* Configure the HW based on the manifest */
|
||||
|
||||
px4_platform_configure();
|
||||
|
||||
if (OK == board_determine_hw_info()) {
|
||||
@ -290,24 +206,14 @@ __EXPORT int board_app_initialize(uintptr_t arg)
|
||||
syslog(LOG_ERR, "[boot] Failed to read HW revision and version\n");
|
||||
}
|
||||
|
||||
/* EEPROM-only boards use a 128Kbit chip: 512 pages × 32 B = 16 KB.
|
||||
* FRAM boards use a 64Kbit EEPROM: 256 pages × 32 B = 8 KB.
|
||||
* Always use 32-byte page writes (safe for 64-byte page chips too). */
|
||||
unsigned int mtd_count = 0;
|
||||
mtd_instance_s **instances = px4_mtd_get_instances(&mtd_count);
|
||||
uint16_t eeprom_npages = fram_available ? 256 : 512;
|
||||
/* Configure the Actual SPI interfaces (after we determined the HW version) */
|
||||
|
||||
/* instances[0] is always EEPROM on both board variants. */
|
||||
if (mtd_count > 0 && instances[0]->mtd_dev != NULL) {
|
||||
px4_at24c_set_npages(instances[0]->mtd_dev, eeprom_npages);
|
||||
}
|
||||
|
||||
/* Second SPI init. Now HW version is determined and complete init can be done. */
|
||||
stm32_spiinitialize();
|
||||
|
||||
board_spi_reset(10, 0xffff);
|
||||
|
||||
/* Configure the DMA allocator */
|
||||
|
||||
if (board_dma_alloc_init() < 0) {
|
||||
syslog(LOG_ERR, "[boot] DMA alloc FAILED\n");
|
||||
}
|
||||
@ -328,33 +234,7 @@ __EXPORT int board_app_initialize(uintptr_t arg)
|
||||
led_on(LED_RED);
|
||||
}
|
||||
|
||||
/* Mount LittleFS as /fs/microsd:
|
||||
* FRAM boards: EEPROM->mtdblock0-4, FRAM->mtdblock5 (LittleFS).
|
||||
* EEPROM-only: EEPROM->mtdblock0-2, FTL on free EEPROM region mtdblock3 (LittleFS). */
|
||||
const char *lfs_dev;
|
||||
|
||||
if (fram_available) {
|
||||
lfs_dev = "/dev/mtdblock5";
|
||||
|
||||
} else {
|
||||
if (mtd_count > 0 && instances[0]->mtd_dev != NULL) {
|
||||
FAR struct mtd_dev_s *eeprom_fs = mtd_partition(instances[0]->mtd_dev, 3, eeprom_npages - 3);
|
||||
|
||||
if (!eeprom_fs || ftl_initialize(3, eeprom_fs) != 0) {
|
||||
syslog(LOG_ERR, "[boot] EEPROM: FTL init failed\n");
|
||||
led_on(LED_RED);
|
||||
}
|
||||
|
||||
} else {
|
||||
syslog(LOG_ERR, "[boot] EEPROM not found\n");
|
||||
led_on(LED_RED);
|
||||
}
|
||||
|
||||
lfs_dev = "/dev/mtdblock3";
|
||||
}
|
||||
|
||||
if (nx_mount(lfs_dev, "/fs/microsd", "littlefs", 0, "autoformat") != 0) {
|
||||
syslog(LOG_ERR, "[boot] failed to mount /fs/microsd\n");
|
||||
if (nx_mount("/dev/mtdblock0", "/fs/microsd", "littlefs", 0, "autoformat") != 0) {
|
||||
led_on(LED_RED);
|
||||
}
|
||||
|
||||
|
||||
@ -37,15 +37,16 @@
|
||||
#include <nuttx/spi/spi.h>
|
||||
#include <px4_platform_common/px4_manifest.h>
|
||||
|
||||
static const px4_mft_device_t spi4 = { // MB85RS1MT on FMUM native: 1Mbit, emulated as (1024 Blocks of 32)
|
||||
static const px4_mft_device_t spi4 = { // FM25V02A on FMUM native: 128K X 8, emulated as (1024 Blocks of 32)
|
||||
.bus_type = px4_mft_device_t::SPI,
|
||||
.devid = SPIDEV_FLASH(0)
|
||||
};
|
||||
static const px4_mft_device_t i2c4 = { // 24LC64T 8K 32 X 256 or 16K for EEPROM only boards
|
||||
static const px4_mft_device_t i2c4 = { // 24LC64T 8K 32 X 256
|
||||
.bus_type = px4_mft_device_t::I2C,
|
||||
.devid = PX4_MK_I2C_DEVID(4, 0x50)
|
||||
};
|
||||
|
||||
|
||||
static const px4_mtd_entry_t fmum_fram = {
|
||||
.device = &spi4,
|
||||
.npart = 1,
|
||||
@ -58,36 +59,7 @@ static const px4_mtd_entry_t fmum_fram = {
|
||||
},
|
||||
};
|
||||
|
||||
/* EEPROM layout for EEPROM-only boards (128Kbit, 512 pages x 32B = 16KB). */
|
||||
static constexpr uint32_t kEepromParts = 3;
|
||||
|
||||
static const px4_mtd_entry_t fmum_eeprom = {
|
||||
.device = &i2c4,
|
||||
.npart = kEepromParts,
|
||||
.partd = {
|
||||
{
|
||||
.type = MTD_MFT_VER,
|
||||
.path = "/fs/mtd_mft_ver",
|
||||
.nblocks = 1
|
||||
},
|
||||
{
|
||||
.type = MTD_MFT_REV,
|
||||
.path = "/fs/mtd_mft_rev",
|
||||
.nblocks = 1
|
||||
},
|
||||
{
|
||||
.type = MTD_NET,
|
||||
.path = "/fs/mtd_net",
|
||||
.nblocks = 1
|
||||
}
|
||||
},
|
||||
};
|
||||
static_assert(kEepromParts == 3,
|
||||
"EEPROM partition count changed: update init.c accordingly");
|
||||
|
||||
/* EEPROM layout for FRAM boards (64Kbit, 256 pages x 32B = 8KB).
|
||||
* Matches existing layout for backwards compatibility reasons. */
|
||||
static const px4_mtd_entry_t fmum_eeprom_fram = {
|
||||
.device = &i2c4,
|
||||
.npart = 5,
|
||||
.partd = {
|
||||
@ -109,24 +81,22 @@ static const px4_mtd_entry_t fmum_eeprom_fram = {
|
||||
{
|
||||
.type = MTD_ID,
|
||||
.path = "/fs/mtd_id",
|
||||
.nblocks = 8
|
||||
.nblocks = 8 // 256 = 32 * 8
|
||||
},
|
||||
{
|
||||
.type = MTD_NET,
|
||||
.path = "/fs/mtd_net",
|
||||
.nblocks = 8
|
||||
.nblocks = 8 // 256 = 32 * 8
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static const px4_mtd_manifest_t board_mtd_config = {
|
||||
.nconfigs = 1,
|
||||
.entries = { &fmum_eeprom }
|
||||
};
|
||||
|
||||
static const px4_mtd_manifest_t board_mtd_config_fram = {
|
||||
.nconfigs = 2,
|
||||
.entries = { &fmum_eeprom_fram, &fmum_fram }
|
||||
.nconfigs = 2,
|
||||
.entries = {
|
||||
&fmum_fram,
|
||||
&fmum_eeprom
|
||||
}
|
||||
};
|
||||
|
||||
static const px4_mft_entry_s mtd_mft = {
|
||||
@ -134,36 +104,20 @@ static const px4_mft_entry_s mtd_mft = {
|
||||
.pmft = (void *) &board_mtd_config,
|
||||
};
|
||||
|
||||
static const px4_mft_entry_s mtd_mft_fram = {
|
||||
.type = MTD,
|
||||
.pmft = (void *) &board_mtd_config_fram,
|
||||
};
|
||||
|
||||
static const px4_mft_entry_s mft_mft = {
|
||||
.type = MFT,
|
||||
.pmft = (void *) system_query_manifest,
|
||||
};
|
||||
|
||||
/* Manifest for EEPROM only boards */
|
||||
static const px4_mft_s mft = {
|
||||
.nmft = 2,
|
||||
.mfts = { &mtd_mft, &mft_mft }
|
||||
.mfts = {
|
||||
&mtd_mft,
|
||||
&mft_mft,
|
||||
}
|
||||
};
|
||||
|
||||
/* Manifest for EEPROM + FRAM boards */
|
||||
static const px4_mft_s mft_fram = {
|
||||
.nmft = 2,
|
||||
.mfts = { &mtd_mft_fram, &mft_mft }
|
||||
};
|
||||
|
||||
static const px4_mft_s *g_manifest = &mft;
|
||||
|
||||
const px4_mft_s *board_get_manifest(void)
|
||||
{
|
||||
return g_manifest;
|
||||
}
|
||||
|
||||
void board_configure_fram(void)
|
||||
{
|
||||
g_manifest = &mft_fram;
|
||||
return &mft;
|
||||
}
|
||||
|
||||
@ -13,7 +13,6 @@ param set-default PWM_MAIN_TIM0 -4
|
||||
param set-default RC_INPUT_PROTO -1
|
||||
|
||||
param set-default IMU_GYRO_CUTOFF 80
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
param set-default MC_PITCHRATE_K 0.4
|
||||
param set-default MC_ROLLRATE_K 0.35
|
||||
param set-default MC_YAWRATE_K 1.2
|
||||
|
||||
@ -3,9 +3,6 @@
|
||||
# Flywoo GNF405 board specific defaults
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
# default airframe quadrotor
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
|
||||
# system_power unavailable
|
||||
param set-default CBRK_SUPPLY_CHK 894281
|
||||
|
||||
|
||||
@ -13,7 +13,6 @@ param set-default PWM_MAIN_TIM0 -4
|
||||
param set-default RC_INPUT_PROTO -1
|
||||
|
||||
param set-default IMU_GYRO_RATEMAX 2000
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
param set-default MC_PITCHRATE_K 0.4
|
||||
param set-default MC_ROLLRATE_K 0.35
|
||||
param set-default MC_YAWRATE_K 1.2
|
||||
|
||||
@ -9,9 +9,6 @@ param set-default BAT1_A_PER_V 17
|
||||
# system_power unavailable
|
||||
param set-default CBRK_SUPPLY_CHK 894281
|
||||
|
||||
# Select the Generic 250 Racer by default
|
||||
param set-default SYS_AUTOSTART 4050
|
||||
|
||||
param set-default SYS_HAS_MAG 0
|
||||
|
||||
# enable gravity fusion
|
||||
|
||||
@ -19,9 +19,6 @@ param set-default BAT1_A_PER_V 59.5
|
||||
# system_power unavailable
|
||||
param set-default CBRK_SUPPLY_CHK 894281
|
||||
|
||||
# Select the Generic 250 Racer by default
|
||||
param set-default SYS_AUTOSTART 4050
|
||||
|
||||
# use EKF2 without mag
|
||||
param set-default SYS_HAS_MAG 0
|
||||
# and enable gravity fusion
|
||||
|
||||
@ -19,9 +19,6 @@ param set-default BAT1_A_PER_V 59.5
|
||||
# system_power unavailable
|
||||
param set-default CBRK_SUPPLY_CHK 894281
|
||||
|
||||
# Select the Generic 250 Racer by default
|
||||
param set-default SYS_AUTOSTART 4050
|
||||
|
||||
# use EKF2 without mag
|
||||
param set-default SYS_HAS_MAG 0
|
||||
# and enable gravity fusion
|
||||
|
||||
@ -19,9 +19,6 @@ param set-default BAT1_A_PER_V 59.5
|
||||
# system_power unavailable
|
||||
param set-default CBRK_SUPPLY_CHK 894281
|
||||
|
||||
# Select the Generic 250 Racer by default
|
||||
param set-default SYS_AUTOSTART 4050
|
||||
|
||||
# use EKF2 without mag
|
||||
param set-default SYS_HAS_MAG 0
|
||||
# and enable gravity fusion
|
||||
|
||||
@ -19,9 +19,6 @@ param set-default BAT1_A_PER_V 59.5
|
||||
# system_power unavailable
|
||||
param set-default CBRK_SUPPLY_CHK 894281
|
||||
|
||||
# Select the Generic 250 Racer by default
|
||||
param set-default SYS_AUTOSTART 4050
|
||||
|
||||
# use EKF2 without mag
|
||||
param set-default SYS_HAS_MAG 0
|
||||
# and enable gravity fusion
|
||||
|
||||
@ -13,7 +13,6 @@ param set-default PWM_MAIN_TIM0 -4
|
||||
param set-default RC_INPUT_PROTO -1
|
||||
|
||||
param set-default IMU_GYRO_CUTOFF 100
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
param set-default MC_PITCHRATE_K 0.4
|
||||
param set-default MC_ROLLRATE_K 0.35
|
||||
param set-default MC_YAWRATE_K 1.2
|
||||
|
||||
@ -17,7 +17,6 @@ param set-default MAV_2_CONFIG 104
|
||||
param set-default SER_TEL4_BAUD 115200
|
||||
|
||||
param set-default IMU_GYRO_CUTOFF 80
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
param set-default MC_PITCHRATE_K 0.4
|
||||
param set-default MC_ROLLRATE_K 0.35
|
||||
param set-default MC_YAWRATE_K 1.2
|
||||
|
||||
@ -16,7 +16,6 @@ param set-default MAV_2_CONFIG 104
|
||||
param set-default SER_TEL4_BAUD 115200
|
||||
|
||||
param set-default IMU_GYRO_CUTOFF 80
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
param set-default MC_PITCHRATE_K 0.4
|
||||
param set-default MC_ROLLRATE_K 0.35
|
||||
param set-default MC_YAWRATE_K 1.2
|
||||
|
||||
@ -13,7 +13,6 @@ param set-default PWM_MAIN_TIM0 -4
|
||||
param set-default RC_INPUT_PROTO -1
|
||||
|
||||
param set-default IMU_GYRO_CUTOFF 80
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
param set-default MC_PITCHRATE_K 0.4
|
||||
param set-default MC_ROLLRATE_K 0.35
|
||||
param set-default MC_YAWRATE_K 1.2
|
||||
|
||||
@ -9,7 +9,4 @@ param set-default BAT1_A_PER_V 17
|
||||
# system_power unavailable
|
||||
param set-default CBRK_SUPPLY_CHK 894281
|
||||
|
||||
# Select the Generic 250 Racer by default
|
||||
param set-default SYS_AUTOSTART 4050
|
||||
|
||||
param set-default SYS_HAS_MAG 0
|
||||
|
||||
@ -13,7 +13,6 @@ param set-default PWM_MAIN_TIM0 -4
|
||||
param set-default RC_INPUT_PROTO -1
|
||||
|
||||
param set-default IMU_GYRO_RATEMAX 2000
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
param set-default MC_PITCHRATE_K 0.4
|
||||
param set-default MC_ROLLRATE_K 0.35
|
||||
param set-default MC_YAWRATE_K 1.2
|
||||
|
||||
@ -13,7 +13,6 @@ param set-default PWM_MAIN_TIM0 -4
|
||||
param set-default RC_INPUT_PROTO -1
|
||||
|
||||
param set-default IMU_GYRO_RATEMAX 2000
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
param set-default MC_PITCHRATE_K 0.4
|
||||
param set-default MC_ROLLRATE_K 0.35
|
||||
param set-default MC_YAWRATE_K 1.2
|
||||
|
||||
@ -13,7 +13,6 @@ param set-default PWM_MAIN_TIM0 -4
|
||||
param set-default RC_INPUT_PROTO -1
|
||||
|
||||
param set-default IMU_GYRO_RATEMAX 2000
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
param set-default MC_PITCHRATE_K 0.4
|
||||
param set-default MC_ROLLRATE_K 0.35
|
||||
param set-default MC_YAWRATE_K 1.2
|
||||
|
||||
@ -13,7 +13,6 @@ param set-default PWM_MAIN_TIM0 -4
|
||||
param set-default RC_INPUT_PROTO -1
|
||||
|
||||
param set-default IMU_GYRO_RATEMAX 2000
|
||||
param set-default SYS_AUTOSTART 4001
|
||||
param set-default MC_PITCHRATE_K 0.4
|
||||
param set-default MC_ROLLRATE_K 0.35
|
||||
param set-default MC_YAWRATE_K 1.2
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
- [Introduction](index.md)
|
||||
- [Basic Concepts](getting_started/px4_basic_concepts.md)
|
||||
|
||||
- [Try PX4 (Simulation)](simulation/px4_simulation_quickstart.md)
|
||||
- [Multicopters](frames_multicopter/index.md)
|
||||
- [Features](features_mc/index.md)
|
||||
- [Flight Modes](flight_modes_mc/index.md)
|
||||
@ -474,6 +474,7 @@
|
||||
- [Worlds](sim_gazebo_classic/worlds.md)
|
||||
- [Multi-Vehicle Sim](sim_gazebo_classic/multi_vehicle_simulation.md)
|
||||
- [Simulate Failsafes](simulation/failsafes.md)
|
||||
- [Pre-built Packages](simulation/px4_sitl_prebuilt_packages.md)
|
||||
- [Hardware](hardware/index.md)
|
||||
- [Flight Controller Reference Design](hardware/reference_design.md)
|
||||
- [Manufacturer’s Board Support Guide](hardware/board_support_guide.md)
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
# Setting up a Developer Environment (Toolchain)
|
||||
|
||||
::: tip
|
||||
You only need a toolchain if you want to **modify and build** PX4 from source.
|
||||
If you just want to run PX4 simulation without changing the code, use a pre-built [Docker container or .deb package](../simulation/px4_sitl_prebuilt_packages.md) instead.
|
||||
:::
|
||||
|
||||
The _supported platforms_ for PX4 development are:
|
||||
|
||||
- [Ubuntu Linux (24.04/22.04)](../dev_setup/dev_env_linux_ubuntu.md)
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
This section contains topics about getting started with PX4 development:
|
||||
|
||||
- [PX4 Simulation QuickStart](../simulation/px4_simulation_quickstart.md) — Try PX4 in simulation without a build environment!
|
||||
- [Initial Setup](../dev_setup/config_initial.md)
|
||||
- [Toolchain Installation](../dev_setup/dev_env.md)
|
||||
- [Building the Code](../dev_setup/building_px4.md)
|
||||
|
||||
@ -21,11 +21,13 @@ Documented changes since the stable release are captured in the evolving [releas
|
||||
|
||||
</div>
|
||||
|
||||
## Try PX4
|
||||
|
||||
No hardware needed. Run PX4 in simulation with a single command using [Docker or a .deb package](simulation/px4_simulation_quickstart.md). Connect [QGroundControl](https://qgroundcontrol.com), [MAVSDK](https://mavsdk.mavlink.io/), or [ROS 2](ros2/index.md) and start flying immediately.
|
||||
|
||||
## For Developers
|
||||
|
||||
:::tip
|
||||
Building on PX4 or extending the platform? Start here: [Development Guide](development/development.md). Set up your [dev environment](dev_setup/config_initial.md), [build from source](dev_setup/building_px4.md), run [SITL simulation](simulation/index.md), or integrate via [ROS 2](ros2/index.md) and [MAVSDK](https://mavsdk.mavlink.io/).
|
||||
:::
|
||||
Want to modify PX4 or build from source? Start with the [Development Guide](development/development.md): set up your [dev environment](dev_setup/dev_env.md), [build the code](dev_setup/building_px4.md), and run [SITL simulation](simulation/index.md).
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
||||
@ -95,207 +95,207 @@ They are not build into the module, and hence are neither published or subscribe
|
||||
|
||||
::: details See messages
|
||||
|
||||
- [VehicleLocalPositionV0](../msg_docs/VehicleLocalPositionV0.md)
|
||||
- [DebugVect](../msg_docs/DebugVect.md)
|
||||
- [FollowTargetEstimator](../msg_docs/FollowTargetEstimator.md)
|
||||
- [Mission](../msg_docs/Mission.md)
|
||||
- [VehicleAttitudeSetpointV0](../msg_docs/VehicleAttitudeSetpointV0.md)
|
||||
- [CameraStatus](../msg_docs/CameraStatus.md)
|
||||
- [GpioRequest](../msg_docs/GpioRequest.md)
|
||||
- [EstimatorStatus](../msg_docs/EstimatorStatus.md)
|
||||
- [PositionControllerLandingStatus](../msg_docs/PositionControllerLandingStatus.md)
|
||||
- [LoggerStatus](../msg_docs/LoggerStatus.md)
|
||||
- [Rpm](../msg_docs/Rpm.md)
|
||||
- [AdcReport](../msg_docs/AdcReport.md)
|
||||
- [GimbalManagerSetManualControl](../msg_docs/GimbalManagerSetManualControl.md)
|
||||
- [Gripper](../msg_docs/Gripper.md)
|
||||
- [UavcanParameterValue](../msg_docs/UavcanParameterValue.md)
|
||||
- [RateCtrlStatus](../msg_docs/RateCtrlStatus.md)
|
||||
- [RoverAttitudeStatus](../msg_docs/RoverAttitudeStatus.md)
|
||||
- [CameraTrigger](../msg_docs/CameraTrigger.md)
|
||||
- [AirspeedWind](../msg_docs/AirspeedWind.md)
|
||||
- [GeneratorStatus](../msg_docs/GeneratorStatus.md)
|
||||
- [DistanceSensorModeChangeRequest](../msg_docs/DistanceSensorModeChangeRequest.md)
|
||||
- [Ekf2Timestamps](../msg_docs/Ekf2Timestamps.md)
|
||||
- [RaptorInput](../msg_docs/RaptorInput.md)
|
||||
- [GeofenceStatus](../msg_docs/GeofenceStatus.md)
|
||||
- [Ping](../msg_docs/Ping.md)
|
||||
- [SensorAccelFifo](../msg_docs/SensorAccelFifo.md)
|
||||
- [EventV0](../msg_docs/EventV0.md)
|
||||
- [EstimatorInnovations](../msg_docs/EstimatorInnovations.md)
|
||||
- [TecsStatus](../msg_docs/TecsStatus.md)
|
||||
- [FollowTargetStatus](../msg_docs/FollowTargetStatus.md)
|
||||
- [InternalCombustionEngineStatus](../msg_docs/InternalCombustionEngineStatus.md)
|
||||
- [OrbTest](../msg_docs/OrbTest.md)
|
||||
- [SensorSelection](../msg_docs/SensorSelection.md)
|
||||
- [MountOrientation](../msg_docs/MountOrientation.md)
|
||||
- [NavigatorStatus](../msg_docs/NavigatorStatus.md)
|
||||
- [FixedWingLateralStatus](../msg_docs/FixedWingLateralStatus.md)
|
||||
- [VehicleAcceleration](../msg_docs/VehicleAcceleration.md)
|
||||
- [EstimatorFusionControl](../msg_docs/EstimatorFusionControl.md)
|
||||
- [PurePursuitStatus](../msg_docs/PurePursuitStatus.md)
|
||||
- [VehicleOpticalFlow](../msg_docs/VehicleOpticalFlow.md)
|
||||
- [RegisterExtComponentReplyV0](../msg_docs/RegisterExtComponentReplyV0.md)
|
||||
- [QshellRetval](../msg_docs/QshellRetval.md)
|
||||
- [SensorGnssRelative](../msg_docs/SensorGnssRelative.md)
|
||||
- [DatamanRequest](../msg_docs/DatamanRequest.md)
|
||||
- [VehicleRoi](../msg_docs/VehicleRoi.md)
|
||||
- [EstimatorBias](../msg_docs/EstimatorBias.md)
|
||||
- [VehicleAngularAccelerationSetpoint](../msg_docs/VehicleAngularAccelerationSetpoint.md)
|
||||
- [LandingTargetPose](../msg_docs/LandingTargetPose.md)
|
||||
- [DifferentialPressure](../msg_docs/DifferentialPressure.md)
|
||||
- [PowerButtonState](../msg_docs/PowerButtonState.md)
|
||||
- [DistanceSensorModeChangeRequest](../msg_docs/DistanceSensorModeChangeRequest.md)
|
||||
- [EscStatus](../msg_docs/EscStatus.md)
|
||||
- [DebugValue](../msg_docs/DebugValue.md)
|
||||
- [EventV0](../msg_docs/EventV0.md)
|
||||
- [TuneControl](../msg_docs/TuneControl.md)
|
||||
- [VehicleStatusV0](../msg_docs/VehicleStatusV0.md)
|
||||
- [EscEepromWrite](../msg_docs/EscEepromWrite.md)
|
||||
- [GpsDump](../msg_docs/GpsDump.md)
|
||||
- [ParameterSetValueRequest](../msg_docs/ParameterSetValueRequest.md)
|
||||
- [MagnetometerBiasEstimate](../msg_docs/MagnetometerBiasEstimate.md)
|
||||
- [GimbalManagerStatus](../msg_docs/GimbalManagerStatus.md)
|
||||
- [DatamanResponse](../msg_docs/DatamanResponse.md)
|
||||
- [SensorHygrometer](../msg_docs/SensorHygrometer.md)
|
||||
- [AutotuneAttitudeControlStatus](../msg_docs/AutotuneAttitudeControlStatus.md)
|
||||
- [ManualControlSwitches](../msg_docs/ManualControlSwitches.md)
|
||||
- [DeviceInformation](../msg_docs/DeviceInformation.md)
|
||||
- [OrbTestLarge](../msg_docs/OrbTestLarge.md)
|
||||
- [YawEstimatorStatus](../msg_docs/YawEstimatorStatus.md)
|
||||
- [VehicleStatusV2](../msg_docs/VehicleStatusV2.md)
|
||||
- [PwmInput](../msg_docs/PwmInput.md)
|
||||
- [AirspeedValidatedV0](../msg_docs/AirspeedValidatedV0.md)
|
||||
- [FlightPhaseEstimation](../msg_docs/FlightPhaseEstimation.md)
|
||||
- [ParameterSetValueResponse](../msg_docs/ParameterSetValueResponse.md)
|
||||
- [DatamanRequest](../msg_docs/DatamanRequest.md)
|
||||
- [VehicleOpticalFlow](../msg_docs/VehicleOpticalFlow.md)
|
||||
- [RoverRateStatus](../msg_docs/RoverRateStatus.md)
|
||||
- [EscEepromRead](../msg_docs/EscEepromRead.md)
|
||||
- [HoverThrustEstimate](../msg_docs/HoverThrustEstimate.md)
|
||||
- [Cpuload](../msg_docs/Cpuload.md)
|
||||
- [MissionResult](../msg_docs/MissionResult.md)
|
||||
- [EstimatorSensorBias](../msg_docs/EstimatorSensorBias.md)
|
||||
- [RaptorStatus](../msg_docs/RaptorStatus.md)
|
||||
- [SensorUwb](../msg_docs/SensorUwb.md)
|
||||
- [NeuralControl](../msg_docs/NeuralControl.md)
|
||||
- [SensorBaro](../msg_docs/SensorBaro.md)
|
||||
- [Ekf2Timestamps](../msg_docs/Ekf2Timestamps.md)
|
||||
- [BatteryStatusV0](../msg_docs/BatteryStatusV0.md)
|
||||
- [EscReport](../msg_docs/EscReport.md)
|
||||
- [ButtonEvent](../msg_docs/ButtonEvent.md)
|
||||
- [CanInterfaceStatus](../msg_docs/CanInterfaceStatus.md)
|
||||
- [LedControl](../msg_docs/LedControl.md)
|
||||
- [TakeoffStatus](../msg_docs/TakeoffStatus.md)
|
||||
- [EstimatorAidSource2d](../msg_docs/EstimatorAidSource2d.md)
|
||||
- [Gripper](../msg_docs/Gripper.md)
|
||||
- [SensorMag](../msg_docs/SensorMag.md)
|
||||
- [LandingGearWheel](../msg_docs/LandingGearWheel.md)
|
||||
- [EstimatorGpsStatus](../msg_docs/EstimatorGpsStatus.md)
|
||||
- [ConfigOverridesV0](../msg_docs/ConfigOverridesV0.md)
|
||||
- [TrajectorySetpoint6dof](../msg_docs/TrajectorySetpoint6dof.md)
|
||||
- [VehicleConstraints](../msg_docs/VehicleConstraints.md)
|
||||
- [CameraTrigger](../msg_docs/CameraTrigger.md)
|
||||
- [GimbalControls](../msg_docs/GimbalControls.md)
|
||||
- [SatelliteInfo](../msg_docs/SatelliteInfo.md)
|
||||
- [TaskStackInfo](../msg_docs/TaskStackInfo.md)
|
||||
- [ControlAllocatorStatus](../msg_docs/ControlAllocatorStatus.md)
|
||||
- [HomePositionV0](../msg_docs/HomePositionV0.md)
|
||||
- [FailureDetectorStatus](../msg_docs/FailureDetectorStatus.md)
|
||||
- [GpioRequest](../msg_docs/GpioRequest.md)
|
||||
- [VehicleAcceleration](../msg_docs/VehicleAcceleration.md)
|
||||
- [Rpm](../msg_docs/Rpm.md)
|
||||
- [EstimatorAidSource1d](../msg_docs/EstimatorAidSource1d.md)
|
||||
- [RtlTimeEstimate](../msg_docs/RtlTimeEstimate.md)
|
||||
- [OpenDroneIdSystem](../msg_docs/OpenDroneIdSystem.md)
|
||||
- [VehicleStatusV1](../msg_docs/VehicleStatusV1.md)
|
||||
- [TrajectorySetpoint6dof](../msg_docs/TrajectorySetpoint6dof.md)
|
||||
- [DebugValue](../msg_docs/DebugValue.md)
|
||||
- [Airspeed](../msg_docs/Airspeed.md)
|
||||
- [SensorBaro](../msg_docs/SensorBaro.md)
|
||||
- [FigureEightStatus](../msg_docs/FigureEightStatus.md)
|
||||
- [ParameterResetRequest](../msg_docs/ParameterResetRequest.md)
|
||||
- [LogMessage](../msg_docs/LogMessage.md)
|
||||
- [PwmInput](../msg_docs/PwmInput.md)
|
||||
- [ActuatorArmed](../msg_docs/ActuatorArmed.md)
|
||||
- [SensorsStatus](../msg_docs/SensorsStatus.md)
|
||||
- [ActuatorTest](../msg_docs/ActuatorTest.md)
|
||||
- [VehicleStatusV2](../msg_docs/VehicleStatusV2.md)
|
||||
- [EscStatus](../msg_docs/EscStatus.md)
|
||||
- [GimbalManagerSetAttitude](../msg_docs/GimbalManagerSetAttitude.md)
|
||||
- [SensorUwb](../msg_docs/SensorUwb.md)
|
||||
- [SensorCorrection](../msg_docs/SensorCorrection.md)
|
||||
- [TiltrotorExtraControls](../msg_docs/TiltrotorExtraControls.md)
|
||||
- [OrbTestLarge](../msg_docs/OrbTestLarge.md)
|
||||
- [VelocityLimits](../msg_docs/VelocityLimits.md)
|
||||
- [RaptorStatus](../msg_docs/RaptorStatus.md)
|
||||
- [ParameterSetUsedRequest](../msg_docs/ParameterSetUsedRequest.md)
|
||||
- [SensorAirflow](../msg_docs/SensorAirflow.md)
|
||||
- [GpioConfig](../msg_docs/GpioConfig.md)
|
||||
- [OrbTestMedium](../msg_docs/OrbTestMedium.md)
|
||||
- [VehicleConstraints](../msg_docs/VehicleConstraints.md)
|
||||
- [GimbalDeviceInformation](../msg_docs/GimbalDeviceInformation.md)
|
||||
- [OpenDroneIdOperatorId](../msg_docs/OpenDroneIdOperatorId.md)
|
||||
- [OpenDroneIdSelfId](../msg_docs/OpenDroneIdSelfId.md)
|
||||
- [ActuatorControlsStatus](../msg_docs/ActuatorControlsStatus.md)
|
||||
- [GeofenceResult](../msg_docs/GeofenceResult.md)
|
||||
- [VehicleMagnetometer](../msg_docs/VehicleMagnetometer.md)
|
||||
- [ArmingCheckReplyV0](../msg_docs/ArmingCheckReplyV0.md)
|
||||
- [WheelEncoders](../msg_docs/WheelEncoders.md)
|
||||
- [Event](../msg_docs/Event.md)
|
||||
- [ActuatorServosTrim](../msg_docs/ActuatorServosTrim.md)
|
||||
- [InputRc](../msg_docs/InputRc.md)
|
||||
- [PositionSetpoint](../msg_docs/PositionSetpoint.md)
|
||||
- [UlogStreamAck](../msg_docs/UlogStreamAck.md)
|
||||
- [TakeoffStatus](../msg_docs/TakeoffStatus.md)
|
||||
- [TuneControl](../msg_docs/TuneControl.md)
|
||||
- [FixedWingRunwayControl](../msg_docs/FixedWingRunwayControl.md)
|
||||
- [VehicleOpticalFlowVel](../msg_docs/VehicleOpticalFlowVel.md)
|
||||
- [VehicleAirData](../msg_docs/VehicleAirData.md)
|
||||
- [QshellReq](../msg_docs/QshellReq.md)
|
||||
- [EstimatorSelectorStatus](../msg_docs/EstimatorSelectorStatus.md)
|
||||
- [CellularStatus](../msg_docs/CellularStatus.md)
|
||||
- [ActionRequest](../msg_docs/ActionRequest.md)
|
||||
- [IrlockReport](../msg_docs/IrlockReport.md)
|
||||
- [GpioOut](../msg_docs/GpioOut.md)
|
||||
- [LandingTargetInnovations](../msg_docs/LandingTargetInnovations.md)
|
||||
- [GimbalManagerInformation](../msg_docs/GimbalManagerInformation.md)
|
||||
- [HoverThrustEstimate](../msg_docs/HoverThrustEstimate.md)
|
||||
- [SystemPower](../msg_docs/SystemPower.md)
|
||||
- [SensorTemp](../msg_docs/SensorTemp.md)
|
||||
- [MavlinkTunnel](../msg_docs/MavlinkTunnel.md)
|
||||
- [GainCompression](../msg_docs/GainCompression.md)
|
||||
- [HeaterStatus](../msg_docs/HeaterStatus.md)
|
||||
- [ParameterSetValueResponse](../msg_docs/ParameterSetValueResponse.md)
|
||||
- [SensorGyroFft](../msg_docs/SensorGyroFft.md)
|
||||
- [NavigatorMissionItem](../msg_docs/NavigatorMissionItem.md)
|
||||
- [FlightPhaseEstimation](../msg_docs/FlightPhaseEstimation.md)
|
||||
- [VehicleCommandAckV0](../msg_docs/VehicleCommandAckV0.md)
|
||||
- [EscEepromRead](../msg_docs/EscEepromRead.md)
|
||||
- [AirspeedValidatedV0](../msg_docs/AirspeedValidatedV0.md)
|
||||
- [FixedWingLateralGuidanceStatus](../msg_docs/FixedWingLateralGuidanceStatus.md)
|
||||
- [EstimatorAidSource2d](../msg_docs/EstimatorAidSource2d.md)
|
||||
- [HealthReport](../msg_docs/HealthReport.md)
|
||||
- [RangingBeacon](../msg_docs/RangingBeacon.md)
|
||||
- [EstimatorSensorBias](../msg_docs/EstimatorSensorBias.md)
|
||||
- [Vtx](../msg_docs/Vtx.md)
|
||||
- [RegisterExtComponentRequestV0](../msg_docs/RegisterExtComponentRequestV0.md)
|
||||
- [OrbTest](../msg_docs/OrbTest.md)
|
||||
- [IridiumsbdStatus](../msg_docs/IridiumsbdStatus.md)
|
||||
- [VehicleImuStatus](../msg_docs/VehicleImuStatus.md)
|
||||
- [YawEstimatorStatus](../msg_docs/YawEstimatorStatus.md)
|
||||
- [SensorGyro](../msg_docs/SensorGyro.md)
|
||||
- [MavlinkLog](../msg_docs/MavlinkLog.md)
|
||||
- [UlogStream](../msg_docs/UlogStream.md)
|
||||
- [NormalizedUnsignedSetpoint](../msg_docs/NormalizedUnsignedSetpoint.md)
|
||||
- [OpenDroneIdArmStatus](../msg_docs/OpenDroneIdArmStatus.md)
|
||||
- [SensorGnssStatus](../msg_docs/SensorGnssStatus.md)
|
||||
- [GpioIn](../msg_docs/GpioIn.md)
|
||||
- [PpsCapture](../msg_docs/PpsCapture.md)
|
||||
- [RoverRateStatus](../msg_docs/RoverRateStatus.md)
|
||||
- [ActuatorOutputs](../msg_docs/ActuatorOutputs.md)
|
||||
- [PositionControllerStatus](../msg_docs/PositionControllerStatus.md)
|
||||
- [EstimatorGpsStatus](../msg_docs/EstimatorGpsStatus.md)
|
||||
- [EstimatorEventFlags](../msg_docs/EstimatorEventFlags.md)
|
||||
- [OrbitStatus](../msg_docs/OrbitStatus.md)
|
||||
- [VehicleGlobalPositionV0](../msg_docs/VehicleGlobalPositionV0.md)
|
||||
- [BatteryStatusV0](../msg_docs/BatteryStatusV0.md)
|
||||
- [NeuralControl](../msg_docs/NeuralControl.md)
|
||||
- [DronecanNodeStatus](../msg_docs/DronecanNodeStatus.md)
|
||||
- [ArmingCheckRequestV0](../msg_docs/ArmingCheckRequestV0.md)
|
||||
- [GpsInjectData](../msg_docs/GpsInjectData.md)
|
||||
- [LaunchDetectionStatus](../msg_docs/LaunchDetectionStatus.md)
|
||||
- [ParameterUpdate](../msg_docs/ParameterUpdate.md)
|
||||
- [SensorAccel](../msg_docs/SensorAccel.md)
|
||||
- [EstimatorAidSource1d](../msg_docs/EstimatorAidSource1d.md)
|
||||
- [LandingGearWheel](../msg_docs/LandingGearWheel.md)
|
||||
- [FailureDetectorStatus](../msg_docs/FailureDetectorStatus.md)
|
||||
- [RcParameterMap](../msg_docs/RcParameterMap.md)
|
||||
- [DebugArray](../msg_docs/DebugArray.md)
|
||||
- [Cpuload](../msg_docs/Cpuload.md)
|
||||
- [UavcanParameterRequest](../msg_docs/UavcanParameterRequest.md)
|
||||
- [SensorsStatusImu](../msg_docs/SensorsStatusImu.md)
|
||||
- [ConfigOverridesV0](../msg_docs/ConfigOverridesV0.md)
|
||||
- [FuelTankStatus](../msg_docs/FuelTankStatus.md)
|
||||
- [MissionResult](../msg_docs/MissionResult.md)
|
||||
- [VehicleAngularVelocity](../msg_docs/VehicleAngularVelocity.md)
|
||||
- [VehicleImu](../msg_docs/VehicleImu.md)
|
||||
- [SensorPreflightMag](../msg_docs/SensorPreflightMag.md)
|
||||
- [CameraCapture](../msg_docs/CameraCapture.md)
|
||||
- [RadioStatus](../msg_docs/RadioStatus.md)
|
||||
- [VehicleLocalPositionSetpoint](../msg_docs/VehicleLocalPositionSetpoint.md)
|
||||
- [EstimatorBias3d](../msg_docs/EstimatorBias3d.md)
|
||||
- [PowerMonitor](../msg_docs/PowerMonitor.md)
|
||||
- [EstimatorAidSource3d](../msg_docs/EstimatorAidSource3d.md)
|
||||
- [RoverSpeedStatus](../msg_docs/RoverSpeedStatus.md)
|
||||
- [SensorGyroFifo](../msg_docs/SensorGyroFifo.md)
|
||||
- [EstimatorStates](../msg_docs/EstimatorStates.md)
|
||||
- [RtlStatus](../msg_docs/RtlStatus.md)
|
||||
- [VehicleStatusV0](../msg_docs/VehicleStatusV0.md)
|
||||
- [InternalCombustionEngineControl](../msg_docs/InternalCombustionEngineControl.md)
|
||||
- [BatteryInfo](../msg_docs/BatteryInfo.md)
|
||||
- [MagWorkerData](../msg_docs/MagWorkerData.md)
|
||||
- [Px4ioStatus](../msg_docs/Px4ioStatus.md)
|
||||
- [SensorMag](../msg_docs/SensorMag.md)
|
||||
- [DebugKeyValue](../msg_docs/DebugKeyValue.md)
|
||||
- [FollowTarget](../msg_docs/FollowTarget.md)
|
||||
- [GimbalDeviceInformation](../msg_docs/GimbalDeviceInformation.md)
|
||||
- [RcChannels](../msg_docs/RcChannels.md)
|
||||
- [SensorAirflow](../msg_docs/SensorAirflow.md)
|
||||
- [FollowTargetStatus](../msg_docs/FollowTargetStatus.md)
|
||||
- [GeofenceStatus](../msg_docs/GeofenceStatus.md)
|
||||
- [HealthReport](../msg_docs/HealthReport.md)
|
||||
- [WheelEncoders](../msg_docs/WheelEncoders.md)
|
||||
- [EstimatorStatus](../msg_docs/EstimatorStatus.md)
|
||||
- [OpenDroneIdOperatorId](../msg_docs/OpenDroneIdOperatorId.md)
|
||||
- [Mission](../msg_docs/Mission.md)
|
||||
- [VehicleOpticalFlowVel](../msg_docs/VehicleOpticalFlowVel.md)
|
||||
- [UlogStream](../msg_docs/UlogStream.md)
|
||||
- [DebugVect](../msg_docs/DebugVect.md)
|
||||
- [ControlAllocatorStatus](../msg_docs/ControlAllocatorStatus.md)
|
||||
- [ArmingCheckRequestV0](../msg_docs/ArmingCheckRequestV0.md)
|
||||
- [SensorTemp](../msg_docs/SensorTemp.md)
|
||||
- [PositionSetpoint](../msg_docs/PositionSetpoint.md)
|
||||
- [VehicleAngularAccelerationSetpoint](../msg_docs/VehicleAngularAccelerationSetpoint.md)
|
||||
- [LedControl](../msg_docs/LedControl.md)
|
||||
- [InternalCombustionEngineControl](../msg_docs/InternalCombustionEngineControl.md)
|
||||
- [EstimatorSelectorStatus](../msg_docs/EstimatorSelectorStatus.md)
|
||||
- [RtlStatus](../msg_docs/RtlStatus.md)
|
||||
- [EstimatorFusionControl](../msg_docs/EstimatorFusionControl.md)
|
||||
- [GpsInjectData](../msg_docs/GpsInjectData.md)
|
||||
- [OrbTestMedium](../msg_docs/OrbTestMedium.md)
|
||||
- [SensorGyro](../msg_docs/SensorGyro.md)
|
||||
- [GimbalDeviceSetAttitude](../msg_docs/GimbalDeviceSetAttitude.md)
|
||||
- [UavcanParameterRequest](../msg_docs/UavcanParameterRequest.md)
|
||||
- [ParameterResetRequest](../msg_docs/ParameterResetRequest.md)
|
||||
- [MagWorkerData](../msg_docs/MagWorkerData.md)
|
||||
- [GpioConfig](../msg_docs/GpioConfig.md)
|
||||
- [CellularStatus](../msg_docs/CellularStatus.md)
|
||||
- [VehicleAirData](../msg_docs/VehicleAirData.md)
|
||||
- [EstimatorAidSource3d](../msg_docs/EstimatorAidSource3d.md)
|
||||
- [PositionControllerStatus](../msg_docs/PositionControllerStatus.md)
|
||||
- [LogMessage](../msg_docs/LogMessage.md)
|
||||
- [GeneratorStatus](../msg_docs/GeneratorStatus.md)
|
||||
- [ManualControlSwitches](../msg_docs/ManualControlSwitches.md)
|
||||
- [HeaterStatus](../msg_docs/HeaterStatus.md)
|
||||
- [FollowTargetEstimator](../msg_docs/FollowTargetEstimator.md)
|
||||
- [FigureEightStatus](../msg_docs/FigureEightStatus.md)
|
||||
- [DebugKeyValue](../msg_docs/DebugKeyValue.md)
|
||||
- [VehicleGlobalPositionV0](../msg_docs/VehicleGlobalPositionV0.md)
|
||||
- [DeviceInformation](../msg_docs/DeviceInformation.md)
|
||||
- [Ping](../msg_docs/Ping.md)
|
||||
- [CanInterfaceStatus](../msg_docs/CanInterfaceStatus.md)
|
||||
- [AdcReport](../msg_docs/AdcReport.md)
|
||||
- [RoverSpeedStatus](../msg_docs/RoverSpeedStatus.md)
|
||||
- [AutotuneAttitudeControlStatus](../msg_docs/AutotuneAttitudeControlStatus.md)
|
||||
- [RangingBeacon](../msg_docs/RangingBeacon.md)
|
||||
- [PurePursuitStatus](../msg_docs/PurePursuitStatus.md)
|
||||
- [SensorHygrometer](../msg_docs/SensorHygrometer.md)
|
||||
- [MagnetometerBiasEstimate](../msg_docs/MagnetometerBiasEstimate.md)
|
||||
- [Event](../msg_docs/Event.md)
|
||||
- [TiltrotorExtraControls](../msg_docs/TiltrotorExtraControls.md)
|
||||
- [LandingTargetPose](../msg_docs/LandingTargetPose.md)
|
||||
- [VehicleAttitudeSetpointV0](../msg_docs/VehicleAttitudeSetpointV0.md)
|
||||
- [UlogStreamAck](../msg_docs/UlogStreamAck.md)
|
||||
- [EstimatorStates](../msg_docs/EstimatorStates.md)
|
||||
- [Airspeed](../msg_docs/Airspeed.md)
|
||||
- [GimbalManagerInformation](../msg_docs/GimbalManagerInformation.md)
|
||||
- [QshellRetval](../msg_docs/QshellRetval.md)
|
||||
- [ParameterSetUsedRequest](../msg_docs/ParameterSetUsedRequest.md)
|
||||
- [SensorGyroFifo](../msg_docs/SensorGyroFifo.md)
|
||||
- [DifferentialPressure](../msg_docs/DifferentialPressure.md)
|
||||
- [NavigatorMissionItem](../msg_docs/NavigatorMissionItem.md)
|
||||
- [OpenDroneIdArmStatus](../msg_docs/OpenDroneIdArmStatus.md)
|
||||
- [PowerMonitor](../msg_docs/PowerMonitor.md)
|
||||
- [OrbitStatus](../msg_docs/OrbitStatus.md)
|
||||
- [RoverAttitudeStatus](../msg_docs/RoverAttitudeStatus.md)
|
||||
- [RcParameterMap](../msg_docs/RcParameterMap.md)
|
||||
- [ButtonEvent](../msg_docs/ButtonEvent.md)
|
||||
- [InternalCombustionEngineStatus](../msg_docs/InternalCombustionEngineStatus.md)
|
||||
- [GainCompression](../msg_docs/GainCompression.md)
|
||||
- [VehicleMagnetometer](../msg_docs/VehicleMagnetometer.md)
|
||||
- [VehicleImu](../msg_docs/VehicleImu.md)
|
||||
- [CameraCapture](../msg_docs/CameraCapture.md)
|
||||
- [InputRc](../msg_docs/InputRc.md)
|
||||
- [GpioOut](../msg_docs/GpioOut.md)
|
||||
- [PpsCapture](../msg_docs/PpsCapture.md)
|
||||
- [MavlinkTunnel](../msg_docs/MavlinkTunnel.md)
|
||||
- [EstimatorBias3d](../msg_docs/EstimatorBias3d.md)
|
||||
- [QshellReq](../msg_docs/QshellReq.md)
|
||||
- [ActuatorControlsStatus](../msg_docs/ActuatorControlsStatus.md)
|
||||
- [FixedWingLateralGuidanceStatus](../msg_docs/FixedWingLateralGuidanceStatus.md)
|
||||
- [DebugArray](../msg_docs/DebugArray.md)
|
||||
- [PowerButtonState](../msg_docs/PowerButtonState.md)
|
||||
- [NormalizedUnsignedSetpoint](../msg_docs/NormalizedUnsignedSetpoint.md)
|
||||
- [GimbalManagerStatus](../msg_docs/GimbalManagerStatus.md)
|
||||
- [SystemPower](../msg_docs/SystemPower.md)
|
||||
- [VehicleLocalPositionV0](../msg_docs/VehicleLocalPositionV0.md)
|
||||
- [GimbalManagerSetAttitude](../msg_docs/GimbalManagerSetAttitude.md)
|
||||
- [SensorAccel](../msg_docs/SensorAccel.md)
|
||||
- [RegisterExtComponentReplyV0](../msg_docs/RegisterExtComponentReplyV0.md)
|
||||
- [VelocityLimits](../msg_docs/VelocityLimits.md)
|
||||
- [ActuatorTest](../msg_docs/ActuatorTest.md)
|
||||
- [TecsStatus](../msg_docs/TecsStatus.md)
|
||||
- [LandingTargetInnovations](../msg_docs/LandingTargetInnovations.md)
|
||||
- [SensorsStatus](../msg_docs/SensorsStatus.md)
|
||||
- [GimbalManagerSetManualControl](../msg_docs/GimbalManagerSetManualControl.md)
|
||||
- [FixedWingLateralStatus](../msg_docs/FixedWingLateralStatus.md)
|
||||
- [EstimatorBias](../msg_docs/EstimatorBias.md)
|
||||
- [SensorAccelFifo](../msg_docs/SensorAccelFifo.md)
|
||||
- [RadioStatus](../msg_docs/RadioStatus.md)
|
||||
- [SensorsStatusImu](../msg_docs/SensorsStatusImu.md)
|
||||
- [SensorGyroFft](../msg_docs/SensorGyroFft.md)
|
||||
- [VehicleImuStatus](../msg_docs/VehicleImuStatus.md)
|
||||
- [SatelliteInfo](../msg_docs/SatelliteInfo.md)
|
||||
- [ActuatorServosTrim](../msg_docs/ActuatorServosTrim.md)
|
||||
- [LoggerStatus](../msg_docs/LoggerStatus.md)
|
||||
- [VehicleRoi](../msg_docs/VehicleRoi.md)
|
||||
- [VehicleCommandAckV0](../msg_docs/VehicleCommandAckV0.md)
|
||||
- [MountOrientation](../msg_docs/MountOrientation.md)
|
||||
- [DatamanResponse](../msg_docs/DatamanResponse.md)
|
||||
- [GpsDump](../msg_docs/GpsDump.md)
|
||||
- [RegisterExtComponentRequestV0](../msg_docs/RegisterExtComponentRequestV0.md)
|
||||
- [BatteryInfo](../msg_docs/BatteryInfo.md)
|
||||
- [ActuatorArmed](../msg_docs/ActuatorArmed.md)
|
||||
- [NavigatorStatus](../msg_docs/NavigatorStatus.md)
|
||||
- [IridiumsbdStatus](../msg_docs/IridiumsbdStatus.md)
|
||||
- [UavcanParameterValue](../msg_docs/UavcanParameterValue.md)
|
||||
- [GeofenceResult](../msg_docs/GeofenceResult.md)
|
||||
- [EstimatorInnovations](../msg_docs/EstimatorInnovations.md)
|
||||
- [IrlockReport](../msg_docs/IrlockReport.md)
|
||||
- [DronecanNodeStatus](../msg_docs/DronecanNodeStatus.md)
|
||||
- [ActionRequest](../msg_docs/ActionRequest.md)
|
||||
- [SensorPreflightMag](../msg_docs/SensorPreflightMag.md)
|
||||
- [FuelTankStatus](../msg_docs/FuelTankStatus.md)
|
||||
- [ParameterSetValueRequest](../msg_docs/ParameterSetValueRequest.md)
|
||||
- [Vtx](../msg_docs/Vtx.md)
|
||||
- [FollowTarget](../msg_docs/FollowTarget.md)
|
||||
- [ArmingCheckReplyV0](../msg_docs/ArmingCheckReplyV0.md)
|
||||
- [SensorGnssStatus](../msg_docs/SensorGnssStatus.md)
|
||||
- [PositionControllerLandingStatus](../msg_docs/PositionControllerLandingStatus.md)
|
||||
- [GpioIn](../msg_docs/GpioIn.md)
|
||||
- [LaunchDetectionStatus](../msg_docs/LaunchDetectionStatus.md)
|
||||
- [SensorGnssRelative](../msg_docs/SensorGnssRelative.md)
|
||||
- [CameraStatus](../msg_docs/CameraStatus.md)
|
||||
- [AirspeedWind](../msg_docs/AirspeedWind.md)
|
||||
- [ActuatorOutputs](../msg_docs/ActuatorOutputs.md)
|
||||
- [TaskStackInfo](../msg_docs/TaskStackInfo.md)
|
||||
- [RateCtrlStatus](../msg_docs/RateCtrlStatus.md)
|
||||
- [RaptorInput](../msg_docs/RaptorInput.md)
|
||||
- [OpenDroneIdSelfId](../msg_docs/OpenDroneIdSelfId.md)
|
||||
- [VehicleStatusV1](../msg_docs/VehicleStatusV1.md)
|
||||
- [HomePositionV0](../msg_docs/HomePositionV0.md)
|
||||
- [ParameterUpdate](../msg_docs/ParameterUpdate.md)
|
||||
- [MavlinkLog](../msg_docs/MavlinkLog.md)
|
||||
:::
|
||||
|
||||
@ -6,6 +6,11 @@ SIH (Simulation-In-Hardware) is a lightweight, headless simulator with zero exte
|
||||
No GUI, no external processes, no rendering overhead — just PX4 running a C++ physics model.
|
||||
This makes it the fastest way to iterate on flight code.
|
||||
|
||||
::: tip
|
||||
SIH is also available as a [prebuilt Docker container or .deb package](../simulation/px4_sitl_prebuilt_packages.md), which is useful if you don't need to modify PX4 itself.
|
||||
See [PX4 Simulation QuickStart](px4_simulation_quickstart.md) for a one-line instruction on how this is used.
|
||||
:::
|
||||
|
||||
## Overview
|
||||
|
||||
SIH runs as a PX4 module that replaces real sensor and actuator hardware with a simulated physics model.
|
||||
|
||||
@ -27,11 +27,15 @@ See [PX4-Autopilot#23602](https://github.com/PX4/PX4-Autopilot/issues/23602) for
|
||||
| Simulator | Description |
|
||||
| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Gazebo](../sim_gazebo_gz/index.md) | Gazebo supersedes [Gazebo Classic](../sim_gazebo_classic/index.md), featuring more advanced rendering, physics and sensor models. It is the only version of Gazebo available from Ubuntu Linux 22.04<br><br>A powerful 3D simulation environment that is particularly suitable for testing object-avoidance and computer vision. It can also be used for [multi-vehicle simulation](../simulation/multi-vehicle-simulation.md) and is commonly used with [ROS](../simulation/ros_interface.md), a collection of tools for automating vehicle control. <br><br><strong>Supported Vehicles:</strong> Quad, VTOL (Standard, Tailsitter, Tiltroter), Plane, Rovers |
|
||||
| [Gazebo Classic](../sim_gazebo_classic/index.md) | A powerful 3D simulation environment that is particularly suitable for testing object-avoidance and computer vision. It can also be used for [multi-vehicle simulation](../simulation/multi-vehicle-simulation.md) and is commonly used with [ROS](../simulation/ros_interface.md), a collection of tools for automating vehicle control.<br><br>**Supported Vehicles:** Quad ([Iris](../airframes/airframe_reference.md#copter_quadrotor_x_generic_quadcopter)), Hex (Typhoon H480), [Generic Standard VTOL (QuadPlane)](../airframes/airframe_reference.md#vtol_standard_vtol_generic_standard_vtol), Tailsitter, Plane, Rover, Submarine |
|
||||
| [SIH](../sim_sih/index.md) | A lightweight, headless simulator that runs physics directly inside PX4 as a C++ module (no external dependencies). Headless by default for fastest iteration. Supports ROS 2 via uXRCE-DDS. Can also run on flight controller hardware (`SYS_HITL=2`).<br><br>**Supported Vehicles:** Quad, Hex, Plane, Tailsitter, Standard VTOL, Rover |
|
||||
| [Gazebo Classic](../sim_gazebo_classic/index.md) | A powerful 3D simulation environment that is particularly suitable for testing object-avoidance and computer vision. It can also be used for [multi-vehicle simulation](../simulation/multi-vehicle-simulation.md) and is commonly used with [ROS](../simulation/ros_interface.md), a collection of tools for automating vehicle control.<br><br>**Supported Vehicles:** Quad ([Iris](../airframes/airframe_reference.md#copter_quadrotor_x_generic_quadcopter)), Hex (Typhoon H480), [Generic Standard VTOL (QuadPlane)](../airframes/airframe_reference.md#vtol_standard_vtol_generic_standard_vtol), Tailsitter, Plane, Rover, Submarine |
|
||||
|
||||
There are also a number of [Community Supported Simulators](../simulation/community_supported_simulators.md).
|
||||
|
||||
:::tip
|
||||
To run PX4 SITL without setting up a build environment, [pre-built packages and containers](px4_sitl_prebuilt_packages.md) are available.
|
||||
:::
|
||||
|
||||
### Simulator Comparison
|
||||
|
||||
| Feature | Gazebo | SIH |
|
||||
|
||||
25
docs/en/simulation/px4_simulation_quickstart.md
Normal file
25
docs/en/simulation/px4_simulation_quickstart.md
Normal file
@ -0,0 +1,25 @@
|
||||
# PX4 Simulation QuickStart
|
||||
|
||||
First install [Docker](https://docs.docker.com/get-docker/) (a free tool that runs containers).
|
||||
|
||||
The following command will then run a PX4 quadrotor simulation that you can connect to [QGroundControl](https://qgroundcontrol.com), [MAVSDK](https://mavsdk.mavlink.io/) or [ROS 2](../ros2/user_guide.md) (on Linux, macOS, and Windows):
|
||||
|
||||
```sh
|
||||
docker run --rm -it -p 14550:14550/udp px4io/px4-sitl:latest
|
||||
```
|
||||
|
||||
That's it — open [QGroundControl](https://qgroundcontrol.com) and fly!
|
||||
|
||||
::: tip
|
||||
|
||||
To try [other vehicle types](../sim_sih/#supported-vehicle-types) append the corresponding line below to the command:
|
||||
|
||||
```sh
|
||||
-e PX4_SIM_MODEL=sihsim_airplane # Plane
|
||||
-e PX4_SIM_MODEL=sihsim_standard_vtol # Standard VTOL
|
||||
-e PX4_SIM_MODEL=sihsim_rover # Ackermann rover
|
||||
```
|
||||
|
||||
For more information and options see [Container Images](../simulation/px4_sitl_prebuilt_packages.md#container-images) (in _Pre-built SITL Packages_) and [SIH Simulation](../sim_sih/index.md).
|
||||
|
||||
:::
|
||||
297
docs/en/simulation/px4_sitl_prebuilt_packages.md
Normal file
297
docs/en/simulation/px4_sitl_prebuilt_packages.md
Normal file
@ -0,0 +1,297 @@
|
||||
# Pre-built SITL Packages
|
||||
|
||||
Pre-built packages let you run [PX4 SITL simulation](index.md) without setting up a build environment.
|
||||
|
||||
This is very useful if you don't need to modify PX4 itself.
|
||||
For example, if you want to write drone apps using [MAVSDK](https://mavsdk.mavlink.io) or [ROS 2](../ros2/user_guide.md), or you just want to fly with PX4.
|
||||
|
||||
::: tip
|
||||
See [PX4 Simulation QuickStart](px4_simulation_quickstart.md) for a one-line instruction to run the SIH package in a container.
|
||||
:::
|
||||
|
||||
## What's Available
|
||||
|
||||
Two simulators are packaged, each available as a `.deb` package (Ubuntu) or a Docker [container](#container-images) (any OS):
|
||||
|
||||
| Simulator | Format | Package / Image | Size |
|
||||
| -------------------------------------------- | --------- | ----------------------- | ------- |
|
||||
| [SIH](../sim_sih/index.md) | .deb | `px4` | ~10 MB |
|
||||
| | container | `px4io/px4-sitl` | ~100 MB |
|
||||
| [Gazebo Harmonic](../sim_gazebo_gz/index.md) | .deb | `px4-gazebo` | ~30 MB |
|
||||
| | container | `px4io/px4-sitl-gazebo` | ~2 GB |
|
||||
|
||||
SIH is a lightweight, headless simulator built into PX4 with no external dependencies.
|
||||
Gazebo provides full 3D simulation with camera, LiDAR, and custom worlds.
|
||||
Sizes are approximate and vary between releases.
|
||||
|
||||
For help choosing between simulators, see the [simulator comparison table](index.md#simulator-comparison).
|
||||
|
||||
### Versions and Releases
|
||||
|
||||
Packages and images are versioned to match PX4 tags (e.g. `v1.17.0`, `v1.17.0~beta1`).
|
||||
`.deb` packages are built for **Ubuntu 22.04 (Jammy)** and **24.04 (Noble)**, on both **amd64** and **arm64**.
|
||||
Container images support **amd64** and **arm64**.
|
||||
Stable releases and pre-releases are published on the [PX4 Releases](https://github.com/PX4/PX4-Autopilot/releases) page.
|
||||
|
||||
## .deb Packages (Ubuntu)
|
||||
|
||||
Download the `.deb` file for your Ubuntu version and architecture from the [PX4 Releases](https://github.com/PX4/PX4-Autopilot/releases) page, then install as shown below.
|
||||
After installation the binary is added to the default Ubuntu system paths, and can be run from anywhere.
|
||||
|
||||
### px4 (SIH)
|
||||
|
||||
No extra repositories are required:
|
||||
|
||||
```bash
|
||||
sudo apt install ./px4_*.deb
|
||||
```
|
||||
|
||||
### px4-gazebo (Gazebo Harmonic)
|
||||
|
||||
The package depends on Gazebo Harmonic runtime libraries from the OSRF repository.
|
||||
Add the repository first, then install:
|
||||
|
||||
```bash
|
||||
# Add OSRF Gazebo repository (one-time setup)
|
||||
sudo curl -fsSL https://packages.osrfoundation.org/gazebo.gpg \
|
||||
-o /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] \
|
||||
http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" \
|
||||
| sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null
|
||||
sudo apt update
|
||||
|
||||
# Install (resolves Gazebo dependencies automatically)
|
||||
sudo apt install ./px4-gazebo_*.deb
|
||||
```
|
||||
|
||||
### Uninstalling
|
||||
|
||||
```bash
|
||||
sudo apt remove px4 # SIH package
|
||||
sudo apt remove px4-gazebo # Gazebo package
|
||||
```
|
||||
|
||||
## Container Images
|
||||
|
||||
Container images are built using the same `.deb` packages described above, packaged into minimal Docker images.
|
||||
They are published to [Docker Hub](https://hub.docker.com/u/px4io) on every tagged release.
|
||||
You will need to [install Docker](https://docs.docker.com/get-docker/).
|
||||
|
||||
| Image | Simulator |
|
||||
| ----------------------------- | --------------- |
|
||||
| `px4io/px4-sitl:<tag>` | SIH (headless) |
|
||||
| `px4io/px4-sitl-gazebo:<tag>` | Gazebo Harmonic |
|
||||
|
||||
Tags follow PX4 versions (e.g. `v1.17.0`).
|
||||
|
||||
### Running
|
||||
|
||||
```bash
|
||||
# SIH
|
||||
docker run --rm -it -p 14550:14550/udp px4io/px4-sitl:latest
|
||||
|
||||
# Gazebo
|
||||
docker run --rm -it -p 14550:14550/udp px4io/px4-sitl-gazebo:latest
|
||||
```
|
||||
|
||||
Pass environment variables with `-e`:
|
||||
|
||||
```bash
|
||||
docker run --rm -it -p 14550:14550/udp \
|
||||
-e PX4_SIM_MODEL=sihsim_airplane \
|
||||
px4io/px4-sitl:latest
|
||||
```
|
||||
|
||||
The quick-start command above only exposes the QGroundControl port.
|
||||
To use MAVSDK, uXRCE-DDS (ROS 2), or MAVSim Viewer, expose the additional ports:
|
||||
|
||||
```bash
|
||||
docker run --rm -it \
|
||||
-p 14550:14550/udp \
|
||||
-p 14540:14540/udp \
|
||||
-p 8888:8888/udp \
|
||||
-p 19410:19410/udp \
|
||||
px4io/px4-sitl:latest
|
||||
```
|
||||
|
||||
| Port | Protocol | Used by |
|
||||
| ----- | -------- | --------------------------- |
|
||||
| 14550 | UDP | QGroundControl |
|
||||
| 14540 | UDP | MAVSDK / offboard API |
|
||||
| 8888 | UDP | uXRCE-DDS agent (ROS 2) |
|
||||
| 19410 | UDP | SIH display (MAVSim Viewer) |
|
||||
|
||||
On Linux, you can skip individual port flags and use `--network host` instead:
|
||||
|
||||
```bash
|
||||
docker run --rm -it --network host px4io/px4-sitl:latest
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
These options apply to both `.deb` packages and containers.
|
||||
Note that after the first section below we only show how to use them with the deb packages (the pattern for using the options doesn't change).
|
||||
|
||||
### Vehicle Selection
|
||||
|
||||
Set `PX4_SIM_MODEL` to choose a vehicle.
|
||||
|
||||
SIH:
|
||||
|
||||
```bash
|
||||
# Deb package
|
||||
PX4_SIM_MODEL=sihsim_airplane px4
|
||||
|
||||
# Container
|
||||
docker run --rm -it -p 14550:14550/udp px4io/px4-sitl:latest -e PX4_SIM_MODEL=sihsim_airplane
|
||||
```
|
||||
|
||||
Gazebo:
|
||||
|
||||
```
|
||||
# Deb package
|
||||
PX4_SIM_MODEL=gz_x500 px4-gazebo
|
||||
|
||||
# Container
|
||||
docker run --rm -it -p 14550:14550/udp px4io/px4-sitl-gazebo:latest -e PX4_SIM_MODEL=gz_x500
|
||||
```
|
||||
|
||||
See [SIH Supported Vehicles](../sim_sih/index.md#supported-vehicle-types) and [Gazebo Vehicles](../sim_gazebo_gz/vehicles.md) for the full lists.
|
||||
|
||||
### World Selection (Gazebo only)
|
||||
|
||||
```sh
|
||||
PX4_GZ_WORLD=baylands PX4_SIM_MODEL=gz_x500 px4-gazebo
|
||||
```
|
||||
|
||||
See [Gazebo Worlds](../sim_gazebo_gz/worlds.md) for available worlds.
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------------------- | ------------------------------------------------------------------ | -------------------- |
|
||||
| `PX4_SIM_MODEL` | Vehicle model (e.g. `gz_x500`, `sihsim_quadx`) | (required) |
|
||||
| `PX4_GZ_WORLD` | Gazebo world name, without `.sdf` (e.g. `baylands`) | `default` |
|
||||
| `HEADLESS` | Set to `1` to disable Gazebo GUI | (unset) |
|
||||
| `PX4_UXRCE_DDS_PORT` | uXRCE-DDS agent UDP port | `8888` |
|
||||
| `PX4_UXRCE_DDS_NS` | uXRCE-DDS ROS namespace | (none) |
|
||||
| `XDG_DATA_HOME` | Base directory for per-instance working data (parameters, dataman) | `$HOME/.local/share` |
|
||||
|
||||
## Multi-Instance
|
||||
|
||||
Multiple simulated vehicles can run simultaneously by passing the `-i` flag with an instance number.
|
||||
Each instance must be started in a separate terminal (or container). This works with both simulators.
|
||||
|
||||
```sh
|
||||
# Terminal 1
|
||||
PX4_SIM_MODEL=sihsim_quadx px4 -i 0
|
||||
|
||||
# Terminal 2
|
||||
PX4_SIM_MODEL=sihsim_quadx px4 -i 1
|
||||
```
|
||||
|
||||
MAVLink and uXRCE-DDS port numbers are automatically offset by the instance number.
|
||||
|
||||
Each package (`px4` and `px4-gazebo`) is a standalone install. Do not mix instances from the two packages in the same session.
|
||||
|
||||
## MAVLink and QGroundControl
|
||||
|
||||
PX4 opens several MAVLink UDP ports on startup.
|
||||
[QGroundControl](https://qgroundcontrol.com) auto-connects on UDP port 14550.
|
||||
You can also connect [MAVSDK](https://mavsdk.mavlink.io) or any MAVLink-compatible tool.
|
||||
|
||||
| Link | Mode | UDP Local Port | UDP Remote Port | Data Rate |
|
||||
| ---------------------- | ------- | ---------------- | ---------------- | --------- |
|
||||
| GCS link | Normal | 18570 + instance | 14550 | 4 Mbps |
|
||||
| API/Offboard link | Onboard | 14580 + instance | 14540 + instance | 4 Mbps |
|
||||
| Onboard link to camera | Onboard | 14280 + instance | 14030 + instance | 4 kbps |
|
||||
| Onboard link to gimbal | Gimbal | 13030 + instance | 13280 + instance | 400 kbps |
|
||||
| SIH display (SIH only) | Custom | 19450 + instance | 19410 + instance | 400 kbps |
|
||||
|
||||
By default, MAVLink only listens on localhost.
|
||||
Set parameter `MAV_{i}_BROADCAST = 1` to enable network access.
|
||||
|
||||
## ROS 2 Integration
|
||||
|
||||
The `uxrce_dds_client` module starts automatically and connects to a Micro XRCE-DDS Agent over UDP.
|
||||
Run the agent before starting PX4:
|
||||
|
||||
```sh
|
||||
MicroXRCEAgent udp4 -p 8888
|
||||
```
|
||||
|
||||
| Setting | Default |
|
||||
| ---------- | ----------- |
|
||||
| Transport | UDP |
|
||||
| Agent IP | `127.0.0.1` |
|
||||
| Agent Port | `8888` |
|
||||
|
||||
Environment variables `PX4_UXRCE_DDS_PORT` and `PX4_UXRCE_DDS_NS` override the corresponding PX4 parameters ([UXRCE_DDS_PRT](../advanced_config/parameter_reference.md#UXRCE_DDS_PRT), [UXRCE_DDS_NS_IDX](../advanced_config/parameter_reference.md#UXRCE_DDS_NS_IDX)) at runtime without modifying stored parameters:
|
||||
|
||||
```sh
|
||||
PX4_UXRCE_DDS_PORT=9999 PX4_UXRCE_DDS_NS=drone1 PX4_SIM_MODEL=sihsim_quadx px4
|
||||
```
|
||||
|
||||
## Daemon Mode
|
||||
|
||||
Start PX4 without an interactive shell (useful for CI pipelines and automated testing):
|
||||
|
||||
```sh
|
||||
PX4_SIM_MODEL=sihsim_quadx px4 -d
|
||||
```
|
||||
|
||||
## Installed File Layout
|
||||
|
||||
### px4
|
||||
|
||||
```txt
|
||||
/opt/px4/
|
||||
bin/
|
||||
px4 # PX4 binary
|
||||
px4-* # Module symlinks
|
||||
px4-alias.sh # Shell aliases
|
||||
etc/ # ROMFS (init scripts, mixers, airframes)
|
||||
init.d-posix/
|
||||
|
||||
/usr/bin/px4 -> /opt/px4/bin/px4
|
||||
```
|
||||
|
||||
### px4-gazebo
|
||||
|
||||
```txt
|
||||
/opt/px4-gazebo/
|
||||
bin/
|
||||
px4 # PX4 binary
|
||||
px4-gazebo # Gazebo wrapper (sets GZ_SIM_* env vars)
|
||||
px4-* # Module symlinks
|
||||
px4-alias.sh # Shell aliases
|
||||
etc/ # ROMFS (init scripts, mixers, airframes)
|
||||
init.d-posix/
|
||||
share/gz/
|
||||
models/ # Gazebo vehicle models
|
||||
worlds/ # Gazebo world files
|
||||
server.config
|
||||
lib/gz/plugins/ # PX4 Gazebo plugins
|
||||
|
||||
/usr/bin/px4-gazebo -> /opt/px4-gazebo/bin/px4-gazebo
|
||||
```
|
||||
|
||||
### Runtime directories (created on first run, per user)
|
||||
|
||||
```sh
|
||||
$XDG_DATA_HOME/px4/rootfs/<instance>/ # parameters, dataman, eeprom
|
||||
```
|
||||
|
||||
## Building .deb Files Locally
|
||||
|
||||
To build `.deb` files locally (e.g. to package a custom PX4 branch):
|
||||
|
||||
```sh
|
||||
# SIH — produces px4_*.deb
|
||||
make px4_sitl_sih
|
||||
cd build/px4_sitl_sih && cpack -G DEB
|
||||
|
||||
# Gazebo — produces px4-gazebo_*.deb
|
||||
make px4_sitl_default
|
||||
cd build/px4_sitl_default && cpack -G DEB
|
||||
```
|
||||
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
#***************************************************************************
|
||||
#
|
||||
# Copyright (c) 2015 PX4 Development Team. All rights reserved.
|
||||
@ -35,9 +35,6 @@
|
||||
#
|
||||
# @author Andreas Antener <andreas@uaventure.com>
|
||||
#
|
||||
# The shebang of this file is currently Python2 because some
|
||||
# dependencies such as pymavlink don't play well with Python3 yet.
|
||||
from __future__ import division
|
||||
|
||||
PKG = 'px4'
|
||||
|
||||
@ -46,7 +43,6 @@ from geometry_msgs.msg import Quaternion, Vector3
|
||||
from mavros_msgs.msg import AttitudeTarget
|
||||
from mavros_test_common import MavrosTestCommon
|
||||
from pymavlink import mavutil
|
||||
from six.moves import xrange
|
||||
from std_msgs.msg import Header
|
||||
from threading import Thread
|
||||
from tf.transformations import quaternion_from_euler
|
||||
@ -124,7 +120,7 @@ class MavrosOffboardAttctlTest(MavrosTestCommon):
|
||||
loop_freq = 2 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
crossed = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
if (self.local_position.pose.position.x > boundary_x and
|
||||
self.local_position.pose.position.y > boundary_y and
|
||||
self.local_position.pose.position.z > boundary_z):
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
#***************************************************************************
|
||||
#
|
||||
# Copyright (c) 2015 PX4 Development Team. All rights reserved.
|
||||
@ -35,9 +35,6 @@
|
||||
#
|
||||
# @author Andreas Antener <andreas@uaventure.com>
|
||||
#
|
||||
# The shebang of this file is currently Python2 because some
|
||||
# dependencies such as pymavlink don't play well with Python3 yet.
|
||||
from __future__ import division
|
||||
|
||||
PKG = 'px4'
|
||||
|
||||
@ -48,7 +45,6 @@ from geometry_msgs.msg import PoseStamped, Quaternion
|
||||
from mavros_msgs.msg import ParamValue
|
||||
from mavros_test_common import MavrosTestCommon
|
||||
from pymavlink import mavutil
|
||||
from six.moves import xrange
|
||||
from std_msgs.msg import Header
|
||||
from threading import Thread
|
||||
from tf.transformations import quaternion_from_euler
|
||||
@ -132,7 +128,7 @@ class MavrosOffboardPosctlTest(MavrosTestCommon):
|
||||
loop_freq = 2 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
reached = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
if self.is_at_position(self.pos.pose.position.x,
|
||||
self.pos.pose.position.y,
|
||||
self.pos.pose.position.z, self.radius):
|
||||
@ -174,7 +170,7 @@ class MavrosOffboardPosctlTest(MavrosTestCommon):
|
||||
positions = ((0, 0, 0), (50, 50, 20), (50, -50, 20), (-50, -50, 20),
|
||||
(0, 0, 20))
|
||||
|
||||
for i in xrange(len(positions)):
|
||||
for i in range(len(positions)):
|
||||
self.reach_position(positions[i][0], positions[i][1],
|
||||
positions[i][2], 30)
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
#***************************************************************************
|
||||
#
|
||||
# Copyright (c) 2020 PX4 Development Team. All rights reserved.
|
||||
@ -35,8 +35,6 @@
|
||||
# @author Pedro Roque <padr@kth.se>
|
||||
#
|
||||
|
||||
from __future__ import division
|
||||
|
||||
PKG = 'px4'
|
||||
|
||||
import rospy
|
||||
@ -44,7 +42,6 @@ from geometry_msgs.msg import Quaternion, Vector3
|
||||
from mavros_msgs.msg import AttitudeTarget
|
||||
from mavros_test_common import MavrosTestCommon
|
||||
from pymavlink import mavutil
|
||||
from six.moves import xrange
|
||||
from std_msgs.msg import Header
|
||||
from threading import Thread
|
||||
from tf.transformations import quaternion_from_euler
|
||||
@ -134,7 +131,7 @@ class MavrosOffboardYawrateTest(MavrosTestCommon):
|
||||
loop_freq = 2 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
crossed = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
if (self.local_position.pose.position.x < boundary_x and
|
||||
self.local_position.pose.position.x > -boundary_x and
|
||||
self.local_position.pose.position.y < boundary_y and
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
from __future__ import division
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import unittest
|
||||
import rospy
|
||||
@ -11,7 +10,6 @@ from mavros_msgs.srv import CommandBool, ParamGet, ParamSet, SetMode, SetModeReq
|
||||
WaypointPush
|
||||
from pymavlink import mavutil
|
||||
from sensor_msgs.msg import NavSatFix, Imu
|
||||
from six.moves import xrange
|
||||
|
||||
|
||||
class MavrosTestCommon(unittest.TestCase):
|
||||
@ -183,7 +181,7 @@ class MavrosTestCommon(unittest.TestCase):
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
arm_set = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
if self.state.armed == arm:
|
||||
arm_set = True
|
||||
rospy.loginfo("set arm success | seconds: {0} of {1}".format(
|
||||
@ -213,7 +211,7 @@ class MavrosTestCommon(unittest.TestCase):
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
mode_set = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
if self.state.mode == mode:
|
||||
mode_set = True
|
||||
rospy.loginfo("set mode success | seconds: {0} of {1}".format(
|
||||
@ -247,7 +245,7 @@ class MavrosTestCommon(unittest.TestCase):
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
param_set = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
try:
|
||||
res = self.set_param_srv(param_id, param_value)
|
||||
if res.success:
|
||||
@ -274,7 +272,7 @@ class MavrosTestCommon(unittest.TestCase):
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
simulation_ready = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
if all(value for value in self.sub_topics_ready.values()):
|
||||
simulation_ready = True
|
||||
rospy.loginfo("simulation topics ready | seconds: {0} of {1}".
|
||||
@ -297,7 +295,7 @@ class MavrosTestCommon(unittest.TestCase):
|
||||
loop_freq = 10 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
landed_state_confirmed = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
if self.extended_state.landed_state == desired_landed_state:
|
||||
landed_state_confirmed = True
|
||||
rospy.loginfo("landed state confirmed | seconds: {0} of {1}".
|
||||
@ -325,7 +323,7 @@ class MavrosTestCommon(unittest.TestCase):
|
||||
loop_freq = 10 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
transitioned = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
if transition == self.extended_state.vtol_state:
|
||||
rospy.loginfo("transitioned | seconds: {0} of {1}".format(
|
||||
i / loop_freq, timeout))
|
||||
@ -348,7 +346,7 @@ class MavrosTestCommon(unittest.TestCase):
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
wps_cleared = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
if not self.mission_wp.waypoints:
|
||||
wps_cleared = True
|
||||
rospy.loginfo("clear waypoints success | seconds: {0} of {1}".
|
||||
@ -381,7 +379,7 @@ class MavrosTestCommon(unittest.TestCase):
|
||||
rate = rospy.Rate(loop_freq)
|
||||
wps_sent = False
|
||||
wps_verified = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
if not wps_sent:
|
||||
try:
|
||||
res = self.wp_push_srv(start_index=0, waypoints=waypoints)
|
||||
@ -417,7 +415,7 @@ class MavrosTestCommon(unittest.TestCase):
|
||||
loop_freq = 1 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
res = False
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
try:
|
||||
res = self.get_param_srv('MAV_TYPE')
|
||||
if res.success:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env python2
|
||||
#!/usr/bin/env python3
|
||||
#***************************************************************************
|
||||
#
|
||||
# Copyright (c) 2015-2016 PX4 Development Team. All rights reserved.
|
||||
@ -36,10 +36,6 @@
|
||||
# @author Andreas Antener <andreas@uaventure.com>
|
||||
#
|
||||
|
||||
# The shebang of this file is currently Python2 because some
|
||||
# dependencies such as pymavlink don't play well with Python3 yet.
|
||||
from __future__ import division
|
||||
|
||||
PKG = 'px4'
|
||||
|
||||
import rospy
|
||||
@ -47,13 +43,13 @@ import glob
|
||||
import json
|
||||
import math
|
||||
import os
|
||||
from px4tools import ulog
|
||||
import numpy as np
|
||||
from pyulog import ULog
|
||||
import sys
|
||||
from mavros import mavlink
|
||||
from mavros_msgs.msg import Mavlink, Waypoint, WaypointReached
|
||||
from mavros_test_common import MavrosTestCommon
|
||||
from pymavlink import mavutil
|
||||
from six.moves import xrange
|
||||
from threading import Thread
|
||||
|
||||
|
||||
@ -70,6 +66,49 @@ def get_last_log():
|
||||
return last_log
|
||||
|
||||
|
||||
def analyze_estimator_attitude(log_file):
|
||||
"""Compute attitude estimator error metrics from a ULog file."""
|
||||
ulog = ULog(log_file)
|
||||
|
||||
att = ulog.get_dataset('vehicle_attitude').data
|
||||
att_gt = ulog.get_dataset('vehicle_attitude_groundtruth').data
|
||||
|
||||
def quat_to_euler(q0, q1, q2, q3):
|
||||
"""Quaternion (w,x,y,z) to (roll, pitch, yaw) via ZYX Tait-Bryan."""
|
||||
roll = np.arctan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1**2 + q2**2))
|
||||
sinp = 2 * (q0 * q2 - q3 * q1)
|
||||
pitch = np.where(np.abs(sinp) >= 1,
|
||||
np.copysign(np.pi / 2, sinp), np.arcsin(sinp))
|
||||
yaw = np.arctan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2**2 + q3**2))
|
||||
return roll, pitch, yaw
|
||||
|
||||
roll, pitch, yaw = quat_to_euler(
|
||||
att['q[0]'], att['q[1]'], att['q[2]'], att['q[3]'])
|
||||
roll_gt, pitch_gt, yaw_gt = quat_to_euler(
|
||||
att_gt['q[0]'], att_gt['q[1]'], att_gt['q[2]'], att_gt['q[3]'])
|
||||
|
||||
# interpolate groundtruth onto attitude timestamps
|
||||
ts = att['timestamp']
|
||||
ts_gt = att_gt['timestamp']
|
||||
roll_gt = np.interp(ts, ts_gt, roll_gt)
|
||||
pitch_gt = np.interp(ts, ts_gt, pitch_gt)
|
||||
yaw_gt = np.interp(ts, ts_gt, yaw_gt)
|
||||
|
||||
wrap = lambda x: np.arcsin(np.sin(x))
|
||||
e_roll = wrap(roll - roll_gt)
|
||||
e_pitch = wrap(pitch - pitch_gt)
|
||||
e_yaw = wrap(yaw - yaw_gt)
|
||||
|
||||
return {
|
||||
'roll_error_mean': np.rad2deg(np.mean(e_roll)),
|
||||
'pitch_error_mean': np.rad2deg(np.mean(e_pitch)),
|
||||
'yaw_error_mean': np.rad2deg(np.mean(e_yaw)),
|
||||
'roll_error_std': np.rad2deg(np.std(e_roll)),
|
||||
'pitch_error_std': np.rad2deg(np.std(e_pitch)),
|
||||
'yaw_error_std': np.rad2deg(np.std(e_yaw)),
|
||||
}
|
||||
|
||||
|
||||
def read_mission(mission_filename):
|
||||
wps = []
|
||||
with open(mission_filename, 'r') as f:
|
||||
@ -188,7 +227,7 @@ class MavrosMissionTest(MavrosTestCommon):
|
||||
# does it reach the position in 'timeout' seconds?
|
||||
loop_freq = 2 # Hz
|
||||
rate = rospy.Rate(loop_freq)
|
||||
for i in xrange(timeout * loop_freq):
|
||||
for i in range(timeout * loop_freq):
|
||||
pos_xy_d, pos_z_d = self.distance_to_wp(lat, lon, alt)
|
||||
|
||||
# remember best distances
|
||||
@ -295,9 +334,7 @@ class MavrosMissionTest(MavrosTestCommon):
|
||||
rospy.loginfo("mission done, calculating performance metrics")
|
||||
last_log = get_last_log()
|
||||
rospy.loginfo("log file {0}".format(last_log))
|
||||
data = ulog.read_ulog(last_log).concat(dt=0.1)
|
||||
data = ulog.compute_data(data)
|
||||
res = ulog.estimator_analysis(data, False)
|
||||
res = analyze_estimator_attitude(last_log)
|
||||
|
||||
# enforce performance
|
||||
self.assertTrue(abs(res['roll_error_mean']) < 5.0, str(res))
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# We want to test the lockstep schedule even if it is not used otherwise.
|
||||
px4_add_library(lockstep_scheduler
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
add_executable(lockstep_scheduler_test
|
||||
src/lockstep_scheduler_test.cpp
|
||||
|
||||
@ -210,16 +210,15 @@ BMP388::collect()
|
||||
bool
|
||||
BMP388::soft_reset()
|
||||
{
|
||||
bool result = false;
|
||||
uint8_t status;
|
||||
int ret;
|
||||
|
||||
ret = _interface->get_reg(BMP3_SENS_STATUS_REG_ADDR, &status);
|
||||
int ret = _interface->get_reg(BMP3_SENS_STATUS_REG_ADDR, &status);
|
||||
|
||||
if (ret != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (status & BMP3_CMD_RDY) {
|
||||
ret = _interface->set_reg(BPM3_CMD_SOFT_RESET, BMP3_CMD_ADDR);
|
||||
|
||||
@ -248,16 +247,9 @@ BMP388::soft_reset()
|
||||
static int8_t cal_crc(uint8_t seed, uint8_t data)
|
||||
{
|
||||
int8_t poly = 0x1D;
|
||||
int8_t var2;
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if ((seed & 0x80) ^ (data & 0x80)) {
|
||||
var2 = 1;
|
||||
|
||||
} else {
|
||||
var2 = 0;
|
||||
}
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
int8_t var2 = ((seed & 0x80) ^ (data & 0x80)) ? 1 : 0;
|
||||
|
||||
seed = (seed & 0x7F) << 1;
|
||||
data = (data & 0x7F) << 1;
|
||||
@ -276,7 +268,6 @@ bool
|
||||
BMP388::validate_trimming_param()
|
||||
{
|
||||
uint8_t crc = 0xFF;
|
||||
uint8_t stored_crc;
|
||||
uint8_t *trim_param = (uint8_t *)_cal;
|
||||
|
||||
static_assert(BMP3_CALIB_DATA_LEN <= sizeof(*_cal), "unexpected struct size");
|
||||
@ -287,6 +278,8 @@ BMP388::validate_trimming_param()
|
||||
|
||||
crc = (crc ^ 0xFF);
|
||||
|
||||
uint8_t stored_crc;
|
||||
|
||||
if (_interface->get_reg(BMP3_TRIM_CRC_DATA_ADDR, &stored_crc) != OK) {
|
||||
return false;
|
||||
}
|
||||
@ -425,18 +418,14 @@ BMP388::set_sensor_settings()
|
||||
bool
|
||||
BMP388::set_op_mode(uint8_t op_mode)
|
||||
{
|
||||
bool result = false;
|
||||
uint8_t last_set_mode;
|
||||
uint8_t op_mode_reg_val;
|
||||
int ret = OK;
|
||||
|
||||
ret = _interface->get_reg(BMP3_PWR_CTRL_ADDR, &op_mode_reg_val);
|
||||
int ret = _interface->get_reg(BMP3_PWR_CTRL_ADDR, &op_mode_reg_val);
|
||||
|
||||
if (ret != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
last_set_mode = BMP3_GET_BITS(op_mode_reg_val, BMP3_OP_MODE);
|
||||
uint8_t last_set_mode = BMP3_GET_BITS(op_mode_reg_val, BMP3_OP_MODE);
|
||||
|
||||
/* Device needs to be put in sleep mode to transition */
|
||||
if (last_set_mode != BMP3_SLEEP_MODE) {
|
||||
@ -450,6 +439,8 @@ BMP388::set_op_mode(uint8_t op_mode)
|
||||
px4_usleep(BMP3_POST_SLEEP_WAIT_TIME);
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
|
||||
if (ret == OK) {
|
||||
ret = _interface->get_reg(BMP3_PWR_CTRL_ADDR, &op_mode_reg_val);
|
||||
|
||||
@ -478,13 +469,9 @@ BMP388::set_op_mode(uint8_t op_mode)
|
||||
*/
|
||||
static void parse_sensor_data(const uint8_t *reg_data, struct bmp3_uncomp_data *uncomp_data)
|
||||
{
|
||||
uint32_t data_xlsb;
|
||||
uint32_t data_lsb;
|
||||
uint32_t data_msb;
|
||||
|
||||
data_xlsb = (uint32_t)reg_data[0];
|
||||
data_lsb = (uint32_t)reg_data[1] << 8;
|
||||
data_msb = (uint32_t)reg_data[2] << 16;
|
||||
uint32_t data_xlsb = (uint32_t)reg_data[0];
|
||||
uint32_t data_lsb = (uint32_t)reg_data[1] << 8;
|
||||
uint32_t data_msb = (uint32_t)reg_data[2] << 16;
|
||||
uncomp_data->pressure = data_msb | data_lsb | data_xlsb;
|
||||
|
||||
data_xlsb = (uint32_t)reg_data[3];
|
||||
@ -503,24 +490,16 @@ static void parse_sensor_data(const uint8_t *reg_data, struct bmp3_uncomp_data *
|
||||
*/
|
||||
static int64_t compensate_temperature(const struct bmp3_uncomp_data *uncomp_data, struct bmp3_calib_data *calib_data)
|
||||
{
|
||||
int64_t partial_data1;
|
||||
int64_t partial_data2;
|
||||
int64_t partial_data3;
|
||||
int64_t partial_data4;
|
||||
int64_t partial_data5;
|
||||
int64_t partial_data6;
|
||||
int64_t comp_temp;
|
||||
|
||||
partial_data1 = ((int64_t)uncomp_data->temperature - (256 * calib_data->reg_calib_data.par_t1));
|
||||
partial_data2 = calib_data->reg_calib_data.par_t2 * partial_data1;
|
||||
partial_data3 = (partial_data1 * partial_data1);
|
||||
partial_data4 = (int64_t)partial_data3 * calib_data->reg_calib_data.par_t3;
|
||||
partial_data5 = ((int64_t)(partial_data2 * 262144) + partial_data4);
|
||||
partial_data6 = partial_data5 / 4294967296;
|
||||
int64_t partial_data1 = ((int64_t)uncomp_data->temperature - (256 * calib_data->reg_calib_data.par_t1));
|
||||
int64_t partial_data2 = calib_data->reg_calib_data.par_t2 * partial_data1;
|
||||
int64_t partial_data3 = (partial_data1 * partial_data1);
|
||||
int64_t partial_data4 = (int64_t)partial_data3 * calib_data->reg_calib_data.par_t3;
|
||||
int64_t partial_data5 = ((int64_t)(partial_data2 * 262144) + partial_data4);
|
||||
int64_t partial_data6 = partial_data5 / 4294967296;
|
||||
|
||||
/* Store t_lin in dev. structure for pressure calculation */
|
||||
calib_data->reg_calib_data.t_lin = partial_data6;
|
||||
comp_temp = (int64_t)((partial_data6 * 25) / 16384);
|
||||
int64_t comp_temp = (int64_t)((partial_data6 * 25) / 16384);
|
||||
|
||||
return comp_temp;
|
||||
}
|
||||
@ -536,27 +515,19 @@ static uint64_t compensate_pressure(const struct bmp3_uncomp_data *uncomp_data,
|
||||
const struct bmp3_calib_data *calib_data)
|
||||
{
|
||||
const struct bmp3_reg_calib_data *reg_calib_data = &calib_data->reg_calib_data;
|
||||
int64_t partial_data1;
|
||||
int64_t partial_data2;
|
||||
int64_t partial_data3;
|
||||
int64_t partial_data4;
|
||||
int64_t partial_data5;
|
||||
int64_t partial_data6;
|
||||
int64_t offset;
|
||||
int64_t sensitivity;
|
||||
uint64_t comp_press;
|
||||
|
||||
partial_data1 = reg_calib_data->t_lin * reg_calib_data->t_lin;
|
||||
partial_data2 = partial_data1 / 64;
|
||||
partial_data3 = (partial_data2 * reg_calib_data->t_lin) / 256;
|
||||
partial_data4 = (reg_calib_data->par_p8 * partial_data3) / 32;
|
||||
partial_data5 = (reg_calib_data->par_p7 * partial_data1) * 16;
|
||||
partial_data6 = (reg_calib_data->par_p6 * reg_calib_data->t_lin) * 4194304;
|
||||
offset = (reg_calib_data->par_p5 * 140737488355328) + partial_data4 + partial_data5 + partial_data6;
|
||||
int64_t partial_data1 = reg_calib_data->t_lin * reg_calib_data->t_lin;
|
||||
int64_t partial_data2 = partial_data1 / 64;
|
||||
int64_t partial_data3 = (partial_data2 * reg_calib_data->t_lin) / 256;
|
||||
int64_t partial_data4 = (reg_calib_data->par_p8 * partial_data3) / 32;
|
||||
int64_t partial_data5 = (reg_calib_data->par_p7 * partial_data1) * 16;
|
||||
int64_t partial_data6 = (reg_calib_data->par_p6 * reg_calib_data->t_lin) * 4194304;
|
||||
int64_t offset = (reg_calib_data->par_p5 * 140737488355328) + partial_data4 + partial_data5 + partial_data6;
|
||||
partial_data2 = (reg_calib_data->par_p4 * partial_data3) / 32;
|
||||
partial_data4 = (reg_calib_data->par_p3 * partial_data1) * 4;
|
||||
partial_data5 = (reg_calib_data->par_p2 - 16384) * reg_calib_data->t_lin * 2097152;
|
||||
sensitivity = ((reg_calib_data->par_p1 - 16384) * 70368744177664) + partial_data2 + partial_data4 + partial_data5;
|
||||
int64_t sensitivity = ((reg_calib_data->par_p1 - 16384) * 70368744177664) + partial_data2 + partial_data4 +
|
||||
partial_data5;
|
||||
partial_data1 = (sensitivity / 16777216) * uncomp_data->pressure;
|
||||
partial_data2 = reg_calib_data->par_p10 * reg_calib_data->t_lin;
|
||||
partial_data3 = partial_data2 + (65536 * reg_calib_data->par_p9);
|
||||
@ -568,7 +539,7 @@ static uint64_t compensate_pressure(const struct bmp3_uncomp_data *uncomp_data,
|
||||
partial_data2 = (reg_calib_data->par_p11 * partial_data6) / 65536;
|
||||
partial_data3 = (partial_data2 * uncomp_data->pressure) / 128;
|
||||
partial_data4 = (offset / 4) + partial_data1 + partial_data5 + partial_data3;
|
||||
comp_press = (((uint64_t)partial_data4 * 25) / (uint64_t)1099511627776);
|
||||
uint64_t comp_press = (((uint64_t)partial_data4 * 25) / (uint64_t)1099511627776);
|
||||
|
||||
return comp_press;
|
||||
}
|
||||
@ -589,7 +560,7 @@ BMP388::compensate_data(uint8_t sensor_comp,
|
||||
struct bmp3_reg_calib_data *reg_calib_data = &calib_data.reg_calib_data;
|
||||
memcpy(reg_calib_data, _cal, 21);
|
||||
|
||||
if ((uncomp_data != NULL) && (comp_data != NULL)) {
|
||||
if ((uncomp_data != nullptr) && (comp_data != nullptr)) {
|
||||
if (sensor_comp & (BMP3_PRESS | BMP3_TEMP)) {
|
||||
comp_data->temperature = compensate_temperature(uncomp_data, &calib_data);
|
||||
}
|
||||
@ -599,10 +570,10 @@ BMP388::compensate_data(uint8_t sensor_comp,
|
||||
}
|
||||
|
||||
} else {
|
||||
rslt = -1;
|
||||
rslt = ERROR;
|
||||
}
|
||||
|
||||
return (rslt == 0);
|
||||
return (rslt == OK);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -613,13 +584,12 @@ BMP388::compensate_data(uint8_t sensor_comp,
|
||||
bool
|
||||
BMP388::get_sensor_data(uint8_t sensor_comp, struct bmp3_data *comp_data)
|
||||
{
|
||||
uint8_t reg_data[BMP3_P_T_DATA_LEN] {};
|
||||
|
||||
int8_t rslt = _interface->get_reg_buf(BMP3_SENS_STATUS_REG_ADDR, reg_data, BMP3_P_T_DATA_LEN);
|
||||
|
||||
bool result = false;
|
||||
int8_t rslt;
|
||||
|
||||
uint8_t reg_data[BMP3_P_T_DATA_LEN];
|
||||
struct bmp3_uncomp_data uncomp_data;
|
||||
|
||||
rslt = _interface->get_reg_buf(BMP3_SENS_STATUS_REG_ADDR, reg_data, BMP3_P_T_DATA_LEN);
|
||||
struct bmp3_uncomp_data uncomp_data {};
|
||||
|
||||
if (rslt == OK) {
|
||||
uint8_t status = reg_data[0];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user