diff --git a/.flake8 b/.flake8 deleted file mode 100644 index a882cbdd0..000000000 --- a/.flake8 +++ /dev/null @@ -1,6 +0,0 @@ -[flake8] -max-line-length = 140 -extend-ignore = E203 -per-file-ignores = - # imported but unused - __init__.py: F401 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 4bfdd2ea7..ca57b8766 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1,2 @@ * @njooma +* @viamrobotics/sdk-netcode diff --git a/.github/workflows/build-wheels.yml b/.github/workflows/build-wheels.yml new file mode 100644 index 000000000..715e562dd --- /dev/null +++ b/.github/workflows/build-wheels.yml @@ -0,0 +1,88 @@ +name: Build Wheels + +on: + workflow_call: + inputs: + branch: + required: true + type: string + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + include: + - arch: macosx_x86_64 + ext: dylib + whl: macosx_10_16_x86_64 + - arch: macosx_arm64 + ext: dylib + whl: macosx_11_0_arm64 + - arch: linux_aarch64 + ext: so + whl: manylinux2014_aarch64 + - arch: linux_x86_64 + ext: so + whl: manylinux2014_x86_64 + - arch: musllinux_x86_64 + ext: so + whl: musllinux_1_2_x86_64 + - arch: musllinux_x86 + ext: so + whl: musllinux_1_2_i686 + - arch: musllinux_aarch64 + ext: so + whl: musllinux_1_2_aarch64 + - arch: musllinux_armv7l + ext: so + whl: musllinux_1_2_armv7l + - arch: linux_armv6l + ext: so + whl: linux_armv6l + - arch: linux_armv6l + ext: so + whl: linux_armv7l + - arch: windows_x86_64 + ext: dll + whl: win_amd64 + steps: + - name: Checkout Code - Call + uses: actions/checkout@v4 + with: + ref: ${{ inputs.branch }} + if: ${{ inputs.branch }} + - name: Checkout Code - Dispatch + uses: actions/checkout@v4 + if: ${{ !inputs.branch }} + + - name: Install uv + uses: astral-sh/setup-uv@v3 + + - name: Setup Python + run: uv python install 3.12 + + - name: Install package + run: uv sync + + - name: Download binary + run: curl -sL -o src/viam/rpc/libviam_rust_utils.${{ matrix.ext }} https://github.com/viamrobotics/rust-utils/releases/latest/download/libviam_rust_utils-${{ matrix.arch }}.${{ matrix.ext }} + + - name: HACK for arm7l + if: ${{ matrix.whl == 'linux_armv7l' }} + run: echo "This file enables arm7l support. PyPI doesn't allow for packages with the same hash, so this file must be added to differentiate this arm7l package from the arm6l package." > src/viam/arm7l.txt + + - name: Build + run: uv build --wheel + + - name: Rename + run: | + echo "WHL_NAME=viam_sdk-$(uv run python3 -c 'import viam; print(viam.__version__)')-py3-none-${{ matrix.whl }}.whl" >> $GITHUB_ENV + mv dist/viam_sdk-$(uv run python3 -c "import viam; print(viam.__version__)")-py3-none-any.whl dist/viam_sdk-$(uv run python3 -c 'import viam; print(viam.__version__)')-py3-none-${{ matrix.whl }}.whl + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ env.WHL_NAME }} + path: dist/${{ env.WHL_NAME }} diff --git a/.github/workflows/code-samples-comment.yml b/.github/workflows/code-samples-comment.yml new file mode 100644 index 000000000..02acaab20 --- /dev/null +++ b/.github/workflows/code-samples-comment.yml @@ -0,0 +1,43 @@ +name: Code Samples Pull Request Update + +on: + pull_request_target: + branches: ["main"] + types: [opened] + paths: + - "src/viam/components/base/base.py" + - "src/viam/components/board/board.py" + - "src/viam/components/camera/camera.py" + - "src/viam/components/motor/motor.py" + - "src/viam/components/sensor/sensor.py" + - "src/viam/components/servo/servo.py" + - "src/viam/components/arm/arm.py" + - "src/viam/components/audio_input/audio_input.py" + - "src/viam/components/gantry/gantry.py" + - "src/viam/components/gripper/gripper.py" + - "src/viam/components/input/input.py" + - "src/viam/components/movement_sensor/movement_sensor.py" + - "src/viam/components/pose_tracker/pose_tracker.py" + - "src/viam/services/motion/motion.py" + - "src/viam/services/vision/vision.py" + +jobs: + comment: + name: "Post Comment on PR" + runs-on: ubuntu-latest + steps: + - name: Get code + uses: actions/checkout@v3 + - name: Write Function + uses: actions/setup-python@v4 + with: + python-version: "3.10" + - run: python ./etc/code-samples-action.py + + - name: Add code samples PR Comment + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: Code-Samples Warning + number: ${{ env.PR_NUMBER }} + recreate: true + path: code-samples-warning.md diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e88f411ca..f81924823 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,36 +4,36 @@ on: workflow_dispatch: push: paths: - - 'src/viam/**' - - 'docs/**' - - 'README.md' - branches: [ main ] + - "src/viam/**" + - "docs/**" + - "README.md" + branches: [main] jobs: generate-docs: if: github.repository_owner == 'viamrobotics' - runs-on: [self-hosted, x64] - container: - image: ghcr.io/viamrobotics/canon:amd64 + runs-on: ubuntu-latest steps: - - name: Checkout Push/Workflow Dispatch - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - name: Install Poetry - uses: snok/install-poetry@v1 + - name: Install uv + uses: astral-sh/setup-uv@v3 + + - name: Setup Python + run: uv python install 3.12 - name: Install package - run: poetry install + run: make install - name: Generate docs run: | - poetry run python3 -m examples.server.v1.server 0.0.0.0 9091 & + uv run python3 -m docs.examples._server & + uv run python3 -m examples.server.v1.server 0.0.0.0 9091 & sleep 2 - make documentation - kill -9 `ps aux | grep "[e]xamples.server.v1.server" | awk '{print $2}'` + uv run make documentation - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: html-docs path: docs/_build/html/ diff --git a/.github/workflows/license_finder.yml b/.github/workflows/license_finder.yml index cd55a77e4..cb4c3b6a3 100644 --- a/.github/workflows/license_finder.yml +++ b/.github/workflows/license_finder.yml @@ -4,34 +4,37 @@ on: workflow_dispatch: workflow_call: pull_request: - branches: ['main'] + branches: ["main"] jobs: license_finder: if: github.repository_owner == 'viamrobotics' name: Audit 3rd-Party Licenses - runs-on: [x64, qemu-host] - container: - image: ghcr.io/viamrobotics/canon:amd64-cache - options: --platform linux/amd64 + runs-on: ubuntu-latest timeout-minutes: 30 steps: - - name: Check out code in rdk directory - uses: actions/checkout@v2 - with: - fetch-depth: 2 + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 - - name: Install Poetry - uses: snok/install-poetry@v1 + - name: Setup Python + run: | + uv python install + uv sync + + - uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" - - name: Install package - run: poetry install + - run: gem install license_finder - name: Generate requirements.txt (exclude dev dependencies) run: | - poetry export -f requirements.txt --without-hashes > requirements.txt + uv pip compile pyproject.toml -o requirements.txt + uv pip install -r requirements.txt - name: Run license finder run: | - poetry run license_finder --python-version=3 + uv run license_finder diff --git a/.github/workflows/linkcheck.yml b/.github/workflows/linkcheck.yml new file mode 100644 index 000000000..9df5e011a --- /dev/null +++ b/.github/workflows/linkcheck.yml @@ -0,0 +1,67 @@ +# .github/workflows/run-htmltest.yml +# (c) 2021 Robb Romans +# +# Run htmltest link checker on generated HTML output in dist/ +# https://github.com/wjdp/htmltest +# +name: run-htmltest-external +on: + schedule: + # 10am UTC on Mondays + - cron: "0 10 * * 1" +jobs: + htmltest: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + + - name: Setup Python + run: uv python install 3.12 + + - name: Install package + run: make install + + - name: Generate docs + run: uv run make documentation + + - name: Test HTML + # https://github.com/wjdp/htmltest-action/ + # Don't fail the build on broken links + continue-on-error: false + uses: wjdp/htmltest-action@master + with: + config: .htmltest.yml + - name: Archive htmltest results + uses: actions/upload-artifact@v4 + # Note: Set ACTIONS_RUNTIME_TOKEN env variable to test with nektos/act + with: + name: htmltest-report + path: tmp/.htmltest/htmltest.log + retention-days: 7 # Default is 90 days + - name: Login to Jira + if: failure() + uses: atlassian/gajira-login@v3 + env: + JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} + JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} + JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }} + - name: Create Jira ticket + if: failure() + id: create + uses: atlassian/gajira-create@v3 + env: + GITHUB_RUN_ID: ${{ github.run_id }} + with: + project: DOCS + issuetype: Bug + summary: Broken link detected + description: "For more info see https://github.com/viamrobotics/viam-python-sdk/actions/runs/${{ env.GITHUB_RUN_ID }}." + - name: Log created Jira issue + if: failure() + run: echo "Issue ${{ steps.create.outputs.issue }} was created" diff --git a/.github/workflows/prerelease_version_bump.yml b/.github/workflows/prerelease_version_bump.yml deleted file mode 100644 index 9f049f1de..000000000 --- a/.github/workflows/prerelease_version_bump.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Bump Prerelease Version - -on: - workflow_dispatch: - push: - branches: [ main ] - -jobs: - bump-version: - if: github.repository_owner == 'viamrobotics' - runs-on: [self-hosted, x64] - container: - image: ghcr.io/viamrobotics/canon:amd64 - steps: - - name: Checkout Code - uses: actions/checkout@v3 - with: - token: ${{ secrets.REPO_READ_TOKEN }} - - - name: Install Poetry - uses: snok/install-poetry@v1 - - - name: Install package - run: poetry install - - - name: Bump Version - id: bump_version - run: | - poetry version prerelease - echo "SDK_VERSION=$(poetry version -s)" >> $GITHUB_ENV - - - name: Commit + Push - uses: EndBug/add-and-commit@v9.0.0 - with: - default_author: github_actions - message: Bumping prerelease version to v${{ env.SDK_VERSION }} [skip ci] diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 000000000..ab38a8e65 --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,38 @@ +name: Publish to PyPI + +on: + release: + types: [published] + +jobs: + publish: + if: github.repository_owner == 'viamrobotics' + runs-on: ubuntu-latest + + steps: + - name: Download Release + uses: dsaltares/fetch-gh-release-asset@master + with: + file: "viam_sdk.*\\.whl" + regex: true + target: "dist/" + version: tags/${{ github.event.release.tag_name }} + + - name: Publish + uses: pypa/gh-action-pypi-publish@release/v1 + with: + password: ${{ secrets.PYPI_API_TOKEN }} + verbose: true + + - name: Notify Slack + uses: slackapi/slack-github-action@v1.24.0 + if: ${{ !contains(github.event.release.tag_name, 'rc') && !contains(github.event.release.tag_name, 'a') && !contains(github.event.release.tag_name, 'b')}} + with: + payload: | + { + "text": "${{ github.event.release.tag_name }} was released.\n${{ github.event.release.html_url }}", + "username": "Python SDK", + "icon_url": "https://s3.dualstack.us-east-2.amazonaws.com/pythondotorg-assets/media/community/logos/python-logo-only.png" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..469bbbe41 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,116 @@ +name: Release + +on: + workflow_dispatch: + inputs: + version: + description: 'The type of version bump. Use the blank option for no change. Use the "release" option to promote prerelease. See docs for details: https://hatch.pypa.io/latest/version/#supported-segments' + type: choice + required: true + default: "" + options: + - major + - minor + - patch + - release + - "" + prerelease: + description: "If this is a prerelease and which type. See docs for details: https://hatch.pypa.io/latest/version/#supported-segments" + type: choice + required: false + options: + - "" + - "alpha" + - "beta" + - "rc" + +jobs: + prepare: + if: github.repository_owner == 'viamrobotics' + runs-on: ubuntu-latest + outputs: + version: ${{ steps.set_version.outputs.version }} + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v3 + + - name: Setup Python + run: uv python install 3.12 + + - name: Install Package + run: uv sync --all-extras + + - name: Clean Format Test + run: uv run make clean format typecheck test + + - name: Bump Version + shell: bash + if: inputs.version != '-s' && inputs.prerelease != '' + run: uvx hatch version ${{ inputs.version }},${{ inputs.prerelease }} + + - name: Bump Version + shell: bash + if: inputs.version != '-s' && inputs.prerelease == '' + run: uvx hatch version ${{ inputs.version }} + + - name: Set Version + id: set_version + run: | + echo "SDK_VERSION=$(uvx hatch version)" >> $GITHUB_ENV + echo "version=$(uvx hatch version)" >> $GITHUB_OUTPUT + + - name: Check if release exists + uses: cardinalby/git-get-release-action@1.2.4 + id: release_exists + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + releaseName: v${{ env.SDK_VERSION }} + doNotFailIfNotFound: "true" + + - name: Cancelling - release already exists + uses: andymckay/cancel-action@0.2 + if: | + steps.release_exists.outputs.id != '' + + - name: Add + Commit + uses: EndBug/add-and-commit@v9 + with: + new_branch: release/v${{ env.SDK_VERSION }} + message: Bump version to ${{ env.SDK_VERSION }} + + - name: Open PR + run: | + gh pr create -t "release/v${{ env.SDK_VERSION }}" -b "This is an auto-generated PR to merge the release branch back into main upon successful release" -B "main" -H "release/v${{ env.SDK_VERSION }}" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + build: + uses: ./.github/workflows/build-wheels.yml + with: + branch: release/v${{ needs.prepare.outputs.version }} + needs: prepare + if: github.repository_owner == 'viamrobotics' + + release: + needs: [prepare, build] + if: github.repository_owner == 'viamrobotics' + runs-on: ubuntu-latest + + steps: + - uses: actions/download-artifact@v4 + with: + path: dist + + - name: Release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ needs.prepare.outputs.version }} + files: dist/** + draft: true + prerelease: false + fail_on_unmatched_files: true + target_commitish: release/v${{ needs.prepare.outputs.version }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 68aefce76..cee52cf86 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,39 +1,66 @@ name: Test +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + on: workflow_dispatch: push: - branches: [ main ] + branches: [main] pull_request_target: - branches: [ main ] + branches: [main, "rc-*"] jobs: test: if: github.repository_owner == 'viamrobotics' - runs-on: [self-hosted, x64] - container: - image: ghcr.io/viamrobotics/canon:amd64 + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] + requirements-version: ["min", "max"] steps: - name: Checkout Push/Workflow Dispatch if: github.event_name == 'workflow_dispatch' || github.event_name == 'push' - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Checkout PR if: github.event_name == 'pull_request_target' - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true - - name: Install Poetry - uses: snok/install-poetry@v1 + - name: Setup Python + run: uv python install ${{ matrix.python-version }} - name: Install package - run: poetry install + run: make install + + - name: Install minimum package versions + run: uv pip install -r requirements-test.txt + if: ${{ matrix.requirements-version == 'min' }} + + - name: Type Check + run: uv run make typecheck - name: Lint - run: make lint + run: uv run make lint - name: Test - run: make test + run: uv run make test - name: Test Documentation - run: make test_docs + run: uv run make test_docs + + test_passing: + if: github.repository_owner == 'viamrobotics' + needs: test + runs-on: ubuntu-latest + steps: + - name: Check Results + run: | + echo Python tests: ${{ needs.test.result }} + [ "${{ needs.test.result }}" == "success" ] diff --git a/.github/workflows/update_protos.yml b/.github/workflows/update_protos.yml index 0acd73692..7a6832ab3 100644 --- a/.github/workflows/update_protos.yml +++ b/.github/workflows/update_protos.yml @@ -9,43 +9,66 @@ on: jobs: update-protos: if: github.repository_owner == 'viamrobotics' - runs-on: [self-hosted, x64] - container: - image: ghcr.io/viamrobotics/canon:amd64 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: bufbuild/buf-setup-action@v1.8.0 + - uses: actions/checkout@v4 + with: + token: ${{ secrets.REPO_READ_TOKEN }} + - uses: bufbuild/buf-setup-action@v1 with: github_token: ${{ secrets.GITHUB_TOKEN }} - - uses: arduino/setup-protoc@v1 + - uses: arduino/setup-protoc@v3 with: repo-token: ${{ secrets.GITHUB_TOKEN }} + version: "29.2" + + - name: Install uv + uses: astral-sh/setup-uv@v3 + with: + enable-cache: true + + - name: prune origin + run: git remote prune origin - - name: Install Poetry - uses: snok/install-poetry@v1 + - name: Setup Python + run: uv python install - - name: Install package - run: poetry install + - name: Store API version + run: | + uv run python3 etc/_update_version_metadata.py src/viam/version_metadata.py ${{ github.event.client_payload.tag }} + echo "Updated API version to ${{ github.event.client_payload.tag }} in src/viam/version_metadata.py" - name: Generate buf - run: make buf + run: uv run make buf env: BUF_TOKEN: ${{ secrets.BUF_TOKEN }} - - name: Generate better imports - run: make better_imports - - name: Format - run: make format + run: uv run make format - name: Add + Commit + Open PR - uses: peter-evans/create-pull-request@v3 + id: cpr + uses: peter-evans/create-pull-request@v7 with: commit-message: '[WORKFLOW] Updating protos from ${{ github.event.client_payload.repo_name }}, commit: ${{ github.event.client_payload.sha }}' - branch: 'workflow/update-protos' + branch: 'workflow/update-proto' delete-branch: true base: main title: Automated Protos Update body: This is an auto-generated PR to update proto definitions. Check the commits to see which repos and commits are responsible for the changes assignees: njooma reviewers: njooma + token: ${{ secrets.GIT_ACCESS_TOKEN }} + + - name: Notify slack of failure + uses: slackapi/slack-github-action@v1.24.0 + if: ${{ failure() }} + with: + payload: | + { + "text": "Python SDK update protos job has failed", + "username": "Python SDK", + "icon_url": "https://media.tenor.com/bZMubztJxGkAAAAe/charlie-brown-walking-charlie-brown.png" + } + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TEAM_SDK_WEBHOOK_URL }} diff --git a/.gitignore b/.gitignore index 157d23891..c077a9291 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,9 @@ __pycache__/ *$py.class # C extensions -*.so +src/**/*.so +src/**/*.dylib +src/**/*.dll # Distribution / packaging .Python diff --git a/.htmltest.yml b/.htmltest.yml new file mode 100644 index 000000000..dfb68ba70 --- /dev/null +++ b/.htmltest.yml @@ -0,0 +1,17 @@ +DirectoryPath: "docs/_build/html" +EnforceHTTPS: true +IgnoreEmptyHref: true +IgnoreInternalEmptyHash: true +IgnoreDirectoryMissingTrailingSlash: false +IgnoreURLs: + - "app.viam.com" + - "fonts.gstatic.com" + - "contributor-covenant.org" +IgnoreDirs: + - "lib" +CacheExpires: "6h" +# IgnoreDirs: - if we need to ever ignore files +CheckInternal: false +CheckDoctype: false +CheckScripts: false +FileExtension: ".html" diff --git a/.vscode/settings.json b/.vscode/settings.json index 923a08a4e..6f25a6e8b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,30 +1,29 @@ { - "python.linting.flake8Enabled": true, + "[python]": { + "diffEditor.ignoreTrimWhitespace": false, "editor.formatOnSave": true, - "python.analysis.typeCheckingMode": "basic", - "python.linting.ignorePatterns": [ - "**/site-packages/**/*.py", - ".vscode/*.py", - "gen/**/*", - "viam/proto/**/*" - ], - "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true, + "editor.formatOnType": true, + "editor.defaultFormatter": "charliermarsh.ruff", + "editor.wordBasedSuggestions": "off", "editor.rulers": [ - { - "column": 140, - "color": "#777777" - } - ], - "python.formatting.provider": "black", - "cSpell.words": [ - "frombytes", - "grpclib", - "klass", - "pointcloud", - "segmenters", - "TFLITE", - "tobytes", - "viam" - ], + { + "color": "#777777", + "column": 140 + } + ] + }, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "cSpell.words": [ + "frombytes", + "grpclib", + "klass", + "pointcloud", + "RSDK", + "segmenters", + "TFLITE", + "tobytes", + "viam", + "viamrobotics" + ] } diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 905be2f5a..000000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,7 +0,0 @@ -# Changelog - - - -## v0.1.0 (03/06/2022) - -- First release of `viam`! \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d4a50cfeb..5412dbe79 100755 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,9 +9,9 @@ helps, and credit will always be given. If you are reporting a bug, please include: -* Your operating system name and version. -* Any details about your local setup that might be helpful in troubleshooting. -* Detailed steps to reproduce the bug. +- Your operating system name and version. +- Any details about your local setup that might be helpful in troubleshooting. +- Detailed steps to reproduce the bug. ### Fix Bugs @@ -33,30 +33,36 @@ on the web in blog posts, articles, and such. If you are proposing a feature: -* Explain in detail how it would work. -* Keep the scope as narrow as possible, to make it easier to implement. -* Remember that this is a volunteer-driven project, and that contributions +- Explain in detail how it would work. +- Keep the scope as narrow as possible, to make it easier to implement. +- Remember that this is a volunteer-driven project, and that contributions are welcome :) ## Get Started! Ready to contribute? Here's how to set up `viam` for local development. -1. Download a copy of `viam` locally. -2. Install `viam` using `poetry`: +### Prerequisites - ```console - $ poetry install - ``` +We use [`uv`](https://docs.astral.sh/uv/) to manage our environments and dependencies. Make sure you have `uv` installed. + +1. Download/clone a copy of `viam` locally. +2. Install `viam` using `make`: + + ```console + $ make install + ``` 3. Use `git` (or similar) to create a branch for local development and make your changes: - ```console - $ git checkout -b name-of-your-bugfix-or-feature - ``` + ```console + $ git checkout -b name-of-your-bugfix-or-feature + ``` 4. When you're done making changes, check that your changes conform to any code formatting requirements and pass any tests. +- When testing, make sure you use the correct virtual environment by running either `uv run make test` or `source .venv/bin/activate; make test` + 5. Commit your changes and open a pull request. ## Pull Request Guidelines diff --git a/Makefile b/Makefile index 8af977090..f6763a82b 100644 --- a/Makefile +++ b/Makefile @@ -1,56 +1,54 @@ +.PHONY: clean clean: find . -type d -name '__pycache__' | xargs rm -rf -_lint: - flake8 --exclude=**/gen/** - +.PHONY: lint lint: - poetry run make _lint - -_format: - black --exclude ".*/gen/.*" ./src + ruff check ./src ./tests +.PHONY: format format: - poetry run make _format + ruff format ./src ./tests + ruff check --select I --fix ./src ./tests -_buf: clean +.PHONY: buf +buf: clean rm -rf src/viam/gen - buf generate buf.build/viamrobotics/api + chmod +x plugin/main.py + uv pip install protoletariat + uv pip install protobuf==5.29.2 + $(eval API_VERSION := $(shell grep 'API_VERSION' src/viam/version_metadata.py | awk -F '"' '{print $$2}')) + buf generate buf.build/viamrobotics/api:${API_VERSION} buf generate buf.build/viamrobotics/goutils protol -e googl* --in-place -s _grpc.py -s _pb2.py -s _pb2.pyi -o src/viam/gen buf buf.build/viamrobotics/api protol -e googl* --in-place -s _grpc.py -s _pb2.py -s _pb2.pyi -o src/viam/gen buf buf.build/viamrobotics/goutils find src/viam/gen -type d -exec touch {}/__init__.py \; + uv run python3 -m etc.generate_proto_import -v -buf: - poetry run make _buf - -_better_imports: - python3 -m etc.generate_proto_import -v - @echo Add init files for specific documented protos - -better_imports: - poetry run make _better_imports - -_test: - coverage run -m pytest && coverage html - +.PHONY: test test: - poetry run make _test - -_test_docs: - pytest --nbmake "./docs" + coverage run -m pytest && coverage html +.PHONY: test_docs test_docs: + kill -9 `ps aux | grep "[d]ocs.examples._server" | awk '{print $$2}'` || true kill -9 `ps aux | grep "[e]xamples.server.v1.server" | awk '{print $$2}'` || true - poetry run python3 -m examples.server.v1.server 0.0.0.0 9091 quiet & - poetry run make _test_docs + python3 -m docs.examples._server & + python3 -m examples.server.v1.server 0.0.0.0 9091 quiet & + sleep 3 + pytest --nbmake "./docs" + kill -9 `ps aux | grep "[d]ocs.examples._server" | awk '{print $$2}'` kill -9 `ps aux | grep "[e]xamples.server.v1.server" | awk '{print $$2}'` -_documentation: - cd docs && $(MAKE) clean html +.PHONY: typecheck +typecheck: + pyright +.PHONY: documentation documentation: - poetry run make _documentation + $(MAKE) -C docs clean html -package: buf better_imports test - @echo "TODO: Create pip-installable package" +.PHONY: install +install: + uv sync --all-extras + bash etc/postinstall.sh diff --git a/README.md b/README.md index 0e99f6037..67c5804cf 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,137 @@ # Viam Python SDK -[![documentation](https://img.shields.io/static/v1?label=docs&message=python.viam.dev&color=informational)](https://python.viam.dev) + +![PyPI](https://img.shields.io/pypi/v/viam-sdk) +![PyPI - Python Version](https://img.shields.io/pypi/pyversions/viam-sdk) +[![documentation](https://img.shields.io/static/v1?label=docs&message=python.viam.dev&color=lightgray)](https://python.viam.dev) ![build status](https://github.com/viamrobotics/python-sdk/actions/workflows/test.yml/badge.svg) [![license](https://img.shields.io/badge/license-Apache_2.0-blue)](https://github.com/viamrobotics/viam-python-sdk/blob/main/LICENSE) -## (In)stability Notice -This is an alpha release of the Viam Python SDK. Stability is not guaranteed. Breaking changes are likely to occur, and occur often. +The Viam Python SDK allows you to build robots, access existing Viam robots, and manage your fleet of Viam robots. + +If you would like a blueprint on setting up a Python environment with Viam from scratch, you can follow our [Setup](https://python.viam.dev/#installing-from-source) guide. + +If you would like to develop and contribute to Viam's Python SDK, take a look at the [Development](#development) portion of the README. + +## Installation + +Currently, we have pre-built binaries for macOS (both Intel `x86_64` and Apple Silicon) and Linux (`x86`, `aarch64`, `armv6l`) that you can install using `pip`: -## Installation - pre PyPI +`pip install viam-sdk` -`pip install git+https://github.com/viamrobotics/python-sdk.git` +If you want to install on Windows, you can install from github directly with `pip`: -This will install the current `main` branch to your project. If you would like a specific branch or commit, you can do so with the command +`pip install git+https://github.com/viamrobotics/viam-python-sdk.git` -`pip install git+https://github.com/viamrobotics/python-sdk.git@FULL_COMMIT_HASH` +Note that only direct gRPC connections are supported on Windows; you will need to [disable webrtc](https://python.viam.dev/autoapi/viam/rpc/dial/index.html#viam.rpc.dial.DialOptions.disable_webrtc) or else connection will fail. Full support (including webRTC) _does_ exist on WSL. + +If you intend to use the [`MLModel` service](https://python.viam.dev/autoapi/viam/services/mlmodel/mlmodel/index.html#viam.services.mlmodel.mlmodel.MLModel), use the following command instead, which installs additional required dependencies: + +`pip install 'viam-sdk[mlmodel]'` + +You can also run this command on an existing Python SDK install to add support for the ML model service. +See the [ML (machine learning) model service](https://docs.viam.com/data-ai/ai/deploy/) documentation for more information. ### Upgrading -Because the SDK is under active development, we suggest that you upgrade the package frequently. To do so, simply run the `pip install` command with the `-U` option: -`pip install -U git+https://github.com/viamrobotics/python-sdk.git` +To upgrade, simply run the `pip install` command with the `-U` option: +`pip install -U viam-sdk` -## Easy Setup via [app.viam.com](app.viam.com) -The easiest way to get started writing a client application (that is, one which is not directly responsible for interacting with hardware, -but rather calls into the viam-server to actuate hardware or read from sensors), is to navigate to the robot page on [app.viam.com](app.viam.com), -select the `CONNECT` tab, and copy the boilerplate code from the section labeled `Python SDK`. +### Installing from Source -It is recommended that you save and run this simple program. Doing so will ensure that the python-sdk is properly installed, -that the `viam-server` instance on your robot is alive, and that the computer running the program is able to connect to that instance. +The Viam Python SDK uses native libraries to support communication over WebRTC, which will allow you to connect to robots that are not on the same network. In order to facilitate that communication, there is a [rust-utils repo](https://github.com/viamrobotics/rust-utils) that contains the necessary protocols. Therefore, to build from source, you will need both the Rust utils and the Rust compiler. -## Examples -The [Example Usage](https://python.viam.dev/examples/example.html) has the info required to access a component, build a custom component, and expose -custom components as a remote to existing robots. +1. Download/clone this [repository](https://github.com/viamrobotics/viam-python-sdk) +1. Download/clone the [rust-utils](https://github.com/viamrobotics/rust-utils) +1. [Install Rust](https://www.rust-lang.org/tools/install) if not already available +1. From the `rust-utils` directory, run `cargo build` + - You can optionally provide the `--release` flag: `cargo build --release` +1. Find the compiled library in `rust-utils/target/debug/libviam_rust_utils.*` + - If you provided the `--release` flag, the enclosing directory will be `release`: `rust-utils/target/release/libviam_rust_utils.*` + - The extension of the executable will depend on your operating system. For example, on macOS it will be `libviam_rust_utils.dylib`, whereas on Linux it will be `libviam_rust_utils.so` +1. Copy the compiled library to the directory `viam-python-sdk/src/viam/rpc/` +1. From the `viam-python-sdk` directory, run `uv build --wheel` to create an installable package +1. Find the newly created installable package located in `viam-python-sdk/dist/` and pip install it directly, for example: `pip install viam-python-sdk/dist/viam_sdk-0.1.0-py3-none-any.whl` + +If you have a macOS or Linux based operating system and do not want to build rust-utils manually, you can also look for the executable in the [releases](https://github.com/viamrobotics/rust-utils/releases/latest) page of the rust-utils library. + +If you do **NOT** need communication over WebRTC (and thus, do not need the native library), the steps are: + +1. Download/clone this repository +1. Run `uv build --wheel` from the `viam-python-sdk` directory +1. Find the newly created installable package located in `viam-python-sdk/dist/` and pip install it directly, for example: `pip install viam-python-sdk/dist/viam_sdk-0.1.0-py3-none-any.whl` +1. Ensure that every connection has the option `disable_webrtc` set to `True`: `viam.rpc.dial.DialOptions(disable_webrtc=True)` + - For more information about connecting to a robot, see the [documentation](https://python.viam.dev) and [example usage](https://python.viam.dev/examples/example.html) + +## Configure a client application at [app.viam.com](https://app.viam.com) + +Your client application does not directly interact with your hardware. Instead, your client application makes calls to the `viam-server` which can then issue commands to your hardware or read from sensors. + +To create a client application, to navigate to [app.viam.com](https://app.viam.com). After you log in, perform these steps: + +1. Create a location (for example `home`) +2. Create a robot (for example `arduino`) +3. Follow the steps on the setup tab: + + 1. Setup Viam App Config on Single Board Computer (SBC) + 2. Download and Install Viam Server + 3. Wait until the robot shows as connected. If this doesn't happen try restarting the viam-server: + + ``` + sudo systemctl restart viam-server + ``` -Further examples can be found in the `examples` directory. +Next, select the `CODE SAMPLE` tab in the Viam Web UI, and copy the boilerplate code from the section labeled `Python SDK`. -## The `do` method -Every component provided by the SDK includes a generic `do` method which is useful to execute commands that are not already defined on the component. -```python -async def do(self, command: Dict[str, Any]) -> Dict[str, Any] -``` +To ensure the installation succeeded and the systems are functional, save and run this simple program. If the program runs successfully, the python-sdk is properly installed, the `viam-server` instance on your robot is alive, and the computer running the program is able to connect to that instance. -If this is not generic enough, you can also create your own custom component by subclassing the `viam.components.generic.Generic` component -class. For more details, you can view the documentation for the `Generic` component. +## The `RobotClient` & connectivity + +The main entry point for using the SDK as a client is the `RobotClient` class. This class can manage resources, operations, frames, etc., for the robot. It can also manage connectivity and behavior around sessions and reconnection through the `RobotClient.Options` nested class. + +The `RobotClient` will attempt to refresh its resources at a set interval (customizable via `Options`). + +In the event that connection is lost to the robot, the `RobotClient` will attempt to reconnect at a set interval. There are two options available for customizing this behavior: how often the client checks the connection status (`RobotClient.Options.check_connection_interval`), and how often the client attempts to reconnect upon detecting a loss of connection (`RobotClient.Options.attempt_reconnect_interval`). + +Upon a loss of connection, outstanding requests are **NOT** terminated and can possibly error with a `GRPCError` whose status is `DEADLINE_EXCEEDED`. When connection is restored, existing built-in clients will automatically receive the new connection - no need to re-obtain the client. Tasks initiated by Viam will automatically resume, but any user-defined tasks that depend on the connection should be checked and potentially restarted. + +The Viam Python SDK utilizes gRPC and, optionally WebRTC (defaults to on). gRPC is provided purely in python, but WebRTC is provided by the external [viam Rust utils](https://github.com/viamrobotics/rust-utils) library. WebRTC settings can be changed using the appropriate attributes in `viam.rpc.dial.DialOptions`. These options can be passed to the `RobotClient` through `RobotClient.Options.dial_options`. + +### Sessions + +Sessions are a safety feature that automatically cancel operations made by the python client if it loses connection to a robot. Sessions are enabled by default but can be disabled by setting `RobotClient.Options.disable_sessions = True`. Please see the [RDK session documentation](https://pkg.go.dev/go.viam.com/rdk/session) for more details and server-side configuration options. + +## Examples + +Read the [Example Usage](https://python.viam.dev/examples/example.html) page, to learn how to access a component, build a custom component, and expose +custom components as a remote to existing robots. + +More examples can be found in the [`examples`](https://github.com/viamrobotics/viam-python-sdk/tree/main/examples) directory. ## Documentation + Documentation, like this entire project, is under active development, and can be found at [python.viam.dev](https://python.viam.dev). --- + ## Development -To develop the python SDK, please see the [contribution guidelines](https://python.viam.dev/contributing.html). -### Adding new component types -The SDK provides a number of abstract base components to start. To add more abstract base components, please make sure you follow these guidelines: - -* Create a new directory in `viam.components` with the name of the new component -* Implement 3 new files in the newly created directory: - * `__init__.py`, where you should include the imports for the package - * `{COMPONENT}.py`, which should define the requirements of the component - * `service.py`, which should implement the gRPC service for this component -* Add the new service in `viam.rpc.server` to expose the gRPC service -* If the component needs to be included to the robot/status service, add it in `viam.robot.service` -* Write tests and add the component to `tests.mocks.components` -* Add the component to `examples.server.v1.components` and its corresponding concrete type in `examples.server.v1.server` + +To contribute to the python SDK, please see the [contribution guidelines](https://python.viam.dev/contributing.html). + +### Adding new resource types + +The SDK provides a number of abstract base components and services (collectively: resources). To add more abstract resources, follow these steps: + +1. Create a new directory in `viam.components` or `viam.services` with the name of the new component +1. Create 4 new files in the newly created directory: + 1. Define all requirements of the resource in `{RESOURCE_NAME}.py` + 1. Implement the gRPC service for the new resource in `service.py` + 1. Create a gRPC client for the new resource in `client.py` + 1. Register the API and define package exports in `__init__.py` +1. Write tests for the new resource and add the resource to `tests.mocks.{components|services}` +1. If the resource is a component, add the component to `examples.server.v1.components` and its corresponding concrete type in `examples.server.v1.server` ## License -Copyright 2021-2022 Viam Inc. + +Copyright 2021-2024 Viam Inc. Apache 2.0 - See [LICENSE](https://github.com/viamrobotics/viam-python-sdk/blob/main/LICENSE) file diff --git a/SETUP.md b/SETUP.md new file mode 100644 index 000000000..903d5506a --- /dev/null +++ b/SETUP.md @@ -0,0 +1,62 @@ +# Viam + Python + +This guide will help you get set up with Viam with Python. It assumes that you are starting from scratch, and will walk you through setting up a fresh environment and installing the necessary requirements. + +## Setup your project + +The first step is to create a directory to house your project. For this guide, we will be using the directory name `viam-python`: + +```bash +mkdir viam-python +cd viam-python +``` + +## Create a Virtual Environment + +Now that we are in the project directory, let's create and activate a virtual environment for python to run in. + +> **INFO** +> Creating a virtual environment (`venv`) is important as it isolates this python environment from any other you might already have. This allows us to ensure a clean project and easier dependency management, and it avoids bloating your global python environment. + +```bash +python3 -m venv viam-env +source viam-env/bin/activate +``` + +> **INFO** +> Some Linux environments may not have the necessary requirements to create a virtual environment. If you receive an error, you can try running `apt install python3-venv` and then running the above commands. + +You will now see `(viam-env)` prepend the commands in your terminal window. This shows that the python packages being used are from this particular environment. + +You can exit this environment by running `deactivate`. + +## Install Viam + +Inside the activated `viam-env` python environment, you can now install the Viam SDK: + +```bash +pip3 install viam-sdk +``` + +This will install Viam and all required dependencies. + +> **INFO** +> Some features of the Viam SDK require additional dependencies. For example, the ML Models feature requires extras, which can be installed with `pip3 install viam-sdk[mlmodel]`. Read the documentation to determine if a specific feature requires extra dependencies. + +Should you need to install your own requirements, be sure to do so in this environment. + +## Setup your IDE + +You'll now want to point your IDE to use the python interpreter of your new environment, rather than the default interpreter (likely the global python interpreter). + +The following steps are for VS Code. If you're not using VS Code, please read your IDE's documentation on selecting python interpreters. + +1. Open the `viam-python` directory in VS Code +1. Open the Command Palette (using `⇧⌘P` or through the menus View -> Command Palette) +1. Select the command `Python: Select Interpreter`. There, you should see all the interpreters available to you. You're looking for one the on you just made: `viam-env`. It will look something like: `Python 3.11.5 ('viam-env': venv) ./viam-env/bin/python`. If you don't see it, click the `Refresh` icon on the top right of the Command Palette. + +Your IDE will now recognize all packages installed in this environment. + +## Start building! + +You are now ready to start using Viam's Python SDK! diff --git a/buf.gen.yaml b/buf.gen.yaml index 0d592a5d4..3032a325b 100644 --- a/buf.gen.yaml +++ b/buf.gen.yaml @@ -2,7 +2,8 @@ version: v1 plugins: - name: python out: ./src/viam/gen - - name: grpclib_python + - name: viam_grpc_python out: ./src/viam/gen + path: [python3, ./plugin/main.py] - name: mypy out: ./src/viam/gen diff --git a/buf.lock b/buf.lock deleted file mode 100644 index 38a602a2f..000000000 --- a/buf.lock +++ /dev/null @@ -1,15 +0,0 @@ -# Generated by buf. DO NOT EDIT. -version: v1 -deps: - - remote: buf.build - owner: googleapis - repository: googleapis - commit: 62f35d8aed1149c291d606d958a7ce32 - - remote: buf.build - owner: viamrobotics - repository: api - commit: ca0b55452786466ab26c1ca07ec6302f - - remote: buf.build - owner: viamrobotics - repository: goutils - commit: 97870753c5dc4acc86deaa33213df626 diff --git a/doc/dependency_decisions.yml b/doc/dependency_decisions.yml index 0e1ade20f..1bed1572e 100644 --- a/doc/dependency_decisions.yml +++ b/doc/dependency_decisions.yml @@ -1,57 +1,63 @@ --- - - :permit - Apache 2.0 - - :who: - :why: + - :who: + :why: :versions: [] :when: 2022-08-12 14:31:48.226163463 Z - - :permit - MIT - - :who: - :why: + - :who: + :why: :versions: [] :when: 2022-08-12 14:31:52.953331422 Z - - :permit - BSD-3-Clause - - :who: - :why: + - :who: + :why: :versions: [] :when: 2022-08-15 12:18:01.493210046 Z - - :permit - BSD - - :who: - :why: + - :who: + :why: :versions: [] :when: 2022-08-15 12:18:10.367754569 Z - - :permit - Simplified BSD - - :who: - :why: + - :who: + :why: :versions: [] :when: 2022-08-15 12:18:30.799597748 Z - - :permit - ISC License (ISCL) - - :who: - :why: + - :who: + :why: :versions: [] :when: 2022-08-15 12:30:07.716433134 Z - - :license - matplotlib-inline - BSD-3-Clause - - :who: + - :who: :why: 'TODO(RSDK-583): why is license finder unable to detect this license automatically?' :license_links: https://github.com/ipython/matplotlib-inline/blob/fbf0ab8f2d81993ff8e319a003f58896c77f2443/LICENSE :versions: [] :when: 2022-09-01 18:14:44.253208011 Z - - :permit - Historical Permission Notice and Disclaimer (HPND) - - :who: - :why: + - :who: + :why: :versions: [] :when: 2022-09-16 15:36:22.534210576 Z - - :permit - Python Software Foundation License - - :who: - :why: + - :who: + :why: :versions: [] :when: 2022-09-16 15:36:35.544888808 Z +- - :approve + - protobuf + - :who: + :why: + :versions: [] + :when: 2022-11-17 17:47:26.832844000 Z diff --git a/docs/conf.py b/docs/conf.py index ba8f732fc..54528133e 100755 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,9 +6,9 @@ # -- Project information ----------------------------------------------------- -project = u"Viam Python SDK" -copyright = u"2022, Viam Inc" -author = u"Viam Inc" +project = "Viam Python SDK" +copyright = "2022-2024, Viam Inc" +author = "Viam Inc" # -- General configuration --------------------------------------------------- @@ -22,13 +22,14 @@ "sphinx.ext.viewcode", ] autoapi_dirs = ["../src"] -autoapi_file_patterns = ['*.pyi', '*.py'] +autoapi_file_patterns = ["*.pyi", "*.py"] autoapi_options = [ - 'undoc-members', - 'show-inhertiance', - 'show-module-summary', - 'special-members', - 'imported-members', + "undoc-members", + "show-inheritance", + "inherited-members", + "show-module-summary", + "special-members", + "imported-members", ] # List of patterns, relative to source directory, that match files and @@ -38,24 +39,28 @@ def skip_member(app, what, name, obj, skip, options) -> bool: - if 'gen.proto' in name: + if "gen.proto" in name: return True - if 'proto' in name: - if 'google' in name: + if "proto" in name: + if "google" in name: return True - if what == 'attribute': - if '_FIELD_NUMBER' in name: + should_keep = ["do_command", "get_geometries", "close", "from_robot", "get_resource_name", "get_operation"] + if obj.inherited and not any(method in name for method in should_keep): + return True + if what == "attribute": + if "_FIELD_NUMBER" in name: return True - if 'DESCRIPTOR' in name: + if "DESCRIPTOR" in name: return True - if what == 'method': - if 'ClearField' in name: + if what == "method": + if "ClearField" in name: return True return skip def setup(sphinx): - sphinx.connect('autoapi-skip-member', skip_member) + sphinx.connect("autoapi-skip-member", skip_member) + # -- Options for HTML output ------------------------------------------------- @@ -66,5 +71,5 @@ def setup(sphinx): html_theme = "sphinx_rtd_theme" html_theme_options = { - 'navigation_depth': -1, + "navigation_depth": -1, } diff --git a/docs/examples/_server.py b/docs/examples/_server.py new file mode 100644 index 000000000..a44084680 --- /dev/null +++ b/docs/examples/_server.py @@ -0,0 +1,606 @@ +import asyncio +from datetime import datetime + +from google.protobuf.struct_pb2 import Struct, Value +from google.protobuf.timestamp_pb2 import Timestamp +from grpclib.server import Server, Stream +from grpclib.utils import graceful_exit + +from viam.app.data_client import DataClient +from viam.proto.app import ( + AddRoleRequest, + AddRoleResponse, + ChangeRoleRequest, + ChangeRoleResponse, + CheckPermissionsRequest, + CheckPermissionsResponse, + CreateFragmentRequest, + CreateFragmentResponse, + CreateKeyFromExistingKeyAuthorizationsRequest, + CreateKeyFromExistingKeyAuthorizationsResponse, + CreateKeyRequest, + CreateKeyResponse, + CreateLocationRequest, + CreateLocationResponse, + CreateLocationSecretRequest, + CreateLocationSecretResponse, + CreateModuleRequest, + CreateModuleResponse, + CreateOrganizationInviteRequest, + CreateOrganizationInviteResponse, + CreateOrganizationRequest, + CreateOrganizationResponse, + CreateRegistryItemRequest, + CreateRegistryItemResponse, + CreateRobotPartSecretRequest, + CreateRobotPartSecretResponse, + DeleteFragmentRequest, + DeleteFragmentResponse, + DeleteKeyRequest, + DeleteKeyResponse, + DeleteLocationRequest, + DeleteLocationResponse, + DeleteLocationSecretRequest, + DeleteLocationSecretResponse, + DeleteOrganizationInviteRequest, + DeleteOrganizationInviteResponse, + DeleteOrganizationMemberRequest, + DeleteOrganizationMemberResponse, + DeleteOrganizationRequest, + DeleteOrganizationResponse, + DeleteRegistryItemRequest, + DeleteRegistryItemResponse, + DeleteRobotPartRequest, + DeleteRobotPartResponse, + DeleteRobotPartSecretRequest, + DeleteRobotPartSecretResponse, + DeleteRobotRequest, + DeleteRobotResponse, + GetFragmentRequest, + GetFragmentResponse, + GetLocationRequest, + GetLocationResponse, + GetModuleRequest, + GetModuleResponse, + GetOrganizationNamespaceAvailabilityRequest, + GetOrganizationNamespaceAvailabilityResponse, + GetOrganizationRequest, + GetOrganizationResponse, + GetOrganizationsWithAccessToLocationRequest, + GetOrganizationsWithAccessToLocationResponse, + GetRegistryItemRequest, + GetRegistryItemResponse, + GetRobotAPIKeysRequest, + GetRobotAPIKeysResponse, + GetRobotPartHistoryRequest, + GetRobotPartHistoryResponse, + GetRobotPartLogsRequest, + GetRobotPartLogsResponse, + GetRobotPartRequest, + GetRobotPartResponse, + GetRobotPartsRequest, + GetRobotPartsResponse, + GetRobotRequest, + GetRobotResponse, + GetRoverRentalRobotsRequest, + GetRoverRentalRobotsResponse, + GetUserIDByEmailRequest, + GetUserIDByEmailResponse, + ListAuthorizationsRequest, + ListAuthorizationsResponse, + ListFragmentsRequest, + ListFragmentsResponse, + ListKeysRequest, + ListKeysResponse, + ListLocationsRequest, + ListLocationsResponse, + ListModulesRequest, + ListModulesResponse, + ListOrganizationMembersRequest, + ListOrganizationMembersResponse, + ListOrganizationsByUserRequest, + ListOrganizationsByUserResponse, + ListOrganizationsRequest, + ListOrganizationsResponse, + ListRegistryItemsRequest, + ListRegistryItemsResponse, + ListRobotsRequest, + ListRobotsResponse, + Location, + LocationAuthRequest, + LocationAuthResponse, + MarkPartAsMainRequest, + MarkPartAsMainResponse, + MarkPartForRestartRequest, + MarkPartForRestartResponse, + NewRobotPartRequest, + NewRobotPartResponse, + NewRobotRequest, + NewRobotResponse, + Organization, + RemoveRoleRequest, + RemoveRoleResponse, + ResendOrganizationInviteRequest, + ResendOrganizationInviteResponse, + Robot, + RotateKeyRequest, + RotateKeyResponse, + ShareLocationRequest, + ShareLocationResponse, + TailRobotPartLogsRequest, + TailRobotPartLogsResponse, + UnimplementedAppServiceBase, + UnshareLocationRequest, + UnshareLocationResponse, + UpdateFragmentRequest, + UpdateFragmentResponse, + UpdateLocationRequest, + UpdateLocationResponse, + UpdateModuleRequest, + UpdateModuleResponse, + UpdateOrganizationInviteAuthorizationsRequest, + UpdateOrganizationInviteAuthorizationsResponse, + UpdateOrganizationRequest, + UpdateOrganizationResponse, + UpdateRegistryItemRequest, + UpdateRegistryItemResponse, + UpdateRobotPartRequest, + UpdateRobotPartResponse, + UpdateRobotRequest, + UpdateRobotResponse, + UploadModuleFileRequest, + UploadModuleFileResponse, +) +from viam.proto.app.data import ( + AddBinaryDataToDatasetByIDsRequest, + AddBinaryDataToDatasetByIDsResponse, + AddBoundingBoxToImageByIDRequest, + AddBoundingBoxToImageByIDResponse, + AddTagsToBinaryDataByFilterRequest, + AddTagsToBinaryDataByFilterResponse, + AddTagsToBinaryDataByIDsRequest, + AddTagsToBinaryDataByIDsResponse, + BinaryDataByFilterRequest, + BinaryDataByFilterResponse, + BinaryDataByIDsRequest, + BinaryDataByIDsResponse, + BoundingBoxLabelsByFilterRequest, + BoundingBoxLabelsByFilterResponse, + CaptureMetadata, + ConfigureDatabaseUserRequest, + ConfigureDatabaseUserResponse, + DeleteBinaryDataByFilterRequest, + DeleteBinaryDataByFilterResponse, + DeleteBinaryDataByIDsRequest, + DeleteBinaryDataByIDsResponse, + DeleteTabularDataRequest, + DeleteTabularDataResponse, + GetDatabaseConnectionRequest, + GetDatabaseConnectionResponse, + RemoveBinaryDataFromDatasetByIDsRequest, + RemoveBinaryDataFromDatasetByIDsResponse, + RemoveBoundingBoxFromImageByIDRequest, + RemoveBoundingBoxFromImageByIDResponse, + RemoveTagsFromBinaryDataByFilterRequest, + RemoveTagsFromBinaryDataByFilterResponse, + RemoveTagsFromBinaryDataByIDsRequest, + RemoveTagsFromBinaryDataByIDsResponse, + TabularData, + TabularDataByFilterRequest, + TabularDataByFilterResponse, + TabularDataByMQLRequest, + TabularDataByMQLResponse, + TabularDataBySQLRequest, + TabularDataBySQLResponse, + TagsByFilterRequest, + TagsByFilterResponse, + UnimplementedDataServiceBase, +) +from viam.proto.app.datasync import ( + DataCaptureUploadRequest, + DataCaptureUploadResponse, + DataSyncServiceBase, + FileUploadRequest, + FileUploadResponse, + StreamingDataCaptureUploadRequest, + StreamingDataCaptureUploadResponse, +) +from viam.proto.common import LogEntry as LogEntryPB +from viam.utils import datetime_to_timestamp, dict_to_struct, value_to_primitive + + +class MockData(UnimplementedDataServiceBase): + def __init__(self): + self.tabular_data_requested = False + self.tabular_response = [ + DataClient.TabularData( + {"PowerPct": 0, "IsPowered": False}, + CaptureMetadata(method_name="IsPowered"), + datetime(2022, 1, 1, 1, 1, 1), + datetime(2022, 12, 31, 23, 59, 59), + ), + DataClient.TabularData( + {"PowerPct": 0, "IsPowered": False}, CaptureMetadata(location_id="loc-id"), datetime(2023, 1, 2), datetime(2023, 3, 4) + ), + DataClient.TabularData( + {"Position": 0}, + CaptureMetadata(), + datetime(2023, 5, 6), + datetime(2023, 7, 8), + ), + ] + + async def TabularDataByFilter(self, stream: Stream[TabularDataByFilterRequest, TabularDataByFilterResponse]) -> None: + if self.tabular_data_requested: + await stream.send_message(TabularDataByFilterResponse()) + return + self.tabular_data_requested = True + _ = await stream.recv_message() + tabular_structs = [] + tabular_metadata = [data.metadata for data in self.tabular_response] + for idx, tabular_data in enumerate(self.tabular_response): + tabular_structs.append( + TabularData( + data=dict_to_struct(tabular_data.data), + metadata_index=idx, + time_requested=datetime_to_timestamp(tabular_data.time_requested), + time_received=datetime_to_timestamp(tabular_data.time_received), + ) + ) + await stream.send_message( + TabularDataByFilterResponse( + data=tabular_structs, + metadata=tabular_metadata, + ) + ) + + async def BinaryDataByFilter(self, stream: Stream[BinaryDataByFilterRequest, BinaryDataByFilterResponse]) -> None: + pass + + async def BinaryDataByIDs(self, stream: Stream[BinaryDataByIDsRequest, BinaryDataByIDsResponse]) -> None: + pass + + async def DeleteTabularData(self, stream: Stream[DeleteTabularDataRequest, DeleteTabularDataResponse]) -> None: + pass + + async def DeleteBinaryDataByFilter(self, stream: Stream[DeleteBinaryDataByFilterRequest, DeleteBinaryDataByFilterResponse]) -> None: + pass + + async def DeleteBinaryDataByIDs(self, stream: Stream[DeleteBinaryDataByIDsRequest, DeleteBinaryDataByIDsResponse]) -> None: + pass + + async def AddTagsToBinaryDataByIDs(self, stream: Stream[AddTagsToBinaryDataByIDsRequest, AddTagsToBinaryDataByIDsResponse]) -> None: + pass + + async def AddTagsToBinaryDataByFilter( + self, stream: Stream[AddTagsToBinaryDataByFilterRequest, AddTagsToBinaryDataByFilterResponse] + ) -> None: + pass + + async def RemoveTagsFromBinaryDataByIDs( + self, stream: Stream[RemoveTagsFromBinaryDataByIDsRequest, RemoveTagsFromBinaryDataByIDsResponse] + ) -> None: + pass + + async def RemoveTagsFromBinaryDataByFilter( + self, stream: Stream[RemoveTagsFromBinaryDataByFilterRequest, RemoveTagsFromBinaryDataByFilterResponse] + ) -> None: + pass + + async def TagsByFilter(self, stream: Stream[TagsByFilterRequest, TagsByFilterResponse]) -> None: + pass + + async def AddBoundingBoxToImageByID(self, stream: Stream[AddBoundingBoxToImageByIDRequest, AddBoundingBoxToImageByIDResponse]) -> None: + pass + + async def RemoveBoundingBoxFromImageByID( + self, stream: Stream[RemoveBoundingBoxFromImageByIDRequest, RemoveBoundingBoxFromImageByIDResponse] + ) -> None: + pass + + async def BoundingBoxLabelsByFilter(self, stream: Stream[BoundingBoxLabelsByFilterRequest, BoundingBoxLabelsByFilterResponse]) -> None: + pass + + async def GetDatabaseConnection(self, stream: Stream[GetDatabaseConnectionRequest, GetDatabaseConnectionResponse]) -> None: + pass + + async def ConfigureDatabaseUser(self, stream: Stream[ConfigureDatabaseUserRequest, ConfigureDatabaseUserResponse]) -> None: + pass + + async def AddBinaryDataToDatasetByIDs( + self, stream: Stream[AddBinaryDataToDatasetByIDsRequest, AddBinaryDataToDatasetByIDsResponse] + ) -> None: + pass + + async def RemoveBinaryDataFromDatasetByIDs( + self, stream: Stream[RemoveBinaryDataFromDatasetByIDsRequest, RemoveBinaryDataFromDatasetByIDsResponse] + ) -> None: + pass + + async def TabularDataBySQL(self, stream: Stream[TabularDataBySQLRequest, TabularDataBySQLResponse]) -> None: + pass + + async def TabularDataByMQL(self, stream: Stream[TabularDataByMQLRequest, TabularDataByMQLResponse]) -> None: + pass + + +class MockDataSync(DataSyncServiceBase): + async def DataCaptureUpload(self, stream: Stream[DataCaptureUploadRequest, DataCaptureUploadResponse]) -> None: + await stream.send_message(DataCaptureUploadResponse()) + + async def FileUpload(self, stream: Stream[FileUploadRequest, FileUploadResponse]) -> None: + pass + + async def StreamingDataCaptureUpload( + self, stream: Stream[StreamingDataCaptureUploadRequest, StreamingDataCaptureUploadResponse] + ) -> None: + pass + + +class MockApp(UnimplementedAppServiceBase): + def __init__(self): + self.organization = Organization(id="id", name="name", public_namespace="public_namespace", default_region="default_region") + self.locations = [Location()] + self.robots = [Robot(name=f"robot-{i}") for i in range(4)] + self.new_id = "new_id" + self.updated_robot = Robot(id="id", name="name", location="location") + self.log = LogEntryPB( + host="mypi", + level="error", + time=Timestamp(seconds=1690824474, nanos=501000000), + logger_name="robot_server", + message="module config validation error; skipping", + caller=dict_to_struct( + { + "Line": 922, + "Function": "go.viam.com/rdk/robot/impl.(*resourceManager).updateResources", + "File": "/__w/rdk/rdk/robot/impl/resource_manager.go", + "Defined": True, + } + ), + fields=[ + dict_to_struct({"Type": 15, "String": "my-module", "Key": "module", "Interface": 0, "Integer": 0}), + dict_to_struct( + { + "Type": 26, + "String": "module modules.0 executable path error: stat /Users/user-1/Desktop/run.sh: no such file or directory", + "Key": "error", + "Interface": value_to_primitive(value=Value(struct_value=Struct())), + "Integer": 0, + } + ), + ], + ) + + async def GetUserIDByEmail(self, stream: Stream[GetUserIDByEmailRequest, GetUserIDByEmailResponse]) -> None: + pass + + async def CreateOrganization(self, stream: Stream[CreateOrganizationRequest, CreateOrganizationResponse]) -> None: + pass + + async def ListOrganizations(self, stream: Stream[ListOrganizationsRequest, ListOrganizationsResponse]) -> None: + await stream.recv_message() + await stream.send_message(ListOrganizationsResponse(organizations=[self.organization])) + + async def ListOrganizationsByUser(self, stream: Stream[ListOrganizationsByUserRequest, ListOrganizationsByUserResponse]) -> None: + pass + + async def GetOrganization(self, stream: Stream[GetOrganizationRequest, GetOrganizationResponse]) -> None: + pass + + async def GetOrganizationNamespaceAvailability( + self, stream: Stream[GetOrganizationNamespaceAvailabilityRequest, GetOrganizationNamespaceAvailabilityResponse] + ) -> None: + pass + + async def UpdateOrganization(self, stream: Stream[UpdateOrganizationRequest, UpdateOrganizationResponse]) -> None: + pass + + async def DeleteOrganization(self, stream: Stream[DeleteOrganizationRequest, DeleteOrganizationResponse]) -> None: + pass + + async def ListOrganizationMembers(self, stream: Stream[ListOrganizationMembersRequest, ListOrganizationMembersResponse]) -> None: + pass + + async def CreateOrganizationInvite(self, stream: Stream[CreateOrganizationInviteRequest, CreateOrganizationInviteResponse]) -> None: + pass + + async def UpdateOrganizationInviteAuthorizations( + self, stream: Stream[UpdateOrganizationInviteAuthorizationsRequest, UpdateOrganizationInviteAuthorizationsResponse] + ) -> None: + pass + + async def DeleteOrganizationMember(self, stream: Stream[DeleteOrganizationMemberRequest, DeleteOrganizationMemberResponse]) -> None: + pass + + async def DeleteOrganizationInvite(self, stream: Stream[DeleteOrganizationInviteRequest, DeleteOrganizationInviteResponse]) -> None: + pass + + async def ResendOrganizationInvite(self, stream: Stream[ResendOrganizationInviteRequest, ResendOrganizationInviteResponse]) -> None: + pass + + async def CreateLocation(self, stream: Stream[CreateLocationRequest, CreateLocationResponse]) -> None: + pass + + async def GetLocation(self, stream: Stream[GetLocationRequest, GetLocationResponse]) -> None: + pass + + async def UpdateLocation(self, stream: Stream[UpdateLocationRequest, UpdateLocationResponse]) -> None: + pass + + async def DeleteLocation(self, stream: Stream[DeleteLocationRequest, DeleteLocationResponse]) -> None: + pass + + async def ListLocations(self, stream: Stream[ListLocationsRequest, ListLocationsResponse]) -> None: + await stream.recv_message() + await stream.send_message(ListLocationsResponse(locations=self.locations)) + + async def ShareLocation(self, stream: Stream[ShareLocationRequest, ShareLocationResponse]) -> None: + pass + + async def UnshareLocation(self, stream: Stream[UnshareLocationRequest, UnshareLocationResponse]) -> None: + pass + + async def LocationAuth(self, stream: Stream[LocationAuthRequest, LocationAuthResponse]) -> None: + pass + + async def CreateLocationSecret(self, stream: Stream[CreateLocationSecretRequest, CreateLocationSecretResponse]) -> None: + pass + + async def DeleteLocationSecret(self, stream: Stream[DeleteLocationSecretRequest, DeleteLocationSecretResponse]) -> None: + pass + + async def GetRobot(self, stream: Stream[GetRobotRequest, GetRobotResponse]) -> None: + pass + + async def GetRoverRentalRobots(self, stream: Stream[GetRoverRentalRobotsRequest, GetRoverRentalRobotsResponse]) -> None: + pass + + async def GetRobotParts(self, stream: Stream[GetRobotPartsRequest, GetRobotPartsResponse]) -> None: + pass + + async def GetRobotPart(self, stream: Stream[GetRobotPartRequest, GetRobotPartResponse]) -> None: + pass + + async def GetRobotPartLogs(self, stream: Stream[GetRobotPartLogsRequest, GetRobotPartLogsResponse]) -> None: + await stream.recv_message() + await stream.send_message(GetRobotPartLogsResponse(logs=[self.log])) + + async def TailRobotPartLogs(self, stream: Stream[TailRobotPartLogsRequest, TailRobotPartLogsResponse]) -> None: + pass + + async def GetRobotPartHistory(self, stream: Stream[GetRobotPartHistoryRequest, GetRobotPartHistoryResponse]) -> None: + pass + + async def UpdateRobotPart(self, stream: Stream[UpdateRobotPartRequest, UpdateRobotPartResponse]) -> None: + pass + + async def NewRobotPart(self, stream: Stream[NewRobotPartRequest, NewRobotPartResponse]) -> None: + pass + + async def DeleteRobotPart(self, stream: Stream[DeleteRobotPartRequest, DeleteRobotPartResponse]) -> None: + pass + + async def MarkPartAsMain(self, stream: Stream[MarkPartAsMainRequest, MarkPartAsMainResponse]) -> None: + pass + + async def MarkPartForRestart(self, stream: Stream[MarkPartForRestartRequest, MarkPartForRestartResponse]) -> None: + pass + + async def CreateRobotPartSecret(self, stream: Stream[CreateRobotPartSecretRequest, CreateRobotPartSecretResponse]) -> None: + pass + + async def DeleteRobotPartSecret(self, stream: Stream[DeleteRobotPartSecretRequest, DeleteRobotPartSecretResponse]) -> None: + pass + + async def ListRobots(self, stream: Stream[ListRobotsRequest, ListRobotsResponse]) -> None: + await stream.recv_message() + await stream.send_message(ListRobotsResponse(robots=self.robots)) + + async def NewRobot(self, stream: Stream[NewRobotRequest, NewRobotResponse]) -> None: + await stream.recv_message() + await stream.send_message(NewRobotResponse(id=self.new_id)) + + async def UpdateRobot(self, stream: Stream[UpdateRobotRequest, UpdateRobotResponse]) -> None: + await stream.recv_message() + await stream.send_message(UpdateRobotResponse(robot=self.updated_robot)) + + async def DeleteRobot(self, stream: Stream[DeleteRobotRequest, DeleteRobotResponse]) -> None: + await stream.recv_message() + await stream.send_message(DeleteRobotResponse()) + + async def ListFragments(self, stream: Stream[ListFragmentsRequest, ListFragmentsResponse]) -> None: + pass + + async def GetFragment(self, stream: Stream[GetFragmentRequest, GetFragmentResponse]) -> None: + pass + + async def CreateFragment(self, stream: Stream[CreateFragmentRequest, CreateFragmentResponse]) -> None: + pass + + async def UpdateFragment(self, stream: Stream[UpdateFragmentRequest, UpdateFragmentResponse]) -> None: + pass + + async def DeleteFragment(self, stream: Stream[DeleteFragmentRequest, DeleteFragmentResponse]) -> None: + pass + + async def AddRole(self, stream: Stream[AddRoleRequest, AddRoleResponse]) -> None: + pass + + async def RemoveRole(self, stream: Stream[RemoveRoleRequest, RemoveRoleResponse]) -> None: + pass + + async def ChangeRole(self, stream: Stream[ChangeRoleRequest, ChangeRoleResponse]) -> None: + pass + + async def ListAuthorizations(self, stream: Stream[ListAuthorizationsRequest, ListAuthorizationsResponse]) -> None: + pass + + async def CheckPermissions(self, stream: Stream[CheckPermissionsRequest, CheckPermissionsResponse]) -> None: + pass + + async def CreateModule(self, stream: Stream[CreateModuleRequest, CreateModuleResponse]) -> None: + pass + + async def UpdateModule(self, stream: Stream[UpdateModuleRequest, UpdateModuleResponse]) -> None: + pass + + async def UploadModuleFile(self, stream: Stream[UploadModuleFileRequest, UploadModuleFileResponse]) -> None: + pass + + async def GetModule(self, stream: Stream[GetModuleRequest, GetModuleResponse]) -> None: + pass + + async def ListModules(self, stream: Stream[ListModulesRequest, ListModulesResponse]) -> None: + pass + + async def CreateKey(self, stream: Stream[CreateKeyRequest, CreateKeyResponse]) -> None: + pass + + async def GetRobotAPIKeys(self, stream: Stream[GetRobotAPIKeysRequest, GetRobotAPIKeysResponse]) -> None: + pass + + async def DeleteKey(self, stream: Stream[DeleteKeyRequest, DeleteKeyResponse]) -> None: + pass + + async def ListKeys(self, stream: Stream[ListKeysRequest, ListKeysResponse]) -> None: + pass + + async def RotateKey(self, stream: Stream[RotateKeyRequest, RotateKeyResponse]) -> None: + pass + + async def CreateKeyFromExistingKeyAuthorizations( + self, stream: Stream[CreateKeyFromExistingKeyAuthorizationsRequest, CreateKeyFromExistingKeyAuthorizationsResponse] + ) -> None: + pass + + async def CreateRegistryItem(self, stream: Stream[CreateRegistryItemRequest, CreateRegistryItemResponse]) -> None: + raise NotImplementedError() + + async def GetOrganizationsWithAccessToLocation( + self, stream: Stream[GetOrganizationsWithAccessToLocationRequest, GetOrganizationsWithAccessToLocationResponse] + ) -> None: + raise NotImplementedError() + + async def ListRegistryItems(self, stream: Stream[ListRegistryItemsRequest, ListRegistryItemsResponse]) -> None: + raise NotImplementedError() + + async def UpdateRegistryItem(self, stream: Stream[UpdateRegistryItemRequest, UpdateRegistryItemResponse]) -> None: + raise NotImplementedError() + + async def DeleteRegistryItem(self, stream: Stream[DeleteRegistryItemRequest, DeleteRegistryItemResponse]) -> None: + raise NotImplementedError() + + async def GetRegistryItem(self, stream: Stream[GetRegistryItemRequest, GetRegistryItemResponse]) -> None: + raise NotImplementedError() + + +async def main(*, host: str = "127.0.0.1", port: int = 9092) -> None: + server = Server([MockData(), MockDataSync(), MockApp()]) + with graceful_exit([server]): + await server.start(host, port) + await server.wait_closed() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/examples/codediff.png b/docs/examples/codediff.png new file mode 100644 index 000000000..c7c2b26a2 Binary files /dev/null and b/docs/examples/codediff.png differ diff --git a/docs/examples/example.ipynb b/docs/examples/example.ipynb index 2be315959..6369249b8 100644 --- a/docs/examples/example.ipynb +++ b/docs/examples/example.ipynb @@ -1,14 +1,16 @@ { "cells": [ { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Example usage\n", "\n", - "The Viam SDK can be used in two ways:\n", + "The Viam SDK can be used in three ways:\n", "1. As a client to connect to a (remote or local) robot\n", - "2. To create custom components and provide additional functionality to a robot\n", + "2. Integrate custom resources to a robot\n", + "3. As a client to connect to app.viam.com to upload and retrieve data\n", "\n", "## Connect as a client\n", "\n", @@ -17,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 1, "metadata": { "tags": [ "remove-input" @@ -28,7 +30,8 @@ "# Please excuse the boilerplate\n", "%autoawait asyncio\n", "import warnings\n", - "warnings.filterwarnings('ignore')" + "\n", + "warnings.filterwarnings(\"ignore\")" ] }, { @@ -39,6 +42,33 @@ "hide-output" ] }, + "outputs": [], + "source": [ + "from viam import logging\n", + "from viam.robot.client import RobotClient\n", + "from viam.rpc.dial import DialOptions\n", + "\n", + "\n", + "async def connect() -> RobotClient:\n", + " options = RobotClient.Options(dial_options=DialOptions(insecure=True, disable_webrtc=True), log_level=logging.FATAL)\n", + " return await RobotClient.at_address(\"localhost:9091\", options)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also create a `RobotClient` by providing an existing connection" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [ + "hide-output" + ] + }, "outputs": [ { "name": "stdout", @@ -50,6 +80,10 @@ "name: \"arm0\"\n", ", namespace: \"rdk\"\n", "type: \"component\"\n", + "subtype: \"audio_input\"\n", + "name: \"audio_input0\"\n", + ", namespace: \"rdk\"\n", + "type: \"component\"\n", "subtype: \"base\"\n", "name: \"base0\"\n", ", namespace: \"rdk\"\n", @@ -95,16 +129,15 @@ "source": [ "from viam import logging\n", "from viam.robot.client import RobotClient\n", - "from viam.rpc.dial import DialOptions\n", + "from viam.rpc.dial import DialOptions, dial\n", + "\n", + "\n", + "async def connect_with_channel() -> RobotClient:\n", + " async with await dial(\"localhost:9091\", DialOptions(insecure=True, disable_webrtc=True)) as channel:\n", + " return await RobotClient.with_channel(channel, RobotClient.Options(refresh_interval=10, log_level=logging.FATAL))\n", "\n", - "async def connect() -> RobotClient:\n", - " options = RobotClient.Options(\n", - " dial_options=DialOptions(insecure=True),\n", - " log_level=logging.FATAL\n", - " )\n", - " return await RobotClient.at_address('0.0.0.0:9091', options)\n", "\n", - "robot = await connect()\n", + "robot = await connect_with_channel()\n", "print(robot.resource_names)\n", "await robot.close()" ] @@ -113,37 +146,40 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "You can also create a `RobotClient` by providing an existing connection" + "Once you have a connected `RobotClient`, you can then obtain the robot's components by their name" ] }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, + "execution_count": 5, + "metadata": { + "tags": [ + "hide-output" + ] + }, "outputs": [], "source": [ - "from viam import logging\n", - "from viam.robot.client import RobotClient\n", - "from viam.rpc.dial import DialOptions, dial_direct\n", + "from viam.components.camera import Camera\n", + "from viam.media.video import CameraMimeType\n", + "from viam.media.utils.pil import viam_to_pil_image\n", "\n", - "async def connect_with_channel() -> RobotClient:\n", - " async with await dial_direct('0.0.0.0:5432', DialOptions(insecure=True)) as channel:\n", - " return await RobotClient.with_channel(channel, RobotClient.Options(refresh_interval=10, log_level=logging.FATAL))" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Once you have a connected `RobotClient`, you can then obtain the robot's components by their name" + "robot = await connect_with_channel()\n", + "camera = Camera.from_robot(robot, \"camera0\")\n", + "image = await camera.get_image(CameraMimeType.JPEG)\n", + "pil = viam_to_pil_image(image)\n", + "pil.save(\"foo.png\")\n", + "\n", + "# Don't forget to close the robot when you're done!\n", + "await robot.close()" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": { "tags": [ - "hide-output" + "hide-output", + "remove-input" ] }, "outputs": [ @@ -159,16 +195,7 @@ } ], "source": [ - "from viam.components.camera import Camera\n", - "from viam.components.types import CameraMimeType\n", - "\n", - "robot = await connect()\n", - "camera = Camera.from_robot(robot, \"camera0\")\n", - "image = await camera.get_image()\n", - "display(image)\n", - "\n", - "# Don't forget to close the robot when you're done!\n", - "await robot.close()\n" + "display(image)" ] }, { @@ -184,11 +211,12 @@ "metadata": {}, "outputs": [], "source": [ - "from viam.services.vision import VisionServiceClient\n", + "from viam.services.vision import VisionClient\n", + "\n", "\n", "async def vision():\n", - " robot = await connect()\n", - " vision = VisionServiceClient.from_robot(robot)\n", + " robot = await connect_with_channel()\n", + " vision = VisionClient.from_robot(robot)\n", " detections = await vision.get_detections_from_camera(\"camera_1\", \"detector_1\")" ] }, @@ -210,19 +238,453 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "## Create custom components\n", + "## Create custom modules\n", + "Make a modular resource from an existing resource API. In this document, we will be going over subclassing existing resource APIs. To learn more about creating a new resource API, see the [Viam docs](https://docs.viam.com/extend/modular-resources/#code-your-module), and [complex module example](https://github.com/viamrobotics/viam-python-sdk/tree/main/examples/complex_module).\n", + "\n", + "For a fully fleshed-out example of a Python module that uses Github CI to upload to the Viam Registry, take a look at [python-example-module](https://github.com/viam-labs/python-example-module). For an example that uses Docker to manage dependencies, take a look at [python-container-module](https://github.com/viamrobotics/python-container-module). For a list of example modules in different Viam SDKs, take a look [here](https://github.com/viamrobotics/upload-module/#example-repos).\n", + "\n", + "The code below resembles the [simple module example](https://github.com/viamrobotics/viam-python-sdk/tree/main/examples/simple_module), so look there for the final, completed modular resource.\n", + "\n", + "The steps required in creating a modular resource and connecting it to a robot are:\n", + "1. **Subclass a resource and implement desired functions.** This will be your custom resource model.\n", + " - For functions that you do not wish to implement, you must at least define them by putting `pass` or `raise NotImplementedError()` in the function.\n", + "2. **Register a new model.** This will register the new modular resource model into the `Registry`.\n", + "3. **Start the module.** Create and start the new module.\n", + "4. **Make the module executable.** This allows `viam-server` to access and execute the module.\n", + "5. **Configure a modular resource.** Use the new module and instantiate a new resource to a robot!\n", + "\n", + "Knowing this, let's implement a custom resource.\n", + " \n", + "### 1. Subclass a resource\n", + "The SDK provides a wide array of resource APIs to customize. You can browse through the API Reference to see the available resources. Subclass a resource and implement the required functions. You may leave the other methods unimplemented by putting `pass` or `raise NotImplementedError()` in the function.\n", + "\n", + "This example uses a `Sensor` as an example.\n", + "\n", + "Create a sensor that returns the Wi-Fi signal of a Pi. Start by creating a directory on the Pi named `wifi-sensor`, then create a directory inside the `wifi-sensor` directory named `src`. Then, inside the `src` directory, create a new file named `wifi_sensor_module.py`.\n", + "Copy the code below into your `wifi_sensor_module.py` file:\n", + "\n", + "```python\n", + "# wifi-sensor/src/wifi_sensor_module.py\n", + "import asyncio\n", + "from typing import Any, Dict, Mapping, Optional\n", "\n", - "While the main RDK is written in golang, you can create custom components in python and connect them to a robot as a `remote` component. This allows you to extend the functionality of a robot, or even create an entire robot exclusively in python.\n", + "from viam.components.sensor import Sensor\n", + "from viam.utils import SensorReading\n", "\n", - "The steps required in creating a custom component and connecting it to the RDK are\n", - "1. Subclass a component and implement desired functions\n", - "2. Create an `rpc.server.Server` instance and register the custom component\n", - "3. Start the `Server` and register the running server as a remote\n", "\n", - "### Subclass a component\n", + "class MySensor(Sensor):\n", + " # Subclass the Viam Sensor component and implement the required functions\n", + " async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]:\n", + " with open(\"/proc/net/wireless\") as wifi_stats:\n", + " content = wifi_stats.readlines()\n", + " wifi_signal = [x for x in content[2].split(\" \") if x != \"\"]\n", + " return {\"link\": wifi_signal[2], \"level\": wifi_signal[3], \"noise\": wifi_signal[4]}\n", + " \n", + " async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]:\n", + " raise NotImplementedError\n", + "\n", + " async def close(self):\n", + " # This is a completely optional function to include. This will be called when the resource is removed from the config or the module\n", + " # is shutting down.\n", + " self.logger.info(f\"{self.name} is closed.\")\n", + "\n", + "# Anything below this line is optional and will be replaced later, but may come in handy for debugging and testing.\n", + "# To use, call `python wifi_sensor_module.py` in the command line while in the `src` directory.\n", + "async def main():\n", + " wifi=MySensor(name=\"wifi\")\n", + " signal = await wifi.get_readings()\n", + " print(signal)\n", + "\n", + "if __name__ == '__main__':\n", + " asyncio.run(main())\n", + "```\n", + "\n", + "You can view more component implementations in the [examples](https://github.com/viamrobotics/python-sdk/blob/main/examples/server/v1/components.py)." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2. Register the new modular resource\n", + "Now that we've created the modular resource model, we must register it to the registry.\n", + "\n", + "Define a [`Model`](https://docs.viam.com/appendix/glossary/#term-model) name and a creator function in the resource. A creator function is a function that can create a resource given a config and map of dependencies.\n", + "\n", + "To define a creator function and `Model` in `wifi_sensor_module.py`, import `ComponentConfig`, `ResourceName`, `ResourceBase`, `Model`, and `ModelFamily` from the relative viam packages. Please note that the import packages also have to be changed to include `Mapping` and `ClassVar` from `typing` and `Self` from `typing_extensions`. The changes can be seen in the example below.\n", + "\n", + "In your `wifi_sensor_module.py`, define a new `MODEL` variable in your `MySensor` class, shown below. Then add a `new()` function that will act as our creator function. We get the name from the config and return the resource. We can also assign class variables based on what the config has. \n", + "\n", + "```python\n", + "# wifi-sensor/src/wifi_sensor_module.py\n", + "import asyncio\n", + "from typing import Any, ClassVar, Dict, Mapping, Optional\n", + "from typing_extensions import Self\n", + "\n", + "from viam.proto.app.robot import ComponentConfig\n", + "from viam.proto.common import ResourceName\n", + "from viam.resource.base import ResourceBase\n", + "from viam.resource.types import Model, ModelFamily\n", + "\n", + "class MySensor(Sensor):\n", + " # Subclass the Viam Sensor component and implement the required functions\n", + " MODEL: ClassVar[Model] = Model(ModelFamily(\"viam\",\"sensor\"), \"linux-wifi\")\n", + "\n", + " @classmethod\n", + " def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self:\n", + " sensor = cls(config.name)\n", + " return sensor\n", + "```\n", + "\n", + "After the resource model has a defined model and creator function, the resource model must be registered to the `Registry`. This can be done using [`register_resource_creator()`](https://python.viam.dev/autoapi/viam/resource/registry/index.html#viam.resource.registry.Registry.register_resource_creator) and passing a [`ResourceCreatorRegistration`](https://python.viam.dev/autoapi/viam/resource/registry/index.html#viam.resource.registry.ResourceCreatorRegistration) object with the creator function as a parameter.\n", + "\n", + "In the main function of `wifi_sensor_module.py`, call `Registry.register_resource_creator()` with the subtype of the resource that was subclassed, the model name, and a `ResourceCreatorRegistration` object with the creator function defined in the modular resource. In a more complex module, it makes sense to call the function in an `__init__.py` file in the same folder as the new resource model. See [here](https://github.com/viamrobotics/viam-python-sdk/tree/main/examples/complex_module/src/arm/__init__.py) for an example.\n", + "\n", + "```python\n", + "# wifi-sensor/src/wifi_sensor_module.py\n", + "from viam.resource.registry import Registry, ResourceCreatorRegistration\n", + "\n", + "async def main():\n", + " Registry.register_resource_creator(Sensor.SUBTYPE, MySensor.MODEL, ResourceCreatorRegistration(MySensor.new))\n", + "```\n", + "\n", + "Click [here](https://github.com/viamrobotics/viam-python-sdk/blob/main/docs/examples/module_step2.py) to see the file with its new `MODEL` variable, creator function, and `main()` function.\n", + "\n", + "#### Optional Validator and Reconfiguration Functions\n", + "Modules also have support for validator and reconfiguration functions.\n", + "\n", + "Custom validation can be done by specifying a validate function. Validate functions can raise errors that will be returned to the parent through gRPC. Validate functions return a sequence of strings representing the implicit dependencies of the resource. If there are none, return an empty sequence `[]`.\n", + "\n", + "For example, let's say `MySensor` had a `multiplier` argument that is used to multiply the returned readings in `get_readings()`. The validation function can check if a multiplier attribute was provided and prevent erroneous resource start ups.\n", + "\n", + "Reconfiguration specifies what values can change based on a new configuration. To allow this, add a `reconfigure()` function to the `MySensor` class. A good pattern is to call `reconfigure()` within `new()`, since initialization and reconfiguration are usually very similar.\n", + "\n", + "```python\n", + "# ADD `Sequence` FROM `typing`.\n", + "from typing import Sequence\n", + "\n", + "from viam.utils import SensorReading\n", + "\n", + "class MySensor(Sensor):\n", + " # ADD A VALIDATOR FUNCTION \n", + " @classmethod\n", + " def validate_config(cls, config: ComponentConfig) -> Sequence[str]:\n", + " if \"multiplier\" in config.attributes.fields:\n", + " if not config.attributes.fields[\"multiplier\"].HasField(\"number_value\"):\n", + " raise Exception(\"Multiplier must be a float.\")\n", + " multiplier = config.attributes.fields[\"multiplier\"].number_value\n", + " if multiplier == 0:\n", + " raise Exception(\"Multiplier cannot be 0.\")\n", + " # RETURN AN EMPTY SEQUENCE\n", + " return []\n", + "\n", + " @classmethod\n", + " def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self:\n", + " sensor = cls(config.name)\n", + " # CALL RECONFIGURE TO INITIALIZE THE RESOURCE\n", + " sensor.reconfigure(config, dependencies) \n", + " return sensor\n", + "\n", + " def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]):\n", + " if \"multiplier\" in config.attributes.fields:\n", + " multiplier = config.attributes.fields[\"multiplier\"].number_value\n", + " else:\n", + " multiplier = 1.0\n", + " self.multiplier = multiplier\n", + "\n", + " async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]:\n", + " with open(\"/proc/net/wireless\") as wifi_stats:\n", + " content = wifi_stats.readlines()\n", + " result = [x for x in content[2].split(\" \") if x != \"\"]\n", + " \n", + " # MULTIPLY THE READINGS WITH MULTIPLIER\n", + " wifi_signal = []\n", + " for i in range(2:5):\n", + " wifi_signal.append(int(result[i]) * self.multiplier)\n", + " return {\"link\": wifi_signal[0], \"level\": wifi_signal[1], \"noise\": wifi_signal[2]}\n", + "```\n", + "\n", + "If a validator function is added, it must also be called when registering the resource. The `ResourceCreatorRegistration` should now also have the validator function passed in as the second parameter. \n", + "\n", + "```python\n", + " async def main():\n", + " Registry.register_resource_creator(Sensor.SUBTYPE, MySensor.MODEL, ResourceCreatorRegistration(MySensor.new, MySensor.validate_config))\n", + "```\n", + "Click [here](https://github.com/viamrobotics/viam-python-sdk/blob/main/docs/examples/module_step2_optional.py) to see the new `wifi_sensor_module.py` with the optional validator and reconfiguration functions." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3. Start the module\n", + "The module now has to be created and started. In the `wifi_sensor_module.py` file, update the main function to the following. If there is a validator function in the class, don't forget to add the function to the `ResourceCreatorRegistration` object. In a more complex module, it makes sense to create an entirely new entrypoint file. See [here](https://github.com/viamrobotics/viam-python-sdk/tree/main/examples/complex_module/src/main.py) for an example.\n", + "\n", + "```python\n", + "# wifi-sensor/src/wifi_sensor_module.py\n", + "from viam.module.module import Module\n", + "\n", + "\n", + "async def main():\n", + " \"\"\"\n", + " This function creates and starts a new module, after adding all desired resource model.\n", + " Resource creators must be registered to the resource registry before the module adds the resource model.\n", + " \"\"\"\n", + " Registry.register_resource_creator(Sensor.SUBTYPE, MySensor.MODEL, ResourceCreatorRegistration(MySensor.new))\n", + "\n", + " module = Module.from_args()\n", + " module.add_model_from_registry(Sensor.SUBTYPE, MySensor.MODEL)\n", + " await module.start()\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " asyncio.run(main())\n", + "```\n", + "\n", + "To see `wifi_sensor_module.py` with all of the changes, click [here](https://github.com/viamrobotics/viam-python-sdk/blob/main/docs/examples/module_step3.py)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4. Make the module executable\n", + "To add the module to your robot, you must provide it as an [executable file](https://docs.viam.com/extend/modular-resources/#make-your-module-executable) that [`viam-server` can access](https://docs.viam.com/extend/modular-resources/#make-sure-viam-server-can-access-your-executable).\n", + "\n", + "Dependencies for the module (including Viam SDK) have to be installed. In the `wifi-sensor` directory, create a new file called `requirements.txt` that has all your dependencies. For this example, add `viam-sdk` in `requirements.txt`.\n", + "\n", + "```\n", + "# add a version if viam should be pinned to a specific version\n", + "viam-sdk\n", + "```\n", + "\n", + "One option with the Python SDK is to create a new shell script (`.sh`) that runs your module, which can also be used to install the dependencies. For example, in the `wifi-sensor` directory, add an `run.sh` file:\n", + "\n", + "```sh\n", + "#!/bin/sh\n", + "cd `dirname $0`\n", + "\n", + "# Create a virtual environment to run our code\n", + "VENV_NAME=\"venv\"\n", + "PYTHON=\"$VENV_NAME/bin/python\"\n", + "ENV_ERROR=\"This module requires Python >=3.8, pip, and virtualenv to be installed.\"\n", + "\n", + "if ! python3 -m venv $VENV_NAME >/dev/null 2>&1; then\n", + " echo \"Failed to create virtualenv.\"\n", + " if command -v apt-get >/dev/null; then\n", + " echo \"Detected Debian/Ubuntu, attempting to install python3-venv automatically.\"\n", + " SUDO=\"sudo\"\n", + " if ! command -v $SUDO >/dev/null; then\n", + " SUDO=\"\"\n", + " fi\n", + "\t\tif ! apt info python3-venv >/dev/null 2>&1; then\n", + "\t\t\techo \"Package info not found, trying apt update\"\n", + "\t\t\t$SUDO apt -qq update >/dev/null\n", + "\t\tfi\n", + " $SUDO apt install -qqy python3-venv >/dev/null 2>&1\n", + " if ! python3 -m venv $VENV_NAME >/dev/null 2>&1; then\n", + " echo $ENV_ERROR >&2\n", + " exit 1\n", + " fi\n", + " else\n", + " echo $ENV_ERROR >&2\n", + " exit 1\n", + " fi\n", + "fi\n", + "\n", + "# remove -U if viam-sdk should not be upgraded whenever possible\n", + "# -qq suppresses extraneous output from pip\n", + "echo \"Virtualenv found/created. Installing/upgrading Python packages...\"\n", + "if ! $PYTHON -m pip install -r requirements.txt -Uqq; then\n", + " exit 1\n", + "fi\n", + "\n", + "# Be sure to use `exec` so that termination signals reach the python process,\n", + "# or handle forwarding termination signals manually\n", + "echo \"Starting module...\"\n", + "exec $PYTHON src/wifi-sensor.py $@\n", + "```\n", + "\n", + "**Please note that a more complex module should be run as a Python module, meaning that the file extension `.py` has to be omitted. See [here](https://github.com/viamrobotics/viam-python-sdk/tree/main/examples/complex_module/run.sh) for an example.** \n", + "\n", + "To make this shell script executable, run this in the Terminal:\n", + "\n", + "```sudo chmod +x /```\n", + "\n", + "`` would be `run.sh`.\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 5. Configure a modular resource\n", + "**NOTE:** *If you are adding your module to the registry, follow [these instructions](https://docs.viam.com/extend/modular-resources/configure/) instead. Otherwise, continue with these instructions, which will show you how to configure the module locally.*\n", + "\n", + "[Configure your new module](https://docs.viam.com/extend/modular-resources/#configure-your-module) on your robot by navigating to the **Config** tab of the robot's page on the Viam app, then click on the **Modules** subtab. Add the name of your module and the executable path. For our example, the path would be `/linux-wifi/run.sh`.\n", + "\n", + "Once you have configured a module as part of your robot configuration, [configure your modular resource](https://docs.viam.com/extend/modular-resources/#configure-your-modular-resource) made available by that module by adding new components or services configured with your modular resources' new type or model. To instantiate a new resource from your module, specify the `type`, `model`, and `name` of your modular resource. The aforementioned `type`, `model`, and `name` should be the same as those in the `MODEL` class attribute defined in your [component class](#register-the-custom-component). This is a JSON example:\n", + "\n", + "```json\n", + "{\n", + " \"components\": [\n", + " {\n", + " \"model\": \"viam:sensor:linux-wifi\",\n", + " \"attributes\": {},\n", + " \"depends_on\": [],\n", + " \"name\": \"my-sensor\",\n", + " \"type\": \"sensor\"\n", + " }\n", + " ],\n", + " \"modules\": [\n", + " {\n", + " \"executable_path\": \"/linux-wifi/run.sh\",\n", + " \"name\": \"wifi_sensor\"\n", + " }\n", + " ]\n", + "}\n", + "```\n", + "\n", + "Note the nomenclature of the above `model` field, `viam:sensor:linux-wifi`. Models are uniquely namespaced as colon-delimited-triplets in the form `namespace:family:name`, and are named according to the Viam API that your model implements. A model with the `viam` namespace is always Viam-provided. Read more about making custom namespaces [here](https://docs.viam.com/registry/create/#name-your-new-resource-model).\n", + "\n", + "Viam also provides many built-in models that implement API capabilities, each using `rdk` as the `namespace`, and `builtin` as the `family`:\n", + "- The `rdk:builtin:gpio` model of the `rdk:component:motor` API provides RDK support for [GPIO-controlled DC motors](https://docs.viam.com/components/motor/gpio/).\n", + "- The `rdk:builtin:DMC4000` model of the same `rdk:component:motor` API provides RDK support for the [DMC4000](https://docs.viam.com/components/motor/dmc4000/) motor." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Custom Modular Arm Example\n", + "The following is an example of how to implement a custom modular arm. For further instructions, read the detailed `Sensor` example above. Our custom Arm will be extremely simple. Please note that the minimum set of endpoints you need to implement for an arm are `GetKinematics`, `GetJointPositions`, and `MoveToJointPositions`.\n", + "\n", + "This arm example contains a minimal kinematics model. For a full model, take a look [here](https://github.com/viamrobotics/viam-python-sdk/blob/main/examples/complex_module/src/arm/my_arm_kinematics.json).\n", + "\n", + "Subclassing the `Arm` component and implementing the required functions:\n", + "```python\n", + "# modular-arm/src/my_modular_arm.py\n", + "import asyncio\n", + "import os\n", + "from typing import Any, ClassVar, Dict, Mapping, Optional, Tuple\n", + "from typing_extensions import Self\n", + "\n", + "from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose\n", + "from viam.module.module import Module\n", + "from viam.operations import run_with_operation\n", + "from viam.proto.app.robot import ComponentConfig\n", + "from viam.proto.common import ResourceName\n", + "from viam.resource.base import ResourceBase\n", + "from viam.resource.registry import Registry, ResourceCreatorRegistration\n", + "from viam.resource.types import Model, ModelFamily\n", + "\n", + "\n", + "class MyModularArm(Arm):\n", + " # Subclass the Viam Arm component and implement the required functions\n", + " MODEL: ClassVar[Model] = Model(ModelFamily(\"viam\", \"arm\"), \"my-arm\")\n", + "\n", + " def __init__(self, name: str):\n", + " # Starting joint positions\n", + " self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0])\n", + "\n", + "\n", + " # Minimal working kinematics\n", + " self.kinematics = json.dumps(\n", + " {\n", + " \"name\": \"MyArm\",\n", + " \"links\": [{\"id\": \"base\", \"parent\": \"world\", \"translation\": {\"x\": 0, \"y\": 0, \"z\": 0}}],\n", + " \"joints\": [\n", + " {\"id\": \"waist\", \"type\": \"revolute\", \"parent\": \"base\", \"axis\": {\"x\": 0, \"y\": 0, \"z\": 1}, \"max\": 359, \"min\": -359}\n", + " ],\n", + " }\n", + " ).encode(\"utf-8\")\n", + " super().__init__(name)\n", + "\n", + " @classmethod\n", + " def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self:\n", + " arm = cls(config.name)\n", + " return arm\n", + "\n", + " async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose:\n", + " raise NotImplementedError()\n", + "\n", + " async def move_to_position(self, pose: Pose, extra: Optional[Dict[str, Any]] = None, **kwargs):\n", + " raise NotImplementedError()\n", + "\n", + " async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> JointPositions:\n", + " return self.joint_positions\n", + "\n", + " @run_with_operation\n", + " async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None, **kwargs):\n", + " operation = self.get_operation(kwargs)\n", + "\n", + " self.is_stopped = False\n", + "\n", + " # Simulate the length of time it takes for the arm to move to its new joint position\n", + " for x in range(10):\n", + " await asyncio.sleep(1)\n", + "\n", + " # Check if the operation is cancelled and, if it is, stop the arm's motion\n", + " if await operation.is_cancelled():\n", + " await self.stop()\n", + " break\n", + " \n", + " self.joint_positions = positions\n", + " self.is_stopped = True\n", + "\n", + " async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs):\n", + " self.is_stopped = True\n", + "\n", + " async def is_moving(self) -> bool:\n", + " return not self.is_stopped\n", + "\n", + " async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:\n", + " return KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, self.kinematics\n", + "\n", + "async def main():\n", + " \"\"\"This function creates and starts a new module, after adding all desired resource model.\n", + " Resource creators must be registered to the resource registry before the module adds the resource model.\n", + " \"\"\"\n", + " Registry.register_resource_creator(Arm.SUBTYPE, MyModularArm.MODEL, ResourceCreatorRegistration(MyModularArm.new))\n", + " module = Module.from_args()\n", + " module.add_model_from_registry(Arm.SUBTYPE, MyModularArm.MODEL)\n", + " await module.start()\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " asyncio.run(main())\n", + "```\n", + "\n", + "Lastly, make the module executable and configure the module on your robot." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create custom remotes\n", + "\n", + "**CAUTION** *[Modular resources](https://python.viam.dev/examples/example.html#create-custom-modules) are the preferred method of creating custom resource implementations for the Python SDK unless you are hosting viam-server on a non-Linux system or have another issue with compilation.*\n", + "\n", + "While the main RDK is written in golang, you can create custom components in python and connect them to a robot as a [`remote` component](https://docs.viam.com/extend/custom-components-remotes/). This allows you to extend the functionality of a robot, or even create an entire robot exclusively in python.\n", + "\n", + "To create a custom component and connect it to the RDK:\n", + "1. **Subclass a component and implement desired functions**. This will be your custom component.\n", + " - For functions you do not wish to implement, you must at least define them by putting `pass` or `raiseNotImplementedError()` in the function.\n", + "1. **Create an `rpc.server.Server` instance and register the custom component**. This is the `RPC` server that enables communication with your custom component.\n", + "1. **Start the `Server` and register the running server as a remote**. Registering this `RPC` server as a remote allows the RDK to communicate with the server.\n", + "\n", + "\n", + "### 1. Subclass a component\n", "\n", "The SDK provides a wide array of components to customize. You can browse through the API Reference to see all of them, but for now we'll use an `Arm` as an example. Our custom Arm will be extremely simple -- it will only save and return the positions provided to it.\n", "\n", @@ -314,8 +776,14 @@ ".highlight .vg { color: #19177C } /* Name.Variable.Global */\n", ".highlight .vi { color: #19177C } /* Name.Variable.Instance */\n", ".highlight .vm { color: #19177C } /* Name.Variable.Magic */\n", - ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
from typing import Any, Dict, Optional\n",
-       "from viam.components.arm import Arm, JointPositions, Pose, WorldState\n",
+       ".highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
# my-python-robot/my_cool_arm.py\n",
+       "\n",
+       "import asyncio\n",
+       "import json\n",
+       "from typing import Any, Dict, Optional, Tuple\n",
+       "\n",
+       "from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose\n",
+       "from viam.operations import run_with_operation\n",
        "\n",
        "\n",
        "class MyCoolArm(Arm):\n",
@@ -335,25 +803,75 @@
        "\n",
        "        # Starting joint positions\n",
        "        self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0])\n",
-       "        self.is_stoppped = True\n",
+       "        self.is_stopped = True\n",
+       "\n",
+       "        # Minimal working kinematics model\n",
+       "        self.kinematics = json.dumps(\n",
+       "            {\n",
+       "                "name": "MyArm",\n",
+       "                "links": [{"id": "base", "parent": "world", "translation": {"x": 0, "y": 0, "z": 0}}],\n",
+       "                "joints": [\n",
+       "                    {"id": "waist", "type": "revolute", "parent": "base", "axis": {"x": 0, "y": 0, "z": 1}, "max": 359, "min": -359}\n",
+       "                ],\n",
+       "            }\n",
+       "        ).encode("utf-8")\n",
        "        super().__init__(name)\n",
        "\n",
-       "    async def get_end_position(self, extra: Optional[Dict[str, Any]] = None) -> Pose:\n",
+       "    async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose:\n",
        "        return self.position\n",
        "\n",
-       "    async def move_to_position(self, pose: Pose, world_state: Optional[WorldState] = None, extra: Optional[Dict[str, Any]] = None):\n",
-       "        self.is_stoppped = False\n",
+       "    @run_with_operation\n",
+       "    async def move_to_position(\n",
+       "        self,\n",
+       "        pose: Pose,\n",
+       "        extra: Optional[Dict[str, Any]] = None,\n",
+       "        **kwargs,\n",
+       "    ):\n",
+       "        operation = self.get_operation(kwargs)\n",
+       "\n",
+       "        self.is_stopped = False\n",
        "        self.position = pose\n",
        "\n",
-       "    async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None) -> JointPositions:\n",
+       "        # Simulate the length of time it takes for the arm to move to its new position\n",
+       "        for x in range(10):\n",
+       "            await asyncio.sleep(1)\n",
+       "\n",
+       "            # Check if the operation is cancelled and, if it is, stop the arm's motion\n",
+       "            if await operation.is_cancelled():\n",
+       "                await self.stop()\n",
+       "                break\n",
+       "\n",
+       "        self.is_stopped = True\n",
+       "\n",
+       "    async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> JointPositions:\n",
        "        return self.joint_positions\n",
        "\n",
-       "    async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None):\n",
-       "        self.is_stoppped = False\n",
+       "    @run_with_operation\n",
+       "    async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None, **kwargs):\n",
+       "        operation = self.get_operation(kwargs)\n",
+       "\n",
+       "        self.is_stopped = False\n",
        "        self.joint_positions = positions\n",
        "\n",
-       "    async def stop(self, extra: Optional[Dict[str, Any]] = None):\n",
-       "        self.is_stoppped = True\n",
+       "        # Simulate the length of time it takes for the arm to move to its new joint position\n",
+       "        for x in range(10):\n",
+       "            await asyncio.sleep(1)\n",
+       "\n",
+       "            # Check if the operation is cancelled and, if it is, stop the arm's motion\n",
+       "            if await operation.is_cancelled():\n",
+       "                await self.stop()\n",
+       "                break\n",
+       "\n",
+       "        self.is_stopped = True\n",
+       "\n",
+       "    async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs):\n",
+       "        self.is_stopped = True\n",
+       "\n",
+       "    async def is_moving(self) -> bool:\n",
+       "        return not self.is_stopped\n",
+       "\n",
+       "    async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]:\n",
+       "        return KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, self.kinematics\n",
        "
\n" ], "text/plain": [ @@ -371,31 +889,35 @@ "from pygments.formatters import HtmlFormatter\n", "import IPython\n", "\n", - "with open('my_cool_arm.py') as f:\n", + "with open(\"my_cool_arm.py\") as f:\n", " code = f.read()\n", "\n", "formatter = HtmlFormatter()\n", - "IPython.display.HTML('{}'.format(\n", - " formatter.get_style_defs('.highlight'),\n", - " highlight(code, PythonLexer(), formatter)))" + "IPython.display.HTML(\n", + " '{}'.format(formatter.get_style_defs(\".highlight\"), highlight(code, PythonLexer(), formatter))\n", + ")" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "You can view more component implementations in the [examples](https://github.com/viamrobotics/python-sdk/blob/main/examples/server/v1/components.py)." + "You can view more component implementations in the [examples](https://github.com/viamrobotics/python-sdk/blob/main/examples/server/v1/components.py).\n", + "\n", + "This arm example contains a minimal kinematics model. For a full model, take a look [here](https://github.com/viamrobotics/viam-python-sdk/blob/main/examples/complex_module/src/arm/my_arm_kinematics.json)." ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "### Register the custom component\n", + "### 2. Register the custom component\n", "\n", - "Now that we've created the custom component, we must register it with a server so that it will be visible to any robots connecting to it.\n", + "Now that we've created the custom component, we must register it so that it will be visible to any robots connecting to it. This is done by creating an `RPC` server and adding `MyCoolArm` as a connected component. This `RPC` server will receive gRPC requests from the Viam Server or any other connections and forward those requests to your custom component.\n", "\n", - "In the same `my-python-robot` directory, create a new file called `python_server.py`." + "In the same `my-python-robot` directory, create a new file called `main.py`." ] }, { @@ -414,17 +936,20 @@ } ], "source": [ - "# my-python-robot/python_server.py\n", + "# my-python-robot/main.py\n", "\n", "import asyncio\n", "from viam.rpc.server import Server\n", "\n", "from my_cool_arm import MyCoolArm\n", "\n", + "\n", "async def main():\n", - " srv = Server([MyCoolArm('my-arm')])\n", + " srv = Server([MyCoolArm(\"my-arm\")])\n", + " await srv.serve()\n", "\n", - "if __name__ == '__main__':\n", + "\n", + "if __name__ == \"__main__\":\n", " try:\n", " asyncio.run(main())\n", " except:\n", @@ -432,15 +957,16 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "### Start the Server and add it as a Remote\n", + "### 3. Start the Server and add it as a Remote\n", "\n", "Now that we have a server that knows about our custom Arm component, we need to start the server so it can receive gRPC calls. Once it's started, we can then register this server as a remote.\n", "\n", "```python3\n", - "# my-python-robot/python_server.py\n", + "# my-python-robot/main.py\n", "\n", "async def main():\n", " ...\n", @@ -463,11 +989,11 @@ "```json\n", "[\n", " {\n", - " \"id\": \"0\",\n", + " \"id\": \"my-python-server-process\",\n", " \"log\": true,\n", " \"name\": \"python\",\n", " \"args\": [\n", - " \"/home/pi/my-python-robot/python_server.py\"\n", + " \"/home/pi/my-python-robot/main.py\"\n", " ]\n", " }\n", "]\n", @@ -484,18 +1010,472 @@ " \"-u\",\n", " \"pi\",\n", " \"python\",\n", - " \"/home/pi/my-python-robot/python-server.py\"\n", + " \"/home/pi/my-python-robot/main.py\"\n", " ]\n", " }\n", "]\n", - "```\n" + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Convert a custom remote to a custom module\n", + "Modular resources are the preferred method of creating custom resource implementations for the Python SDK unless you are hosting viam-server on a non-Linux system or have another issue with compilation. Already existing custom remote resources can be converted to custom module resources.\n", + "\n", + "This section will go over the steps it takes to convert a custom remote to a custom module.\n", + "`MySensor` is currently a custom remote component, written here:\n", + "```python\n", + "import asyncio\n", + "from typing import Any, Dict, List, Mapping, Optional\n", + "\n", + "from viam.components.sensor import Geometry, Sensor\n", + "from viam.rpc.server import Server\n", + "from viam.utils import SensorReading\n", + "\n", + "\n", + "class MySensor(Sensor):\n", + " async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]:\n", + " with open(\"/proc/net/wireless\") as wifi_stats:\n", + " content = wifi_stats.readlines()\n", + " wifi_signal = [x for x in content[2].split(\" \") if x != \"\"]\n", + " return {\"link\": wifi_signal[2], \"level\": wifi_signal[3], \"noise\": wifi_signal[4]}\n", + "\n", + " async def get_geometries(self) -> List[Geometry]:\n", + " raise NotImplementedError\n", + "\n", + "\n", + "async def main():\n", + " srv = Server([MySensor(\"my-sensor\")])\n", + " await srv.serve()\n", + "\n", + "\n", + "if __name__ == \"__main__\":\n", + " asyncio.run(main())\n", + "```\n", + "\n", + "The two steps that are required to convert a custom remote resource to a custom module resource are as follows:\n", + "1. **Register the resource.** This will register a new modular resource model into the Registry.\n", + "2. **Make the module executable.** This allows viam-server to access and execute the module.\n", + "\n", + "Add a [`Model`](https://docs.viam.com/extend/modular-resources/#models) name and a creator function in the resource by defining a new `MODEL` variable in the class. Then add a function that will act as the creator function. For more details on how to add these two, follow the pertaining steps in [Register the new modular resource](https://python.viam.dev/examples/example.html#register-the-new-modular-resource).\n", + "\n", + "```python\n", + "# ADD THE NECESSARY IMPORTS TO DEFINE A MODEL NAME AND CREATOR FUNCTION.\n", + "from typing import ClassVar, Mapping\n", + "from typing_extensions import Self\n", + "\n", + "from viam.proto.app.robot import ComponentConfig\n", + "from viam.proto.common import ResourceName\n", + "from viam.resource.base import ResourceBase\n", + "from viam.resource.types import Model, ModelFamily\n", + "\n", + "class MySensor(Sensor):\n", + " # ADD A MODEL NAME IN THE EXISTING RESOURCE CLASS.\n", + " MODEL: ClassVar[Model] = Model(ModelFamily(\"viam-labs\",\"sensor\"), \"test\")\n", + "\n", + " # ADD A CREATOR FUNCTION IN THE EXISTING RESOURCE CLASS.\n", + " @classmethod\n", + " def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self:\n", + " resource = cls(config.name)\n", + " return resource\n", + "```\n", + "\n", + "In the resource's main function or `main.py` file, replace the content of `main()` with the content below:\n", + "```python\n", + "# ADD THE NECESSARY IMPORTS TO REGISTER THE NEW MODEL\n", + "from viam.module.module import Module\n", + "from viam.resource.registry import Registry, ResourceCreatorRegistration\n", + "\n", + "# REPLACE THE MAIN FUNCTION/FILE SO THAT IT CALLS `register_resource_creator()` AND STARTS THE MODULE.\n", + "async def main():\n", + " Registry.register_resource_creator(Sensor.SUBTYPE, MySensor.MODEL, ResourceCreatorRegistration(MySensor.new))\n", + "\n", + " module = Module.from_args()\n", + " module.add_model_from_registry(Sensor.SUBTYPE, MySensor.MODEL)\n", + " await module.start()\n", + "```\n", + "\n", + "Follow the steps outlined in the section [Make the module executable](https://python.viam.dev/examples/example.html#make-the-module-executable) in the directory of the component.\n", + "\n", + "The custom remote component is now a custom module component. To use the component on the Viam app, follow the steps to [configure the new modular resource](https://python.viam.dev/examples/example.html#configure-a-modular-resource).\n", + "\n", + "For easy visualization, the differences in the configuration JSONs and code are below.\n", + "\n", + "| Custom remote | Custom module |\n", + "| :--- | :--- |\n", + "|
{
\"remotes\": [
{
\"name\": \"my-sensor\",
\"insecure\": true,
\"address\": \"localhost:9090\"
}
],
\"processes\": [
{
\"id\": \"my-python-server-process\",
\"log\": true,
\"name\": \"python\",
\"args\": [
\"/home/my_sensor/my_sensor.py\"
]
}
]
}
|
{
\"components\": [
{
\"depends_on\": [],
\"model\": \"viam-labs:sensor:test\",
\"name\": \"my-sensor\",
\"type\": \"sensor\",
\"attributes\": {}
}
],
\"modules\": [
{
\"executable_path\": \"/home/my_sensor/run.sh\",
\"name\": \"testsensor\",
\"type\": \"local\"
}
]
}
|" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "tags": [ + "hide-input" + ] + }, + "outputs": [ + { + "data": { + "image/png": "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RepaFVtaZ4vrCCAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAmGbgEcFVvyLv0OHDlHkyJEpderUFDt2bIoRIwZ9+PCBHj9+TGfPnlV/Mejj40OVK1emhAkThu0zgt6BAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAh4LQEIrLz21KBhIAACIAACIAACIAACIAACIAACIAACIBBsBLxCYKWnd/nz5yc2u44AAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAp4iAIGVp8jjuCAAAiAAAiAAAiAAAiAAAiAAAiAAAiDgOQIeFVjdv3+fTp48SXfv3lUtVVmjSJQoEX333XewXGUNBtsgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIhTmDJkiX04sULedyGDRtKy+wh3ggcEARAAARAAARAAARAAARAAARAAARAAARAIEQJeFRgpfTUZDLJhak3b95QQECATGZXgTFjxqRIkSIpxfANAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiFKwCsEViHaYxwMBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABHQSgMBKJygUAwEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQ+PoIQGD19Z1z9BgEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAEQEAnAQisdIJCMRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAga+PAARWX985R49BAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAAR0EoDASicoFAMBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEPj6CEBg9fWdc/QYBEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEAABEBAJ4GvVmD16OlznYhQDARAAARAAARAAARAAARAAARAAARAAARAwAiBeLH9jOz+1ex74tnlr6av6CgIgAAI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EFhl3Nib1KzWMxa05y2YT7oBvP+FvlSpUVVZ/EryFRr/5Id0KeGiy7hBYHWDrmLIKQPefZJKVCh54+JNFIPA6iYabHQVBEAg4AlAYGU4hBBYGYJCNhAAARAAARAAARAAARAAARAAARAAARBwSCCzBVZJSUm0ePFiqlOnDpUrV062/uDBg8YCq23bttHFixepXr16lDdvXll+1qxZlFkCq12royj64EmL+sb56yjpcqLczKvTuoGVzpGSlUpTtdtquaTxydUrV2nV98uFYYxrVKZ6OarcuBrxX+H/9dsGijl2mniTsEiJMKrRog7VaF7HxToDl794/iJtW7pJtuPMkWgKzRsqNslKUaSwkMIuCt3DuehY+mvxRplc9ZYatOnX9XRy3zGxoRwpLHY0E26RihC7sjocdUBsWuYl7kft2+u7V2P7fJNwoZgQG08huUOp2f23e63nrLD0ErVss7xesX4VKlu7gkve/Zv3urBXF0uUL2ltsKo092NyYjId2rafuI7Yk2flhjpbkylWtgQVLx8h3TuGebDUsFVwjhP5I8RYch92rdpGQhEo87P7qr837JZjwXXyBv3tfe6i/EUKuN/e8bnTOSOmGu1eEyXcXR2RDC+cS6Di5SLkHG3QvomwvJYnzTby3Ij6/S/iucRigzz581CBsEJUqmoZqtqkhmTorQJf56uqZ8+6nXT68Clp9adp15Yq2TpeSrhEPLc4lK9bSc5/6+L1iFNuXI1id3zvMTpz5BSdOxUnrKYVpPDSxajW7fWobM3y7re1zlVZu9ytigwjvI4c2LLXyr1zVZQUgOYSgoSmXVpY6SpSv10TKly8iDqV6wSPMc/x5vffQcGhni0uJV1OorWzV8hyzEGtfWyxacui9TK9atOaFCqe+R0rt9GR7QcpOSmZSleNpMqNqqX7vDrldvmCmBsL/6STfx8TczZOrqn5wwqIdTVc3pvXl9z5clv99lck8WIibf71T4/VNexwi8fnzB/rs7+4231WucN2nzV+F14V7zwO8WfjKWp5yjugXJ2KVKZaWZmufniNrtumoTq1fdSFXI073ibXkWti0j0+ZjgViQiX9X710kQ6uf84NenUjDb8skampSWw2r/lbzoo/uPvA15fw4VVxQjxbqol3qVFIsLSbCuvD7vX7qATe4+I5+SKXFOqiefnSvJVmvlmisXPu5/q4rXv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/PHGxMRIu3bt/K1iGQIIIIAAAggggAACCCCAAAIIIIAAAn4FNm3aJMuWLZPY2FjP+lKlSknTpk09990zx48fl7lz58rmzZttqMq9zpnXcFXNmjWlUqVKziJuEUAAAQQQQAABBBBAAAEEEEAAAQRSsUCKCFitW7dO5s2bJ6dPn/ZQp02bVvT/U6dO2WUFChSQDh06eNYzgwACCCCAAAIIIIAAAggggAACCCCAQCCB9evX22CVVkn3nRIKWB04cEDGjBnjtUmaNGkkXbp0nutUzsoGDRpI+fLlnbvcIoAAAggggAACCCCAAAIIIIAAAgikUoFkD1idPHlSRo8eLTo8oE46HGCdOnVEA1V64erIkSOyZs0aW4q9fv36qfRp4GEhgAACCCCAAAIIIIAAAggggAACCERL4NixYzJixIiA3QUTsNIqVRUrVpQSJUpInjx57HWqvXv3yoIFCzzVsNKnTy/dunWTrFmzBtwXKxBAAAEEEEAAAQQQQAABBBBAAAEELn6BZA9YrVixQhYtWmQlixQpIs2bNxe9OMWEAAIIIIAAAggggAACCCCAAAIIIIBAOALugJV+ia969eqiVahmzpxpu0soYKVfBtQv+5UpU0YyZMgQb/c6hODYsWPl6NGjdl2zZs1sCCteQxYggAACCCCAAAIIIIAAAggggAACCKQagWQNWJ09e1a+/PJLOXTokAXt1KmT5MuXL9Xg8kAQQAABBBBAAAEEEEAAAQQQQAABBC68gIakZs+ebStQFS1a1B7Axo0bgwpYBXO02ve6dets01q1akmNGjWC2Yw2CCCAAAIIIIAAAggggAACCCCAAAIXqUCyBqwOHDggY8aMsXQxMTHSrl27i5SRw0YAAQQQQAABBBBAAAEEEEAAAQQQSMkC0QxYzZo1SzZs2GAfbt26daVSpUop+aFzbAgggAACCCCAAAIIIIAAAggggAACEQoka8Bq27ZtMnnyZPsQqlWrJlqefe3atbJ582Zb1SpPnjySN29eqVq1quTMmTPCh8rmCCCAAAIIIIAAAggggAACCCCAAAKXqkC0AlZakX3UqFGeIQI7duwo+fPnv1RZedwIIIAAAggggAACCCCAAAIIIIDAJSGQrAErDVPNmTPHQpctW1bWr18vp06digefPn16qV+/vmgbJgQQQAABBBBAAAEEEEAAAQQQQAABBEIViFbA6u+//5b58+fb3WfNmlV69uwpadKkCfVwaI8AAggggAACCCCAAAIIIIAAAgggcBEJJGvAavny5bJ48WIvrixZskiRIkXshSmtcHXo0CG7Pm3atNKlSxcqWXlpcQcBBBBAAAEEEEAAAQQQQAABBBBAIBiBaASsDhw4IOPGjZMTJ07YXbZs2VKKFi0azO5pgwACCCCAAAIIIIAAAggggAACCCBwEQska8BqyZIlsmzZMg+fllPXC1MastJJL1bNmDFDYmNj7f0rrrhCGjdubOf5gQACCCCAAAIIIIAAAggggAACCCCAQLACkQasTp48KRMnTpQ9e/bYXZYvX14aNGgQ7O5phwACCCCAAAIIIIAAAggggAACCCBwEQska8Bq5cqV8vPPP3v42rVrJzExMZ77OrNjxw6ZMGGCXabBq969e3ut5w4CCCCAAAIIIIAAAggggAACCCCAAAKJCUQSsDp9+rRMnz5dtm7danejXxJs06aNZMiQIbHdsh4BBBBAAAEEEEAAAQQQQAABBBBAIBUIJGvAau3atTJnzhzLmC1bNunZs6df0uHDh3tKr99y+HxLVgAAQABJREFUyy2SPn16v+1YiAACCCCAAAIIIIAAAggggAACCCCAgD+BcANWZ8+eldmzZ8v69etttzlz5hT9kqBTgd3fvliGAAIIIIAAAggggAACCCCAAAIIIJC6BJI1YLV582aZNm2aFS1YsKC0b9/er+7YsWNl3759dl3nzp0lT548ftuxEAEEEEAAAQQQQAABBBBAAAEEEEAAAX8C4Qas5s+fL3///bftMmvWrNK2bVvRkBUTAggggAACCCCAAAIIIIAAAggggMClI5CsAas9e/bIN998Y7Vz584tXbp08Ss/evRoOXz4sF3Xq1cv0YtZTAgggAACCCCAAAIIIIAAAggggAACCAQrEE7AatGiRbJixQq7i0yZMtlhAfPmzRvsLmmHAAIIIIAAAggggAACCCCAAAIIIJBKBJI1YHXmzBkZNWqUHDt2TNKlS2eHCMycObMXra4bOXKkaDl2HRpQhwhkQgABBBBAAAEEEEAAAQQQQAABBBBAIBSBUANWS5culV9//dXuIkOGDHLDDTeIVmBnQgABBBBAAAEEEEAAAQQQQAABBBC49ASSNWCl3D///LOsXLnSylepUkXq1Knj9SzohSy9oKVTQsMIem3EHQQQQAABBBBAAAEEEEAAAQQQQAABBFwCoQSs/vjjD/npp5/s1vqlwJYtW0rhwoVdvTGLAAIIIIAAAggggAACCCCAAAIIIHApCSR7wMo9TKDCV6hQQcqWLStp0qSR9evXy/Llyz3PR/PmzaV48eKe+8wggAACCCCAAAIIIIAAAggggAACCCDgT+DAgQOeL/Xp+v3798vmzZtt0+zZs3tdY9LwVMmSJe26HTt2yIQJE+y8/tC2xYoV89z3nYmJiZEyZcr4LuY+AggggAACCCCAAAIIIIAAAggggEAqEkj2gJVaagUrrWSV0FSiRAlp1qxZQk1YhwACCCCAAAIIIIAAAggggAACCCCAgBXYtGmTTJ8+PSiN8uXLS4MGDULeTjcoXbq0NGnSJKj90AgBBBBAAAEEEEAAAQQQQAABBBBA4OIUSBEBK6Vbt26dLF68WA4dOuQlmT59eqlVq5ZUrlzZVrXyWskdBBBAAAEEEEAAAQQQQAABBBBAAAEE/Ahs2bJFpk6d6mdN/EUVK1aUevXq2RWhbKcbaPWqRo0axe+UJQgggAACCCCAAAIIIIAAAggggAACqUYgxQSsHNGjR4/Kzp075fTp05InTx7JmTOnpE2b1lnNLQIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCBwwQRSXMDqgj1ydoQAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIJCJAwCoRIFYjgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIDApStAwOrSfe555AgggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIJCIAAGrRIBYjQACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAApeuAAGrS/e555EjgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIBAIgIErBIBYjUCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAghcugIErC7d555HjgACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAokIELBKBIjVCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggcOkKELC6dJ97HjkCCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggkIkDAKhEgViOAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggMClK0DA6tJ97nnkCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggkIhAsgas9u/fL7GxsYkc4vnVGTNmlJIlS55fwBwCCCCAAAIIIIAAAggggAACCCCAAAJBCOzYsUMOHz5sW+bIkUPy588fcKvTp0/Lrl27ZPv27XLgwAE5cuSInDlzRnLmzGn/L126tGTJkiXg9qxAAAEEEEAAAQQQQAABBBBAAAEEEEhdAskasFqxYoUsWrQoaNHMmTNLnz59gm5PQwQQQAABBBBAAAEEEEAAAQQQQACBS1tg06ZNsmzZMq8v+ZUqVUqaNm0aEGb06NGeMJa/RunTp5dq1apJjRo1/K1mGQIIIIAAAggggAACCCCAAAIIIIBAKhO4qAJWMTEx0q5du1T2FPBwEEAAAQQQQAABBBBAAAEEEEAAAQSiLbB+/XobrNq9e3e8rhMLWI0aNcpWrXI21ECVVrDS/91TnTp1pEqVKu5FzCOAAAIIIIAAAggggAACCCCAAAIIpEKBZA1YHT9+3JZZT8j1hx9+kD179tgmDRo0kPLlyyfUnHUIIIAAAggggAACCCCAAAIIIIAAApe4wLFjx2TEiBEBFRILWE2fPl10GMHixYtLwYIFJVOmTHLy5EnZvHmzrF69WjZu3Gj7Tps2rfTo0UOyZs0acF+sQAABBBBAAAEEEEAAAQQQQAABBBC4+AWSNWCVGJ9eDNNvDOq3A/Wbgr1795YMGTIkthnrEUAAAQQQQAABBBBAAAEEEEAAAQQuYQF3wKpAgQJSvXp1SZMmjcycOdOqJBawSohOr1ONGzfO84XAFi1aSLFixRLahHUIIIAAAggggAACCCCAAAIIIIAAAhe5QIoOWK1YsUIWLVpkicuUKSONGjW6yLk5fAQQQAABBBBAAAEEEEAAAQQQQACBpBbQalOzZ8+WihUrStGiRe3utOpUNAJW2tmCBQvkr7/+sv0yTKBl4AcCCCCAAAIIIIAAAggggAACCCCQqgVSdMBq7Nixsm/fPvsEtG7dWgoXLpyqnwweHAIIIIAAAggggAACCCCAAAIIIIBA0ghEM2A1ZcoU2bp1qz1Q/UKgfjGQCQEEEEAAAQQQQAABBBBAAAEEEEAg9Qqk2IBVbGysTJo0ycrnyJFDunfvnnqfBR4ZAggggAACCCCAAAIIIIAAAggggECSCkQjYHX48GFbuWrp0qX2WNOlSyddu3aV7NmzJ+mx0zkCCCCAAAIIIIAAAggggAACCCCAQPIKpNiA1bx582T16tVWp2bNmqL/MyGAAAIIIIAAAggggAACCCCAAAIIIBCOQDgBqyNHjsiSJUvk7NmzsmvXLtmzZ4/XruvWrSuVKlXyWsYdBBBAAAEEEEAAAQQQQAABBBBAAIHUJ5AiA1YnTpyQUaNGyalTp6y4Vq/SKlZMCCCAAAIIIIAAAggggAACCCCAAAIIhCMQTsBKQ1Xjxo3zu7uWLVtK0aJF/a5jIQIIIIAAAggggAACCCCAAAIIIIBA6hJIkQGrVatWyY8//milCxcuLK1bt05d6jwaBBBAAAEEEEAAAQQQQAABBBBAAIELKhBOwGrfvn0yadIkW8Hq+PHjXsebOXNmad68ucTExHgt5w4CCCCAAAIIIIAAAggggAACCCCAQOoTSJEBK/1moH5DUKdGjRpJmTJlUp88jwgBBBBAAAEEEEAAAQQQQAABBBBA4IIJhBOwch/c6dOnZffu3fLrr7/K5s2b7ar06dNLly5dJHv27O6mzCOAAAIIIIAAAggggAACCCCAAAIIpDKBFBewcpdez5gxo/Tq1Uv0YhUTAggggAACCCCAAAIIIIAAAggggAAC4QpEGrBy9nv27FmZPn26J2RVoUIFqV+/vrOaWwQQQAABBBBAAAEEEEAAAQQQQACBVCiQ4gJWOjSgDhGoU/ny5aVBgwapkJ2HhAACCCCAAAIIIIAAAggggAACCCBwIQWiFbDSY966datMmTLFHn6BAgWkQ4cOF/KhsC8EEEAAAQQQQAABBBBAAAEEEEAAgQsskKICVqdOnZJRo0bJiRMnLEO7du0kJibmApOwOwQQQAABBBBAAAEEEEAAAQQQQACB1CYQzYDVvn37ZOzYsZYoa9astgJ7avPi8SCAAAIIIIAAAggggAACCCCAAAIInBdIUQGr1atXy7x58+zR5c6dW7p06XL+SJlDAAEEEEAAAQQQQAABBBBAAAEEEEAgTIFoBqzWrVsns2fPtkeSK1cu6dq1a5hHxWYIIIAAAggggAACCCCAAAIIIIAAAheDQIoKWE2aNEliY2Ot29VXXy1Vq1a9GAw5RgQQQAABBBBAAAEEEEAAAQQQQACBFC4QbMBqx44dcvDgQSlZsqSkTZs23qM6duyYjBs3Tg4dOmTXXXnllXLttdfGa8cCBBBAAAEEEEAAAQQQQAABBBBAAIHUI5BiAlZ79+6Vr7/+2sqmSZNGevbsKVpinQkBBBBAAAEEEEAAAQQQQAABBBBAAIFQBQ4cOCArV670bLZ//37ZvHmzvZ89e3YpXry4Z13hwoVtoEoXLFu2TJYsWSKZM2eWUqVKSZ48eSRHjhxy8uRJ2b17t6xatUpOnDhht9VrWG3btpWYmBhPX8wggAACCCCAAAIIIIAAAggggAACCKQ+gRQTsFq0aJGsWLHCChcrVkxatGiR+rR5RAgggAACCCCAAAIIIIAAAggggAACF0Rg06ZNMn369KD2Vb58eWnQoIFt6wSsgtmwTp06UqVKlWCa0gYBBBBAAAEEEEAAAQQQQAABBBBA4CIWSDEBqzFjxoh+s1Cn66+/3vOtwYvYlkNHAAEEEEAAAQQQQAABBBBAAAEEEEgmgS1btsjUqVOD2nvFihWlXr16tm1sbKz9EuDWrVs9lap8OylUqJBcffXVVK7yheE+AggggAACCCCAAAIIIIAAAgggkEoFUkzAKpX68rAQQAABBBBAAAEEEEAAAQQQQAABBC5CgbNnz9ovAx45ckSOHj1qH4EOFZgzZ07JlCnTRfiIOGQEEEAAAQQQQAABBBBAAAEEEEAAgXAFCFiFK8d2CCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAgggkOoFCFil+qeYB4gAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAALhChCwCleO7RBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQCDVCxCwSvVPMQ8QAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEwhUgYBWuHNshgAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIBAqhcgYJXqn2IeIAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCIQrQMAqXDm2QwABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAgVQvQMAq1T/FPEAEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBAIV4CAVbhybIcAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAKpXoCAVap/inmACKRsgW3bYmXNuvWSPn16qVvnqpR9sJfo0R0/flwWL/nNPvqa1atKtmzZLlEJHjYCCCCAAAIIIIAAAggggAACCFwKAlyvSvnPMterUv5zxBEigAACCCCAAAIIIJDaBAhYRfiMHjh4UA4ePBSvl8yZM0m+vHnjLWcBAgh4C/S57U5ZvvIPqVW9ugz5ZKD3Su6lCIHDR45Iy3Y3yv4DB6R3j27yUP97U8RxcRAIIIAAAggggAACCCCAAAL+Bbhe5d+FpQgEK8D1qmClkq8d16uSz549I4AAAggggAACCCBwqQqkmIDViRMnZM2aNbJ//345YD7Ez5Ahg+TIkUMKFiwoJUqUSLHPzwsvvy7fTpwU7/iqVKooI4cMirf8Ul5w8uRJWbf+Xw9Bnjy5JcY8v77T0aPHZON//3kW58+fX/LnS5qwmgZ71q77V/7btElOnTolBQsUkKKXF5HaNWuY8y+75xiYSRqBmd/NlkeeetZ2PmzQh1KjWtVEd7R37z5ZtnyFbNj4n8Ru3yG5cuWUmJiCUunK8lK+XNlEt6dBeAJDPx8l7330iWRIn0HGfzXKvk7C64mtEEAAAQQQQAABBBBAAIHkEdixY4ccPnzY7lyvOen1hlCnvXv3ivbjTGXKlJG0adM6d1PMLdergn8quF4VvNWl0pLrVRfPM831qovnueJIEUAAAQQQQAABBBBIDQIpImD1zz//yKJFi0TL+vqbCpjQS8OGDSVPnjz+Vifrspdee1O+Hjch3jEQsIpHIn//s1q69rnFs6Ja5cry+Wcfe+47M1+O/UZefetd526SVMxZvXadvGb28evSZZ79uGfSpUsnNatXk08/fM+9mPkoCugFzDY3dpdtsbFSp3ZtGfTBOwn2ru1Hf/W1DB4yXPQbav6mAubi+KMDHpDrGzf0t5plEQgcOXpUWrXvInv37TW+jeTNV16MoDc2RQABBBBAAAEEEEAAAQQunMAm86WqZcuWSaz596czlSpVSpo2bercDer2zJkz8s0338i+ffs87Xv16iVZs2b13E8pM1yvCv6Z4HpV8FaXQkuuV11czzLXqy6u54ujRQABBBBAAAEEEEDgYhdI9oDVf6ZS0YwZMzyOWrkqrxlaT6sJ7dmzR86ePWvX5c6dWzp27Cjp06f3tE2JM607dZNNmzcLAav4z47vBas0kkZmTPpWCpnqQ+6p7213ye8rV3oWRXtIsn83bJQeN90m+g9wndKmSSsFCuQXDVXFxm6XM2fPePa9fPGPnnlmoivg/jbgu2+8Io0aXJvgDp5/+TUZN3Gyp03WLFlsxbGdu3Z5Ba7u6ner3HnbzZ52zERP4J2BH8nwUV/Y18y08WPlsssKRa9zekIAAQQQQAABBBBAAAEEoiywfv16G6zavXt3vJ7DCVj99ttvov+7p5QasHIfo85zvcpX5Px9rledt2BOhOtVF99ZwPWqi+8544gRQAABBBBAAAEEELhYBZI9YDVx4kTZvn279StcuLD99mDmzJntfS25Pm3aNNHhA3WqW7euVKpUyc6n1B9csAr8zPhesNKWA+6/V/r07ObZaJsJOLVsd6OcNf85U7QDVt363Cp/mappOrVqfr08cO9dnqEKtYraij/+lGEjv5CFPy8SAlbOsxD921vvus9WEMudK5fMnjYxwfDkDwsWyv0PPWoPIk/uPPLEIw/aKlVp0qSxy7ab94rJ02bIqC+/lm6dOxKwiv7TZXvUym+de/a187fd1Efuu+v2JNoT3SKAAAIIIIAAAggggAACkQkcO3ZMRowYEbCTUANWWrVKq1dpFSv3RMDKrXFxznO96uJ83pLqqLlelVSySdcv16uSzpaeEUAAAQQQQAABBBBAwFsgWQNWWnJ5+PDhnipV3bp1k5w5c3odoZZwX7JkiV1WtmxZO1SgV4MUdoeAVeAnxH3BqqAZ9nHHzp1SscKV8sWwTz0bfT7qS3l74IeiQ71pZSKdohmw0gBXi3adbL81qlWVIR9/IGnTxoV07ELXD610VbJEcdcSZqMl8N+mzWZ4wLhgXZdOHeTJRwYk2PUzL74iE6dMtW0GDXxX6lxVy297vYC+b/+BeFXR/DZmYVgCN/boK2vWrZN8ptLg91MnBnz9hNU5GyGAAAIIIIAAAggggAACURJwB6wKmGsQ1atXF/2SzsyZM+0eQg1YTZo0yQ4xmMt8SUi/CHj0XFVsAlZResKSsRuuVyUjfgrbNderUtgTEsLhcL0qBCyaIoAAAggggAACCCCAQNgCyRqwOnLkiIwaNcpz8LffHr8aysaNGz0Xv7TCVevWrT3toz2jwxHO+n6OrFz1l6xb/69s3bpN8ufPJ8WLFZPWLZuJBnISm0INWG38b5P88ONCWfnnKtmxfacJh+yXwoUvkytKl5KKV5aX5k0b2wuACe13yW9LZfLUGbJl2zYbSsqePbsUyJdPKlWsIA2vrSdlriid0OYS6fYJdu5a6b5g1bNrF/lyrPnmpxmOb+q4sXJ5kcK2Zfe+t8qqv/+R3t27ysgvv7LL3AGrTZu3yITJcUEbfT7qXXO1aw/es/Pm/2hddemNHdrJZYVivMp8a/UdrcIT7rR5y1Z7vvy9eo09X7QSU7myZaRhg3pyVa2afrvdt2+/53FdW+8aKV2ypMyaPddUcloqf69eK0XMkGs1qleTvqaqlw5ZGGg6ePCQjPlmnPxpztWtsbFy8uQpE3jJI0XMuVO/7jVSt85Vki1r1kCbm+pci+Xnxb/IP2vWyt69+8w5XlTKlblCbmjZ3PYRcEOz4qdFv8hvy3635+VtN/W2+/563ARZtORXU41uh+TJk1uuLFdOenbr7Hleffv76pvx8vIbb9nFb736kjRt1NC3idf99l16yr/mvSBjhozyy4LZib4mvDZ23XFe48tX/mke+xrZa76BfEWpUnJl+bLSpWN7yZYtm6v1+VktD6/fhsuUMaPcfutN8vvylbLgp5/N8/a7ubB+XMoau643dpQK5cud38jPXCSvNX1vGD9piqw2z9m6fzdIlsxZ7HNWvVoVadmsqZ+9eS+KxvOmPb7xzvsyasxY2/nYUcPtMXjvyf+9GcZwvTluZ9Ljvuaq2s5dbhFAAAEEEEAAAQQQQACBqArol/pmz54tFStWlKJFi9q+3deYQglY/fXXX7JgwQLbR8uWLe38oUOH7P0LEbBy/i3L9arQrncFe0JxvUqE61VxZwvXq0K/tsz1qmDfaWiHAAIIIIAAAggggAACqUEgWQNWeoFo2LBhcurUKWt58803S4YMGbxc//77b5k/f75dlpQVrDS48+xLr9jgiNcBuO5o6OehB+5zLYk/G0rA6tsJk+WFV16L34lrybX16srLzz8tOXPkcC2Nm9Wy9A89/rTMnvdDvHXOgrRp0sqyRXF+zjLnNtLtnX6CvXVfsOp/953ykwn4aODk/rvukFtNUMf5lpge8/BPP5I+t91pu3YHrPQf7dff0EFOnDwhpUw4afyYkX53r+dW605dRUNQGkSZPX2iDRxpyOPRp5612zRqcK28+8YrfrdPbOHEKdPk1TffkSPnvrHqbp9G0thhD++/+454w96tXbdeOvWIC3XdcevNNuSkQxL6TrVqVJdPP3zPVAdK67tKfv1tmfR/+HE5dDjuYm68BmbB3bffKtq/73TCXGDWgMzYb8f7rrL3s2fLLi88/bg0aXSd3/W68M13B3pCYtPGfy0PPvqkCYetjte+XJkyMnbUsHjLdcHDTz5jw2k6P2vyOM8QjXrf39Sucw/Z8N9/dtXYkcNskM1fu4SW7dy1W556/iVZ9EtcRTzftkWLFJE3XnnRhMPK+q6SAY89Jd/PnSeZMmWSl559Sh598lkbDnQ3zGjCV2+bsJi+Zn2nSF9rv69Yac7b5yT23HCqvv03vLa+vPjsk37fJ5y20XjetC/3a+jRBx+QHl1vdHaR4O29Dz4iCxb+5GkTzPuppzEzCCCAAAIIIIAAAggggEAUBMIJWOmXA8eOHWurVpUoUUKaNWsmX3zxhVyogBXXq/w/8Qld7/K/ReClXK8S4XpV3PnB9ar4r5OEXmtcr4rvxRIEEEAAAQQQQAABBBBI3QLJGrBSWv024Toz3JRO+q3COnXqeCr36EWsyZMny34TqtFJL2LpxaxoT7t27xENcDiBFR2+7uraNaXo5UVky9ZY+X3FCtFKU+1a32DDJwntP5SA1Zivx8krb75tQzg1TdWiEqZSVkzBArLXPN65P8y34SDdV3kzNOJXI4fG2+2IL8bIW+99YJfnyZ3HVrEpXLiQ8TogOrydVtg5fvy4LF/8Y7xtdUGk2/vtNIGFvhescubMIS+++oapgBMXxBk89HP5cNCnohb/Z0IsLdrHBTfcASvtXoMmM7773u5p9NBPTaWuK+PtVSss3XLnvXa5+3n7Z/Ua6dI7LniUIX0GGTb4Q6lsKn2FMmllLQ2r6KQXGRpdd62tErZ7zx75bs48862/fXbd7bfcJPfccZudd364L1g5y3SYxOpVKsvuPXtlpqmgdubMabvq9Zeel+bXN3Ga2VsNSF3fuoNnH3Vq15batarbc2iLqbi2dNlyWbt+vdx52y1yV79bvLbVO6++9a6tHKbzWuGq0XUNpGCB/PLnX3/LYlOByplGDhkkVSpVdO563bqDOvWvqSM//rxINFykldKKXHaZbN0Wa6uQlTFV2AIFrFq07STbTFgov6m0NnvaRK/+/d15wATK5s6P+7Zw86ZNTMjpSbtPf239LTts3ku0CpYOS6mThqk0CJU9ezbz+l4pv/z6m12uQcbJ34yR3Llz2fvODydgpff1vMmYKaOtDpc7V24bklu/YYNtqqG/cV+OiFdhK5LX2s6du6RVxy72Yr7uRCvb1TQBvMOHDsu8BT+a82aP3XfdOlfLx+/FVQWzC3x+RON50y41tHiDOR6dWlzfVF576TmdTXQiYJUoEQ0QQAABBBBAAAEEEEAgiQXCCVh9//33st78Ozt9+vTSuXNnyWH+3XihAlZcrxIJ53pXqKcR16u8A1aOH9eruF6V0LVlrlc5rxRuEUAAAQQQQAABBBBA4FISSPaAlX7jTy9W7dixw7prUENLt2u4KtYMfaaViHQKpXS73SCEH1rVZvK0GXaLJg2vMyGqJ2zwwulCq8+M/OIr2bl7tzzUPy6046zzvQ0lYKXVXP5Y9bcdvq5A/nxeXWlVr/sGPGqGY1tsl2s1I99h53rfeoc41Y+GfDRQatWs7tWHVnuaNuO7gBVmIt3ea2dB3PG9YNWxXRtp3KqtnD59WiaO/cJWCdJw0BMPD5AGZvi8QAGrxUt+k9vv7W/32NkM6/bUow/F27tWI3OGEvx88MdSrWpl20b31bnXTXZIP12gAamrTJhOq1nVrF7VDs2YJk2aeP05C/bs3WsqY3WTw4cPi1Z7+vCdNzx9axutknTzHffIps2bbQBowlejvYbc8w1YaXhswP33eAI5GtB66PGn7O7qmfDSR+++6eza3urQfnc/MMDO16peXYZ8Ehf0cjfSqmBaRcv3fHBf+MiVM6cM+uBdr2pNQz8fJe999IntKqGwjjuoo42rVa4sr774rFxmhjd0Jh0K7vu5P8jtt/R1Fnnd1qrXSE6eOmmHEhwzYojXOn93ps/6Xh57+jnPqgL580vTxg3tEJFVK1dKsHKTbvTeh5/I0BFxw5G2btlCnnn8YVuNyunw81FfytsDP7R3O7VvZ9c76/TWHbDKny+/fPbRe1KyRHHbREOM+pxrSE2nUUMHxwvtRfJac4fiOrRtbY7tUVPZLO4cjTVDMva7p7+p/rbJ7nvEZ5+IevibovG8ab8a8qtdv5HdRaBz0N/+CVj5U2EZAggggAACCCCAAAIIXEiBUANW7va1atWSGjVq2MO9UAErrleJhHO9K9RziutV8QNWXK8qJ1yvMkNHJnBtmetVob7T0B4BBBBAAAEEEEAAAQRSg0CyB6wUUUNUCxculFWrVvk1veqqq6Rq1aqeEIrfRmEu1CBIx2695az5T0Mb08aPDakyju9uQwlY+W7re3/p78ttcEOX33NHv3hhleamCpAOGZY1SxZZOGem3+HkfPt03490e3dfwcz7XrC6pW8vufP+B20FoGZNGsus2XPMY0hnKhpNkOPHjgcMWLmH/8uRPYfMMcP/aTDPmY4dOyaNW7YVrVpUsnhxmTB2tLPK3v656i+5q/8A2X/ggNdyvVMoJsZU5WkinTq0lWKXXx5v/etvvy+jvxprl//fc09L65bN47X56pvx8vIbcZWE7up3q6kmdbOnjTtglS9vXpkx8RuvY9eGrTp0MZXTttqKZhO//sKzrc5oaEzDYzo9cO9dcnPvnnY+mB8ffzpUPvksrhJaoApX7qH4NPRWonixeF27gzq5c+WSaRO+ttWw4jUMsOCQqbxUr0mcW52rasugge8EaOm9+IlnX5SpM2Z6LzT3NEymIbl2rVvZKm6+wypqhacWbW+MG1ayRAn5ygxbmNFnKFIN3unrYeeuXbZC1aIfvvMa3tEdsHr4f/dLr25xFZycg5k0dbo8/cL/2buvvfS8PYecdXob7mtNw1v1m7a01av0dT57+iT7enf37Q6fJVRRKtLnzb3Pq69rKvo6S2iYTnd7nSdg5SvCfQQQQAABBBBAAAEEELjQAu7AVGJf5DtpvlyiQwPqF6xymi8pafWqdOnS2UO+EAErrleFf70r1POK61XeASuuV4lwvSrhVxHXqxL2YS0CCCCAAAIIIIAAAgikXoFkD1hppaZ58+bZcuuBmPUC1jXXXCMVKoQ2lFug/tzLp06fKU8896Jd9OB990jfXt3dq0OeDzdgpQ46fJlWpNF/pOq0Z+8+eeLZF+y8Bjo02OGe+va72w5fqMseeuA+6dm1c0ghq0i3dx9LMPP+Lli5A0Pax9W1a8lgU1lpmxlmLlAFK2332fARMvDjwTprqic9Z4M19o75Ecxzut1UTPto8BCZ+d0cOXrsqLOp5zZz5symktaDNrTjWWhm+tx2pyxf+Ydd9PF7b0uGDOk9q51qaxrs0iHtdNLKWO++EReI0vvugFWbVi3MUHdx1ap0nTM5oTMdwu+nubOcxfZ20S+/yh33PWDnNdzykamg5a4c5dXY5457aMUp335lh8D0aSKfDhshH3wS5/reG69Kwwb1fZvY4RF1mESd7r3zdul3c594bRJaoOd487YdbZPGZojCd15/OaHmXuu00pzuW4d69Dfp8JKv/98LZujBvJ7VPy/+xQb5dMF19etJ7x5dPeuc50wXfDn2W5ljhubUaezIYVKubBk7rz/cAatp47/2qkqm6/Wc0HNDpwH975U+PbrZeedHuK+1DRv/k3ZdethubmjRXF5+/mmnS8+tVpRq0LSVPY8DDSeqjd0Bq3CeN88OzUyjFm3M+9NeG0icOelb96qA81pZTYePdCYdUrJC+XLOXW4RQAABBBBAAAEEEEAAgSQXCCVg9dNPpur4H3H//m/RooUUK3b+C0gXImAVzLWNUMC4XhVYi+tVXK/Ss4PrVcFfW+Z6VeD3E9YggAACCCCAAAIIIIBA6hZI9oDVrFmzZMOGDVY5l6mGU6dOHSlQoIBo4Gjt2rWybNkyO4ScNmjSpImULl3ato3Wjw8HfSaDhw633Q0a+K75hlKtiLoO9YLVyj9XyZDhI+WHBQvlzNkzAffd7caO8rgJ/Lin0WO+ltffec+zKG+ePGaYu2pSpXJFqWEqflWsUD7Bql+Rbu/ZcZAz/i5YHTh4UBq3aGuHi9Nunn78EbmxfdtEA1Y6FJ+GdLTykO+3ym6/9wFZvORXW4HouynjRV0CTUeOHpUlvy4VrRb28+Il8s8a7+DO55+a4QWrxA0vqH1c1+wGWx47UH++y30raLkDVv1u7msCSv18N5FHnnxWZn4/2w5fuGxRXODHaaTH275LT9GAmE5a8evK8mWlRrWqUrnClVLn6tqiw//5m5xh6nRYxN9+muc3jDfju9ny6FPP2s0fffABv8NLuoM6n7z/tlxz9VX+dhdwmT6Gaxpeb9fXrlnDDLf3fsC2gVasW/+v/GICO0uXLZeFZhhN/UaxM/meD19+/a28+mZwVbKcPl43Vaiam0pmzuQErLRa1q8L53pVt9I2/27YKO27xlUTu+f22+T2W2/SxZ4p3NfaT4t+MdXW4l73gaqO6U6cymM5c+SQBd9P9+zXPRPp8+buq3b9xrYiWNkrrpCvRw93r2IeAQQQQAABBBBAAAEEEEixAsEGrHaYf3NPnDjRVlzXYJUGrNzThQhYcb0q/Otd7ucqmHmuV3kHrLheJcL1qoSvLXO9Kph3FtoggAACCCCAAAIIIIBAahRI1oDVLjMc17hx46xrBjNkV5cuXSRbtmxezn/++acdPlAXagCra9fz1We8GoZ55/FnnpdpM7+zW0/6+kspXqxomD3FbRZKwGrqjFnypBn2TIcndCYNSOjjTJMmjQ0P6VBxOt3YoZ08/djDTjN7e+bMGVPJaaSM/GKMaFDJdypSuLC88NTjUqtmdd9VUdneb6cJLPR3wUqb3zfgUZn/40Jban+OGQJNh51LrIKVbqdVoubOX2CDSNPNUHuFYgrKttjt0qp9ZxtWa9zwOnnntf/TpkFPGpR57v9e81QGc19QcQ9tp8GmwpfFJNpv4csuk08/PH9R0B2wCjTEnxOw0jDP74sXxNuHOr71/ofyy6+/xVun1d7at75BHh3QXzJlyuS1vlmbjjaYpYGzuTMme61z7mif/e7pb+/e1KuH/O++u51Vnlt3UCfc18xVDZrYSm3lypSRsWbIvkgmHbbhm/ET5a33PvQE9YZ+8oENG2q/b7zzvowaEzesY57cecx7TJZEd6cV4bT6mDM5ASs1/WX+bGex59YdsLr79lvljlvPDwupjcJ9rY6fNMWcj6/a/Tz5yADp0qmDZ5/umZvvuMeGBHXZ4h++F63A5jtF43nTPnVoQB0iUCen4py9ww8EEEAAAQQQQAABBBBAIIULBBuwGj9+vOw0Vcb12syNN95ohwh0P7Qvv/xSjpjq1Tp1795dspoK1DpcvbaP1sT1qvCvd4X6HHC9yjtgxfWq48L1qrhXUaBry1yvCvVdhvYIIIAAAggggAACCCCQWgSSNWC1atUq+fHHH61lGRO0aNSoUTxXHcJr6NChnipWffv2jRccibdRCAv+7/W3ZOy34+0WXwz/TCpeWT6EreM3DTZgpcPINW7Z1oYVNKxz/923S8d2bbyqD7lLU/sLWDl71+DPuEmTZdnylbZyk7uaT4b0GWTcmJFSrOjlTvN4t5FuH6/DAAsCXbDaum2bbN6y1YZCqlSqaLcOJmA1/8efTDjrEdv+njv6ye239PUa4u6Dt1+Xa+vVDXA0gRdrdaSO3XvbBtmzZZeFc2bYea2WVfvaxvZcLF2qpIz7cmTgTgKsiUbAyulaq27NNBWn9Hn3rbzV7oZW8sIzTzhN7W2Hrr1kvakWlzVLFvl5Xlyo0KuBueM29RcU0vbuoM7saZO8huPz7S/Q/TY3dpf/Nm0SDRTO/25aVC5CP/T40/LdnLl2lzqcpg6rqdPHnw6VTz4baufffOUlub5xQzsfyo9IA1bOvkJ9rc36fo48/OQzdvMB95uhB3t6Dz3o9Nu9762y6u9/bEjx1x+1Oln8i/rReN50f+v/3SAduvWyu257Q0t58ZknncPgFgEEEEAAAQQQQAABBBBI0QLBBqxGjBhhr9eE8mDKly8vDRo0CGWTBNtyvUok1H9DJwiawEquV0UnYOUQc73KkYi75XoV16u8zwjuIYAAAggggAACCCCAwMUskKwBq8WLF8vy5cutX7Vq1eSqq/wPNeYuvd6pUyfJly9f1My1so1WuNHpjZdflGZN4oe8QtmZE7C6slw5GTNiSMBN3cGJu/rdKnfednO8tr8u/V1uveteuzyhgJV7Q60uM3vefBk0ZJhs/G+TXdW7Rzd5qH9cP+62/uYj3d5fn86yQBesnPXu22ACVloVqHnbTrLDfKu0aJEiMvnbMdK2cw8b3ClohpmcOelbv8PgufcTaN4JI+l6HXJNg0A6te3c3brmzp1bfpg5xS4L5Uc0A1bu/WoobOy4CTLGDIenk1a/+nH2DMme/XxFuLsfeEgW/rzIrteAlQatfKdvJ0yWF155zS5+6dmnpE2rFr5NohKweuaFl2Xi1Gm273CrYPkemPs11aNLZ1vFS9tMn/W9PPb0c7b54w89KN06d7TzofyIVsDKvc9gXms6hGivW263m/Xp2V0G3H+PuwvPfNMb2stOUxFQv1k4bXxctS7PynMz0QpYTZg8TZ596WXb6zOPPyqd2rfx3RX3EUAAAQQQQAABBBBAAIEUKZCUAauyZctKw4YNo/a4uV7lTRnMv6G9twj+HterohuwcstzvUqE61Vcr3K/JphHAAEEEEAAAQQQQACBi1sgWQNWK1askEWL4gIfpUuXliZNmsTT1BCNVrDSW510GEENt0RrWvDTz3Lv/+KG3gtnSDnf4+hz252yfOUfopWjfpo7UzJmzOjbxN4fYYb1e+u9D+x8oEpLHw76TAYPHW7bBBuwso3Nj2XLV8hNt8cN79agfj0Z+FZcaMZZn9htpNv76z/aF6x0H26j/nffKe999Inddb+b+8q9d/bzdxhBLbuxR19Zs26drZa2aN73nopAznCG2sk3oz+XMleUDqo/p1FSBayc/nV4P2fowC8/HyIVypdzVsmLr75hh9LTBe+/+Zpcd209zzpn5rGnnzeBpLjqVkM+/kBq1ajmrPLcRiOoM3HKNHnmxbiQzv8997S0btnc03+4M7Pn/iAPPhZXTck9vKFWdtIKTzo1bdRQ3nr1pZB3kRQBK+cgEnqt7dq9R5q0amubBgptbtj4n7Tr0sO2qVWjugz5eKDTtddtNJ437fCFV96QbydMtH2HEo57wgyH6gT8dONunTvJXf1usf3wAwEEEEAAAQQQQAABBBC4EALBBqz+/PNPOXHiRMBDWrZsmZw6dcqur1Klir12UMB80evyywNXDw/YWYAVXK/yD5PQv6H9b5H4Uq5XJV3AytHnehXXq7he5bwauEUAAQQQQAABBBBAAIGLWSBZA1abN2+WadPiqtikTZtWOnfuLLly5fLydIewMmfOLH369PFaH+mdo0eP2XDC9h07bFeBQiXabvXatVK1cqUEd/nEsy/I1BmzbJvPPnpfates4be9u6pOp/bt5JnH40JeTuOdO3fZ49KhBHXyF7DSIQQLxRR0NvG6dVeAur5xI3nzlRe91uudSLeP12EiC5LigpUOL9iqfRc5a/5zJq3eNGXcV3J5kcLOIs/tzl27ZeBHg+SWvr2kRPFinuXumZ8X/yJ3939Izpw9Y4eM1KEjncn9rTMdflCDa2nSxB+OTdtrmOqyQjGSLdv5KlKRBqz27d8vWczrIFOmTM4hed26K0PpEIY6lKEzfT93nmhQSKc6V9WWT95/2+vYtRLYDR26yomTJ+xQldMnfiPZsmZ1NvfcRiOoo+d3M1N97MyZ09Kq+fXyygvPevr3NzN0xGjJnSun6JB06dOnj9dEA5j97u4vv5qL3Dq9+8Yr0qjBtXZe13XpdbMNzOm5MXLoIKlcsYJd5/vj+PHj8u+GjVK+XFmvVZEGrMJ9rekQqZ173mSPXQ/o88EfS7Wqlb2O7ZU33pYx34yzy+6763a57Sb/75HReN70eFq27yzbYmPt62vquLFex5LQnXsffEQWLPzJ06R3967y0AP3ee4zgwACCCCAAAIIIIAAAggktUCwAavEjsNdab1Xr16S1c+/nRPrI7H1XK8K/3pXYra+67leFXnAiutVXK/iepXvOwv3EUAAAQQQQAABBBBAIDUKJGvA6uTJk/L111/LoUOHrG0WM2SZDhNYsGBB+03AdaaC0MqVK0U/1NepQoUKUr9+fTsfzR8zv5stjzwVF/DQylP3332HNG18nQnHFLLDzy1f8Ye888HHNiz1wtOPJ7hrd4hFgzVdO3WQq2rXNLGONJIzZw5PRSENcbTv2tP2pSGW/vfcJU0aXSfZs2UVHRrw+Zdfs/t2duYvYFW3UTMpYyp/derQVmpWr2aP98DBA7Lkt2UybORo+XPVX3bz5558TDq0be105bmNdHtPR0HOJMUFK931Hff9Txb9ssRzFAlV8flv02Zpc2M3SZsmrTRr2lgaNqgvpUqWkJiCBWSXCV99N2eejDTVxZxg24D+90ofM8Sie7rt7vuN8VK7qEa1qua5u1PKlSljqpVlkC1bt4kO6/bN+Imy9PflMnLIIKlSqaJn80gDVlr5bPCQz6V1q+Y2mFS6ZAnJkiWr/LNmjfywYKEM+myYDYYViomRGSYg5Q5/+YZ1bmjRXO654zb72P/48y958vkXZfOWrfZY773zdul3c9IFdXQn/3vkCZnzw3zR4OTcGZP9DlloD8b8ePyZ52XazO/sOd71xg6i1ZxKlSxu3xtWr10nQ4aPtFXbtH1uE9KcNuFrr3CYe7hN3d89t98m1zduKIVMAO6gef/R16M+9xMnT5VKJnz18XtvaVeeKdKAVSSvNT2uhx6PC8bpUJVa8etq855y4OAhOyTkZ8NH2OPMlTOnBArFaYNoBKx+W/a73HJn3HCjD9x7l9zcO+49zAOVwAwBqwRwWIUAAggggAACCCCAAAJJInDgwAF7XcnpfL/50pJ+2U+n7NmzS/HixZ1VUtgMuV6y5PkvKXlW+Jm5EAEr3S3Xq8K73uXnKUtwEderIg9Ycb2qkHC9iutVCb7RsBIBBBBAAAEEEEAAAQRShUCyBqxUMNZUQpk8ebInRBVIVYcF7Nixo9/qNYG2CWW5uwpMoO3atb5BEgtY6bbuAI67r4pXlhd3NaQXXn5dvp04ydNEQ1gaiNHKSTrpkIVz5v1g5wMFrJwgkDbScJiWqHdXc6pkQmlDPxnot+KRhj4i2d4eWAg/kuqC1azZc+XhJ572HMmLzzxpKx15FrhmnICVa1HAWX9VnrSx9tH/4cdl/b//erbV587t7qxIioCVM7Sks4+MGTLaqlPOfT2WN0zFMg0Q+U46fOAD5tjdz7tvm/Jly8rQQR94BZTcbaIR1NH+tFLYnfc/aLt+6dmnpE2rFu7deM07ASuvhX7upE2bTj567025xlTo8p0GDRkmn3w23FbN8l3nvl+3ztVJErBym4fyWtVg3P0PPSbzf1zoPkyv+XTp0smzTzwq7Vq38lruvhON5815z9Lj/27KeMmTJ/jhWglYuZ8N5hFAAAEEEEAAAQQQQOBCCGzatEmmT58e1K7Kly8vDRo0CKrthQpY6cFwvSr0611BPYmuRlyvik7AiutVrpPKZ5brVVyv8jkluIsAAggggAACCCCAAAIXqUCyB6zUbc+ePbJ48WLRC1++kw4HVqVKFalatapkyJDBd3VU7/9sqiDphauN/8U/jlrVq8td/W6RWjWrJ7pPDTkNGjLcVhRa/+8GOXnqpN2mcsWKMsoMT+ZMx44dkw8HD5HRY8bK6dOnncV2SDkNm9xvhvuq17iFDe507dRRnngkLoziNHz1rXftkFtO1SFnud5myZxFunRqL7eZKkRa9cbfFOn2/vpMaJlWGurcs69t8r/77pabevUI2FyHbGzWpqNdr+20faDphKmEVue6ptYwq6mCNmf6ZFPVKbPf5joE3LcTJstsE1xb+vsKv2Gb/Pny2+pNndq3CXjO6T4/Ns/dmK+/lSNHj8bb1xWlStmKZL26d/Hy1/OhQ7detv2A+011rJ7d4m3rhIk0NLP0p7iAndNIq2INMiGhpcuXy4kTJ5zFntuKFa6Ue+/oJ3XrXOVZ5jujAbHHTEUop8KZs15DM/qYB5hh2zIm8Fp7+/0P5fPRX9rN5s2YElLIxtmX3mpwqGufW+Sf1WtEQ11jRgzxqrjlbqsVtiZOnSZzf1ggO3ftcq/yzGsg8V5Tkcs9LKJn5bkZ7efFV9+w+/QNxOm5U7/uNdK+zQ1S75qrvTZ9+MlnRIeH1DY/z/vOa53ecQf3/FX/ivS1plbDzDCJg4YOF33fcE9FL79cXnr2SalWxXvoQHcbnY/0eduzd68dHlD37y/w6bs/3/v9TUhs3oIfPYv79OwuA+6/x3OfGQQQQAABBBBAAAEEEEAg2gJbtmyRqVOnBtVtRXPNpl69ekG1HTNmjGh1LJ369OljKzMHtWGYjbhedR4umOtd51sHN8f1KjFfIuR6lXO2cL0qTiKY1xrXq5yzhlsEEEAAAQQQQAABBBC4VARSRMDKwdYP7vUC1cGDB22lqlxmqK8cJhykQZMLOR06dFjWmepEW7fFSv58eaV4saJSsECBJDuEfaZE/br1/9oh6q4oXUpKlighadOmCXp/sdt3mGPdZrfXEFrhywpJ0aKXJzjkmrvzSLd395Uc8zpcn1YN06ljuza2kk8wx3H48GHZYp5jDXPpc14gf34pYuxiYgoa/7TBdGFDQrGx22WtOV+OHjlqhrcsIEXMsAIF8ucLavtwG2m4Soe122HCRgfNUHEFzbEXLlzIPPeXBd2lbrd67VrZvXuPlChRXEqZ/zXQeCEnrajV757+dpfvvPayqdqW8LeF9cKNvi537NwpO3fukgxmWEZ9zJcXvswGE4M99qNHj9kKZJvMkIg5zLAQhcxzrq+ZhIJlwfadULtIX2tnzpw1Ya5Nsnb9evt4y5W5QvLmyZPQLqO27i0TrBthgnU6pOmUcV9dsP1G7QHQEQIIIIAAAggggAACCCBwkQtwvSq0613J/XRzvYrrVVyvSvpXIderkt6YPSCAAAIIIIAAAggggMB5gRQVsDp/WMwhELyADnk3d/4Cu8GooYOlcsUKwW9My2QXuH/Ao/KDGf6uXJkyMnbUsGQ/Hg4gvsDevfukebtOohXg+t99p9zSN64KW/yWLEEAAQQQQAABBBBAAAEEEEAAARXgetXFfR5wvSrlP39cr0r5zxFHiAACCCCAAAIIIIBAahMgYJXantFL6PHo0HzfTpgkb7470D7qileWly+Gf3YJCaSOh6rfwNVh/9KlTyfFzHB3TClPQIcw1WEQdSp6eZELXuks5YlwRAgggAACCCCAAAIIIIAAAgj4F+B6lX+Xi23p/7N3F3BS1H0cx3909xHSHQICYiEGJja2YosoBghIi2AAEtKIGICkIgYYlAqC8QgWEoIgIAhId/czv/85y+ze7t1/b5YL+Mzzem4n/lPvmdtdnO/9/vz3qrR/xfjvVWn/GnGECCCAAAIIIIAAAgicbgIErE63K3oGnI+WWO/W6zWna78tot1K6qDdSA4d2FfqXXD+GSDAKSKAAAIIIIAAAggggAACCCCAAAIIpCUB/ntVWroaHAsCCCCAAAIIIIAAAggggAACsRcgYBV7U7Z4igW+mfOdtGrfKbCXrFmyyquvdJVrrmwQmMcIAggggAACCCCAAAIIIIAAAggggAACKSXAf69KKWn2gwACCCCAAAIIIIAAAggggEDqCBCwSh139upDYMPGTfL9/36UDBkySJnSpeScGtUlW7ZsPrbIqggggAACCCCAAAIIIIAAAggggAACCCRfgP9elXw71kQAAQQQQAABBBBAAAEEEEAgPQgQsEoPV4ljRAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAgVQRIGCVKuzsFAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBNKDwBkbsNq6Y1d6uD4cIwIIIIAAAggggAACCCCAAAIIIJDuBeIK5Ev355ASJ7Bg54qU2A37QAABBBBAAAEEEEAAAQQQQAABBM54gVr5K0ZlQMAqKi4aI4AAAggggAACCCCAAAIIIIAAAghEK0DAyk6MgJWdE60QQAABBBBAAAEEEEAAAQQQQAABvwIErCwFqWBlCUUzBBBAAAEEEEAAAQQQQAABBBBAwKcAASs7QAJWdk60QgCBtCfw6/9+k+PHjwcdWKHChaR8lXJB85jwJ7B9y3ZZuWxVgo1UrVlF8uTLk2A+MxAIJ7B10zZZt3qtZMqUWWqeVyNcE+YhcNoIcL+fNpeSE4mxwOFDh2XJ70vMVqs43yNy5MwR4z2wOQTShwABK8vrRMDKEopmCCCAAAIIIIAAAggggAACCCCAgE8BAlZ2gASs7JxodTixC3EAAEAASURBVHoJ7Nu7T/bv3Z/gpLJmyyb5CuRNMJ8ZaVPgoYaPJDiwuvXrSsuuLRLMT8szTpw4IQcPHDSHmC17NsmYMWNMD9fv/T5n2hwZMfDdBMfUpttzUuuCcxLMZ8apF0jr90w4gVdadZcVS1dI1ZpV5fm+HcM1SfPzTrX7lo1bwhoUiCsgmTNnDruMmSJ+3+NOheHpcL+fCpczYZsaIDp27Jg51SxZskjmLLH93T165KgcOXJEMmTIINlzZE93pPp9p/WDbWXfnr1y3R3XyX1P3JvuzoEDRiAWAgSsLBUJWFlC0QwBBBBAAAEEEEAAAQQQQAABBBDwKZAWAlabN2+Wffv2mTPJkyePxMXFRTyrLVu2yPbt2yMudxcUK1ZM8uXL5076fj1TA1ZHjx6V9avXB/zy5M8jBeMKBqbdkUMHD8nGdRvdSclfKL8TwAnvr9UKVv65UlY5lV5y5cklFaqWdyrplI/4l9krlq6UI4cPB7atwYpiJYpJztw5A/MSG9Fz+OuPvxJrErSseOkSQeGhXTt2yb///Gva6H71AW56HQ4eOCRrVq6R1X+tlk3rN5lrVLhYnFMlpWbYCjsjB46S2dNmJzjdilUrSNdBXRLMT+8zdu/cLevXxN/vBQsXlKLFiyZ6Snpf6f0lkkGq1aqaaNvUXOgGrHLmyml+d/RYzq5TTe5uclfgsPT3Uh/iuUOp8qXCBpg2b9gsB/YdMM30oWXpCqXdVU7569IFS6Vn+95mP4+3aSqXXntJTPfp937/fd7vMnncZ+aY9F7aunmrGT8dA1an4rMhphfzv42l9Xsm9JznzflJhr76hpndud/zUqVG5aAmuIvs2LZTWt7XKsjFnWjXo22SVb801KHfQWyHspXKRvx+YruNU9nus/c/l49HTzK76DuqtxQuVjji7vy+x0XccDIXJHW/u5tdvni59Os60EzGFSkkPd7s5i6SqR9Ok08dAx0uuvwCebTlI2Y8rfzg2IOvxIa1G2TC8Inmd1A/J93h4ivryZMdmrmTMXkd1vNN+XH2XLOttyYNS7Hf41he8y8+mCITR35ogqO9hr8qRc4qEhObU7WRWJ77qTpGtpv+BAhYWV4zAlaWUDRDAAEEEEAAAQQQQAABBBBAAAEEfAqkZsBq7dq1Mn/+fNm48WQwp3z58nL11VdHPKvvv/9eliyJ7y4hYiNnQb169aRmzZqJNYlq2ZkasFqzYo10eebFgFWlsytJlwGdA9PuyFeffi1j3xjnTob9S+vjx47LO/1GyA8zfwi0c0e04sRTnZ6U8y85z50VeG1+z7PifQjjLsiXP58UK1VMrrzxCql3xUXu7ASvGvxq/5h9FRB9OHfFDQ0C2/n6s5kyZuhYM31v03vkhruuDyxLLyNaTWTy+E9N+OPEieCu4vQc1P+aRldL45C/jh81eLTMmvJNgtM8XQNWC35aKP269DfnW+PcGtK+Z9sE5+7O2LNrjzS/+1k54fwvV+5cMvTDIWEDSW771Hx1A1b1r6ovzdo/HvZQBr44SH6bOz+wrH3PdlLj3OqBaR3Rbgafbdwq6Pfx7clvplhliCW/L5VeHeIDVk2fe0wua3hp0PH5nYjl/T539jx5o+cwc0inY8Aqlp8Nfq9bYuunp3tGw1PtH+1ognnV61SXDr3aJTg13EU09Nzi3pYJbHSGTcAq1DDshjwz9Tro9Uirw+Rxn8onY+MDVq+92zvRYHAs3+P8etjc7+4+/pi/RHp37GMmCzkBqwFj+7mLZNK4yTJp7GQzXe+KevJUx9iGdAI7SuYIx34SbsfWHeb7uP5RRuhwKq6dfgbrZ7EOb33iBKxy5Qjd7SmZjuU11z+MaPNwO9mza7ecf+n50uKFZ07JMcdqo7E891gdE9tJ/wIErCyvIQErSyiaIYAAAggggAACCCCAAAIIIIAAAj4FUiNgtWrVKhOs2rZtW4KjJ2CVgCRVZ4Q+iMzgVOoZMK6faHUf79CtdQ/5a8nJKlGhXVnog7Qh3YbKfE+AI3fe3E53X4fkqNN9hw4ZMmSUx1o/miA0ESlg5d3/uRfVkUdaPiz5C+b3zjbjZ3rASrsYebP32/Lbj78FbDJmyuRUIisg252HXcf/654lUngusJIz0u7RDrLp301yugastLLJ03c2l8NOxbSsWbPKm5+8EbHLGm/ljYsaXChPd3rKS5WmxpMTsLr8usuc38cmQeex+Lc/pE+n14LmnU4Bq6ATcyb83O9nWsAquZ8Noeaxnj7VAavQ4/Vzz3jfU1q91FLOrVcndPMSq8/kBBuO8YyUdP/frB+dz7i3zBkQsEo8YBV6mf3cr6Hbinba5n53t5meQxscu3sVRcYNGy9fTv7KzNBuius1uMipqhlfKTR3vtxhK+SeXDv6sdMhYKVnrRW/pn441fw7qd/o1ySuaKHoMVJojfR8v6cQEbtJhgABK0s0AlaWUDRDAAEEEEAAAQQQQAABBBBAAAEEfAqkdMDq4MGDMmbMmIhHHU3AqkGDBlKgQPju2vLmzSvZsmWLuJ9oF1DB6qRY48fvlevvvC4wY9vmbfLcg21NJR93ZmjA6sdv5sqwXm+axbny5JY23VpLxWoVRAMt+rBFu7/QQbv/GzJhsFMR5+S1cwNWGgrq+XYP2e1Ur9j072ZZu2qtqa505L+Alnbf1+udVyVjpoxmW+4Pb8BKuyJ8uPmD7qKwr9r9hnZd6A7pvYLVyAHvyuzpc8zpqH2zdk3l7NpnS9ZsWZ1w21FZ7nRz98GIiaaKVbjqZK6Dvqbmw1jvcZzK8X4vDJAFPy8wu+jUp4PT9V+1sLvzuj7Z/gm5+KqLw7ZLCzOTE7DSqlyvfzBYMmXOFDiF4f1Hyrczvg1M6wgBqyCOwMSZFrDSE0/OZ0MA7BSNpGTQR0/Bz3tkz3a9ZOnCP0XDx0PeHxT0u+fyhAasdP6Z7u4nYFWlRhW5r9m9Lm/Y1+Kli5vvJmEXpoGZ27dsN8FnPZRK1SuZz3Lbw/Jzv9ruI1I7m/vdXTc9hzY4dvcqirjXXL/Pv/nx0FNe/fJ0CVjpv3c6PxXfLfct994sdz56x0nUNDaWnu/3NEbJ4XgECFh5MBIbJWCVmA7LEEAAAQQQQAABBBBAAAEEEEAAgdgJpGbAqnDhwlKnTh3nL3IzyIwZM8xJRROwuvPOO6VgweBKSrGTCd4SASuRAoUKyI5tO6R85fLy0pCuAaCpH02TCe98EFiuC0IDVr07viZ/zP/DrNO2+3NyzvnnBNbXkbf6vBPoOlCr5mj1HHdwA1ZZsmSREV+84842r//+86+83uMNWbd6nZkOXVdnegNW55xXU9r2aGPa2v5IzwErDb+1faS9HHOqVGm46oV+naREmRIJTv3E8ROyZuUaKVupbIJl3hmp+TDWexyncnzm57Nk9OvxIdDEHmQ991Bb2bppq6ko8PoHgyRPvjyn8rB8bTuagJU++MztBAy1W05v13Zaha7FPS1l3959Qb/rBKzCX5ozKWDl57MhvF7s5qaXgNWm9ZukXZMO5sSvuulKebjFQ2ERvAEr3OOJ/ASs6l5cV1q+2CKs9ZkwM7U+023vd/capOfQBsfuXkWRtg+3l80bN0u5yuXk5SEnux8/2SK2Y6dLwEpVOjd7QdY6/9bR7tEHvz9QMmTMEFusGG0tPd/vMSJgM6dAgICVJSoBK0somiGAAAIIIIAAAggggAACCCCAAAI+BVI6YKXVhmbOnCnVq1eXUqVKmaNfs2YNASuf1/FUre59mNvw1mvly0+/lhMnjkvfUa9JkbMKm912bf6SrP5rtVx3e0OZ/kl8UM4bsNq6aZu0ccIoJ5z/5cmX16mKM8iE6rzH7O16TCswdOnfObA4sYCVNtLglga4dChYuJD0fbd3ULduaSVgtWvHbpn5+UxznPpDg4W3NL45bJWSQCOfI6NfHxvY54133SD3NL3b1xajfRir9r/PWyAr/1xpuiPcu3uv07VJnJQsW8I8YLvw8gsS3AuhB7h/73752nFbtexv2bZ5q1N165jkLZBXChcrLLXOryk1ndBc9hzZQ1cLTC9dsFS+/+oHE4basW2n5MyV0+lKMp+Ur1pe6jhdS5YqVzLQVkc0NKXhKR204tlLg0+GCc1M58fmDZtNcE2nK1arKF0HvuAuCrz6PfdjR4/JpHGTxfm1kYpnV5DaF9aWZYuWOVXbZsuGtRtEA09qoIYXXX5hgsptgQNxRqIJWGV2gowacNR79ZKr68sT7R43m/p93u/Sv+tA8ztc87wa8r+Z/zPzIwWsFv6ySBb/ulj+cSov7HHCWsVKFpPS5Us7lb7qmeP2Hl/o+IolK+Sn738x941WWatSo7Kc53QndNQx6dWht2ne9LnHEnQn6m7nxIkT8tO3P4tu559V/8juXXuce66klK1YRq686QrJkTOH2zTR12jvd+/GzqSAVXI/G7xeOp6a98zmDVuce+YnJ2j6j6xfs94JGeaWMhVKSx2nm76za4evYhd6/Dqd3HvGG+xs0aW5nH/JeeE2H9RFIO7xRCkdsNLKm0t+XyILf15kPgt2OF3tZsqc2YSX9fOkbv1zpWjx+G7PvBdxzvRvZYtzn5WpWFp2bt8lc+fMk4xO18h1Lz7XCaU3dLpQ/l20jX6+lHcCKHc/dpfkzZ/Xuwkzrt3+avA73HDVzVdav7/p+rb36zdTZ8s257uc00u03Hp/o6DvWN7jOHTwkHz+/hdmVlGn67dLr73Euzgwbnu/uyv4CW34dfd77n6O3T3/1Hr1e+zaDaRWX3KHGZO/FL1HNBx66TXB90aZSmUSvO8l93fN3V9SAavVK9bIL9/9YppnyZpFGt1/i7tq4DU53yf8ugV27hkZ/+Z7MmPSl2ZO92HdnO9T8f+G9jRJE6OxOne/3/2T85nu3q/uvfCXU+F3wU8LZemiP+XI4SPmO+zVzntsUn+M4vfY/XwXShM3wSk4CAJWlqgErCyhaIYAAggggAACCCCAAAIIIIAAAgj4FEjpgFW4wyVgFU4lbczzBqzubnKXLHICExpYuevRO+Xme28StwpBBuchYZf+z8srrbubA/cGrH52ghJDur1u5l/U4EJ5utNTCU5O/8P1k7c/LRrAy+RU0Hl36ohAm6QCVvrwo9PjneXftf+adV7o97xUdgIZ7pBWAlZ/L/9bXmzxsntY5vWtScOiehgatLLFxFN3Npd9e/aalr1H9JKznJCLn8H2YazuY/a0OTJy4LuJ7q72BbWkWYcnRLujCzcsXfCnDHx5sBzYtz/cYjPv9gdvk1sfaJRg+fHjx+X17m/ILz/EPzxL0MCZofft6OkjEyzq9ERnE7LImDGjvPHh65Izd86gNvrA9d1Bo8y8Ox+5wwTlvA1ice4H9h+QZrfF/65cccMVTleFVUUfFoYbWr74rHlIH26Zzos2YNWhZ1vp0banuTeHThxiHqa/2fst0SCDHstR5/f0u6++N7sLDVhpIEofBM78YlbYw8nhBNwed8JR511SN+xytdVg4HGn6pp30PeFq2+5KvCAMVLAauf2nfL2a8Nl8W+LvasHxrUL0OYvPGPCVoGZEUaiud9DN3EmBayS+9ngmqX2PfPdl9/L2KHjRLswDh0yOIkS7RJXP/O83WWGtnOnk3vPDHUqMc5zAl46DBzfXwrGha/OGYvPZPdYTxf3lA5Ytbyvtanm6TqGvmrg9/E2j8n5l54ftKjbcz1EH9iHG+o1uEh++u5nU23SXR5aLdSd7w34uvPc19ecgHm4cJe7PPTV9n59/+0JMu3j6Wb15p2fkQsuCz43d7vff/2D8/4bX2m0kRPEuuOh29xFQa+297u7kp/Qhl93v+fu59jd80+tV7/Hrt/99d8ANkO9K+rJUx2bBTVN7u+au5HEAlZ/OQHsvp37iX7XyuwEJPV7wblOoNY7JPf7hF837zG4497vFA88db9ce+s17qI09RqLc/fz3V8xkvuZ7t6vWbNmNX9cMPTVYeaPirzAWk25Rdfmov9+CTf4OXa/n8nhjud0mUfAyvJKErCyhKIZAggggAACCCCAAAIIIIAAAggg4FMgPQescufObaq4aMAmX758UqBAAalSpYoUK+YvRBKOlC4CRfQhugZh3h08yvwVb/dhr8in730mH4/+RKrUrCLN2j3hVP6J737PG7DyViq47cFb5bYHbg1HLB2adjKVeXThsI+GOl3axYdukgpYafvRQ8YEAh1PdmgmF19ZT2eb4UwNWGmViydufdIYxBWJk/5j+/4nkvwX24exuge3a0UNxlR17o9ipc5yQgMFZI9Txeq3H34z3cRouzIVyki3N4KDZzpfHzTow7U9u/fopFSvU90JGVVzHoRlki0bt8iyxctN15B6P+l9FTpM/3iGvPf2+2a2Vk6rd8WFpnqWVtHSClD6V+mHDx+WMTNGha4qHwyfKFM+nGrmhwsvaXDrp+/iwxDd33hFSjuVbryD33PXbXkDVlVqVDGVazQAolXaSjtVUpy3PVOdSbvtDHeM3uOJNmA14rO3pdUDz5kQgW67Zt0a0vzuZ00ApVOfDqYiWKSA1dg3xstXn35ldq9BA+2Cq0Ch/KIBwz+cqi/u0HVgF6f6VwV30rzqX+3rA08dNPymlV2Klz7LWXe1E+5cZOa7P8IFrPSe7/BYp0D4QcNUtS+sZYJi+jBVq87ooO9jfUb2SrJbx2jud/e43Ffvw1BvV4vu8li96sNrrXwT7XCXEwws4Pw+JncIDfok57PB3Xdq3jNadfG9t+LfJ9x7TqsQ7dqxS352Kpu47z+JhUXc89DX5N4zrR9s41To2yb5CuSTIRMGeTcZNI57EIeZ8BOwOtepYvhs18hdBGo3XFpt0js8c1cLc19oBcEKTiVEDTRlzJTR+TxaL7/+8KtouFeHVi+1DApseIM+Wt2pRKni8tvc+YFNa6BXK5fpZ5sGO3QIF5ja41TkGzfsvcB6/2jVtX/WR2wfaBhmxPZ+1c9M/Y6mg3bxrF09hxt6tu9tQvgaTOw35jXzmRuune397q7rJ7Th193vufs5dvf8U+vV77HPcYL2q5zPb3f4wQng6fcurcym3w28QyWnUuclIVWtkvu75m43UsBK/1BEK3JqNS0N07R66Vmp4XzP8Q5+vk/4dfMehzuuFZnaPtLOTEb6YxW3bWq++j1339/9fXymuwEr9dPQnVayqnNRbcmTN4/5AyP3D3lKlC4hr77dPcFng99j9/NdKDWveUrsm4CVpTIBK0somiGAAAIIIIAAAggggAACCCCAAAI+BdJzwCrSqZ999tlSr149UwkpUpto5xOwig9YaddhLRq3MtVltCKS/sfodavXycPNH5Jazl/zhgtYfTxmknw6/lNDfv+T90nD264Ny/9yy26mSzBd2OudV51gRXHTziZgpV2pTRo72bQPrSjkDVhpA33wF2nQh6kjvxge1N2aG5bRde5teo/ccNf1kVZPdH5KV7DS6/J8s/iu6ypWrSBdB3VJ9PhsFto+jNVt/f7TAvnb6dbvihsbOF3y5Q/avHZ/pw+33NBMx94dEnTD5Q3bVK1ZVZ7v2zFoGzqhD8n0imp1p9DhFed+WuF0TahDpz4dE7TRoJU+mA9XBUC74tMKTjpcffNV8lDzB824/jhx/IQ8c08L0fW1m5tB7w0ILHNH/J67bscbsNJp7bqv8eP3yDWNrtZJM6jjrCnfSKWzKybaZUm0AauRX7wj450H+Nqdjz7I00os+ruu4Y/B7w2U4f1HhK1gpV0wtn24nalEl8vpYq1Dr3ZBlaK++GCKTBz5oTn2mnVrSrtX4wOZ/52OdH/uVVn+x3IzqYHN+ldf7C6Szyd8IR+++1FgOlzASret+9Ch/lX15dGWD0vWbFkD60z9aJpMeOcDM33FDQ2c5Y8EloUbieZ+D10/pQJWE0c45zwx/pxDjyGxab9dDIUGfZLz2aDHl5r3zG6n60q9xvq7ppXV2nZrLdpFrTtoyKVHm56y6d9NolUreg1/NcnuLZN7zzS5sakJjJetWFZeGfqSewgJXnFPQGLex7XCng7terR1uo0NDkqEruE1DF0WOh3uM1+rF55b71wnaFQzwQP2PxcuEw0ZaVfKGozt3C8+lKTbdYM+2kWpWxnwnb7DA++lblUa73eWx9s0jdjNnnusk8d9Kp+MnWQmwwWy3HbhXqO5X933Zw2CDRjX3wRnvdv0dgetgWh9/4802N7v7vr6Wbd963YzqZ+FGtp1Bw3CaOhMB+0C2A3nu8tj4e7n3P0cu3sOqfUa62N3g3WhvxuRzi+5v2vu9sIFrPS75eCXh5igl4bA2zjv+/pHIqGDn+8TsXbTY9PwTpObmprDjPSdOPQcUmPa77n7+e7v9zPdG7DSf7foHxSc5fxxiA7aXaX+u0D/LafDi4O6moCtmfjvh59j9/tdyHscp+M4ASvLq0rAyhKKZggggAACCCCAAAIIIIAAAggggIBPgfQasMqWLZvkz59fsmfPLnv37pUdO3YEqgYoSZ06deT888/3qXNydQJW8QGrm+65Ufo831cWO10FXnDpBaaKjz5sG/z+QOc/Ph8JG7DSByTa7ZcOiT0sdLer7byBGJuA1awvvpFRQ0brqtLg+gbSpNUjZlx/eB9WBmYmMjJq6sjTImClIZ/+XeLDP/owWP9C3+8QzcPYpPalVTp6tHnVNLvj4dul0X23BK3y7YzvTJBHZ97z2N1y4903BC1PaqLVA21k+5Zt5j3izUlviN6ntsPxY8fl6buay36na8KzSp4lvUfEh610/dUr1kjXZ140m2pw3eXSpPWjtpsNtEvq3LVhaMDK7ZYzsJEoRpITsFrhVHzSLj+zZc/mVCCrKgt+XiDX3HK1PPjMA+INBXi7CNSQo4YddYhUWUwrTG1Yt8G08XZb+c+qtfLCU/EhwIrVKkrXgfHhQNPQ+aGVCts+0t5UL9N5oQGrXTt2iz681e4LizuVYbTCXuYsmd3VzateV22jVb+0OsE7n76VaLdvfu73lApYffXp16Y7zqATtZh41uniJpquxEI36Q2paHXD5Hw26DZT855xQ4R6HKGBPp2ng20FxvjWyatgdWCf0x3o7fHdgdY4t4a0d7rojDTgnlDGTwWrhFsLnuPe28FzE5/q1aGPqZan7zEjPn9HNLitgxv0qVy9srzgdKmsw4xJX5ouVXX8pcFdpXyV8ua9runNT5ig6n1PNJbr7mioiyMOKRWw8nb/F87l0/FORdMxn5jj1K6gNZwbbojmfg+3frTzYuEeq3OP9thPt/bRBqySOv9Iv2vueqEBqyVOKF+7pzx69KipZNm2R5sEIRld91R8n3CPyc9r01uaOf/WOiRaQannOz38bCrNruvnu7/fz3RvwCrcHwR973SN/bYTitUh3Hucn2P3810ozV7MGB4YAStLTAJWllA0QwABBBBAAAEEEEAAAQQQQAABBHwKpLeA1dq1a+XYsWNSunTpoMDE7t275bvvvpP16+O7SdEwxR13OF0gOd0GxmIgYHUyYOX9D8hqW7322dKhd3vR6gXhKlh5H3A88/zTcuHlF4S9JH1f6C8Lf15olulDj3POq2nGbQJWP34zV4b1etO01y52WnRpHtiHN2ClXYY1uP7ywLLQEb1vrr/zuqCqFLGqYLV/73759X+/BXapvQ5dfNXFQfdxYGEMRrwPBLWaT7P2j/veanIDJ/oX7Rpq2bZluxxx/gpch91OxQm36ohWNdOHGd7hj/l/SO+Or5lZ+jCpjdMlUVzRQt4miY67D1W10X3NGptKVdGErPQh3Lxv47sBHDR+QKA7tSkTp8oHIyaafSfVNZ82Ss6563regJVW1xnsVMrSsFNyhuQErDTQ1OahdrJ189bALl/o97xUrlE5YsDK+7ved1Qf0d+30OGz9z+Xj0Z9bGa3drrQqlOvjhmfN+cnGfrqG2bcreQSuu5H734sn0343MwODVhp6FNDmjrUcbr9uu72k5XytDtFd9BA0q//+9VMhuve0W2nr8m933XdlApY6b5SYwgX9In2s0GPOzXvmVdadZcVS1cYPq18lDlLpgCle89odZyBLw0y822Cqsm5Z7Y774vaJacO2m1Wyxcjd1mHu2EK+uEnYKWfLQ1uiPydQLu3LVOxTND+vBP79uwzn2taOcX02+osnPrxdBNC13ZvfTLMqY6Ww6zifibV1mqfTtUcHby/M97Aqds12q33N5LbH7rNtI30I6UCVlrBRSuYHggTPNZj0wDs5g2bnQpSuWWIE7oPDbi6xx/N/e6u4+c1Fu6xOnc/53E6rOsnYBXN75pr5f18efTZR2T00LGmAq/eo52cf7eEdu/srncqvk+42/bz6v57SLuKHjiun59Npdl1/Xz39/uZ7g1Y9Rv9WoKKle4fHiheuPCrn2P33qvRfn9OsxczhgdGwMoSk4CVJRTNEEAAAQQQQAABBBBAAAEEEEAAAZ8C6S1gldjpHnEql3z00UeyZ098VyEXXHCB1K5dO7FVrJcRsDoZsNq3d5+0uKel+QtwBdRutrS7rUgBqzGvj5WvP59prB9r9ahcHiHg1Nup+vDH70tMuy79Owe6anIfKGgXTSOcrsvCDRqaGPvGOLPIrbLjtvMGrDS0peGtaIZYBayi2Wcs2i7+7Q/p0yk+oFTr/FpOQCn+ga6fbUcbHlj55yrTtdv8ub+bLpMi7Tu0Gz5td/DAIenYtFOgWyANR2n3WZVrVpYKTpWPGudWT9AVkHf73sogOj9v/rymG5hKTnWkyjUqSbnK5YKCdN51ddwbUHuibVO55JpLTJM+nZwKbr8tNl32vfHh65I9R/jQk59z1x15A1bhKjqZg7H8kZyAlW56wvCJMvXDqWYv2h3iwPH9jVmkClZut4wZMmSUd6c6XW2GqRrmDR55g1Te4Frrl1s5IamE791zpn8rIwaMNMcTGrDyvgeYBhY/Egt86urR3u/eXXrPs02355wuVM/xLk734+GCPtF+NihCat4zWqVOu/q0HbSroN7DeybaPDn3jL7XPXFrM7PdarWqmW6JIu0E94QyfgJWSQXaEu4t/rNp1pRZMuOTL01wOFwbd552BZgnXx4z6QZ9tMvVFi88Y+Z53ycGOt3uFSxc0Mxvdf9z5rPvujuucx7k3+tuLuxrSgWsdOejne9zM//7PtdlwAume1qdv9ypSNn9v4qU2u2uvrdHGqK53yNtI5r5sXKPxblHc9ynY9toA1Z6ryTnd82184ZW3Hn6Glc0Tnq82U20u85ww6n4PhFuP9HOa3LT46ZKZ6lypczxR7t+emjv57u/3890N2ClXcmPnDI8QYXTDWs3SAfn3yU63PGQU3n3/uDKu36O3c93ofRwXf0eIwErS0ECVpZQNEMAAQQQQAABBBBAAAEEEEAAAQR8CpxOASulWLBggcybN8+oVK5cWRo0aGDG/f4gYHUyYKWW/bsOlN/n/e50pZdJXp8wSHLnzR0xYOXtNqbx4/eaClHhrkfX5i/J6r9Wm0Wvvds70H2VTcDKW9kmdB9nasDKe94aTHpl6Evh2KOaF014QB96v9X7bTnh/M8dcuXOZe4V59mFU0XgeKC7tytuuMIJ6j3sNgu8apjg/Xc+MN0tBWb+N6L33mXXXuo8yL1PsmbLGrrYdBn6+YQvZPrHM0SDH6FD4WKFTTdz1WpVDV1kpvc4FbaaO0HCEyeOm0pjT7Z/wnmwdVSevP1pOXz4sBPwityVVyzO3RuwqtfgInmq05Nhj9NmZnIDVvr7qL+XOnirjEUKWLmhAA2zvf7BYLNe6I8lvy+VXh16m9k33HWD3Nv0bjM+bth4+XLyV2a8c99OJgwXuq4G9Qa8ONDMDg1YjX/zPdPdli7Mky+v5MiRPXT1BNP3PdlYzv2vglaChc6MaO730PW9wYkzJWClBtF8Nmj71LpnvN2UaRAwrkicHk6iQ1yxOOnoVD1JbEjuPaNdwun7SunypU33lpH2ES5gpW3PZPeUDFjpA/RX2/Z0uopdHbhEmZzPonwF80uWrPFdku7avssJCB80y4c434/yFchnxt2gj3adp91L6fDTtz/L6z2GmnHtajm/sx0dnnuorfOdamvQ+65ZEOZHSgasvPeft4vckQPeldnT55ij6zGsm5QqXyrMkZ6cZXu/n1wj+WOxco/VuSf/TNL/mtEErPz8rrlSkQJWujyx71Wn4vuEe0zJfdUqak1vecKs7lYOTu620vp6yfnuH4vPdDdglTVrVhn++dsJmLwBq9sfvE1ufaBRgjbJOXbdiJ/vQgkO4jScQcDK8qISsLKEohkCCCCAAAIIIIAAAggggAACCCDgU+B0C1itXr1avvzyS6NSrFgxueWW4L8uTS4XAavggJU++Nu8YYsJtlSsVsGwRqpg9c3U2fLuoFGmTaP7bpE7Hr497GVwu5fRhW9PfitQGcgmYDW83wj59svvzHa1MoRWiHAHb9DoTKpgdeTwEdGHmBpwypc/nwz5YJBLkuxX2/CAPlzWcNLhQ4dMFaO7Hr3TdM2YK0+uwL693QRFCli5jZc51THmzZ4ny//4S/5Z9Y8727xe6lSWetypMBVp0IcuWvlo2R/LZcn8JaYylNs2c+bM0vPtHlK0RFF3VtDryy27yco/V5qH3vrw2xsO0i4NNXQUOsTq3L0Bq6R8Qo8hdDq5ASvdztIFS03PVyXLljBVwHRepIBVx6bPy79r/5Xs2bPL25/Gd9mp7b2DBjM1DKKD9wHVx6M/kU/f+8zM79Sng2gln9DB241gaMBq0tjJMmncZLNK6HtA6HZsp23v93DbS6mAlQY0fv3h13CHkOi8ex6/WwrGxVfLSbRhhIXesMHdTe6Sm+650bSM5rNBV0ite0YDnk1uftx0F1WiTAnzPhDhVKOandx7pl2TDrJp/SbREOobH70esboe7gkvR0oGrD4eM0k+Hf+pOYiKVSvIPY/fI5WrVwq6XsP7j3S6/vvWtDndAlZ6Um4YPrsTYnWDtC3ubWk+W7Uy5MtDXjTnntgP2/s9sW3YLotVwEr3F4tztz3u07FdNAErP79rrp03YKVVn7T71dc69zPvtdom9HuEu96p+D7hbju5r+vX/CudnnjerK4VVbWy6uk+RPPdPxaf6bEIWLnXJJpj13X8fBdy93k6vxKwsry6BKwsoWiGAAIIIIAAAggggAACCCCAAAII+BQ43QJWy5Ytkzlz4v+KvmTJknLDDTf4FIpfnYBVcMAqHGqkgNX8H+fLgJfiwz2RAk779uyTp+9qYaoFZc2WTYZ/9lZgF0kFrDRIpH/5u2d3fNeQrwx92elKrkxg/TM1YKUA7l9E63jnfs9LlRqVdTTZgxseSKoi1rxvf5KhPd4w+7ntwVvltgduTbDPPxcuk1fbxXe3FU2AaP2a9TLri2/kq8++NtvUrjze/OQNyZErR4J9hM7QCgC/OGEUrfixcf1GszixLpi81dd6vv2q/OhU5fpswudmPW+VNe9+YnXuaSVg5T03dzxSwKqv8+By4S+LTDNvSNJdT19nT5sjIwe+a2Y90e5xueTq+mbcO1+ru2iVl9DB2+1j6IPRH7+ZK8N6xYe6HnrmQbn6lqtCV4962vZ+D7fhlApYTRzxoXwxcUq4Q0h0XnenykzpJKrMJLaBSEGfcOtE+mzQtql5z7Rv0tG8D+TJm0eGfjgk3KFHPS+594z3d6rPiF5SrGSxsPvGPSGLN2AVqXtR71pew2i7CHQfgmsQrt/o1yRn7pzeTZvxnu16ydKFf5rx9BKwSuoz3XuS30xxQvODR5lZzdo94VQyzRh47330WafL6BsbeJuHHbe938OuHOXMWAasYnHuUR7+adU8moCVn981F80bsBo0foAUiCsgq5b9Ld1ad5djx45JtuzZnOquL8tZIe+3p+L7hHtMyX39dsZ3Mrz/CLN6E6e79QYRultP7vbT+no23/39fqbHMmDl9bQ5dj/fhbz7Ol3HCVhZXlkCVpZQNEMAAQQQQAABBBBAAAEEEEAAAQR8CpxuAauZM2fKypUrjUrt2rXlggsu8CkUvzoBq+QHrLR7thb3tnK6VzviVLzK5oRhhopWDvIOP3/3swzpHt9NTt36daVl1xaBxUkFrLwPHSo4FSVeHNQlsK6OpJWA1dq/10nP9r0Cx6bBoP5j+5oHPIGZMR7xBoSSqvRks+tXWnWXFUtXmOv31qRhTpdIWcKupt3yvff2+2bZc91aS+0LaiVo561YFE3Ayt1Qrw59Al0HvvL6S1K2Ull3UZKvy52KWN3bvGra1b6wtjz3Squw66x2uijs+kx8NY4Hnrpf9EH+qmWrpFiJYtJn5Mlr6V05VueeHgNW7w4aLd9M/cZwRAo6DOv5pvw4e65p8/xrnaTqOVXM+KJfFjuVJfqa8atuvkoebv6gGff+GPTKkEC1ptCAlbc7w/MvOU9adGnuXTVZ47b3e7iNp1TAaubns2TOf9Vywh1HpHnNOz8tRc4qEmlxkvO9IRVvBatwKyYWsErNe8btVk+Puceb3aVUuZLhDj+qecm9Z7778nt5p99wsy8NrdS/+uKw+8U9IcviXxdLn+fj3zsa3d9I7njotoSNPHO8htEGrJ649UnT/V+kSk36faf1A23STReByblf9bPp2cat5NDBQ6bSoHaRuPi3xeb73ZAJAyVHzqTDzrb3u+eyJXs0lgGrWJx7sk/EWfGLD6bI1857vjucXbtauqpkFE3Ays/vmuvjDVi99cmwQBBfHSeO/NA0025ZXxrcVTJnOfnvklPxfcI9puS+aiVgrQisQ2IhXNPA8yO93zOeUzGjiX339/uZfqoCVu45JHbsfr4Luds/nV8JWFleXQJWllA0QwABBBBAAAEEEEAAAQQQQAABBHwKpKeA1a5du0T/X6pUqaDuWFyCv//+W7766it3Uq699lopW7ZsYNrPCAGr5Aes1H3oq8Nk3px55hKEdq127Ogx0f/ovGzxMrNcwy4aenGHxAJWc5xqOOPfej/wMLNDr/ZSvc7Z7qrmNa0ErP5e/re82OLloGPTkJLNw8iglaKY0O7xWj3YRg7s2y/6EPTJDs3kwssThg5379wtC35aKJdee0miW3+z91smZKSNInXjpsu8f/1/xQ0N5NGWj+jswLBj207p8FjHwHULF7Dau3uv6YIya7asgfW8I94KGNrNn3bz5R20C8KChcN3geYNfGh3ktqlXLjhxIkT0vK+1rJz+06n+lcV+WvJX3L8+HG57vaGcl+zxuFWicm564bTY8Dq5+9/EX1ApUONc2tIu1fbBL1X79i6Q9o80t6ELXPlyS0DnIChdjOlw8EDh5xgwnOiAQWtDtN/TN/Aw1Bdvm3zNtFuRLXqhA6hASu9Ll2e6iprV69zoosZpKsTtKxQtbxpG/pDK5ltWLtByngq3YW20Wnb+z3cuikVsAq375SY5w2p+AlYpeY94602pyHQ1s5nT4YMGcLyrXPuq0JFCiX5fp3ce0bfE/X+1/u43hX15KmOzcIeB+4JWTb9u0m0cpgOVWpWkc59OyVs5JnjNYw2YOVWSMmQIaMMGNc3QTeb7709QaZ/PD2wt7RewSq596u3W2b3ZLUaoVYltBls73ebbSXVJpYBK92X33NP6ngTWz5h+ESZ+uHUQJMadWtI+1fbBqbT+kg0ASs/v2uuQ6SA1YnjJ6RXxz6mC2Rte02ja+TBp+93VzPvw7H+PhHYeDJG9Ltom4faydbNW6VIsSLSd3Qf662kt3vGz3d/v5/pfgNWfo7dz3ch65shHTckYGV58QhYWULRDAEEEEAAAQQQQAABBBBAAAEEEPApkBoBq927d8uiRfHdSOnha2hq3bp15kxy584tZcqc7N6tePHiUq5cObNs6dKl8t1334m2qVChguTLl09y5swp+/fvN+uvWrXKtNMfut5NN90UmPY7QsDKX8Bq8W9/SJ9Or5nLkDFjRrnnsbudEFUt2bNrj2i3X/oflnXIXzC/DBzfX7SNO7gBK32gqVVtdu3cJZvWb5K1q9aaMIXb7tyL6kirl1u6k4FXb8CqaPGiooGfxIaSThUT7crQHb7+bKaMGTrWTOr8s2sHB7jcdu5r6QqlnXBJdXcy8JoaASvd+aRxk2XS2MnmONTwxruvl5rOQ7liJc+SzRs2y7JFy2TKh9OkpBNQ6jKgc+B4w414HwBoMOyqm66Us51Am2YSNBTjVpHS8EqHpvEPuTVAo9f7vEvqmnDCn07XSSMGvCs7tu0I7CJcwEorQU0e/6lTyaW+Ezi4yAlQFZfs2bPLmlX/yPy5851u/j4zXUoWLFzIBHVCgxFa/aBUuVLO9b7cPHSPKxJnwjtLFyyVKROnyarl8e8Xj7VuIpdfd1ngWEJHRgwYKXOmfxs0u0Ovdk6QL+E11kaxOHfdTnoMWOlDwBee7BL4vbz4qotNNZmCcQVN5a+3+rwjmzdu1tOTOx+5Q25pfLMZd398NOpj+ez9z81k+crlpWmbJk7XPWeJVhLT7v/0fnWH0ICVzvd2O6nV8rSSjQboChUpKPudkOGGfzbIT857zXdOVzvlq5Q3ATB3e+Febe/3cOsSsDqp4g00hnbJmdr3TM/2vQMP2TVEefdjd5muE7NkySJbNm6RlU7FOu0WTAPAXQd2kYrVKpw8sTBjfu6ZQS87Fdr+96upBPT6B4Od8GG2BHvwhoP8BNtOJ3cNS7zwtBOu/Hut8apUvZJcc8vVkidfbjOtn0v6+eQOXsNoA1bewEYd5zuHhm2r1Kws27fskM8nfBGo4OfuK9YBK71uGlT1DtM+mi4zJn9pZnXq01GKFj9ZmU6rTObJl8fbPGg8uffrX0tWmG7WvBvzViT0zo80bnO/R1o3mvmxDljF4tyjOX5v2/QWlvEeu45HE7Dy87vm7te7DW8FK12+fet26fxkV9m3Z69p3vqlllKnXh131Zh/nwhsOBkj+j29R9ueZk39Pn3j3TdYbyW93TN+v/v7+Uz3G7Dyc+x+P5Otb4h02pCAleWFI2BlCUUzBBBAAAEEEEAAAQQQQAABBBBAwKdAagSs1q5dK9OmTbM68qpVq8pll8WHH9yAVVIragDr5ptvljx5Ij9USmobocsJWPkLWKmnhnw07BNpyJ03t7Tt/pwJPnjbuAEr7zzvuAYp7nzkdml467WSIWPC6iPegJV3vUjjF19Zz1R6cpd7A1buvMReNayjoZ3QIbUCVvof7T9773P5ZMwkOeH8L9JQ6exKSQasdF3vwwvvtkK7TBo5cJTMnjY70ESrCmkS68SJ42aedgX56w+/mvFIASu3m0F3I5md0IN2NekOus3mLzxtQjTuPPfV7V7GndZuKbVamtdAQzbPv9bRVMpy24W+/vL9rzK425DAbA2MvfHR6wm6uQw0cEb8nrtuKz0GrPS4l/y+VAa+NChQnUznhQ5lKpSRzv06BapXucv1r//1IeL6NevdWUGv+rt++NAhMy9cwEoXTB73qQnmaSWgxIaadWsmGbDS9W3v99B9EbA6KZJYwEpbpeY9o2HdgS8NlvX/nLzn9H3F+z7hnolNwErbJvee8XZ1p5WAtCJQ6OANB/kJWOl2Tyd3DXH37zJAjh49Gkomod3Ueg2jDVjp/dLZqZTnvg/pzjI61SGP/1dZT4NcGtLWQIQOsQ5YaeivzcPtzLZtflSsVtEJBr6QaNPk3q+dHu8c+L0pXKyw9B3VJ2IFuHAHYHO/h1sv2nmxDljp/v2ee7Tn4LYfO3ScfPXZ1+6k1Dq/lrTp3jowndZHoglY+fldcx0SC1hpG+/3O/03SI9h3aRAXAF39Zh/nwhsOMoR9zulfo8d9N6AREOToZtOb/eMt5tt91yi+e7v5zM9FgErP/9u8fOZ7Fqdrq8ErCyvLAErSyiaIYAAAggggAACCCCAAAIIIIAAAj4FUiNgtX79epkyZYrVkVevXl3q149/yLhhwwaZO3eubNmyJey62gVajRo1pG7duomGH8KunMTMMzVgpVWiOj/Vxejc2/QeueGu6yNK6V+Dt7r/ObP8hrtukHub3p2g7czPZ5kKNd7qRVqtSqs+Pdn+CSleuniCdZ5t3Mp00eYuyJY9mxQrUUzOKuX836luc/FV9ZyKDUXdxQleteqNdi1mO4R2czPzi1kyesgY29VNhazQLvF0Ze9DXZ3Wh/hvTR6WIGSiy07F8PtPC+Sjdz+WdU54xX0Y7O6nVNmScr1zbcM90HfbuK8aUpr83mcy/8f58u8//wYeaFeoWkFedLplcwfthu3j0Z84lTW+CtqfVr7S/dz56B3y5G1PmyDDVTdfZaqTuevq67LFy83DreXO6xFPqMptoxWO7nj4dql5Xg13VtDr2DfGy4J5CwIVk7wL9R7S6ls3N74pqLKJt407fvDAQXn6zuaB89RgWMuuLdzFYV/9nrtuVLvMa+ZU4dKgRzifsDuOMPOhho+YJfWvqi/N2ofvwmnQK071HCfwpiGm4Z+9FWFL8bPdql56D7/96Zuint5BH3AN6/VWoEqYu0wfDja4/nLTvWLmLJnd2UGv2q2lVqvSLivdkItWXtPqcdc0ukr6vtDftI8UQNGFq5yqQ+8OGi3/rPwnsA13J1oF7Zzzz5HLrrs0qFKduzz01fZ+D13vdA9YxfqzITXvmaNHjpoAqoZpDx48GHoppaTz/nie83vf8PZrk3y/0JWTe89oGLbrMy/KGue+1RDiK0NfShBYwT3B5QnMWP3XatEqeOq3a8euwPy2PdoE/a57Dc+/5Dxp0aV5oK3NiH4mvTt4dFAQVN8Lzyp1ljzpdO0495u5MvWj+D8iGDpxSCAMoeFRDV55Q9zegMeQCYMlX4G85hD0O4t+d7n+juuk8RP3Bg5Lu0rVkIrtULl6ZXmh//OJNk/u/fqxE9r+1KkyqcPtD94mtz7QKNH9hC60ud9D10nOdCzcQ/fr99xDt2c73bX5S6L3uTs81fFJU+HTnU7rrxoO1JBgtXOqSicn3J7UkNzfNXe7bheYiX3fHulUVJ09fY5ZJVxgLZbfJ9zjiuZVu/B+zukeUEOd4f4YIaltpbd7xu93f/VI7mf60B5viHYzqN8T9btt6KDfU9o1ie+ONlwV1lgcu5/vQqHHezpNE7CyvJoErCyhaIYAAggggAACCCCAAAIIIIAAAgj4FEiNgJXPQzYPQLVbwQMHDpjgRTYnEJA/f35TsSq0mzC/+3LXP1MDVu75x/p1+5btohWdcjrVHrTyUbiukGK9T7YXL3Dk8BHTldKmfzc7D3PzSeFicc7/C58yHq1KpBWJdm7f5QQVSkjxUsXDVhqLdAB6vNrt3o5tO003b9qNpB5zXNG4SKsEzdd7bcumrSakpwGfws56RZxAXkrcc37PPehEfEy4ASvdhAaodDiv/rlBldrMzBj/2L93v7nXdjphh+JOGFIDlJkyZ7LaiwattHs2rVimXX55u/iy2oDT6NDBQ869968JKuTMnVMKFS5owpiRwl22243U7oev/2eCF7pcj9utotWm23NS64JzIq3GfI9Aat4zGvjYtnm7eb/SoJV2banvNfqek1KDVrDo1aG32Z0GOTXQmRLDme4erbH+bm9Yu1E2rttg3lPLVy2XrPeoaPebltq7wSUNwA4Y21cKOu+v0Q6pdb9He5yh7WNx7qHbTGpaf0efcsLebhXQEk63zq++2T2q71NJ7SMtLk8rv2sp/X3CvRbvvz1Bpn083fwxhlaJy5s/PoTpLk/sNT3fM36/+6tLan2mx+LY/XwmJ3ZPpNdlBKwsrxwBK0somiGAAAIIIIAAAggggAACCCCAAAI+BdJjwMrnKSdrdQJWyWJjJQQQSAMC3oCVezg2lbjctrzaCcyZNkdGDHw3QWMCVglImJGIwIAXB8n8ufOldPnS0n3YK4m0ZBECqSPgrcip1QW1Slhyh/R2v8fy3KMx+82pGqrd77rDs11ayHmXpEwA090nrykrsGfXHmn9QBs5fPiwJNUlbLgj454Jp8K89ChAwMryqhGwsoSiGQIIIIAAAggggAACCCCAAAIIIOBTgICVHSABKzsnWiGAQNoT0C5H3GpK7tFpRSetYMYQOwHtTnLH1h0JNqjV1rJkzZJgPjMQCCeg1dt2bt8pGTNlTLT723DrMg+BUy2gASMNkrpd1T3XrbXUvqBWsnebnu73WJ97NGjjh73ndLn8pVmlbMWy8vLrLyboQjSa7dE27QscP3ZcNv27yRxokbOKWFcAdc+Me8aV4DW9CxCwsryCBKwsoWiGAAIIIIAAAggggAACCCCAAAII+BQgYGUHSMDKzolWCCCAAAIIIIAAAqeXwKBXhjhdO6+W7Vu2BU6s2jlVpWOfDqd90CctnHvnJ7uYLncVn6qIgVuQkUQEuGcSwWFRuhIgYGV5uQhYWULRDAEEEEAAAQQQQAABBBBAAAEEEPApQMDKDpCAlZ0TrRBAAAEEEEAAAQROL4EXnuoq/6z6J3BSVWpWMUGf7DmyBeadriNp4dy1Yth+p7pdxowZpeo5VU5Xas4rhgLcMzHEZFOpKkDAypKfgJUlFM0QQAABBBBAAAEEEEAAAQQQQAABnwIErOwACVjZOdEKAQQQQAABBBBA4PQS+OX7X2X3zt2SPWd2qVitohQ5q/DpdYKJnM2ZfO6JsLAIAQQQSBEBAlaWzASsLKFohgACCCCAAAIIIIAAAggggAACCPgUIGBlB0jAys6JVggggAACCCCAAAIIIIAAAggggIBfAQJWloIErCyhaIYAAggggAACCCCAAAIIIIAAAgj4FCBgZQdIwMrOiVYIIIAAAggggAACCCCAAAIIIICAXwECVpaCBKwsoWiGAAIIIIAAAggggAACCCCAAAII+BQgYGUHSMDKzolWCCCAAAIIIIAAAggggAACCCCAgF8BAlaWggSsLKFohgACCCCAAAIIIIAAAggggAACCPgUIGBlB0jAys6JVggggAACCCCAAAIIIIAAAggggIBfAQJWloIErCyhaIYAAggggAACCCCAAAIIIIAAAgj4FCBgZQeYlgJW/6xaK9u3bJN8BfJJucrl7E6AVggggAACCCCAAAIIIIAAAggggEA6ESBgZXmhCFhZQtEMAQQQQAABBBBAAAEEEEAAAQQQ8ClAwMoOMC0FrD57/wv5aNRHkj17dunzbi/JXzC/3UnQCgEEEEAAAQQQQAABBBBAAAEEEEgHAgSsLC8SAStLKJohgAACCCCAAAIIIIAAAggggAACPgUIWNkBxiJgtXzxcunXdaDZYVyRQtLjzW6BnU/9cJp8+v7nZvqiyy+QR1s+ElgWOnLwwCFp81Bb2bN7j1zW8DJp+lyT0CZMI4AAAggggAACCCCAAAIIIIAAAulWgICV5aUjYGUJRTMEEEAAAQQQQAABBBBAAAEEEEDApwABKzvAWASs/pi/RHp37GN2WMgJWA0Y2y+w80njJsuksZPNdL0r6slTHZsFloUbmTJxqnwwYqJkyJBRur/xspQqXypcM+YhgAACCCCAAAIIIIAAAggggAAC6U6AgJXlJSNgZQlFMwQQQAABBBBAAAEEEEAAAQQQQMCnAAErO8C0FrDat3eftLinpRw9epQqVnaXkFYIIIAAAggggAACCCCAAAIIIJBOBAhYWV4oAlaWUDRDAAEEEEAAAQQQQAABBBBAAAEEfAoQsLIDTGsBKz3qQa8MkV9/+FWyZssmQ94fKDly5bA7GVohgAACCCCAAAIIIIAAAggggAACaViAgJXlxSFgZQlFMwQQQAABBBBAAAEEEEAAAQQQQMCnAAErO8C0GLD65ftfZXC3IeYEHm7+kFx185V2J0MrBBBAAAEEEEAAAQQQQAABBBBAIA0LELCyvDgErCyhaIYAAggggAACCCCAAAIIIIAAAgj4FCBgZQcYGrBa9MtiWbZomUgGkVsa3yxHjxyVWVO+kT/Ia2fIAABAAElEQVR++0O2b90hefLlkbKVysi1t14rRc4qbHbyx/wl0rtjHzNeqEghGTC2X2Dnk8ZNlkljJ5vpelfUk6c6NgssizSyf+9+eeqOZ+SE87+6F9eVli+2iNSU+QgggAACCCCAAAIIIIAAAggggEC6ESBgZXmpCFhZQtEMAQQQQAABBBBAAAEEEEAAAQQQ8ClAwMoOMDRg9d5b78v0T2aYlfuNfk0Gv/K6rFm5JsHGSpcvLd2HvWLmxzpgpRvt8Fgn2bBug+TKk1ve+HCIZMjgJL4YEEAAAQQQQAABBBBAAAEEEEAAgXQsQMDK8uIRsLKEohkCCCCAAAIIIIAAAggggAACCCDgU4CAlR1gYgGrc84/Rxb+vFCyZMkipcqVkriicbJ181ZZ/ddqKVm25CkNWL3V5x35YeYP5iR6j+glZ5UsZndCtEIAAQQQQAABBBBAAAEEEEAAAQTSqAABK8sLQ8DKEopmCCCAAAIIIIAAAggggAACCCCAgE8BAlZ2gIkFrHQLlc6u5HTr96QTrioU2OD6Nf/KLz/8Io3uu8XMO3b0mNN94HYzntkJYxUolD/Q9uCBg7Jn1x4znTNXTqciVa7AssRGJo78UL74YIpp0qlPR6lWq2pizVmGAAIIIIAAAggggAACCCCAAAIIpHkBAlaWl4iAlSUUzRBAAAEEEEAAAQQQQAABBBBAAAGfAgSs7AATC1jlzptb+o/pK9lzZLfbWAxbTf1wmkwY/oHZYvPOz8gFl50fw62zKQQQQAABBBBAAAEEEEAAAQQQQCDlBQhYWZoTsLKEohkCCCCAAAIIIIAAAggggAACCCDgU4CAlR1gYgGrOx+5Q25pfLPdhmLcauYXs2T0kDFmq02fe0wua3hpjPfA5hBAAAEEEEAAAQQQQAABBBBAAIGUFSBgZelNwMoSimYIIIAAAggggAACCCCAAAIIIICATwECVnaAiQWs2r/aVmrUrWG3oRi30u4BtZtAHVp2bSF169eN8R7YHAIIIIAAAggggAACCCCAAAIIIJCyAgSsLL0JWFlC0QwBBBBAAAEEEEAAAQQQQAABBBDwKUDAyg4wsYBVnxG9pFjJYnYbinGrCcMnytQPp5qtvtDvealco3KM98DmEEAAAQQQQAABBBBAAAEEEEAAgZQVIGBl6U3AyhKKZggggAACCCCAAAIIIIAAAggggIBPAQJWdoCJBayGTBgk+Qrks9tQjFu93v0N+em7n8xW+4/pK3FF42K8BzaHAAIIIIAAAggggAACCCCAAAIIpKwAAStLbwJWllA0QwABBBBAAAEEEEAAAQQQQAABBHwKELCyA0yrAavWD7aRbZu3SYFCBWTQewPsToZWCCCAAAIIIIAAAggggAACCCCAQBoWIGBleXEIWFlC0QwBBBBAAAEEEEAAAQQQQAABBBDwKUDAyg4wLQasdm7fKc82bmVOoF6Di+SpTk/anQytEEAAAQQQQAABBBBAAAEEEEAAgTQsQMDK8uIQsLKEohkCCCCAAAIIIIAAAggggAACCCDgU4CAlR1gWgxYfTvjOxnef4Q5gWbtnpD6V19sdzK0QgABBBBAAAEEEEAAAQQQQAABBNKwAAEry4tDwMoSimYIIIAAAggggAACCCCAAAIIIICATwECVnaAaTFg1bN9b1m6YKnkypNbBjvdA2bJmsXuZGiFAAIIIIAAAggggAACCCCAAAIIpGEBAlaWF4eAlSUUzRBAAAEEEEAAAQQQQAABBBBAAAGfAgSs7ADTWsBq+9bt0vqBtnLixHG57vaGcl+zxnYnQisEEEAAAQQQQAABBBBAAAEEEEAgjQsQsLK8QASsLKFohgACCCCAAAIIIIAAAggggAACCPgUIGBlBxgasJrwzgcy9aNpZuWhE4dInnx57DYUo1bvvz1Bpn08XTJnySJ9RvSUuKJxMdoym0EAAQQQQAABBBBAAAEEEEAAAQRSV4CAlaU/AStLKJohgAACCCCAAAIIIIAAAggggAACPgUIWNkBhgas7NY6Na127dgtbR5uJ4cPHZIb77pB7ml696nZEVtFAAEEEEAAAQQQQAABBBBAAAEEUkGAgJUlOgErSyiaIYAAAggggAACCCCAAAIIIIAAAj4FCFjZAaalgJVbPStPvrzSd1RvyZEzh91J0AoBBBBAAAEEEEAAAQQQQAABBBBIBwIErCwvEgErSyiaIYAAAggggAACCCCAAAIIIIAAAj4FCFjZAaalgNXO7TvlwL4Dkt0JVhUolN/uBGiFAAIIIIAAAggggAACCCCAAAIIpBMBAlaWF4qAlSUUzRBAAAEEEEAAAQQQQAABBBBAAAGfAgSs7ADTUsDK7ohphQACCCCAAAIIIIAAAggggAACCKRPAQJWlteNgJUlFM0QQAABBBBAAAEEEEAAAQQQQAABnwIErOwACVjZOdEKAQQQQAABBBBAAAEEEEAAAQQQ8CtAwMpSkICVJRTNEEAAAQQQQAABBBBAAAEEEEAAAZ8CBKzsAAlY2TnRCgEEEEAAAQQQQAABBBBAAAEEEPArQMDKUpCAlSUUzRBAAAEEEEAAAQQQQAABBBBAAAGfAgSs7AAJWNk50QoBBBBAAAEEEEAAAQQQQAABBBDwK0DAylKQgJUlFM0QQAABBBBAAAEEEEAAAQQQQAABnwIErOwACVjZOdEKAQQQQAABBBBAAAEEEEAAAQQQ8CtAwMpSkICVJRTNEEAAAQQQQAABBBBAAAEEEEAAAZ8CBKzsAAlY2TnRCgEEEEAAAQQQQAABBBBAAAEEEPArQMDKUpCAlSUUzRBAAAEEEEAAAQQQQAABBBBAAAGfAgSs7AAJWNk50QoBBBBAAAEEEEAAAQQQQAABBBDwK0DAylKQgJUlFM0QQAABBBBAAAEEEEAAAQQQQAABnwIErOwACVjZOdEKAQQQQAABBBBAAAEEEEAAAQQQ8CtAwMpSkICVJRTNEEAAAQQQQAABBBBAAAEEEEAAAZ8CBKzsAAlY2TnRCgEEEEAAAQQQQAABBBBAAAEEEPArQMDKUpCAlSUUzRBAAAEEEEAAAQQQQAABBBBAAAGfAmkhYLV582bZt2+fOZM8efJIXFyc1VkdPnxYNm3aJFu2bJFt27bJiRMnJGfOnFKkSBEpXbq0ZM+e3Wo7No0IWNko0QYBBBBAAAEEEEAAAQQQQAABBBDwL0DAytKQgJUlFM0QQAABBBBAAAEEEEAAAQQQQAABnwKpGbBau3atzJ8/XzZu3Bg4i/Lly8vVV18dmI40osGqL7/8Ug4cOBC2SdGiRaVRo0ZhlyVnJgGr5KixDgIIIIAAAggggAACCCCAAAIIIBC9AAErSzMCVpZQNEMAAQQQQAABBBBAAAEEEEAAAQR8CqRGwGrVqlUmWKVVp0IHm4DVypUrZfbs2XLs2LHA6hkzZhT9/9GjR828woULy2233RZY7neEgJVfQdZHAAEEEEAAAQQQQAABBBBAAAEE7AQIWNk5CQErSyiaIYAAAggggAACCCCAAAIIIIAAAj4FUjpgdfDgQRkzZkzEo04qYHXkyBEZP368aPeAOmh3gBdddJFooCpTpkyyf/9++euvv2TPnj1yySWXRNxPtAsIWEUrRnsEEEAAAQQQQAABBBBAAAEEEEAgeQIErCzdCFhZQtEMAQQQQAABBBBAAAEEEEAAAQQQ8CmQmgErDUXVqVNHMmTIIDNmzDBnklTAauHChTJ37lzTtkSJEtKwYUPJnDmzT4WkVydglbQRLRBAAAEEEEAAAQQQQAABBBBAAIFYCBCwslQkYGUJRTMEEEAAAQQQQAABBBBAAAEEEEDAp0BKB6y0AtXMmTOlevXqUqpUKXP0a9assQpYnThxQt5//33Zu3evWe+OO+6QQoUK+RSwW52AlZ0TrRBAAAEEEEAAAQQQQAABBBBAAAG/AgSsLAUJWFlC0QwBBBBAAAEEEEAAAQQQQAABBBDwKZDSAatwh2sbsNq9e7dMmDDBbKJo0aLSqFGjcJs7JfMIWJ0SVjaKAAIIIIAAAggggAACCCCAAAIIJBAgYJWAJPwMAlbhXZiLAAIIIIAAAggggAACCCCAAAIIxFogPQWsNmzYIJ9//rkhqF27tmh3gitWrJB169aZqlYFChSQggULSq1atSRv3rwxpSJgFVNONoYAAggggAACCCCAAAIIIIAAAghEFCBgFZEmeAEBq2APphBAAAEEEEAAAQQQQAABBBBAAIFTJZCeAlYappo1a5ahqFy5sqxatUqOHj2agCZz5sxyySWXiLaJ1UDAKlaSbAcBBBBAAAEEEEAAAQQQQAABBBBIXICAVeI+gaUErAIUjCCAAAIIIIAAAggggAACCCCAAAKnVCA9BawWLFgg8+bNC/LIkSOHlChRQjJkyCBa4Wrv3r1mecaMGeXuu++OWSUrAlZB7EwggAACCCCAAAIIIIAAAggggAACp0yAgJUlLQErSyiaIYAAAggggAACCCCAAAIIIIAAAj4F0lPA6ueff5b58+cHzjguLk6uv/560ZCVDocPH5bp06fLxo0bzXTFihXlyiuvNON+fxCw8ivI+ggggAACCCCAAAIIIIAAAggggICdAAErOychYGUJRTMEEEAAAQQQQAABBBBAAAEEEEDAp0B6ClgtWrRIfvzxx8AZN2rUSIoWLRqY1pHNmzfL5MmTzTwNXj344INBy5M7QcAquXKshwACCCCAAAIIIIAAAggggAACCEQnQMDK0ouAlSUUzRBAAAEEEEAAAQQQQAABBBBAAAGfAukpYLVixQqZNWuWOeNcuXLJ/fffH/bsR40aZapZ6cImTZpI5syZw7aLZiYBq2i0aIsAAggggAACCCCAAAIIIIAAAggkX4CAlaUdAStLKJohgAACCCCAAAIIIIAAAggggAACPgXSU8Bq3bp1MnXqVHPGRYoUkVtvvTXs2U+cOFF27txplt11111SoECBsO2imUnAKhot2iKAAAIIIIAAAggggAACCCCAAALJFyBgZWlHwMoSimYIIIAAAggggAACCCCAAAIIIICAT4H0FLDavn27fPTRR+aM8+fPL3fffXfYsx8/frzs27fPLHvggQckZ86cYdtFM5OAVTRatEUAAQQQQAABBBBAAAEEEEAAAQSSL0DAytKOgJUlFM0QQAABBBBAAAEEEEAAAQQQQAABnwLpKWB1/PhxGTdunBw8eFAyZcpkugjMnj17kIAuGzt2rJw4ccJ0DahdBMZiIGAVC0W2gQACCCCAAAIIIIAAAggggAACCCQtQMAqaSPTgoCVJRTNEEAAAQQQQAABBBBAAAEEEEAAAZ8C6Slgpaf6448/yqJFi8xZn3POOXLRRRcFCfzyyy/y22+/mXmJdSMYtJLFBAErCySaIIAAAggggAACCCCAAAIIIIAAAjEQIGBliUjAyhKKZggggAACCCCAAAIIIIAAAggggIBPgdQIWO3evTsQktLD37Vrl6xbt86cSe7cuaVMmTKBsypevLiUK1cuMO3tJlBnnn322VK5cmXJkCGDrFq1ShYsWBBo27Bhw6BtBRYkY4SAVTLQWAUBBBBAAAEEEEAAAQQQQAABBBBIhgABK0s0AlaWUDRDAAEEEEAAAQQQQAABBBBAAAEEfAqkRsBq7dq1Mm3aNKsjr1q1qlx22WVBbbWClVaySmwoW7asXHvttYk1iWoZAauouGiMAAIIIIAAAggggAACCCCAAAIIJFuAgJUlHQErSyiaIYAAAggggAACCCCAAAIIIIAAAj4FUiNgtX79epkyZYrVkVevXl3q16+foO3KlStl3rx5snfv3qBlmTNnlvPOO09q1qxpqloFLfQxQcDKBx6rIoAAAggggAACCCCAAAIIIIAAAlEIELCyxCJgZQlFMwQQQAABBBBAAAEEEEAAAQQQQMCnQGoErHwectDqBw4ckC1btsixY8ekQIECkjdvXsmYMWNQm1hMELCKhSLbQAABBBBAAAEEEEAAAQQQQAABBJIWIGCVtJFpQcDKEopmCCCAAAIIIIAAAggggAACCCCAgE+B9B6w8nn61qsTsLKmoiECCCCAAAIIIIAAAggggAACCCDgS4CAlSUfAStLKJohgAACCCCAAAIIIIAAAggggAACPgUIWNkBErCyc6IVAggggAACCCCAAAIIIIAAAggg4FeAgJWlIAErSyiaIYAAAggggAACCCCAAAIIIIAAAj4FCFjZARKwsnOiFQIIIIAAAggggAACCCCAAAIIIOBXgICVpSABK0somiGAAAIIIIAAAggggAACCCCAAAI+BQhY2QESsLJzohUCCCCAAAIIIIAAAggggAACCCDgV4CAlaUgAStLKJohgAACCCCAAAIIIIAAAggggAACPgUIWNkBErCyc6IVAggggAACCCCAAAIIIIAAAggg4FeAgJWlIAErSyiaIYAAAggggAACCCCAAAIIIIAAAj4FCFjZARKwsnOiFQIIIIAAAggggAACCCCAAAIIIOBXgICVpSABK0somiGAAAIIIIAAAggggAACCCCAAAI+BQhY2QESsLJzohUCCCCAAAIIIIAAAggggAACCCDgV4CAlaUgAStLKJohgAACCCCAAAIIIIAAAggggAACPgUIWNkBErCyc6IVAggggAACCCCAAAIIIIAAAggg4FeAgJWlIAErSyiaIYAAAggggAACCCCAAAIIIIAAAj4FCFjZARKwsnOiFQIIIIAAAggggAACCCCAAAIIIOBXgICVpSABK0somiGAAAIIIIAAAggggAACCCCAAAI+BQhY2QESsLJzohUCCCCAAAIIIIAAAggggAACCCDgV4CAlaUgAStLKJohgAACCCCAAAIIIIAAAggggAACPgUIWNkBErCyczpVrfbt3Sf79+5PsPms2bJJvgJ5E8xnRuoLHD1yVI4cOSIZM2aUbNmzpf4BhRzB4UOH5dixY5IpUybJmi1ryFImEUAAAQQQQAABBBBAAAEEUlOAgJWlPgErSyiaIYAAAggggAACCCCAAAIIIIAAAj4F0kLAavPmzbJv3z5zJnny5JG4uLiwZ7Vr1y7ZuHFj2GXhZmbNmlXKlSsXblHU8whYRU0W0xVGDhwls6fNTrDNilUrSNdBXRLMZ0bqCwzpPlR+/u5nJ7yUTYZ/9lbqH1DIEfRo21OWLVomcUXipP/YviFLmUQAAQQQQAABBBBAAAEEEEhNAQJWlvoErCyhaIYAAggggAACCCCAAAIIIIAAAgj4FEjNgNXatWtl/vz5QaGp8uXLy9VXXx32rBYuXChz584NuyzczOzZs8tDDz0UblHU8whYRSY7ceKE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CEAAAhCAAAQgAAEIQMCgQFwPWBm8fd2HI2ClmwodIQABCEAAAhCAAAQgAAEIQAACEICAIQEErHTyIWClEwrdIAABCEAAAhCAAAQgAAEIQAACEICAQQEErPQBImClzwm9IAABCEAAAhCAAAQgAAEIQAACEICAUQEErHQKImClEwrdIAABCEAAAhCAAAQgAAEIQAACEICAQQEErPQBImClzwm9IAABCEAAAhCAAAQgAAEIQAACEICAUQEErHQKImClEwrdIAABCEAAAhCAAAQgAAEIQAACEICAQQEErPQBImClzwm9IAABCEAAAhCAAAQgAAEIQAACEICAUQEErHQKImClEwrdIAABCEAAAhCAAAQgAAEIQAACEICAQQEErPQBImClzwm9IAABCEAAAhCAAAQgAAEIQAACEICAUQEErHQKImClEwrdIAABCEAAAhCAAAQgAAEIQAACEICAQQEErPQBImClzwm9IAABCEAAAhCAAAQgAAEIQAACEICAUQEErHQKImClEwrdIAABCEAAAhCAAAQgAAEIQAACEICAQQEErPQBImClzwm9IAABCEAAAhCAAAQgAAEIQAACEICAUQEErHQKImClEwrdIAABCEAAAhCAAAQgAAEIQAACEICAQQEErPQBImClzwm9IACB/7N3Py3GhWEAh+9CKYVZKEk+AR9gypew8HlnZaFsJIS9smZSIm9HmUlvMz1vZ96Zpq6zOf/uc/Jctr8gQIAAAQIECBAgQIAAAQJ5BQRWiYICq0QoYwQIECBAgAABAgQIECBAgACBnAICqzRAgVWakykCBAgQIECAAAECBAgQIECAQF4BgVWioMAqEcoYAQIECBAgQIAAAQIECBAgQCCngMAqDVBgleZkigABAgQIECBAgAABAgQIECCQV0BglSgosEqEMkaAAAECBAgQIECAAAECBAgQyCkgsEoDFFilOZkiQIAAAQIECBAgQIAAAQIECOQVEFglCgqsEqGMESBAgAABAgQIECBAgAABAgRyCvxUYLXdbmO328V+v4/X19col8tRq9Wi2WxGq9VKXtXpdIrsXdfr9fZMp9OJQqGQ/HzqoMAqVcocAQIECBAgQIAAAQIECBAgQCCfgMAq0U9glQhljAABAgQIECBAgAABAgQIECCQU+C7A6v1eh2TySQOh8OHn7zdbke/349qtfrhzPF4jNlsFovFIrLI6r4Nh8N4enq6n37ZXmD1ZZReRIAAAQIECBAgQIAAAQIECBD4VEBg9SnP+02B1buFIwIECBAgQIAAAQIECBAgQIDA/xT47sDq5eUlNpvNw5KKxWJcLpe3X6HKbtbr9RgMBlEqlR5mszBrOp3GarWK8/n8cC87EVj9ReICAQIECBAgQIAAAQIECBAgQOBXCQisEr8ugVUilDECBAgQIECAAAECBAgQIECAQE6Bnwqssr8B7Ha70Wg0olKp3AKr5XIZ4/H4dpwtq9frxfPz88MKR6NRzOfzt2vZ3wFmcdZ9E1jdJewJECBAgAABAgQIECBAgAABAr9T4F8Dqz8AAAD//0MpovMAAEAASURBVOydB5zUxBfHH70X/1KkiNKb9N57kd6bCAhIE0RFAekdBRRQVEB67x3pvYhKFQSRDkpvUhWE/c+bY3LZ3exdNsnd7d39hg+3k+n5zmSSvHl5E8MlHEVDd/PO39HwrHHKIAACIAACIAACIAACIAACIAACIAACIBD+BFK8lCxcK7148SLFjx+fUqVKZVjvwYMHaf/+/TLu5ZdfpoYNG7ql27NnD/32228UO3ZsypkzJ+XNm5c2bNhAN2/elOkaN25ML730klseJw6O3D3tRDEoAwRAAARAAARAAARAAARAAARAAARAAARAIBQC+ZJnCSWFe3QMKFi5A8ERCIAACIAACIAACIAACIAACIAACIAACICAswTCW8EqtNbfvXuXFi1aJJPFjBmT2rVrRzFixNCyHTt2jB4/fkx58uSRilocsWzZMihYaYTgAQEQAAEQAAEQAAEQAAEQAAEQAAEQAIHITQAKVib7DxasTIJCMhAAARAAARAAARAAARAAARAAARAAARCwSSDQFKxu3bpFS5culWcVL148at26dahnCAWrUBEhAQiAAAiAAAiAAAiAAAiAAAiAAAiAAAhEGgJQsDLZVVCwMgkKyUAABEAABEAABEAABEAABEAABEAABEDAJoFAU7A6fvw47d69W55VhgwZqHr16qGeIRSsQkWEBCAAAiAAAiAAAiAAAiAAAiAAAiAAAiAQaQhAwcpkV0HByiQoJAMBEAABEAABEAABEAABEAABEAABEAABmwQCScHq2bNntHjxYrp37548q9KlS1OuXLlCPUMoWIWKCAlAAARAAARAAARAAARAAARAAARAAARAINIQgIKVya6CgpVJUEgGAiAAAiAAAiAAAiAAAiAAAiAAAiAAAjYJBJKCFVuuYgtW7FKnTk116tShGDFihHqGULAKFRESgAAIgAAIgAAIgAAIgAAIgAAIgAAIgECkIQAFK5NdBQUrk6CQDARAAARAAARAAARAAARAAARAAARAAARsEggUBauTJ0/Sjh075NnEjh2bGjZsSMmSJTN1dlCwMoUJiUAABEAABEAABEAABEAABEAABEAABEAgUhCAgpXJboKClUlQSAYCIAACIAACIAACIAACIAACIAACIAACNgkEgoLVpUuXaMOGDfT8+XN5NhUrVqQsWbKYPjMoWJlGhYQgAAIgAAIgAAIgAAIgAAIgAAIgAAIgEPAEoGBlsougYGUSFJKBAAiAAAiAAAiAAAiAAAiAAAiAAAiAgE0CEa1gde3aNVq7di39999/8kyKFy9OefPm9eusoGDlFy4kBgEQAAEQAAEQAAEQAAEQAAEQAAEQAIGAJgAFK5PdAwUrk6CQDARAAARAAARAAARAAARAAARAAARAAARsEohIBavbt2/T6tWr6d9//5VnwYpVrGDlr4OClb/EkB4EQAAEQAAEQAAEQAAEQAAEQAAEQAAEApcAFKxM9g0UrEyCQjIQAAEQAAEQAAEQAAEQAAEQAAEQAAEQsEkgohSs/v77b1q1ahU9fvxYnkGOHDmobNmyls4GClaWsCETCIAACIAACIAACIAACIAACIAACIAACAQkAShYmewWKFiZBIVkIAACIAACIAACIAACIAACIAACIAACIGCTQEQoWD148EAqV/Evu8yZM1PFihUpRowYls4GClaWsCETCIAACIAACIAACIAACIAACIAACIAACAQkAShYmewWKFiZBIVkIAACIAACIAACIAACIAACIAACIAACIGCTQHgrWP3zzz9Sueru3buy5RkyZKCqVatSzJgxLZ8JFKwso0NGEAABEAABEAABEAABEAABEAABEAABEAg4AlCwMtklULAyCQrJQAAEQAAEQAAEQAAEQAAEQAAEQAAEQMAmgfBWsFq/fj1dvHhRa3W2bNkoduzY2rGnp0iRIhQvXjy34GPHjhFvMajcqVOn6MmTJ/IwU6ZMlCBBAulni1hFixYNsXxVRmi/R+6eDi0J4kEABEAABEAABEAABEAABEAABEAABEAABBwgAAUrkxChYGUSFJKB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vYEACIDAu0AAClbvwlnEMYBAOCDwLgqswgF26caQzcOzC0flmjE8jBtjBAEQAAEQeLcIfNtvEG3YvMXwQS2eM4OyZM5kuD4qggAIgAAIgAAIgAAIgIAzApBXOaMS8mngHvKM0QMIgAAIgIBnApBXeWaEEiAAAiAAArYEoGBlywN7IAACIUQAgpMQAuuhWXD3AAjZIAACIAACoUJg3YZNdPL0n4b7atu6BSVK+IHh+qgIAiAAAiAAAiAAAiAAAs4IQG7ijErIp4F7yDNGDyAAAiAAAp4JQF7lmRFKgAAIgAAI2BKAgpUtD+yBAAiEEAF22Xb79h2KHSsWVatSKYR6QbP2BMDdngj2QQAEQAAEQAAEQAAEQAAEQAAEQAAEQMBKAHKTsJkJ4B423NErCIAACIAACIAACIAACICAOQJQsDLHD7VBAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAATeYQJQsHqHTy4ODQRAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAAARAwBwBKFiZ44faIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAAC7zABKFi9wycXhwYCIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIAACIGCOABSszPFDbRAAARAAARAAARAAARDQCNy6dZvOX7xEUaNGpaKFC2rpriJnz1+gO3fuUsKEH1C2j7K6KoZ0EAABEAABEAABEAABEAABEAABEAABEAABEDBEAPIqQ9hQCQRAAARAAAQcCEDBygEJEkAABEAABN51Am/fWigo6IV2mHHixNHiiIDAu0jg5cuX9ObNG4oSJQrFjBnT4RBv3LzlkMYJSRInomjRojnNQ6JzAi0/7UDHTpyk/Hny0LSJ45wX0qVOnTmbxk2YTLFjxaLVSxdS4kQJdbmIggAIgAAIgAAIgAAIgAAIgAAIRBQCkFdFlDON41QEIK9SJEJ+C3lVyDNGDyAAAiAAAhGDQIgoWN29e5eeP38uCcaLF48SJUrkNc1Xr17RzZs3yWKxyDpp0qSRLwO9bgAFQQAEQAAE3lkCU2bMpl8mTZXHt3b5IkqZIrnXx/r2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fXhpcESACBABIhCKBEjBKhRhU1NEgAgQASIQfgkMHDYSduzeo3uAa5YvgaxZMusuTwWJABEgAkSACBABIkAEiAARIAJEgAgQASJABIiAlADJq6Q0aJsIEAEiQASIABEgAkSACBgjQApWxvhRaSJABIgAESACnID/jl1w5doN3TTatmoBSRIn0l2eChIBIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEpAZJXSWnQNhEgAkSACBABIkAEiAARMEaAFKyM8aPSRIAIEAEiQASIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIABEgAkSACIRjAqRgFY4PLg2NCBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIABEgAkSACBABIkAEiAARIAJEgAgQAWMESMHKGD8qTQSIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIABEgAkSACBABIkAEiEA4JkAKVuH44NLQiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAETBGgBSsjPGj0kSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIABEgAkSACBABIkAEiAARIAJEgAiEYwKkYBWODy4NjQgQASLgaQQOHDoCX4O/yrqVIlkyyJkjuyyOdogAESAC7iBw7/4DuB0QYNd08aJFIFbMmHbxRiOePnsOV65ds6umQN68kDBhArt4ivBcAidOnYYvX/6DTBnTQ8oUKTy3owZ7dvPWbXjG5m3ixIkgZ/ZsBmuj4kTAcwnQ+uy5x4Z6RgSIABEgAkQgJAiQvCokqFKdRIAImEWA5FVmkYx49ZC8KuIdcxpx+CZA8irPOL6kYOUZx4F6QQQ4geBgC3z69FGkETt2bHGbNsIugXfv38P790F2A4gR4wdInCiRXXx4jshTpKTd8LzKloGpE8baxXtyxP/+9z/477//eBejRY8O0aNF8+TuUt+IABFQSWDOgsUwd+Fiu9xrfZdBlkwZ7eKNRmzYvBVGjptgV83MqZOgVPFidvHhJSK8XRePnzwNnXv25odn0ewZULBAvvByqOzGsXCpD8yYM58rHG5Z5wdJkyS2yxPWIuiaHtaOWOj0N6Kuz6FDl1oh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H6A1TwpEIDwSCKvrc3g8FjQmIkAEiEBYJkDyqrBz9EheFXaOFfWUCEREAiSvUnfUSV4l50TyKjkP2iMC4YEAyas84yiSgpVnHAfqBREIMQITJk+HfQf1u8KaN2MqpGcWd9wddu87wKzAPOEu+RInSiR25/WbN9Cr/xC4cOkSRI4UGdasWAKZmeUMCp5JAC2cSN0sYi/x6+8kib8fU8/sOfWKCBCBiEDg3fv33Nqc7VjTMqtM6ArI7IBftz9/bu96KFXKFBA9enSzmzOtPhJY2aNEtzBoNh7dF8eJE36Vjl4wK55BQUEQhylWJU2axB4ExRCBcEIgrK7P4QQ/DYMIEIEIQoDkVRHkQIeRYZK8KowcKOomEYigBEhepe7Ak7zKnhPJq+yZUAwRCMsESF7lGUePFKw84zhQL4hAiBEYOGwk7Ni9R3f9a5YvgaxZMusub1ZB4SsNfMGdjfUndapU8J694Lt85Rr7fc+bqVurBowYPMCsJqkeIkAEiAARIAJEQIEACawUoFAUESACRIAIEAEiQASIgCYCJK/ShIsyEwEiQASIABEgAi4IkLzKBSBKJgJEgAgQAVMIkIKVKRipEiLguQT8d+yCK9du6O5g21YtPMK60MKlPjBjznzFcaDSVdtfWkCHNr9AtGjRFPNQJBEgAkSACBABImAOARJYmcORaiECRIAIEAEiQASIQEQmQPKqiHz0aexEgAgQASJABMwnQPIq85lSjUSACBABImBPgBSs7JlQDBEgAh5K4OGjx3D+4iV48vQZvHnzFhIkiMfcF6aDnNmzAbpTokAEiAARIAJEgAiEPIEjx0/AU3YtjhUzJlSrUjnkG6QWiAARIAJEgAgQASJABIiABxMgeZUHHxzqGhEgAkSACEQYAiSvijCHmgZKBIgAEXArAVKwcit+apwIEAEiQASIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIABEgAkSACBABTyZAClaefHSob0SACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIABEgAkSACBABIkAEiAARIAJEgAi4lQApWLkVPzVOBIgAESACRIAIEAEiQASIABEgAkSACBABIkAEiAARIAJEgAgQASJABIgAESACRIAIEAEiQASIgCcTIAUrTz461DciQASIABEgAkSACBAB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KrBiY1WuXLkoSpQotH79eonIV4HVqlWr5BSDCRIkoBcvXtCzZ89kPRBYRZARh90AARAAARAAARAAARAAARAAARAAARAAgVAmcOnSJeKp+95//33dlg4ePCinEOTExIkTU40aNXTz6UXyx4bz5s2THweyV6xGjRo5eMDSK4M4EAABEAABEAABEAABEAABEAABEAABEACBwCZgWmD18uVL2rx5M2XJkoU++OADSePvv//2S2DFXxDu3LlT1lG+fHm5/uTJE7n9f/bOAj6Kow3jL0GLU9zd3S24BQiuRYtDocBXWigupdBCixRKcShFgyYQCO5OcdfiENytcN+8c93N3t1ecnd78Wf4kZsd3//O7u29++w7EFhF7omG0YMACIAACIAACIAACIAACIAACIAACIBARCHAHtR9fHzkcDw8PKhTp07yhUFHxqddQjBnzpxUpUoVR6qhDAiAAAiAAAiAAAiAAAiAAAiAAAiAAAiAQCQmYFhgpbfvrgisXr9+LQ1b7LUqS5YsVLNmTVq8eDFBYKVHGGkgAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAKuEnj06BGtXLlSVo8bNy61b9/e4aZYmMUCLQ7e3t7EyxAigAAIgAAIgAAIgAAIgAAIgAAIgAAIgAAIRG0CEUZgtWXLFrp69Sqxa/VmzZpRokSJILCK2nMPewcCIAACIAACIAACIAACIAACIAACIAAC4ULg7NmztGfPHtl3pkyZyMvLy6Fx3Lt3j/z8/GRZtl198cUXDtVDIRAAARAAARAAARAAARAAARAAARAAARAAgchNIEIIrLQer0qUKEHFihWTVOHBKnJPLoweBEAABEAABEAABEAABEAABEAABEAABCIagY8fP9Ly5cvp+fPncmienp6UL18+h4a5Y8cOunjxoixbvHhx4v8IIAACIAACIAACIAACIAACIAACIAACIAACUZ9AuAusPnz4IJcGfPXqFSVOnFh6r4oZM6YkD4FV1J+A2EMQAAEQAAEQAAEQAAEQAAEQAAEQAAEQCEsC7LmKPVhxSJ06NdWvX59ixIgR4hDev39PCxcupH///VeWZe9V7MUKAQRAAARAAARAAARAAARAAARAAARAAARAIOoTCHeB1b59++j06dOSNLtjZ7fsSoDASiGBTxAAARAAARAAARAAARAAARAAARAAARAAAaMELly4QDt3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LqXlhhtukFtvvdUsL126VDZt2mSWNdO6BmFduXLFrA8dOlSyZctmlvmCAAIIIIAAAggggAACCCCAAAIIIOB/AtdkgNXFixdl/PjxcvnyZSOu2as0ixUFAQQQQAABBBBAAAEEEEAAAQQQQACB1AhERETIvn373HapWLGiNGvWTIKCgly2rV27VlauXGnqatWqJY0bN5aYmBiZMWOGxMfHO4KudOxKs1lp0WkEdTpBCgIIIIAAAggggAACCCCAAAIIIICAfwpckwFW+mbh77//bsRLliwp+lYgBQEEEEAAAQQQQAABBBBAAAEEEEAAgdQKaDYqDbDSF/nsl/nsPqpUqWKCrAIDA+0q+fPPP2XNmjVmvW7duiar+tSpU+X48eOSO3du6dWrl+TMmVMmTpwop0+fNu169uwpBQsWdPTBAgIIIIAAAggggAACCCCAAAIIIICAfwlckwFWOmh19OhRI92iRQupXLmyf6lzNQgggAACCCCAAAIIIIAAAggggAACV11AM07t3LlTVq9eLZpBXUuNGjUkPDzccS4bNmyQZcuWObZpZqoVK1aY9datW0uFChXM8tixY820g7oyYMAAyZMnj6nnCwIIIIAAAggggAACCCCAAAIIIICA/wlccwFWGlilAVZacuTIYQaosmfP7n/yXBECCCCAAAIIIIAAAggggAACCCCAQKYIREVFyezZs82Uf5q9Sqf4swOkNADr119/NedVrFgxk7lKM1+VLVtW2rVr5zjfkSNHSlxcnAQEBMjdd99tPh0bWUAAAQQQQAABBBBAAAEEEEAAAQQQ8CuBay7ASqcG1CkCtVStWtWkafcrcS4GAQQQQAABBBBAAAEEEEAAAQQQQCDTBWbNmiUaaKWlffv2UqZMGbN86NAhE3xlVv75EhQUZKYGzJs3r6nR7Fdjxowxyzpt4MCBA/9pyQcCCCCAAAIIIIAAAggggAACCCCAgD8KXFMBVvo24Pjx4x0p2rt06SKhoaH+6M41IYAAAggggAACCCCAAAIIIIAAAghkosCCBQtk165d5gyaNm0q1apVM8snTpyQyZMnu5yZTiGoUwnaZe/evTJ//nyzWqhQIbnjjjvsTXwigAACCCCAAAIIIIAAAggggAACCPihwDUVYLV9+3ZZtGiRYQ4JCTFvBvqhOZeEAAIIIIAAAggggAACCCCAAAIIIJDJApMm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eMIEILDSCBgCK42g0AwEQAAEQAAEQAAEQAAEQAAEQAAEQAAEdBKAwEobQGcKrEIOnqUV4xfLiQNLFqQ+X+FFrrazgFYgAAKZQWB8z8/lNCxsCSiajzxy5ZQhLcMfu+5xGLFBP42U4S0zYz2YAwSeBAEOCbjpjzUi3O196ebDc7i4uFDn93tT0SolnsSUGBMEnjkCEFg9c6cMCwYBEACB54oABFYaTycEVhpBoRkIgAAIgAAIgAAIgAAIgAAIgAAIgAAI6CQAgZU2gM4UWLGD1Z0rt6UzjG/+3OST11fbItAKBEAABDKBwIS+X1FKcorVmQKC8lPrEe0pf/ECVutRCALPCoGQg+eE2HmRcbmubm700qhOVKZueWMZMiDwXycAgdV//QrA8YMACIBA1hKAwEojfwisNIJCMxAAARAAARAAARAAARAAARAAARAAARDQSQACK20AnSmw0jYjWoEACIBA1hBITkymm+evEztWxd2Po5SkZPIL9Kc8hfJSwbJFiB2skEDgWSdw/240XT4SIsTORLkL5KECpQuTW3a3Z/2wsH4QcCoBCKycihODgQAIgAAIZJAABFYagUFgpREUmoEACIAACIAACIAACIAACIAACIAACICATgIQWGkDeCwqRFtDtAIBEAABEAABEAABEAABEAABEAABEAABENBFAAIrjfggsNIICs1AAARAAARAAARAAARAAARAAARAAARAQCcBCKy0AYTAShsntAIBEAABEAABEAABEAABEAABEAABEAABvQQgsNJIEAIrjaDQzGkEEg//67SxMBAIPGkC2Ws2etJTYHwQAAEQAAEQAAEQAAEQAIH/EAEIrLSdbAistHFCKxAAARAAARAAARAAARAAARAAARAAARDQSwACK40EIbDSCArNnEYAAiunocRAmUAAAqtMgIwpQAAEQAAEQAAEQAAEQOA/RAACK20nGwIrbZzQCgRAAARAAARAAARAAARAAARAAARAAAT0EoDASiNBCKw0gkIzpxGAwMppKDGQIHA7Jp4uRcaQm0s2qlM4b7pMQiIe0J3YBPLPmZ3KBfim2x4Cq3QRoQEIgAAIgAAIgAAIgAAIgEAGCEBgpQ0WBFbaOKEVCIAACIAACIAACIAACIAACIAACIAACOglAIGVRoIQWGkEhWZOIwCBldNQYiBBYMTKfXTyThRVK+BPk9rVSZfJ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7tLl4xelxar4W/GUknxXuqcrEFqQipQLI7ZghWBNgF1EntlzUrhUJAooXIAKlylK3jm9rQvhDARAAASyiADWqCwCj24fKwIQWGX87YbAyiBjCKwMgkIxEAABEAABEAABEAABEAABEAABEAABEDBJAAIrYwD3xZw0VhClQAAEQAAEQAAEQAAEQAAEQAAEQAAEQAAETBGAwMogPgisDIJCMRAAARAAARAAARAAARAAARAAARAAARAwSQACK2MAIbAyxgmlQAAEQAAEQAAEQAAEQAAEQAAEQAAEQMAsAQisDBKEwMogKBQDARAAARAAARAAARAAARAAARAAARAAAZMEILAyBhACK2OcUMp9BPbdOO2+xtASCICAXQJw72IXDTJAAARAAARAAARAAARAIMsIQGBlED0EVgZBoRgIgAAIgAAIgAAIgAAIgAAIgAAIgAAImCQAgZUxgBBYGeOEUu4jAIGV+1iiJRBwRAACK0d0kAcCIAACIAACIAACIAACWUMAAiuD3CGwMggKxUAABEAABEAABEAABEAABEAABEAABEDAJAEIrIwBhMDKGCeUch8BCKzcxxItPdwEbl27SdfOXyVPL08Kr1Yq3YuJOhdJt6NvUR7/vBRSski65SGwShcRCoAACIAACIAACIAACIBAphOAwMogcgisDIJCMRAAARAAARAAARAAARAAARAAARAAARAwSQACK2MAIbAyxgml3EfgYRBY3U26S/dSUsjT05O8c+Vw38U/aOnm1RjdNn0D/cjL20s3D4mPHoF5702nyycuUtEKxanrB73TvcBtizfT5gVrKYdPTnpl7DDKG+DrsA4EVg7xIBMEQAAEQAAEQAAEQAAEsoQABFYGsUNgZRAUioEACIAACIAACIAACIAACIAACIAACICASQJZIbCKjY2lyMhIio6Opri4OEpMTCQfHx/y8/Oj4OBgCgsL072qmzdv0pUrV3Tz9BJz5sxJJUqU0MtyOg0CK6eRZfsKfZ/pRykp96jB00/SgNf7ZbvxPgwCqwX//YkuHjlH+Qr6U/+JI9zKMPbGbfph8FjdNju9/ZIhS0a6lZH4UBE4tuUQLR+/SI65639epqLli6U7/uTEJJo6/FtKuB1PlZvUoFaDnnNYBwIrh3iQCQIgAAIgAAIgAAIgAAJZQgACK4PYIbAyCArFQAAEQAAEQAAEQAAEQAAE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TL1WrVrStm1bU3d80cCs0aNH27NYDRo0SAoVKuQ4hDoCCCCAAAIIIIAAAggggAACCCCAQD4S8FqA1blz52T69Oly6dIlw1O7dm1p3bq121SrVq2STZs2mfE6IaXLApYoUcLt/RmIAAIIIIAAAggggAACCCCAAAIIIICAuwKaRX3v3r1meKtWraROnTqmvnr1atm4caOpN27cWG655RaXh/zpp58kJibG9PXo0UNKlizpchyNCCCAAAIIIIAAAggggAACCCCAAAJ5X8ArAVY6maTBVbZJpRo1aki7du3Ex8fHLaGIiAhZt26dGaup1++++24pU6aMW/syCAEEEEAAAQQQQAABBBBAAAEEEEAAgcwKTJgwQc6ePWt203msmjVrmro+AKgPAmrROa727dubuuNLYmKiyWCl71p0GcGQkBDHIdQRQAABBBBAAAEEEEAAAQQQQAABBPKRgMcBVnFxcSa4yjYhpenUO3bsKL6+vm4xbdmyRX7//XcztkCBAtK5c2cpX768W/syCAEEEEAAAQQQQAABBBBAAAEEEEAAAWcBnW+qVKmSBAcHO3eZ7Q0bNsiaNWvsfQMGDJAiRYqY7SNHjsjs2bNNXee3evXqleo4jkFYAQEBMnDgQPuxqCCAAAIIIIAAAggggAACCCCAAAII5D8BjwOs5s6dK4cOHbLLhIWFiZ+fn33budK0aVPRJQC1nDhxQqZOnWofUrRoUdEArbRKaGio1KpVK61u2hFAAAEEEEAAAQQQQAABBBBAAAEEEJBRo0aJZpcqV66cVKhQQYKCgqRgwYISGxsre/bskcjISLuSBmLpA3+2kpCQIBMnTrRnai9cuLBZJlCzrV+5csUsK7h582ZJSkoyu9StW1datmxp2513BBBAAAEEEEAAAQQQQAABBBBAAIF8KOBxgJUGSGmglLvFMWX64cOHZc6cOe7ummZadrcPwEAEEEAAAQQQQAABBBBAAAEEEEAAgXwvYAuwyuhGixUrJuHh4aJZqBxLVFSUzJgxwx5E5djnWNdlAbt3757uw4aO46kjgAACCCCAAAIIIIAAAggggAACCORNAY8DrKZPny466eRu6du3r3lqUMcfPXpUZs2a5e6uJntV27Zt3R7PQAQQQAABBBBAAAEEEEAAAQQQQACB609g1apVZt4pOjra5c37+/tLo0aNpEGDBqJ1V+X06dOy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Oj+lQVoasBUYGCj9+/d33JU6AggggAACCCCAAAIIIIAAAggggEA+E/BqgNXp06dlxowZ9oknDazSACtPyrFjx8yElx6jdOnSEh4e7snh2BcBBBBAAAEEEEAAAQQQQAABBBBAAAEzh6WBVlo6deoklStXTqUSFxcnUVFRonNely5dktDQUNElA/39/WX06NEmQKtMmTLSrVu3VPvSgAACCCCAAAIIIIAAAggggAACCCCQfwS8FmB17tw5mT59uplsUp7atWtL69atPZY6e/asTJgwwRxHnyQcMGCAx8fkAAgggAACCCCAAAIIIIAAAggggAAC17fAwoULZe/evQahVatWUqdOHbdBdJnBRYsWmfE1atSQ9u3bu70vAxFAAAEEEEAAAQQQQAABBBBAAAEE8p6AVwKsYmJiTHCVvmvRiaV27dqJj4+PxyI60aUTXlqCg4OlT58+Hh+TAyCAAAIIIIAAAggggAACCCCAAAIIXN8C+kCfPtinReexatas6TaIPmSoma20dOzYUapWrWrqvCCAAAIIIIAAAggggAACCCCAAAII5E8BjwOsNFW6TirZJqQ0nbpOLPn6+mYoduLECblw4YJUq1bN5Xg99uTJk8UWuKVPEuoThRQEEEAAAQQQQAABBBBAAAEEEEAAAQTSEtiyZYtUqlTJPKznasyGDRtkzZo19i7NmK6Z0x3L5cuXpVChQo5NZknAP/74Q/T4WvRhwN69e3vlIcMUJ2IDAQQQQAABBBBAAAEEEEAAAQQQQCBXCXgcYDV37lw5dOiQ/abCwsLEz8/Pvu1cadq0qX1yav369bJ27VoJCAiQ6tWrS/HixaVYsWKSkJAg0dHRsm3bNomPjzeH0GxYXbp0kdDQUOdDso0AAggggAACCCCAAAIIIIAAAggggIBdYNSoUZKYmCjlypWTChUqSFBQkBQsWFBiY2NFl/eLjIy0j9VArM6dO9u3taIPBeoDhTpfVb58eSlcuLCcOXNGDh8+nGLfzC4tmOIkbCCAAAIIIIAAAggggAACCCCAAAII5BkBjwOspk6daiad3L1jfaovJCTEDLcFWLmzb7NmzaRhw4buDGUMAggggAACCCCAAAIIIIAAAggggMB1LGALsMqIQB/0Cw8PNw//OY7VACud80qv6EOETZo0SW8IfQgggAACCCCAAAIIIIAAAggggAAC+UTA4wAr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", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.core.display import display, Image\n", + "\n", + "display(Image(filename=\"./codediff.png\"))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Operations\n", + "\n", + "Operations are used by the RDK to keep track of running tasks, and provide a means to query their status and even cancel them. In order for a component to respond appropriately to any Operation requests, we must obtain and listen to the Operation in our method calls.\n", + "\n", + "In order to take advantage of operations, you should wrap the component method with the `run_with_operation` decorator from the `viam.operations` package. Each component has a function, `get_operation(kwargs: Mapping[str, Any]) -> Operation`, that will return an Operation that will tell us if the operation is ever cancelled, allowing us to clean up any long running tasks, close connections, etc.\n", + "\n", + "It is extremely important that we check the `Operation` status, as this not only prevents any unnecessary resource usage, but also allows us to respond to urgent cancellation requests and stop components' motion." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Connect as a client to app\n", + "\n", + "To connect to app as a client and make calls to the data API, you should create an instance of a `ViamClient` and retrieve its `DataClient` member." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from viam.rpc.dial import DialOptions, Credentials\n", + "from viam.app.viam_client import ViamClient\n", + "\n", + "\n", + "async def connect() -> ViamClient:\n", + " dial_options = DialOptions.with_api_key(api_key=\"\", api_key_id=\"\")\n", + " return await ViamClient.create_from_dial_options(dial_options)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once you have a connected `ViamClient`, you can then obtain a `DataClient` as a property." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [ + "remove-input" + ] + }, + "outputs": [], + "source": [ + "# Hidden.\n", + "from typing_extensions import Self\n", + "\n", + "from grpclib.client import Channel\n", + "\n", + "from viam.app.data_client import DataClient\n", + "from viam.app.app_client import AppClient\n", + "from viam.rpc.dial import _dial_direct\n", + "\n", + "\n", + "class MockViamClient:\n", + " @classmethod\n", + " async def create_viam_client(cls) -> Self:\n", + " self = cls()\n", + " self._channel = await _dial_direct(address=\"localhost:9092\", options=DialOptions(insecure=True))\n", + " self.data_client = DataClient(channel=self._channel, metadata={})\n", + " self.app_client = AppClient(channel=self._channel, metadata={})\n", + " return self\n", + "\n", + " _channel: Channel\n", + " data_client: DataClient\n", + " app_client: AppClient\n", + "\n", + " def close(self):\n", + " self._channel.close()\n", + "\n", + "\n", + "viam_client = await MockViamClient.create_viam_client()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [ + "hide-output" + ] + }, + "outputs": [], + "source": [ + "data_client = viam_client.data_client" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This `DataClient` can be used to make method calls that retrieve data from app." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'IsPowered': False, 'PowerPct': 0.0}\n", + "method_name: \"IsPowered\"\n", + "Time requested: 2022-01-01 01:01:01\n", + "Time received: 2022-12-31 23:59:59\n", + "\n", + "{'IsPowered': False, 'PowerPct': 0.0}\n", + "location_id: \"loc-id\"\n", + "Time requested: 2023-01-02 00:00:00\n", + "Time received: 2023-03-04 00:00:00\n", + "\n", + "{'Position': 0.0}\n", + "Time requested: 2023-05-06 00:00:00\n", + "Time received: 2023-07-08 00:00:00\n", + "\n" + ] + } + ], + "source": [ + "from datetime import datetime\n", + "from viam.utils import create_filter\n", + "\n", + "left_motor_filter = create_filter(\n", + " component_name=\"left_motor\", start_time=datetime(2023, 6, 5, 11), end_time=datetime(2023, 6, 5, 13, 30), tags=[\"speed_test_run\"]\n", + ")\n", + "\n", + "data = await data_client.tabular_data_by_filter(filter=left_motor_filter)\n", + "for tab in data:\n", + " print(tab)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also use your `DataClient` to upload data to app." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "time_requested_1 = datetime(2023, 6, 5, 11)\n", + "time_received_1 = datetime(2023, 6, 5, 11, 0, 3)\n", + "\n", + "await data_client.tabular_data_capture_upload(\n", + " part_id=\"\", # Unique ID of the relevant robot part.\n", + " component_type=\"rdk:component:motor\",\n", + " component_name=\"left_motor\",\n", + " method_name=\"IsPowered\",\n", + " method_parameters=None,\n", + " tags=[\"tag_1\", \"tag_2\"],\n", + " data_request_times=[(time_requested_1, time_received_1)],\n", + " tabular_data=[{\"PowerPCT\": 0, \"IsPowered\": False}],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "`ViamClient` objects also house an `AppClient`, which can be used to retrieve information about your organization in App or make certain changes to it. The `AppClient` can be obtained in a manner similar to the `DataClient`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "tags": [ + "hide-output" + ] + }, + "outputs": [], + "source": [ + "app_client = viam_client.app_client" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With this `AppClient`, you can access information about entities within your organization. For example, you can list information about all robots under your organization." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "robot-0\n", + "robot-1\n", + "robot-2\n", + "robot-3\n" + ] + } + ], + "source": [ + "my_organizations = await app_client.list_organizations()\n", + "MY_ORG_ID = my_organizations[0].id\n", + "my_locations = await app_client.list_locations(org_id=MY_ORG_ID)\n", + "robots = []\n", + "\n", + "for location in my_locations:\n", + " more_robots = await app_client.list_robots(location_id=location.id)\n", + " robots += more_robots\n", + "\n", + "for robot in robots:\n", + " print(robot.name)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also create new robots/robot parts or make updates to robots/robot parts that already already exist, or even delete them!" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# Create a new robot named \"new_rover\".\n", + "NEW_ROBOT_ID = await app_client.new_robot(name=\"new_rover\", location_id=my_locations[0].id)\n", + "\n", + "# Change the new robot's name to \"rover\" and assign it a new parent location.\n", + "updated_robot = await app_client.update_robot(robot_id=NEW_ROBOT_ID, name=\"rover\", location_id=\"\", num_log_entries=1)\n", + "assert logs[0].caller is not None\n", + "for item in logs[0].caller.items():\n", + " print(f\"{item[0]}: {item[1]}\")\n", + "\n", + "print(f\"\\n*****PROTO*****\\n\")\n", + "print(logs[0].proto.caller)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "At the end, you may close the connection." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "viam_client.close()" ] } ], "metadata": { "celltoolbar": "Tags", "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "viam-sdk-1ZkIuRmo-py3.11", "language": "python", "name": "python3" }, @@ -509,11 +1489,11 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.4" + "version": "3.11.9 (main, Apr 2 2024, 08:25:04) [Clang 15.0.0 (clang-1500.1.0.2.5)]" }, "vscode": { "interpreter": { - "hash": "be90d0870679ea74a3098707423ded74ee9013b47deb62470047f6fb6d24d029" + "hash": "3dcf3a0dc186f3f37ed02c43aacb5aba79c21834e2f1b6d3d1f9d9c84c46ec17" } } }, diff --git a/docs/examples/foo.png b/docs/examples/foo.png new file mode 100644 index 000000000..d20a101e9 Binary files /dev/null and b/docs/examples/foo.png differ diff --git a/docs/examples/module_step2.py b/docs/examples/module_step2.py new file mode 100644 index 000000000..2e6ed7f35 --- /dev/null +++ b/docs/examples/module_step2.py @@ -0,0 +1,42 @@ +# wifi-sensor/src/wifi_sensor_module.py +import asyncio +from typing import Any, ClassVar, Dict, Mapping, Optional + +from typing_extensions import Self + +from viam.components.sensor import Sensor +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ResourceName +from viam.resource.base import ResourceBase +from viam.resource.registry import Registry, ResourceCreatorRegistration +from viam.resource.types import Model, ModelFamily +from viam.utils import SensorReading + + +class MySensor(Sensor): + # Subclass the Viam Sensor component and implement the required functions + MODEL: ClassVar[Model] = Model(ModelFamily("viam", "sensor"), "linux-wifi") + + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: + sensor = cls(config.name) + return sensor + + async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]: + with open("/proc/net/wireless") as wifi_stats: + content = wifi_stats.readlines() + wifi_signal = [x for x in content[2].split(" ") if x != ""] + return {"link": wifi_signal[2], "level": wifi_signal[3], "noise": wifi_signal[4]} + + async def close(self): + # This is a completely optional function to include. This will be called when the resource is removed from the config or the module + # is shutting down. + self.logger.info(f"{self.name} is closed.") + + +async def main(): + Registry.register_resource_creator(Sensor.API, MySensor.MODEL, ResourceCreatorRegistration(MySensor.new)) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/examples/module_step2_optional.py b/docs/examples/module_step2_optional.py new file mode 100644 index 000000000..ff7175a04 --- /dev/null +++ b/docs/examples/module_step2_optional.py @@ -0,0 +1,64 @@ +# wifi-sensor/src/wifi_sensor_module.py +import asyncio +from typing import Any, ClassVar, Dict, Mapping, Optional, Sequence + +from typing_extensions import Self + +from viam.components.sensor import Sensor +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ResourceName +from viam.resource.base import ResourceBase +from viam.resource.registry import Registry, ResourceCreatorRegistration +from viam.resource.types import Model, ModelFamily +from viam.utils import SensorReading + + +class MySensor(Sensor): + # Subclass the Viam Sensor component and implement the required functions + MODEL: ClassVar[Model] = Model(ModelFamily("viam", "sensor"), "linux-wifi") + + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: + sensor = cls(config.name) + sensor.reconfigure(config, dependencies) + return sensor + + @classmethod + def validate_config(cls, config: ComponentConfig) -> Sequence[str]: + if "multiplier" in config.attributes.fields: + if not config.attributes.fields["multiplier"].HasField("number_value"): + raise Exception("Multiplier must be a float.") + multiplier = config.attributes.fields["multiplier"].number_value + if multiplier == 0: + raise Exception("Multiplier cannot be 0.") + return [] + + async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]: + with open("/proc/net/wireless") as wifi_stats: + content = wifi_stats.readlines() + result = [x for x in content[2].split(" ") if x != ""] + + wifi_signal = [] + for i in range(2, 5): + wifi_signal.append(int(result[i]) * self.multiplier) + return {"link": wifi_signal[0], "level": wifi_signal[1], "noise": wifi_signal[2]} + + def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]): + if "multiplier" in config.attributes.fields: + multiplier = config.attributes.fields["multiplier"].number_value + else: + multiplier = 1.0 + self.multiplier = multiplier + + async def close(self): + # This is a completely optional function to include. This will be called when the resource is removed from the config or the module + # is shutting down. + self.logger.info(f"{self.name} is closed.") + + +async def main(): + Registry.register_resource_creator(Sensor.API, MySensor.MODEL, ResourceCreatorRegistration(MySensor.new, MySensor.validate_config)) + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/examples/module_step3.py b/docs/examples/module_step3.py new file mode 100644 index 000000000..75cdff2a2 --- /dev/null +++ b/docs/examples/module_step3.py @@ -0,0 +1,51 @@ +# wifi-sensor/src/wifi_sensor_module.py +import asyncio +from typing import Any, ClassVar, Dict, Mapping, Optional + +from typing_extensions import Self + +from viam.components.sensor import Sensor +from viam.module.module import Module +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ResourceName +from viam.resource.base import ResourceBase +from viam.resource.registry import Registry, ResourceCreatorRegistration +from viam.resource.types import Model, ModelFamily +from viam.utils import SensorReading + + +class MySensor(Sensor): + # Subclass the Viam Sensor component and implement the required functions + MODEL: ClassVar[Model] = Model(ModelFamily("viam", "sensor"), "linux-wifi") + + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: + sensor = cls(config.name) + return sensor + + async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]: + with open("/proc/net/wireless") as wifi_stats: + content = wifi_stats.readlines() + wifi_signal = [x for x in content[2].split(" ") if x != ""] + return {"link": wifi_signal[2], "level": wifi_signal[3], "noise": wifi_signal[4]} + + async def close(self): + # This is a completely optional function to include. This will be called when the resource is removed from the config or the module + # is shutting down. + self.logger.info(f"{self.name} is closed.") + + +async def main(): + """ + This function creates and starts a new module, after adding all desired resource model. + Resource creators must be registered to the resource registry before the module adds the resource model. + """ + Registry.register_resource_creator(Sensor.API, MySensor.MODEL, ResourceCreatorRegistration(MySensor.new)) + + module = Module.from_args() + module.add_model_from_registry(Sensor.API, MySensor.MODEL) + await module.start() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/docs/examples/my_cool_arm.py b/docs/examples/my_cool_arm.py index 0e6249744..1fcdb11e9 100644 --- a/docs/examples/my_cool_arm.py +++ b/docs/examples/my_cool_arm.py @@ -1,7 +1,12 @@ # my-python-robot/my_cool_arm.py -from typing import Any, Dict, Optional -from viam.components.arm import Arm, JointPositions, Pose, WorldState +import asyncio +import json +from typing import Any, Dict, List, Optional, Tuple + +from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose +from viam.operations import run_with_operation +from viam.proto.common import Capsule, Geometry, Sphere class MyCoolArm(Arm): @@ -21,22 +26,84 @@ def __init__(self, name: str): # Starting joint positions self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0]) - self.is_stoppped = True + self.is_stopped = True + self.geometries = [ + Geometry(center=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20), sphere=Sphere(radius_mm=2)), + Geometry(center=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20), capsule=Capsule(radius_mm=3, length_mm=8)), + ] + + # Minimal working kinematics model + self.kinematics = json.dumps( + { + "name": "MyArm", + "links": [{"id": "base", "parent": "world", "translation": {"x": 0, "y": 0, "z": 0}}], + "joints": [ + {"id": "waist", "type": "revolute", "parent": "base", "axis": {"x": 0, "y": 0, "z": 1}, "max": 359, "min": -359} + ], + } + ).encode("utf-8") super().__init__(name) - async def get_end_position(self, extra: Optional[Dict[str, Any]] = None) -> Pose: + async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose: return self.position - async def move_to_position(self, pose: Pose, world_state: Optional[WorldState] = None, extra: Optional[Dict[str, Any]] = None): - self.is_stoppped = False + @run_with_operation + async def move_to_position( + self, + pose: Pose, + extra: Optional[Dict[str, Any]] = None, + **kwargs, + ): + operation = self.get_operation(kwargs) + + self.is_stopped = False self.position = pose - async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None) -> JointPositions: + # Simulate the length of time it takes for the arm to move to its new position + for x in range(10): + await asyncio.sleep(1) + + # Check if the operation is cancelled and, if it is, stop the arm's motion + if await operation.is_cancelled(): + await self.stop() + break + + self.is_stopped = True + + async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> JointPositions: return self.joint_positions - async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None): - self.is_stoppped = False + @run_with_operation + async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None, **kwargs): + operation = self.get_operation(kwargs) + + self.is_stopped = False self.joint_positions = positions - async def stop(self, extra: Optional[Dict[str, Any]] = None): - self.is_stoppped = True + # Simulate the length of time it takes for the arm to move to its new joint position + for x in range(10): + await asyncio.sleep(1) + + # Check if the operation is cancelled and, if it is, stop the arm's motion + if await operation.is_cancelled(): + await self.stop() + break + + self.is_stopped = True + + async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs): + self.is_stopped = True + + async def is_moving(self) -> bool: + return not self.is_stopped + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + return self.geometries + + async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]: + return KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, self.kinematics + + async def close(self): + # This is a completely optional function to include. This will be called when the resource is removed from the config or the module + # is shutting down. + self.logger.info(f"{self.name} is closed.") diff --git a/docs/index.md b/docs/index.md index 10b59d9b3..c69d1fd1d 100755 --- a/docs/index.md +++ b/docs/index.md @@ -17,9 +17,14 @@ Full Viam Docs autoapi/viam/components/index autoapi/viam/services/index autoapi/viam/robot/index +autoapi/viam/app/index +autoapi/viam/resource/index +autoapi/viam/module/index +autoapi/viam/media/index autoapi/viam/rpc/index autoapi/viam/proto/index ``` ```{include} ../README.md + ``` diff --git a/etc/_update_version_metadata.py b/etc/_update_version_metadata.py new file mode 100644 index 000000000..d2c4c8280 --- /dev/null +++ b/etc/_update_version_metadata.py @@ -0,0 +1,20 @@ +import re +import sys + + +def update_version(file_path: str, new_version: str): + with open(file_path, "r") as file: + data = file.read() + + data = re.sub(r'(API_VERSION\s*=\s*)"v[0-9]+\.[0-9]+\.[0-9]+"', f'\\1"{new_version}"', data) + + with open(file_path, 'w') as file: + file.write(data) + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python3 etc/update_version.py ") + sys.exit(1) + + update_version(sys.argv[1], sys.argv[2]) diff --git a/etc/code-samples-action.py b/etc/code-samples-action.py new file mode 100644 index 000000000..06a1be04b --- /dev/null +++ b/etc/code-samples-action.py @@ -0,0 +1,13 @@ +import json + +data = None +with open("./examples/apis.json") as f: + data = json.load(f) +with open("code-samples-warning.md", "w") as f: + f.write("Warning your change may break code samples. ") + f.write("If your change modifies any of the following functions please contact @viamrobotics/fleet-management. Thanks!\n") + f.write("|component|function|\n") + f.write("|-|-|\n") + for k, v in data.items(): + func = v["func"] + f.write(f"|{k}|{func}|\n") diff --git a/etc/generate_proto_import.py b/etc/generate_proto_import.py index 406086f34..00c7f13cb 100644 --- a/etc/generate_proto_import.py +++ b/etc/generate_proto_import.py @@ -8,7 +8,9 @@ from pathlib import Path from typing import Dict, List -from viam import logging +from google.protobuf.internal.enum_type_wrapper import EnumTypeWrapper + +import logging PACKAGE_PATH = Path(__file__).parent.parent @@ -23,7 +25,7 @@ LOGGER = logging.getLogger(__name__) -logging.setLevel(logging.INFO) +LOGGER.setLevel(logging.INFO) def clean(): @@ -49,7 +51,7 @@ def get_packages(root: str) -> Dict[str, List[str]]: packages: Dict[str, List[str]] = {} - for (dirpath, _, filenames) in os.walk(root): + for dirpath, _, filenames in os.walk(root): rel_path = Path(dirpath).relative_to(root).__str__() if "__" in rel_path: continue @@ -57,7 +59,9 @@ def get_packages(root: str) -> Dict[str, List[str]]: rel_path = rel_path.replace(os.path.sep, ".") packages[rel_path] = list(set([".".join(f.split(".")[:-1]) for f in filenames if "__init__.py" not in f])) LOGGER.debug(f"Packages at path {rel_path}: {packages[rel_path]}") - return packages + common = packages["common"] + del packages["common"] + return {**{"common": common}, **packages} # Always have the common package first def build_dirs(root: str, package: str, modules: List[str]): @@ -78,7 +82,7 @@ def build_dirs(root: str, package: str, modules: List[str]): the package name, and the modules (files) within that package. Then, it will create the appropriate directory structure, and a new - file for each module type (e.g. "arm" for "arm_grpc" and "arm_pb2"). + file for each module type (for example "arm" for "arm_grpc" and "arm_pb2"). """ LOGGER.info(f"Building proto imports for package {package}") @@ -109,12 +113,17 @@ def build_dirs(root: str, package: str, modules: List[str]): # but `import *` is discouraged classes = {} + def check_class(obj) -> bool: + return inspect.isclass(obj) or isinstance(obj, EnumTypeWrapper) + for imp in imports: LOGGER.debug(f"\t\tGrabbing classes from {imp}") class_names = [] mod_name = f"viam.{PROTO_GEN_PACKAGE}.{package}.{imp}" module = importlib.import_module(mod_name) - for name, _ in inspect.getmembers(module, inspect.isclass): + for name, _ in inspect.getmembers(module, check_class): + if name[0] == "_": + continue class_names.append(name) if class_names: @@ -133,18 +142,18 @@ def build_dirs(root: str, package: str, modules: List[str]): f.write("@generated by Viam.\n") f.write("Do not edit manually!\n") f.write("'''\n") - for (imp, cls) in classes.items(): + for imp, cls in classes.items(): f.write(f'from {"."*(len(package.split("."))+IMPORT_LEVEL)}{PROTO_GEN_PACKAGE}.{package}.{imp} import (\n') f.write(" %s\n" % (",\n ".join(cls))) f.write(")\n") f.write("\n__all__ = [\n") - for (imp, cls) in classes.items(): + for imp, cls in classes.items(): f.write(" %s,\n" % (",\n ".join([f"'{c}'" for c in cls]))) f.write("]\n") def add_init_files(root: str): - for (dirpath, _, filenames) in os.walk(root): + for dirpath, _, filenames in os.walk(root): if "__init__.py" not in filenames: LOGGER.debug(f"Adding __init__.py at {dirpath}") path = Path(dirpath) / "__init__.py" @@ -156,7 +165,7 @@ def run(add_inits: bool = True): LOGGER.info("Generating better proto imports") clean() packages = get_packages(GENERATED_PATH.__str__()) - for (package, modules) in packages.items(): + for package, modules in packages.items(): build_dirs(NEW_IMPORT_PATH.__str__(), package, modules) if add_inits: add_init_files(NEW_IMPORT_PATH.__str__()) @@ -172,7 +181,7 @@ def run(add_inits: bool = True): if "q" in arg: LOGGER.disabled = True if "v" in arg: - logging.setLevel(logging.DEBUG) + LOGGER.setLevel(logging.DEBUG) if "i" in arg: add_inits = False except getopt.error: diff --git a/etc/postinstall.sh b/etc/postinstall.sh new file mode 100644 index 000000000..de835f559 --- /dev/null +++ b/etc/postinstall.sh @@ -0,0 +1,6 @@ +rm -rf src/viam/rpc/libviam_rust_utils.* || true +if [ "$(uname)" == "Linux" ]; then + curl -sL -o src/viam/rpc/libviam_rust_utils.so https://github.com/viamrobotics/rust-utils/releases/latest/download/libviam_rust_utils-linux_$(uname -m).so +elif [ "$(uname)" == "Darwin" ]; then + curl -sL -o src/viam/rpc/libviam_rust_utils.dylib https://github.com/viamrobotics/rust-utils/releases/latest/download/libviam_rust_utils-macosx_$(uname -m).dylib +fi diff --git a/etc/requirements.txt b/etc/requirements.txt deleted file mode 100644 index dcc052153..000000000 --- a/etc/requirements.txt +++ /dev/null @@ -1,15 +0,0 @@ -flake8==4.0.1 -googleapis-common-protos==1.56.2 -grpclib==0.4.2 -mypy-protobuf==3.2.0 -protobuf==3.20.0 -protoletariat==0.9.3 -autopep8==1.6.0 -pytest==7.1.2 -pytest-asyncio==0.18.3 -Pillow==9.1.1 -Sphinx==4.5.0 -sphinx-rtd-theme==1.0.0 -myst-parser==0.17.2 -protoc-docs-plugin==0.8.0 -coverage==6.4 diff --git a/examples/apis.json b/examples/apis.json new file mode 100644 index 000000000..ab4498030 --- /dev/null +++ b/examples/apis.json @@ -0,0 +1,90 @@ +{ + "base": { + "func": "is_moving", + "packagePath": "viam.components" + }, + "board": { + "func": "gpio_pin_by_name", + "comment": "Note that the pin supplied is a placeholder. Please change this to a valid pin you are using.", + "args": ["\"16\""], + "packagePath": "viam.components" + }, + "camera": { + "func": "get_image", + "packagePath": "viam.components" + }, + "encoder": { + "func": "get_position", + "packagePath": "viam.components" + }, + "motor": { + "func": "is_moving", + "packagePath": "viam.components" + }, + "sensor": { + "func": "get_readings", + "packagePath": "viam.components" + }, + "servo": { + "func": "get_position", + "packagePath": "viam.components" + }, + "switch": { + "func": "get_position", + "packagePath": "viam.components" + }, + "arm": { + "func": "get_end_position", + "packagePath": "viam.components" + }, + "gantry": { + "func": "get_lengths", + "packagePath": "viam.components" + }, + "gripper": { + "func": "is_moving", + "packagePath": "viam.components" + }, + "movement_sensor": { + "func": "get_linear_acceleration", + "packagePath": "viam.components" + }, + "input_controller": { + "func": "get_controls", + "packagePath": "viam.components", + "packageName": "input", + "importName": "Controller" + }, + "audio": { + "func": "get_properties", + "packagePath": "viam.components" + }, + "pose_tracker": { + "func": "get_poses", + "packagePath": "viam.components" + }, + "power_sensor": { + "func": "get_power", + "packagePath": "viam.components" + }, + "motion": { + "func": "get_pose", + "packagePath": "viam.services", + "importName": "MotionClient" + }, + "vision": { + "func": "get_properties", + "packagePath": "viam.services", + "importName": "VisionClient" + }, + "mlmodel": { + "func": "metadata", + "packagePath": "viam.services", + "importName": "MLModelClient" + }, + "slam": { + "func": "get_point_cloud_map", + "packagePath": "viam.services", + "importName": "SLAMClient" + } +} diff --git a/examples/complex_module/README.md b/examples/complex_module/README.md new file mode 100644 index 000000000..f62141170 --- /dev/null +++ b/examples/complex_module/README.md @@ -0,0 +1,123 @@ +# VIAM Complex Module Example + +This example goes through how to create custom modular resources using Viam's python SDK, and how to connect it to a Robot. + +This is a limited document. For a more in-depth understanding of modules, see the [documentation](https://docs.viam.com/operate/get-started/other-hardware/). + +## Purpose + +Modular resources allow you to define custom components and services, and add them to your robot. Viam ships with many component types, but you're not limited to only using those types -- you can create your own using modules. + +For more information, see the [documentation](https://docs.viam.com/operate/get-started/other-hardware/). For a simpler example, take a look at the [simple module example](https://github.com/viamrobotics/viam-python-sdk/tree/main/examples/simple_module), which only contains one custom resource model in one file. + +For a fully fleshed-out example of a Python module that uses Github CI to upload to the Viam Registry, take a look at [python-example-module](https://github.com/viam-labs/python-example-module). For an example that uses Docker to manage dependencies, take a look at [python-container-module](https://github.com/viamrobotics/python-container-module). For a list of example modules in different Viam SDKs, take a look [here](https://github.com/viamrobotics/upload-module/#example-repos). + +## Project structure + +The definition of the new resources are in the `src` directory. Within this directory are the `proto`, `gizmo`, `arm`, and `summation` subdirectories. + +The `proto` directory contains the `gizmo.proto` and `summation.proto` definitions of all the message types and calls that can be made to the Gizmo component and Summation service. It also has the compiled python output of the protobuf definition. + +The `gizmo` directory contains all the necessary definitions for creating a custom `Gizmo` component type. The `api.py` file defines what a `Gizmo` can do (mirroring the `proto` definition), implements the gRPC `GizmoService` for receiving calls, and the gRPC `GizmoClient` for making calls. See [Design your module](https://docs.viam.com/operate/get-started/other-hardware/#design-your-module) for more info on how to choose an API. The `my_gizmo.py` file in contains the unique implementation of a `Gizmo`. This is defined as a specific `Model`. This implementation uses the `validate_config` function to specify an implicit dependency on a `motor` by default and throw an error if there is an `invalid` attribute. + +Similarly, the `summation` directory contains the analogous definitions for the `Summation` service type. The files in this directory mirror the files in the `gizmo` directory. + +The `arm` directory contains all the necessary definitions for creating a custom modular `Arm` component type. Since it is subclassing an already existing component supported by the Viam SDK, there is no need for an `api.py` file. For a more in-depth tutorial on how to write a modular component from an existing resource, see the [documentation](https://python.viam.dev/examples/example.html#create-custom-modules). + +The `base` directory contains all the necessary definitions for creating a custom modular `Base` component type. Like the previous `Arm` implementation, the `base` directory is subclassing an already existing component supported by the Viam SDK, so an `api.py` is not necessary. A more in-depth tutorial on how to write custom modular components from existing resources can be found in the [documentation](https://python.viam.dev/examples/example.html#create-custom-modules). + +There is also a `main.py` file, which creates a module, adds the desired resources, and starts the module. This file is called by the `run.sh` script, which is the entrypoint for this module. Read further to learn how to connect this module to your robot. + +Outside the `src` directory, there is a `client.py` file. You can use this file to test the module once you have connected to your robot and configured the module. You will have to update the credentials and robot address in that file. + +## Configuring and using the module + +These steps assume that you have a robot available at [app.viam.com](app.viam.com). + +The `run.sh` script is the entrypoint for this module. To connect this module with your robot, you must add this module's entrypoint to the robot's config. For example, the entrypoint file may be at `/home/viam-python-sdk/examples/complex_module/run.sh` and you must add this file path to your configuration. See the [documentation](https://docs.viam.com/operate/get-started/other-hardware/) for more details. + +Once the module has been added to your robot, add a `Gizmo` component that uses the `MyGizmo` model. See the [documentation](https://docs.viam.com/operate/get-started/other-hardware/#add-your-new-modular-resource-to-your-machines) for more details. You can also add an `Arm` component that uses the `MyArm` model and a `Summation` service that uses the `MySum` model in a similar manner. + +Models are uniquely namespaced as colon-delimited-triplets in the form `namespace:family:name`, and are named according to the Viam API that your model implements. A model with the `viam` namespace is always Viam-provided. Read more about making custom namespaces [here](https://docs.viam.com/operate/reference/naming-modules/#create-a-namespace-for-your-organization). + +An example configuration for an Arm component, a Gizmo component, and a Summation service could look like this: + +```json +{ + "components": [ + { + "name": "arm1", + "type": "arm", + "model": "viam:arm:myarm", + "attributes": {}, + "depends_on": [] + }, + { + "name": "gizmo1", + "type": "gizmo", + "namespace": "acme", + "model": "acme:demo:mygizmo", + "attributes": { + "arg1": "arg1", + "motor": "motor1" + }, + "depends_on": [] + }, + { + "name": "motor1", + "type": "motor", + "model": "fake", + "attributes": { + "pins": { + "dir": "", + "pwm": "" + }, + "board": "" + }, + "depends_on": [] + }, + { + "name": "motor2", + "type": "motor", + "model": "fake", + "attributes": { + "pins": { + "dir": "", + "pwm": "" + }, + "board": "" + }, + "depends_on": [] + }, + { + "name": "base1", + "type": "base", + "attributes": { + "left": "motor1", + "right": "motor2" + }, + "model": "viam:base:mybase", + "depends_on": [] + } + ], + "services": [ + { + "name": "mysum1", + "type": "summation", + "namespace": "acme", + "model": "acme:demo:mysum", + "attributes": { + "subtract": false + } + } + ], + "modules": [ + { + "name": "my-module", + "executable_path": "/home/viam-python-sdk/examples/complex_module/run.sh" + } + ] +} +``` + +After the robot has started and connected to the module, you can use the provided `client.py` to connect to your robot and make calls to your custom, modular resources. diff --git a/examples/complex_module/buf.gen.yaml b/examples/complex_module/buf.gen.yaml new file mode 100644 index 000000000..d62214d26 --- /dev/null +++ b/examples/complex_module/buf.gen.yaml @@ -0,0 +1,8 @@ +version: v1 +plugins: + - name: python + out: ./src + - name: grpclib_python + out: ./src + - name: mypy + out: ./src diff --git a/examples/complex_module/buf.lock b/examples/complex_module/buf.lock new file mode 100644 index 000000000..19abf63fb --- /dev/null +++ b/examples/complex_module/buf.lock @@ -0,0 +1,7 @@ +# Generated by buf. DO NOT EDIT. +version: v1 +deps: + - remote: buf.build + owner: googleapis + repository: googleapis + commit: 75b4300737fb4efca0831636be94e517 diff --git a/buf.yaml b/examples/complex_module/buf.yaml similarity index 52% rename from buf.yaml rename to examples/complex_module/buf.yaml index 168981dd7..69baf0fd4 100644 --- a/buf.yaml +++ b/examples/complex_module/buf.yaml @@ -1,10 +1,9 @@ version: v1 +deps: + - buf.build/googleapis/googleapis breaking: use: - FILE lint: use: - DEFAULT -deps: - - buf.build/viamrobotics/goutils - - buf.build/viamrobotics/api diff --git a/examples/complex_module/client.py b/examples/complex_module/client.py new file mode 100644 index 000000000..ad22c9b77 --- /dev/null +++ b/examples/complex_module/client.py @@ -0,0 +1,56 @@ +import asyncio + +from src.gizmo import Gizmo +from src.summation import SummationService + +from viam.robot.client import RobotClient +from viam.components.base import Base + + +async def connect(): + opts = RobotClient.Options.with_api_key(api_key="", api_key_id="") + return await RobotClient.at_address("", opts) + + +async def main(): + robot = await connect() + + print("Resources:") + for resource in robot.resource_names: + print(resource) + + # ####### GIZMO ####### # + gizmo = Gizmo.from_robot(robot, name="gizmo1") + resp = await gizmo.do_one("arg1") + print("do_one result:", resp) + + resp = await gizmo.do_one_client_stream(["arg1", "arg2", "arg3"]) + print("do_one_client_stream result:", resp) + + resp = await gizmo.do_two(False) + print("do_two result:", resp) + + # # Certain types of streams are not currently supported by the viam-rust-utils library that python uses internally. + # # If you are not using WebRTC, you can uncomment the following lines. + + # # resp = await gizmo.do_one_server_stream("arg1") + # # print("do_one_server_stream result:", resp) + + # # resp = await gizmo.do_one_bidi_stream(["arg1", "arg2", "arg3"]) + # # print("do_one_bidi_stream result:", resp) + + # ####### SUMMATION ####### # + summer = SummationService.from_robot(robot, name="mysum1") + sum = await summer.sum([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + print(f"The sum of the numbers [0, 10) is {sum}") + + # ####### BASE ####### # + base = Base.from_robot(robot, name="base1") + resp = await base.is_moving() + print(f"The robot's base is{' ' if resp else ' not '}moving.") + + await robot.close() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/complex_module/requirements.txt b/examples/complex_module/requirements.txt new file mode 100644 index 000000000..df1db24ca --- /dev/null +++ b/examples/complex_module/requirements.txt @@ -0,0 +1,6 @@ +# Uncomment the following line if you would like to use a release version of the SDK +# viam-sdk + +# This line points to the version of the SDK that lives at this project's root. +# In production, you would likely want to use the above `viam-sdk` requirement. +../.. diff --git a/examples/complex_module/run.sh b/examples/complex_module/run.sh new file mode 100755 index 000000000..016ad742d --- /dev/null +++ b/examples/complex_module/run.sh @@ -0,0 +1,42 @@ +#!/bin/sh +cd `dirname $0` + +# Create a virtual environment to run our code +VENV_NAME="venv" +PYTHON="$VENV_NAME/bin/python" +ENV_ERROR="This module requires Python >=3.8, pip, and virtualenv to be installed." + +if ! python3 -m venv $VENV_NAME >/dev/null 2>&1; then + echo "Failed to create virtualenv." + if command -v apt-get >/dev/null; then + echo "Detected Debian/Ubuntu, attempting to install python3-venv automatically." + SUDO="sudo" + if ! command -v $SUDO >/dev/null; then + SUDO="" + fi + if ! apt info python3-venv >/dev/null 2>&1; then + echo "Package info not found, trying apt update" + $SUDO apt -qq update >/dev/null + fi + $SUDO apt install -qqy python3-venv >/dev/null 2>&1 + if ! python3 -m venv $VENV_NAME >/dev/null 2>&1; then + echo $ENV_ERROR >&2 + exit 1 + fi + else + echo $ENV_ERROR >&2 + exit 1 + fi +fi + +# remove -U if viam-sdk should not be upgraded whenever possible +# -qq suppresses extraneous output from pip +echo "Virtualenv found/created. Installing/upgrading Python packages..." +if ! $PYTHON -m pip install -r requirements.txt -Uqq; then + exit 1 +fi + +# Be sure to use `exec` so that termination signals reach the python process, +# or handle forwarding termination signals manually +echo "Starting module..." +exec $PYTHON -m src.main $@ diff --git a/src/viam/gen/app/model/__init__.py b/examples/complex_module/src/__init__.py similarity index 100% rename from src/viam/gen/app/model/__init__.py rename to examples/complex_module/src/__init__.py diff --git a/src/viam/gen/app/model/v1/__init__.py b/examples/complex_module/src/arm/__init__.py similarity index 100% rename from src/viam/gen/app/model/v1/__init__.py rename to examples/complex_module/src/arm/__init__.py diff --git a/examples/complex_module/src/arm/my_arm.py b/examples/complex_module/src/arm/my_arm.py new file mode 100644 index 000000000..00b3000df --- /dev/null +++ b/examples/complex_module/src/arm/my_arm.py @@ -0,0 +1,118 @@ +import asyncio +import os +from typing import Any, ClassVar, Dict, List, Mapping, Optional, Tuple + +from typing_extensions import Self + +from viam.components.arm import Arm, JointPositions, KinematicsFileFormat, Pose +from viam.logging import getLogger +from viam.operations import run_with_operation +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import Capsule, Geometry, ResourceName, Sphere +from viam.resource.base import ResourceBase +from viam.resource.registry import Registry, ResourceCreatorRegistration +from viam.resource.types import Model, ModelFamily + +LOGGER = getLogger(__name__) + + +class MyArm(Arm): + # Subclass the Viam Arm component and implement the required functions + MODEL: ClassVar[Model] = Model(ModelFamily("viam", "arm"), "myarm") + + def __init__(self, name: str): + # Starting position + self.position = Pose( + x=0, + y=0, + z=0, + o_x=0, + o_y=0, + o_z=1, + theta=0, + ) + + # Starting joint positions + self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0]) + self.is_stopped = True + self.geometries = [ + Geometry(center=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20), sphere=Sphere(radius_mm=2)), + Geometry(center=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20), capsule=Capsule(radius_mm=3, length_mm=8)), + ] + super().__init__(name) + + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: + arm = cls(config.name) + return arm + + async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose: + return self.position + + @run_with_operation + async def move_to_position( + self, + pose: Pose, + extra: Optional[Dict[str, Any]] = None, + **kwargs, + ): + operation = self.get_operation(kwargs) + + self.is_stopped = False + + # Simulate the length of time it takes for the arm to move to its new position + for x in range(10): + await asyncio.sleep(1) + + # Check if the operation is cancelled and, if it is, stop the arm's motion + if await operation.is_cancelled(): + await self.stop() + break + + self.position = pose + self.is_stopped = True + + async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> JointPositions: + return self.joint_positions + + @run_with_operation + async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None, **kwargs): + operation = self.get_operation(kwargs) + + self.is_stopped = False + + # Simulate the length of time it takes for the arm to move to its new joint position + for x in range(10): + await asyncio.sleep(1) + + # Check if the operation is cancelled and, if it is, stop the arm's motion + if await operation.is_cancelled(): + await self.stop() + break + + self.joint_positions = positions + self.is_stopped = True + + async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs): + self.is_stopped = True + + async def is_moving(self) -> bool: + return not self.is_stopped + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + return self.geometries + + async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]: + dirname = os.path.dirname(__file__) + filepath = os.path.join(dirname, "./my_arm_kinematics.json") + with open(filepath, mode="rb") as f: + file_data = f.read() + return (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, file_data) + + async def close(self): + # This is a completely optional function to include. This will be called when the resource is removed from the config or the module + # is shutting down. + LOGGER.info(f"{self.name} is closed.") + + +Registry.register_resource_creator(Arm.API, MyArm.MODEL, ResourceCreatorRegistration(MyArm.new)) diff --git a/examples/complex_module/src/arm/my_arm_kinematics.json b/examples/complex_module/src/arm/my_arm_kinematics.json new file mode 100644 index 000000000..2d38086a9 --- /dev/null +++ b/examples/complex_module/src/arm/my_arm_kinematics.json @@ -0,0 +1,217 @@ +{ + "name": "MyArm", + "links": [ + { + "id": "base", + "parent": "world", + "translation": { + "x": 0, + "y": 0, + "z": 0 + } + }, + { + "id": "base_top", + "parent": "waist", + "translation": { + "x": 0, + "y": 0, + "z": 267 + }, + "geometry": { + "r": 50, + "l": 320, + "translation": { + "x": 0, + "y": 0, + "z": 160 + } + } + }, + { + "id": "upper_arm", + "parent": "shoulder", + "translation": { + "x": 53.5, + "y": 0, + "z": 284.5 + }, + "geometry": { + "x": 110, + "y": 190, + "z": 370, + "translation": { + "x": 0, + "y": 0, + "z": 135 + } + } + }, + { + "id": "upper_forearm", + "parent": "elbow", + "translation": { + "x": 77.5, + "y": 0, + "z": -172.5 + }, + "geometry": { + "x": 100, + "y": 190, + "z": 250, + "translation": { + "x": 49.49, + "y": 0, + "z": -49.49 + }, + "orientation": { + "type": "ov_degrees", + "value": { + "x": 0.707106, + "y": 0, + "z": -0.707106, + "th": 0 + } + } + } + }, + { + "id": "lower_forearm", + "parent": "forearm_rot", + "translation": { + "x": 0, + "y": 0, + "z": -170 + }, + "geometry": { + "r": 45, + "l": 285, + "translation": { + "x": 0, + "y": -27.5, + "z": -104.8 + }, + "orientation": { + "type": "ov_degrees", + "value": { + "th": -90, + "x": 0, + "y": 0.2537568, + "z": 0.9672615 + } + } + } + }, + { + "id": "wrist_link", + "parent": "wrist", + "translation": { + "x": 76, + "y": 0, + "z": -97 + }, + "geometry": { + "x": 150, + "y": 100, + "z": 135, + "translation": { + "x": 75, + "y": 10, + "z": -67.5 + } + } + }, + { + "id": "gripper_mount", + "parent": "gripper_rot", + "translation": { + "x": 0, + "y": 0, + "z": 0 + }, + "orientation": { + "type": "ov_degrees", + "value": { + "x": 0, + "y": 0, + "z": -1, + "th": 0 + } + } + } + ], + "joints": [ + { + "id": "waist", + "type": "revolute", + "parent": "base", + "axis": { + "x": 0, + "y": 0, + "z": 1 + }, + "max": 359, + "min": -359 + }, + { + "id": "shoulder", + "type": "revolute", + "parent": "base_top", + "axis": { + "x": 0, + "y": 1, + "z": 0 + }, + "max": 120, + "min": -118 + }, + { + "id": "elbow", + "type": "revolute", + "parent": "upper_arm", + "axis": { + "x": 0, + "y": 1, + "z": 0 + }, + "max": 10, + "min": -225 + }, + { + "id": "forearm_rot", + "type": "revolute", + "parent": "upper_forearm", + "axis": { + "x": 0, + "y": 0, + "z": -1 + }, + "max": 359, + "min": -359 + }, + { + "id": "wrist", + "type": "revolute", + "parent": "lower_forearm", + "axis": { + "x": 0, + "y": 1, + "z": 0 + }, + "max": 179, + "min": -97 + }, + { + "id": "gripper_rot", + "type": "revolute", + "parent": "wrist_link", + "axis": { + "x": 0, + "y": 0, + "z": -1 + }, + "max": 359, + "min": -359 + } + ] +} diff --git a/src/viam/gen/proto/rpc/examples/fileupload/__init__.py b/examples/complex_module/src/base/__init__.py similarity index 100% rename from src/viam/gen/proto/rpc/examples/fileupload/__init__.py rename to examples/complex_module/src/base/__init__.py diff --git a/examples/complex_module/src/base/my_base.py b/examples/complex_module/src/base/my_base.py new file mode 100644 index 000000000..92f7508c3 --- /dev/null +++ b/examples/complex_module/src/base/my_base.py @@ -0,0 +1,150 @@ +from typing import Any, ClassVar, Mapping, Dict, List, Optional, cast, Sequence + +from typing_extensions import Self + +from viam.components.base import Base +from viam.components.motor import Motor +from viam.module.types import Reconfigurable +from viam.proto.app.robot import ComponentConfig +from viam.resource.base import ResourceBase +from viam.proto.common import Geometry, Vector3, ResourceName +from viam.resource.registry import Registry, ResourceCreatorRegistration +from viam.resource.types import Model, ModelFamily +from viam.utils import struct_to_dict + + +class MyBase(Base, Reconfigurable): + """ + MyBase implements a base that only supports set_power (basic forward/back/turn controls), is_moving (check if in motion), and stop (stop + all motion). + + It inherits from the built-in resource subtype Base and conforms to the ``Reconfigurable`` protocol, which signifies that this component + can be reconfigured. Additionally, it specifies a constructor function ``MyBase.new`` which conforms to the + ``resource.types.ResourceCreator`` type required for all models. It also specifies a validator function `MyBase.validate_config` which + conforms to the ``resource.types.Validator`` type and returns implicit dependencies for the model. + """ + + # Subclass the Viam Base component and implement the required functions + MODEL: ClassVar[Model] = Model(ModelFamily("viam", "base"), "mybase") + + def __init__(self, name: str): + super().__init__(name) + + # Constructor + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: + base = cls(config.name) + base.reconfigure(config, dependencies) + return base + + # Validates JSON Configuration + @classmethod + def validate_config(cls, config: ComponentConfig) -> Sequence[str]: + attributes_dict = struct_to_dict(config.attributes) + left_name = attributes_dict.get("left", "") + assert isinstance(left_name, str) + if left_name == "": + raise Exception("A left attribute is required for a MyBase component.") + + right_name = attributes_dict.get("right", "") + assert isinstance(right_name, str) + if right_name == "": + raise Exception("A right attribute is required for a MyBase component.") + return [left_name, right_name] + + # Handles attribute reconfiguration + def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]): + attributes_dict = struct_to_dict(config.attributes) + left_name = attributes_dict.get("left") + right_name = attributes_dict.get("right") + + assert isinstance(left_name, str) and isinstance(right_name, str) + + left_motor = dependencies[Motor.get_resource_name(left_name)] + right_motor = dependencies[Motor.get_resource_name(right_name)] + + self.left = cast(Motor, left_motor) + self.right = cast(Motor, right_motor) + + # Not implemented + async def move_straight( + self, + distance: int, + velocity: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + raise NotImplementedError() + + # Not implemented + async def spin( + self, + angle: float, + velocity: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + raise NotImplementedError() + + # Set the linear and angular velocity of the left and right motors on the base + async def set_power( + self, + linear: Vector3, + angular: Vector3, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + # stop the base if absolute value of linear and angular velocity is less than 0.01 + if abs(linear.y) < 0.01 and abs(angular.z) < 0.01: + await self.stop(extra=extra, timeout=timeout) + return + + # use linear and angular velocity to calculate percentage of max power to pass to SetPower for left & right motors + sum = abs(linear.y) + abs(angular.z) + + await self.left.set_power(power=((linear.y - angular.z) / sum), extra=extra, timeout=timeout) + await self.right.set_power(power=((linear.y + angular.z) / sum), extra=extra, timeout=timeout) + + # Not implemented + async def set_velocity( + self, + linear: Vector3, + angular: Vector3, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + raise NotImplementedError() + + # Stop the base from moving by stopping both motors + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + await self.left.stop(extra=extra, timeout=timeout) + await self.right.stop(extra=extra, timeout=timeout) + + # Check if either motor on the base is moving with motors' is_moving + async def is_moving(self) -> bool: + return await self.left.is_moving() or await self.right.is_moving() + + # Not implemented + async def get_properties(self, *, timeout: Optional[float] | None = None, **kwargs) -> Base.Properties: + raise NotImplementedError() + + # Not implemented + async def get_geometries(self) -> List[Geometry]: + raise NotImplementedError() + + +Registry.register_resource_creator(Base.API, MyBase.MODEL, ResourceCreatorRegistration(MyBase.new, MyBase.validate_config)) diff --git a/examples/complex_module/src/gizmo/__init__.py b/examples/complex_module/src/gizmo/__init__.py new file mode 100644 index 000000000..d0e81a3c1 --- /dev/null +++ b/examples/complex_module/src/gizmo/__init__.py @@ -0,0 +1,10 @@ +""" +This file registers the Gizmo API with the Viam Registry, as well as the specific MyGizmo model. +""" + +from viam.components.motor import * # noqa: F403 Need to import motor so the component registers itself +from viam.resource.registry import Registry, ResourceRegistration + +from .api import Gizmo, GizmoClient, GizmoService + +Registry.register_api(ResourceRegistration(Gizmo, GizmoService, lambda name, channel: GizmoClient(name, channel))) diff --git a/examples/complex_module/src/gizmo/api.py b/examples/complex_module/src/gizmo/api.py new file mode 100644 index 000000000..2c5bf64f2 --- /dev/null +++ b/examples/complex_module/src/gizmo/api.py @@ -0,0 +1,183 @@ +""" +This file outlines the general structure for the API around a custom, modularized component. + +It defines the abstract class definition that all concrete implementations must follow, +the gRPC service that will handle calls to the component, +and the gRPC client that will be able to make calls to this component. + +In this example, the ``Gizmo`` abstract class defines what functionality is required for all Gizmos. It extends ``ComponentBase``, +as all component types must. It also defines its specific ``API``, which is used internally to keep track of supported types. + +The ``GizmoService`` implements the gRPC service for the Gizmo. This will allow other robots and clients to make requests of the Gizmo. +It extends both from ``GizmoServiceBase`` and ``ResourceRPCServiceBase``. The former is the gRPC service as defined by the proto, +and the latter is the class that all gRPC services for components must inherit from. + +Finally, the ``GizmoClient`` is the gRPC client for a Gizmo. It inherits from Gizmo since it implements all the same functions. The +implementations are simply gRPC calls to some remote Gizmo. + +To see how this custom modular component is registered, see the __init__.py file. +To see the custom implementation of this component, see the my_gizmo.py file. +""" + +import abc +from typing import Final, List, Mapping, Optional, Sequence + +from grpclib.client import Channel +from grpclib.server import Stream + +from viam.components.component_base import ComponentBase +from viam.components.generic.client import do_command +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.resource.types import RESOURCE_TYPE_COMPONENT, API +from viam.utils import ValueTypes + +from ..proto.gizmo_grpc import GizmoServiceBase, GizmoServiceStub +from ..proto.gizmo_pb2 import ( + DoOneBiDiStreamRequest, + DoOneBiDiStreamResponse, + DoOneClientStreamRequest, + DoOneClientStreamResponse, + DoOneRequest, + DoOneResponse, + DoOneServerStreamRequest, + DoOneServerStreamResponse, + DoTwoRequest, + DoTwoResponse, +) + + +class Gizmo(ComponentBase): + """Example component to use with the example module.""" + + API: Final = API("acme", RESOURCE_TYPE_COMPONENT, "gizmo") + + @abc.abstractmethod + async def do_one(self, arg1: str, **kwargs) -> bool: + ... + + @abc.abstractmethod + async def do_one_client_stream(self, arg1: Sequence[str], **kwargs) -> bool: + ... + + @abc.abstractmethod + async def do_one_server_stream(self, arg1: str, **kwargs) -> Sequence[bool]: + ... + + @abc.abstractmethod + async def do_one_bidi_stream(self, arg1: Sequence[str], **kwargs) -> Sequence[bool]: + ... + + @abc.abstractmethod + async def do_two(self, arg1: bool, **kwargs) -> str: + ... + + +class GizmoService(GizmoServiceBase, ResourceRPCServiceBase): + """Example gRPC service for the Gizmo component""" + + RESOURCE_TYPE = Gizmo + + async def DoOne(self, stream: Stream[DoOneRequest, DoOneResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + gizmo = self.get_resource(name) + resp = await gizmo.do_one(request.arg1) + response = DoOneResponse(ret1=resp) + await stream.send_message(response) + + async def DoOneClientStream(self, stream: Stream[DoOneClientStreamRequest, DoOneClientStreamResponse]) -> None: + requests = [request async for request in stream] + args = [request.arg1 for request in requests] + names = [request.name for request in requests] + if len(set(names)) != 1: + raise Exception("Unexpectedly received requests for multiple Gizmos") + name = names[0] + gizmo = self.get_resource(name) + resp = await gizmo.do_one_client_stream(args) + response = DoOneClientStreamResponse(ret1=resp) + await stream.send_message(response) + + async def DoOneServerStream(self, stream: Stream[DoOneServerStreamRequest, DoOneServerStreamResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + gizmo = self.get_resource(name) + resps = await gizmo.do_one_server_stream(request.arg1) + for resp in resps: + await stream.send_message(DoOneServerStreamResponse(ret1=resp)) + + async def DoOneBiDiStream(self, stream: Stream[DoOneBiDiStreamRequest, DoOneBiDiStreamResponse]) -> None: + args: List[str] = [] + name: str = "" + async for request in stream: + args.append(request.arg1) + if name == "": + name = request.name + continue + if name != request.name: + raise Exception("Unexpectedly received requests for multiple Gizmos") + gizmo = self.get_resource(name) + + resps = await gizmo.do_one_bidi_stream(args) + for resp in resps: + await stream.send_message(DoOneBiDiStreamResponse(ret1=resp)) + + async def DoTwo(self, stream: Stream[DoTwoRequest, DoTwoResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + gizmo = self.get_resource(name) + resp = await gizmo.do_two(request.arg1) + response = DoTwoResponse(ret1=resp) + await stream.send_message(response) + + +class GizmoClient(Gizmo): + """Example gRPC client for the Gizmo component""" + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = GizmoServiceStub(channel) + super().__init__(name) + + async def do_one(self, arg1: str) -> bool: + resp: DoOneResponse = await self.client.DoOne(DoOneRequest(name=self.name, arg1=arg1)) + return resp.ret1 + + async def do_one_client_stream(self, arg1: Sequence[str]) -> bool: + async with self.client.DoOneClientStream.open() as stream: + await stream.send_request() + for arg in arg1: + await stream.send_message(DoOneClientStreamRequest(name=self.name, arg1=arg)) + await stream.end() + response = await stream.recv_message() + assert response is not None + return response.ret1 + + async def do_one_server_stream(self, arg1: str) -> Sequence[bool]: + async with self.client.DoOneServerStream.open() as stream: + await stream.send_message(DoOneServerStreamRequest(name=self.name, arg1=arg1)) + resps = [resp.ret1 async for resp in stream] + return resps + + async def do_one_bidi_stream(self, arg1: Sequence[str]) -> Sequence[bool]: + async with self.client.DoOneClientStream.open() as stream: + await stream.send_request() + for arg in arg1: + await stream.send_message(DoOneClientStreamRequest(name=self.name, arg1=arg)) + await stream.end() + resps = [resp.ret1 async for resp in stream] + return resps + + async def do_two(self, arg1: bool) -> str: + resp = await self.client.DoTwo(DoTwoRequest(name=self.name, arg1=arg1)) + return resp.ret1 + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + ) -> Mapping[str, ValueTypes]: + return await do_command(self.channel, self.name, command, timeout=timeout) diff --git a/examples/complex_module/src/gizmo/my_gizmo.py b/examples/complex_module/src/gizmo/my_gizmo.py new file mode 100644 index 000000000..35a19ea32 --- /dev/null +++ b/examples/complex_module/src/gizmo/my_gizmo.py @@ -0,0 +1,82 @@ +from typing import ClassVar, Mapping, Sequence + +from typing_extensions import Self + +from viam.logging import getLogger +from viam.module.types import Reconfigurable +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ResourceName +from viam.resource.base import ResourceBase +from viam.resource.registry import Registry, ResourceCreatorRegistration +from viam.resource.types import Model, ModelFamily + +from ..gizmo.api import Gizmo + +LOGGER = getLogger(__name__) + + +class MyGizmo(Gizmo, Reconfigurable): + """This is the specific implementation of a ``Gizmo`` (defined in api.py). + + It inherits from Gizmo, as well conforms to the ``Reconfigurable`` protocol, which signifies that this component can be reconfigured. + It also specifies a function ``MyGizmo.new``, which conforms to the ``resource.types.ResourceCreator`` type, which is required + for all models. + """ + + MODEL: ClassVar[Model] = Model(ModelFamily("acme", "demo"), "mygizmo") + my_arg: str + + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: + gizmo = cls(config.name) + gizmo.my_arg = config.attributes.fields["arg1"].string_value + return gizmo + + @classmethod + def validate_config(cls, config: ComponentConfig) -> Sequence[str]: + # Custom validation can be done by specifiying a validate function like this one. Validate functions + # can raise errors that will be returned to the parent through gRPC. Validate functions can + # also return a sequence of strings representing the implicit dependencies of the resource. + if "invalid" in config.attributes.fields: + raise Exception(f"'invalid' attribute not allowed for model {cls.API}:{cls.MODEL}") + arg1 = config.attributes.fields["arg1"].string_value + if arg1 == "": + raise Exception("A arg1 attribute is required for Gizmo component.") + motor = [config.attributes.fields["motor"].string_value] + if motor == [""]: + raise Exception("A motor is required for Gizmo component.") + return motor + + async def do_one(self, arg1: str, **kwargs) -> bool: + return arg1 == self.my_arg + + async def do_one_client_stream(self, arg1: Sequence[str], **kwargs) -> bool: + if len(arg1) == 0: + return False + resp = True + for arg in arg1: + resp = resp and arg == self.my_arg + return resp + + async def do_one_server_stream(self, arg1: str, **kwargs) -> Sequence[bool]: + return [arg1 == self.my_arg, False, True, False] + + async def do_one_bidi_stream(self, arg1: Sequence[str], **kwargs) -> Sequence[bool]: + resps = [] + for arg in arg1: + resps.append(arg == self.my_arg) + return resps + + async def do_two(self, arg1: bool, **kwargs) -> str: + return f"arg1={arg1}" + + def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]): + self.my_arg = config.attributes.fields["arg1"].string_value + + async def close(self): + # This is a completely optional function to include. This will be called when the resource is removed from the config or the module + # is shutting down. + LOGGER.info(f"{self.name} is closed.") + + +Registry.register_resource_creator(Gizmo.API, MyGizmo.MODEL, ResourceCreatorRegistration(MyGizmo.new, MyGizmo.validate_config)) diff --git a/examples/complex_module/src/main.py b/examples/complex_module/src/main.py new file mode 100644 index 000000000..1974bde57 --- /dev/null +++ b/examples/complex_module/src/main.py @@ -0,0 +1,26 @@ +import asyncio + +from viam.module.module import Module +from viam.components.arm import Arm +from viam.components.base import Base + +from src.arm.my_arm import MyArm +from src.base.my_base import MyBase +from src.gizmo.my_gizmo import Gizmo, MyGizmo +from src.summation.my_summation import MySummationService, SummationService + + +async def main(): + """This function creates and starts a new module, after adding all desired resource models. + Resource models must be pre-registered. For an example, see the `gizmo.__init__.py` file. + """ + module = Module.from_args() + module.add_model_from_registry(Gizmo.API, MyGizmo.MODEL) + module.add_model_from_registry(SummationService.API, MySummationService.MODEL) + module.add_model_from_registry(Arm.API, MyArm.MODEL) + module.add_model_from_registry(Base.API, MyBase.MODEL) + await module.start() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/src/viam/gen/proto/rpc/examples/fileupload/v1/__init__.py b/examples/complex_module/src/proto/__init__.py similarity index 100% rename from src/viam/gen/proto/rpc/examples/fileupload/v1/__init__.py rename to examples/complex_module/src/proto/__init__.py diff --git a/examples/complex_module/src/proto/gizmo.proto b/examples/complex_module/src/proto/gizmo.proto new file mode 100644 index 000000000..23b262f9a --- /dev/null +++ b/examples/complex_module/src/proto/gizmo.proto @@ -0,0 +1,70 @@ +syntax = "proto3"; + +package acme.component.gizmo.v1; + +import "google/api/annotations.proto"; + +service GizmoService { + rpc DoOne(DoOneRequest) returns (DoOneResponse) { + option (google.api.http) = { + post: "/acme/api/v1/component/gizmo/{name}/do_one" + }; + } + + rpc DoOneClientStream(stream DoOneClientStreamRequest) returns (DoOneClientStreamResponse); + + rpc DoOneServerStream(DoOneServerStreamRequest) returns (stream DoOneServerStreamResponse); + + rpc DoOneBiDiStream(stream DoOneBiDiStreamRequest) returns (stream DoOneBiDiStreamResponse); + + rpc DoTwo(DoTwoRequest) returns (DoTwoResponse) { + option (google.api.http) = { + post: "/acme/api/v1/component/gizmo/{name}/do_two" + }; + } +} + +message DoOneRequest { + string name = 1; + string arg1 = 2; +} + +message DoOneResponse { + bool ret1 = 1; +} + +message DoOneServerStreamRequest { + string name = 1; + string arg1 = 2; +} + +message DoOneServerStreamResponse { + bool ret1 = 1; +} + +message DoOneClientStreamRequest { + string name = 1; + string arg1 = 2; +} + +message DoOneClientStreamResponse { + bool ret1 = 1; +} + +message DoOneBiDiStreamRequest { + string name = 1; + string arg1 = 2; +} + +message DoOneBiDiStreamResponse { + bool ret1 = 1; +} + +message DoTwoRequest { + string name = 1; + bool arg1 = 2; +} + +message DoTwoResponse { + string ret1 = 1; +} diff --git a/examples/complex_module/src/proto/gizmo_grpc.py b/examples/complex_module/src/proto/gizmo_grpc.py new file mode 100644 index 000000000..cbd09a435 --- /dev/null +++ b/examples/complex_module/src/proto/gizmo_grpc.py @@ -0,0 +1,111 @@ +# Generated by the Protocol Buffers compiler. DO NOT EDIT! +# source: proto/gizmo.proto +# plugin: grpclib.plugin.main +import abc +import typing + +import grpclib.client +import grpclib.const + +if typing.TYPE_CHECKING: + import grpclib.server + +import google.api.annotations_pb2 + +from .. import proto + + +class GizmoServiceBase(abc.ABC): + @abc.abstractmethod + async def DoOne(self, stream: "grpclib.server.Stream[proto.gizmo_pb2.DoOneRequest, proto.gizmo_pb2.DoOneResponse]") -> None: + pass + + @abc.abstractmethod + async def DoOneClientStream( + self, stream: "grpclib.server.Stream[proto.gizmo_pb2.DoOneClientStreamRequest, proto.gizmo_pb2.DoOneClientStreamResponse]" + ) -> None: + pass + + @abc.abstractmethod + async def DoOneServerStream( + self, stream: "grpclib.server.Stream[proto.gizmo_pb2.DoOneServerStreamRequest, proto.gizmo_pb2.DoOneServerStreamResponse]" + ) -> None: + pass + + @abc.abstractmethod + async def DoOneBiDiStream( + self, stream: "grpclib.server.Stream[proto.gizmo_pb2.DoOneBiDiStreamRequest, proto.gizmo_pb2.DoOneBiDiStreamResponse]" + ) -> None: + pass + + @abc.abstractmethod + async def DoTwo(self, stream: "grpclib.server.Stream[proto.gizmo_pb2.DoTwoRequest, proto.gizmo_pb2.DoTwoResponse]") -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return { + "/acme.component.gizmo.v1.GizmoService/DoOne": grpclib.const.Handler( + self.DoOne, + grpclib.const.Cardinality.UNARY_UNARY, + proto.gizmo_pb2.DoOneRequest, + proto.gizmo_pb2.DoOneResponse, + ), + "/acme.component.gizmo.v1.GizmoService/DoOneClientStream": grpclib.const.Handler( + self.DoOneClientStream, + grpclib.const.Cardinality.STREAM_UNARY, + proto.gizmo_pb2.DoOneClientStreamRequest, + proto.gizmo_pb2.DoOneClientStreamResponse, + ), + "/acme.component.gizmo.v1.GizmoService/DoOneServerStream": grpclib.const.Handler( + self.DoOneServerStream, + grpclib.const.Cardinality.UNARY_STREAM, + proto.gizmo_pb2.DoOneServerStreamRequest, + proto.gizmo_pb2.DoOneServerStreamResponse, + ), + "/acme.component.gizmo.v1.GizmoService/DoOneBiDiStream": grpclib.const.Handler( + self.DoOneBiDiStream, + grpclib.const.Cardinality.STREAM_STREAM, + proto.gizmo_pb2.DoOneBiDiStreamRequest, + proto.gizmo_pb2.DoOneBiDiStreamResponse, + ), + "/acme.component.gizmo.v1.GizmoService/DoTwo": grpclib.const.Handler( + self.DoTwo, + grpclib.const.Cardinality.UNARY_UNARY, + proto.gizmo_pb2.DoTwoRequest, + proto.gizmo_pb2.DoTwoResponse, + ), + } + + +class GizmoServiceStub: + def __init__(self, channel: grpclib.client.Channel) -> None: + self.DoOne = grpclib.client.UnaryUnaryMethod( + channel, + "/acme.component.gizmo.v1.GizmoService/DoOne", + proto.gizmo_pb2.DoOneRequest, + proto.gizmo_pb2.DoOneResponse, + ) + self.DoOneClientStream = grpclib.client.StreamUnaryMethod( + channel, + "/acme.component.gizmo.v1.GizmoService/DoOneClientStream", + proto.gizmo_pb2.DoOneClientStreamRequest, + proto.gizmo_pb2.DoOneClientStreamResponse, + ) + self.DoOneServerStream = grpclib.client.UnaryStreamMethod( + channel, + "/acme.component.gizmo.v1.GizmoService/DoOneServerStream", + proto.gizmo_pb2.DoOneServerStreamRequest, + proto.gizmo_pb2.DoOneServerStreamResponse, + ) + self.DoOneBiDiStream = grpclib.client.StreamStreamMethod( + channel, + "/acme.component.gizmo.v1.GizmoService/DoOneBiDiStream", + proto.gizmo_pb2.DoOneBiDiStreamRequest, + proto.gizmo_pb2.DoOneBiDiStreamResponse, + ) + self.DoTwo = grpclib.client.UnaryUnaryMethod( + channel, + "/acme.component.gizmo.v1.GizmoService/DoTwo", + proto.gizmo_pb2.DoTwoRequest, + proto.gizmo_pb2.DoTwoResponse, + ) diff --git a/examples/complex_module/src/proto/gizmo_pb2.py b/examples/complex_module/src/proto/gizmo_pb2.py new file mode 100644 index 000000000..3d4cfaa94 --- /dev/null +++ b/examples/complex_module/src/proto/gizmo_pb2.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/gizmo.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11proto/gizmo.proto\x12\x17\x61\x63me.component.gizmo.v1\x1a\x1cgoogle/api/annotations.proto\"6\n\x0c\x44oOneRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04\x61rg1\x18\x02 \x01(\tR\x04\x61rg1\"#\n\rDoOneResponse\x12\x12\n\x04ret1\x18\x01 \x01(\x08R\x04ret1\"B\n\x18\x44oOneServerStreamRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04\x61rg1\x18\x02 \x01(\tR\x04\x61rg1\"/\n\x19\x44oOneServerStreamResponse\x12\x12\n\x04ret1\x18\x01 \x01(\x08R\x04ret1\"B\n\x18\x44oOneClientStreamRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04\x61rg1\x18\x02 \x01(\tR\x04\x61rg1\"/\n\x19\x44oOneClientStreamResponse\x12\x12\n\x04ret1\x18\x01 \x01(\x08R\x04ret1\"@\n\x16\x44oOneBiDiStreamRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04\x61rg1\x18\x02 \x01(\tR\x04\x61rg1\"-\n\x17\x44oOneBiDiStreamResponse\x12\x12\n\x04ret1\x18\x01 \x01(\x08R\x04ret1\"6\n\x0c\x44oTwoRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04\x61rg1\x18\x02 \x01(\x08R\x04\x61rg1\"#\n\rDoTwoResponse\x12\x12\n\x04ret1\x18\x01 \x01(\tR\x04ret12\x9e\x05\n\x0cGizmoService\x12\x8a\x01\n\x05\x44oOne\x12%.acme.component.gizmo.v1.DoOneRequest\x1a&.acme.component.gizmo.v1.DoOneResponse\"2\x82\xd3\xe4\x93\x02,\"*/acme/api/v1/component/gizmo/{name}/do_one\x12|\n\x11\x44oOneClientStream\x12\x31.acme.component.gizmo.v1.DoOneClientStreamRequest\x1a\x32.acme.component.gizmo.v1.DoOneClientStreamResponse(\x01\x12|\n\x11\x44oOneServerStream\x12\x31.acme.component.gizmo.v1.DoOneServerStreamRequest\x1a\x32.acme.component.gizmo.v1.DoOneServerStreamResponse0\x01\x12x\n\x0f\x44oOneBiDiStream\x12/.acme.component.gizmo.v1.DoOneBiDiStreamRequest\x1a\x30.acme.component.gizmo.v1.DoOneBiDiStreamResponse(\x01\x30\x01\x12\x8a\x01\n\x05\x44oTwo\x12%.acme.component.gizmo.v1.DoTwoRequest\x1a&.acme.component.gizmo.v1.DoTwoResponse\"2\x82\xd3\xe4\x93\x02,\"*/acme/api/v1/component/gizmo/{name}/do_twoB*Z(go.acme.com/proto/api/component/gizmo/v1b\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.gizmo_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'Z(go.acme.com/proto/api/component/gizmo/v1' + _GIZMOSERVICE.methods_by_name['DoOne']._options = None + _GIZMOSERVICE.methods_by_name['DoOne']._serialized_options = b'\202\323\344\223\002,\"*/acme/api/v1/component/gizmo/{name}/do_one' + _GIZMOSERVICE.methods_by_name['DoTwo']._options = None + _GIZMOSERVICE.methods_by_name['DoTwo']._serialized_options = b'\202\323\344\223\002,\"*/acme/api/v1/component/gizmo/{name}/do_two' + _DOONEREQUEST._serialized_start=76 + _DOONEREQUEST._serialized_end=130 + _DOONERESPONSE._serialized_start=132 + _DOONERESPONSE._serialized_end=167 + _DOONESERVERSTREAMREQUEST._serialized_start=169 + _DOONESERVERSTREAMREQUEST._serialized_end=235 + _DOONESERVERSTREAMRESPONSE._serialized_start=237 + _DOONESERVERSTREAMRESPONSE._serialized_end=284 + _DOONECLIENTSTREAMREQUEST._serialized_start=286 + _DOONECLIENTSTREAMREQUEST._serialized_end=352 + _DOONECLIENTSTREAMRESPONSE._serialized_start=354 + _DOONECLIENTSTREAMRESPONSE._serialized_end=401 + _DOONEBIDISTREAMREQUEST._serialized_start=403 + _DOONEBIDISTREAMREQUEST._serialized_end=467 + _DOONEBIDISTREAMRESPONSE._serialized_start=469 + _DOONEBIDISTREAMRESPONSE._serialized_end=514 + _DOTWOREQUEST._serialized_start=516 + _DOTWOREQUEST._serialized_end=570 + _DOTWORESPONSE._serialized_start=572 + _DOTWORESPONSE._serialized_end=607 + _GIZMOSERVICE._serialized_start=610 + _GIZMOSERVICE._serialized_end=1280 +# @@protoc_insertion_point(module_scope) diff --git a/examples/complex_module/src/proto/gizmo_pb2.pyi b/examples/complex_module/src/proto/gizmo_pb2.pyi new file mode 100644 index 000000000..d7e56e4cd --- /dev/null +++ b/examples/complex_module/src/proto/gizmo_pb2.pyi @@ -0,0 +1,180 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class DoOneRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ARG1_FIELD_NUMBER: builtins.int + name: builtins.str + arg1: builtins.str + def __init__( + self, + *, + name: builtins.str = ..., + arg1: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["arg1", b"arg1", "name", b"name"]) -> None: ... + +global___DoOneRequest = DoOneRequest + +@typing_extensions.final +class DoOneResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RET1_FIELD_NUMBER: builtins.int + ret1: builtins.bool + def __init__( + self, + *, + ret1: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ret1", b"ret1"]) -> None: ... + +global___DoOneResponse = DoOneResponse + +@typing_extensions.final +class DoOneServerStreamRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ARG1_FIELD_NUMBER: builtins.int + name: builtins.str + arg1: builtins.str + def __init__( + self, + *, + name: builtins.str = ..., + arg1: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["arg1", b"arg1", "name", b"name"]) -> None: ... + +global___DoOneServerStreamRequest = DoOneServerStreamRequest + +@typing_extensions.final +class DoOneServerStreamResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RET1_FIELD_NUMBER: builtins.int + ret1: builtins.bool + def __init__( + self, + *, + ret1: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ret1", b"ret1"]) -> None: ... + +global___DoOneServerStreamResponse = DoOneServerStreamResponse + +@typing_extensions.final +class DoOneClientStreamRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ARG1_FIELD_NUMBER: builtins.int + name: builtins.str + arg1: builtins.str + def __init__( + self, + *, + name: builtins.str = ..., + arg1: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["arg1", b"arg1", "name", b"name"]) -> None: ... + +global___DoOneClientStreamRequest = DoOneClientStreamRequest + +@typing_extensions.final +class DoOneClientStreamResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RET1_FIELD_NUMBER: builtins.int + ret1: builtins.bool + def __init__( + self, + *, + ret1: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ret1", b"ret1"]) -> None: ... + +global___DoOneClientStreamResponse = DoOneClientStreamResponse + +@typing_extensions.final +class DoOneBiDiStreamRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ARG1_FIELD_NUMBER: builtins.int + name: builtins.str + arg1: builtins.str + def __init__( + self, + *, + name: builtins.str = ..., + arg1: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["arg1", b"arg1", "name", b"name"]) -> None: ... + +global___DoOneBiDiStreamRequest = DoOneBiDiStreamRequest + +@typing_extensions.final +class DoOneBiDiStreamResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RET1_FIELD_NUMBER: builtins.int + ret1: builtins.bool + def __init__( + self, + *, + ret1: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ret1", b"ret1"]) -> None: ... + +global___DoOneBiDiStreamResponse = DoOneBiDiStreamResponse + +@typing_extensions.final +class DoTwoRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ARG1_FIELD_NUMBER: builtins.int + name: builtins.str + arg1: builtins.bool + def __init__( + self, + *, + name: builtins.str = ..., + arg1: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["arg1", b"arg1", "name", b"name"]) -> None: ... + +global___DoTwoRequest = DoTwoRequest + +@typing_extensions.final +class DoTwoResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RET1_FIELD_NUMBER: builtins.int + ret1: builtins.str + def __init__( + self, + *, + ret1: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ret1", b"ret1"]) -> None: ... + +global___DoTwoResponse = DoTwoResponse diff --git a/examples/complex_module/src/proto/summation.proto b/examples/complex_module/src/proto/summation.proto new file mode 100644 index 000000000..caa041acf --- /dev/null +++ b/examples/complex_module/src/proto/summation.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package acme.service.summation.v1; + +import "google/api/annotations.proto"; + +service SummationService { + rpc Sum(SumRequest) returns (SumResponse) { + option (google.api.http) = { + post: "/acme/api/v1/service/summation/{name}/sum" + }; + } +} + +message SumRequest { + string name = 1; + repeated double numbers = 2; +} + +message SumResponse { + double sum = 1; +} diff --git a/examples/complex_module/src/proto/summation_grpc.py b/examples/complex_module/src/proto/summation_grpc.py new file mode 100644 index 000000000..acb14a5fc --- /dev/null +++ b/examples/complex_module/src/proto/summation_grpc.py @@ -0,0 +1,40 @@ +# Generated by the Protocol Buffers compiler. DO NOT EDIT! +# source: src/proto/summation.proto +# plugin: grpclib.plugin.main +import abc +import typing + +import grpclib.const +import grpclib.client + +if typing.TYPE_CHECKING: + import grpclib.server + +import google.api.annotations_pb2 +from .. import proto + + +class SummationServiceBase(abc.ABC): + @abc.abstractmethod + async def Sum(self, stream: "grpclib.server.Stream[proto.summation_pb2.SumRequest, proto.summation_pb2.SumResponse]") -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return { + "/acme.service.summation.v1.SummationService/Sum": grpclib.const.Handler( + self.Sum, + grpclib.const.Cardinality.UNARY_UNARY, + proto.summation_pb2.SumRequest, + proto.summation_pb2.SumResponse, + ), + } + + +class SummationServiceStub: + def __init__(self, channel: grpclib.client.Channel) -> None: + self.Sum = grpclib.client.UnaryUnaryMethod( + channel, + "/acme.service.summation.v1.SummationService/Sum", + proto.summation_pb2.SumRequest, + proto.summation_pb2.SumResponse, + ) diff --git a/examples/complex_module/src/proto/summation_pb2.py b/examples/complex_module/src/proto/summation_pb2.py new file mode 100644 index 000000000..f8dba3c91 --- /dev/null +++ b/examples/complex_module/src/proto/summation_pb2.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: src/proto/summation.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import builder as _builder +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19src/proto/summation.proto\x12\x19\x61\x63me.service.summation.v1\x1a\x1cgoogle/api/annotations.proto\":\n\nSumRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07numbers\x18\x02 \x03(\x01R\x07numbers\"\x1f\n\x0bSumResponse\x12\x10\n\x03sum\x18\x01 \x01(\x01R\x03sum2\x9c\x01\n\x10SummationService\x12\x87\x01\n\x03Sum\x12%.acme.service.summation.v1.SumRequest\x1a&.acme.service.summation.v1.SumResponse\"1\x82\xd3\xe4\x93\x02+\")/acme/api/v1/service/summation/{name}/sumb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'src.proto.summation_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _SUMMATIONSERVICE.methods_by_name['Sum']._options = None + _SUMMATIONSERVICE.methods_by_name['Sum']._serialized_options = b'\202\323\344\223\002+\")/acme/api/v1/service/summation/{name}/sum' + _SUMREQUEST._serialized_start=86 + _SUMREQUEST._serialized_end=144 + _SUMRESPONSE._serialized_start=146 + _SUMRESPONSE._serialized_end=177 + _SUMMATIONSERVICE._serialized_start=180 + _SUMMATIONSERVICE._serialized_end=336 +# @@protoc_insertion_point(module_scope) diff --git a/examples/complex_module/src/proto/summation_pb2.pyi b/examples/complex_module/src/proto/summation_pb2.pyi new file mode 100644 index 000000000..21d204dcb --- /dev/null +++ b/examples/complex_module/src/proto/summation_pb2.pyi @@ -0,0 +1,51 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class SumRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + NUMBERS_FIELD_NUMBER: builtins.int + name: builtins.str + @property + def numbers(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: ... + def __init__( + self, + *, + name: builtins.str = ..., + numbers: collections.abc.Iterable[builtins.float] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["name", b"name", "numbers", b"numbers"]) -> None: ... + +global___SumRequest = SumRequest + +@typing_extensions.final +class SumResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SUM_FIELD_NUMBER: builtins.int + sum: builtins.float + def __init__( + self, + *, + sum: builtins.float = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["sum", b"sum"]) -> None: ... + +global___SumResponse = SumResponse diff --git a/examples/complex_module/src/summation/__init__.py b/examples/complex_module/src/summation/__init__.py new file mode 100644 index 000000000..90a61268f --- /dev/null +++ b/examples/complex_module/src/summation/__init__.py @@ -0,0 +1,9 @@ +""" +This file registers the Summation API with the Viam Registry, as well as the specific MySummation model. +""" + +from viam.resource.registry import Registry, ResourceRegistration + +from .api import SummationClient, SummationRPCService, SummationService + +Registry.register_api(ResourceRegistration(SummationService, SummationRPCService, lambda name, channel: SummationClient(name, channel))) diff --git a/examples/complex_module/src/summation/api.py b/examples/complex_module/src/summation/api.py new file mode 100644 index 000000000..df11c3aef --- /dev/null +++ b/examples/complex_module/src/summation/api.py @@ -0,0 +1,72 @@ +""" +This file outlines the general structure for the API around a custom, modularized service. + +It defines the abstract class definition that all concrete implementations must follow, +the gRPC service that will handle calls to the service, +and the gRPC client that will be able to make calls to this service. + +In this example, the ``Summation`` abstract class defines what functionality is required for all Summation services. +It extends ``ServiceBase``, as all service types must. +It also defines its specific ``API``, which is used internally to keep track of supported types. + +The ``SummationRPCService`` implements the gRPC service for the Summation service. This will allow other robots and clients to make +requests of the Summation service. It extends both from ``SummationServiceBase`` and ``RPCServiceBase``. +The former is the gRPC service as defined by the proto, and the latter is the class that all gRPC services must inherit from. + +Finally, the ``SummationClient`` is the gRPC client for a Summation service. It inherits from SummationService since it implements + all the same functions. The implementations are simply gRPC calls to some remote Summation service. + +To see how this custom modular service is registered, see the __init__.py file. +To see the custom implementation of this service, see the my_summation.py file. +""" + +import abc +from typing import Final, Sequence + +from grpclib.client import Channel +from grpclib.server import Stream + +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.resource.types import RESOURCE_TYPE_SERVICE, API +from viam.services.service_base import ServiceBase + +from ..proto.summation_grpc import SummationServiceBase, SummationServiceStub +from ..proto.summation_pb2 import SumRequest, SumResponse + + +class SummationService(ServiceBase): + """Example service to use with the example module""" + + API: Final = API("acme", RESOURCE_TYPE_SERVICE, "summation") + + @abc.abstractmethod + async def sum(self, nums: Sequence[float]) -> float: + ... + + +class SummationRPCService(SummationServiceBase, ResourceRPCServiceBase): + """Example gRPC service for the Summation service""" + + RESOURCE_TYPE = SummationService + + async def Sum(self, stream: Stream[SumRequest, SumResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + service = self.get_resource(name) + resp = await service.sum(request.numbers) + await stream.send_message(SumResponse(sum=resp)) + + +class SummationClient(SummationService): + """Example gRPC client for the Summation Service""" + + def __init__(self, name: str, channel: Channel) -> None: + self.channel = channel + self.client = SummationServiceStub(channel) + super().__init__(name) + + async def sum(self, nums: Sequence[float]) -> float: + request = SumRequest(name=self.name, numbers=nums) + response: SumResponse = await self.client.Sum(request) + return response.sum diff --git a/examples/complex_module/src/summation/my_summation.py b/examples/complex_module/src/summation/my_summation.py new file mode 100644 index 000000000..7eca4f29c --- /dev/null +++ b/examples/complex_module/src/summation/my_summation.py @@ -0,0 +1,48 @@ +from typing import ClassVar, Mapping, Sequence + +from typing_extensions import Self + +from viam.module.types import Reconfigurable +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ResourceName +from viam.resource.base import ResourceBase +from viam.resource.registry import Registry, ResourceCreatorRegistration +from viam.resource.types import Model + +from ..summation.api import SummationService + + +class MySummationService(SummationService, Reconfigurable): + """This is the specific implementation of a ``SummationService`` (defined in api.py) + + It inherits from SummationService, as well as conforms to the ``Reconfigurable`` protocol, which signifies that this component can be + reconfigured. It also specifies a function ``MySummationService.new``, which conforms to the ``resource.types.ResourceCreator`` type, + which is required for all models. + """ + + MODEL: ClassVar[Model] = Model.from_string("acme:demo:mysum") + subtract: bool + + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: + summer = cls(config.name) + summer.subtract = config.attributes.fields["subtract"].bool_value or False + return summer + + async def sum(self, nums: Sequence[float]) -> float: + if len(nums) <= 0: + raise ValueError("Must provided at least one number to sum") + + result = 0 + for num in nums: + if self.subtract: + result -= num + else: + result += num + return result + + def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]): + self.subtract = config.attributes.fields["subtract"].bool_value or False + + +Registry.register_resource_creator(SummationService.API, MySummationService.MODEL, ResourceCreatorRegistration(MySummationService.new)) diff --git a/examples/easy_resource/.gitignore b/examples/easy_resource/.gitignore new file mode 100644 index 000000000..849ddff3b --- /dev/null +++ b/examples/easy_resource/.gitignore @@ -0,0 +1 @@ +dist/ diff --git a/examples/easy_resource/Makefile b/examples/easy_resource/Makefile new file mode 100644 index 000000000..038ba1db4 --- /dev/null +++ b/examples/easy_resource/Makefile @@ -0,0 +1,2 @@ +dist/main: main.py + pyinstaller --onefile --hidden-import="googleapiclient" $^ diff --git a/examples/easy_resource/README.md b/examples/easy_resource/README.md new file mode 100644 index 000000000..b145c5e7a --- /dev/null +++ b/examples/easy_resource/README.md @@ -0,0 +1,41 @@ +# easy_resource + +This is an example using the `EasyResource` mixin, a helper for creating Viam modules. + +## Instructions + +### Run standalone in this directory + +If you know how to set up a python virtualenv, do that first to give yourself an isolated environment. (If not, it's okay to proceed without it). + +```sh +# install the SDK +pip install viam-sdk +# or if you plan to edit the SDK: `pip install -e ../..` + +# run the module +./main.py socket-path + +# Ctrl-C the running process when you're done +``` + +Running it this way won't do anything interesting, but it's a quick way to ensure your module can boot. + +### Run with viam-server + +If you are running viam-server on your laptop, you can use easy_resource as a module. + +```sh +# first, build a binary for the module: +pip install pyinstaller +make dist/main +``` + +Then, in the Viam config builder for your machine: +1. Add a local module with executable path `/PATH/TO/REPO/examples/easy_resource/dist/main`. (Replace `/PATH/TO/REPO`). +1. Then add a local component with type 'sensor' and triplet 'viam:sensor:easy-resource-example' (see screenshot). +1. Save, then go to the control page to interact with the sensor. + +Screenshot of 'local component' screen: + +![Screenshot showing type and triplet of local component](./add-sensor.png) diff --git a/examples/easy_resource/add-sensor.png b/examples/easy_resource/add-sensor.png new file mode 100644 index 000000000..2f205968a Binary files /dev/null and b/examples/easy_resource/add-sensor.png differ diff --git a/examples/easy_resource/main.py b/examples/easy_resource/main.py new file mode 100755 index 000000000..478af25b7 --- /dev/null +++ b/examples/easy_resource/main.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +import asyncio +from viam.components.sensor import Sensor +from viam.resource.easy_resource import EasyResource +from viam.module.module import Module + + +class MySensor(Sensor, EasyResource): + MODEL = "viam:sensor:easy-resource-example" + + async def get_readings(self, **kwargs): + return {"ok": True} + + +if __name__ == '__main__': + asyncio.run(Module.run_from_registry()) diff --git a/examples/echo/v1/client.py b/examples/echo/v1/client.py index 4b5da8f87..a7a7322df 100644 --- a/examples/echo/v1/client.py +++ b/examples/echo/v1/client.py @@ -1,47 +1,38 @@ import asyncio import sys -from viam.rpc.dial import Credentials, DialOptions, dial_direct from viam.proto.rpc.examples.echo import ( - EchoServiceStub, - EchoRequest, + EchoBiDiRequest, EchoMultipleRequest, - EchoBiDiRequest + EchoRequest, + EchoServiceStub, ) +from viam.rpc.dial import DialOptions, dial async def echo(msg: str): - creds = Credentials(type="api-key", payload="supersecretkeyohmy") - opts = DialOptions(credentials=creds, - allow_insecure_with_creds_downgrade=True) - opts = DialOptions(insecure=True) - async with await dial_direct("127.0.0.1:8080", opts) as channel: - service = EchoServiceStub(channel) + opts = DialOptions(insecure=True, disable_webrtc=True) + async with await dial("127.0.0.1:8080", opts) as channel: + service = EchoServiceStub(channel.channel) # Simple Echo request = EchoRequest(message=msg) response = await service.Echo(request) - print(f'Echo response received: {response.message}') + print(f"Echo response received: {response.message}") # Echo Multiple (Unary Stream) request = EchoMultipleRequest(message=msg) async with service.EchoMultiple.open() as stream: await stream.send_message(request, end=True) replies = [reply.message async for reply in stream] - print(f'Echo Multiple response received: {replies}') + print(f"Echo Multiple response received: {replies}") # Echo BiDi (Stream Stream) - requests = [ - EchoBiDiRequest(message=f'First message: {msg}'), - EchoBiDiRequest(message=f'Second message: {msg}') - ] + requests = [EchoBiDiRequest(message=f"First message: {msg}"), EchoBiDiRequest(message=f"Second message: {msg}")] responses = await service.EchoBiDi(requests) - print( - 'Received Echo BiDi response: ' + - f'{[response.message for response in responses]}' - ) + print("Received Echo BiDi response: " + f"{[response.message for response in responses]}") -if __name__ == '__main__': +if __name__ == "__main__": msg = sys.argv[1] if len(sys.argv) > 1 else "foo" asyncio.run(echo(msg)) diff --git a/examples/server/v1/Makefile b/examples/server/v1/Makefile index c87b01ce2..44adbd01b 100644 --- a/examples/server/v1/Makefile +++ b/examples/server/v1/Makefile @@ -1,7 +1,7 @@ ROOT_DIR:=$(shell dirname $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))/../../../..) run_server: - cd ${ROOT_DIR} && poetry run python3 -m examples.server.v1.server + cd ${ROOT_DIR} && uv run python3 -m examples.server.v1.server run_client: - cd ${ROOT_DIR} && poetry run python3 -m examples.server.v1.client + cd ${ROOT_DIR} && uv run python3 -m examples.server.v1.client diff --git a/examples/server/v1/README.md b/examples/server/v1/README.md index 92cfdb7d4..ce2b8bb02 100644 --- a/examples/server/v1/README.md +++ b/examples/server/v1/README.md @@ -13,4 +13,4 @@ If you would like to test a the client against an running robot, you should open ## Server -The `server.py` file shows how to create your own custom Viam server with custom components, all written in python. You can view the example implementations of the custom components in the `compnents.py` file. To start the server, run `make run_server`. If you would then like to connect to the server via a robot client, you can run `make run_client` (with the proper endpoint and credentials, which is the default state of the client). +The `server.py` file shows how to create your own custom Viam server with custom components, all written in python. You can view the example implementations of the custom components in the `components.py` file. To start the server, run `make run_server`. If you would then like to connect to the server via a robot client, you can run `make run_client` (with the proper endpoint and credentials, which is the default state of the client). diff --git a/examples/server/v1/client.py b/examples/server/v1/client.py index 9357ffb4f..a16f53a16 100644 --- a/examples/server/v1/client.py +++ b/examples/server/v1/client.py @@ -5,7 +5,9 @@ from viam.components.arm import Arm, Pose from viam.components.base import Base, Vector3 from viam.components.camera import Camera +from viam.components.encoder import Encoder from viam.components.motor import Motor +from viam.media.video import CameraMimeType from viam.robot.client import RobotClient from viam.rpc.dial import DialOptions @@ -13,7 +15,6 @@ async def client(): opts = RobotClient.Options(dial_options=DialOptions(insecure=True)) async with await RobotClient.at_address("localhost:9090", opts) as robot: - print("\n#### RESOURCES ####") print(f"Resources: {robot.resource_names}") @@ -33,12 +34,16 @@ async def client(): print("\n#### CAMERA ####") camera = Camera.from_robot(robot, "camera0") - img = await camera.get_image() + img = await camera.get_image(mime_type=CameraMimeType.PNG) assert isinstance(img, Image) img.show() await asyncio.sleep(1) img.close() + print("\n#### ENCODER ####") + encoder = Encoder.from_robot(robot, "encoder0") + await encoder.get_position() + print("\n#### MOTOR ####") motor = Motor.from_robot(robot, "motor0") await motor.go_for(rpm=100, revolutions=10) diff --git a/examples/server/v1/components.py b/examples/server/v1/components.py index 9c49ead93..281d83947 100644 --- a/examples/server/v1/components.py +++ b/examples/server/v1/components.py @@ -1,17 +1,28 @@ import asyncio +import math import random import struct -from multiprocessing import Lock, Queue +import sys + +if sys.version_info >= (3, 9): + from collections.abc import AsyncIterator +else: + from typing import AsyncIterator + +from datetime import timedelta +from io import BytesIO +from multiprocessing import Lock from pathlib import Path from typing import Any, Dict, List, Mapping, Optional, Tuple from PIL import Image from viam.components.arm import Arm +from viam.components.audio_input import AudioInput from viam.components.base import Base -from viam.components.board import Board -from viam.components.board.board import PostProcessor +from viam.components.board import Board, TickStream from viam.components.camera import Camera +from viam.components.encoder import Encoder from viam.components.gantry import Gantry from viam.components.gripper import Gripper from viam.components.input import Control, ControlFunction, Controller, Event, EventType @@ -20,20 +31,32 @@ from viam.components.pose_tracker import PoseTracker from viam.components.sensor import Sensor from viam.components.servo import Servo -from viam.components.types import CameraMimeType -from viam.errors import ComponentNotFoundError +from viam.errors import ResourceNotFoundError +from viam.streams import StreamWithIterator +from viam.media.audio import Audio, AudioStream +from viam.media.video import CameraMimeType, NamedImage, ViamImage +from viam.operations import run_with_operation from viam.proto.common import ( - AnalogStatus, - BoardStatus, - DigitalInterruptStatus, + Capsule, + Geometry, GeoPoint, + KinematicsFileFormat, Orientation, Pose, PoseInFrame, + ResponseMetadata, + Sphere, Vector3, - WorldState, ) from viam.proto.component.arm import JointPositions +from viam.proto.component.audioinput import AudioChunk, AudioChunkInfo, SampleFormat +from viam.proto.component.encoder import PositionType +from viam.utils import SensorReading + +GEOMETRIES = [ + Geometry(center=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20), sphere=Sphere(radius_mm=2)), + Geometry(center=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20), capsule=Capsule(radius_mm=3, length_mm=8)), +] class ExampleArm(Arm): @@ -49,33 +72,106 @@ def __init__(self, name: str): ) self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0]) self.is_stopped = True + self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02") super().__init__(name) - async def get_end_position(self, extra: Optional[Dict[str, Any]] = None) -> Pose: + async def get_end_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Pose: return self.position async def move_to_position( self, pose: Pose, - world_state: Optional[WorldState] = None, extra: Optional[Dict[str, Any]] = None, + **kwargs, ): self.is_stopped = False self.position = pose - async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None) -> JointPositions: + async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> JointPositions: return self.joint_positions - async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None): + async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None, **kwargs): self.is_stopped = False self.joint_positions = positions - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs): self.is_stopped = True async def is_moving(self): return not self.is_stopped + async def get_kinematics(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[KinematicsFileFormat.ValueType, bytes]: + return self.kinematics + + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + + +class ExampleAudioInput(AudioInput): + def __init__(self, name: str): + super().__init__(name) + self.latency = timedelta(milliseconds=20) + self.sample_rate = 48_000 + self.channel_count = 1 + self.step = 0 + self.timer = asyncio.get_event_loop().call_later(self.latency.total_seconds(), self.run_task) + + def run_task(self): + self.step += 1 + self.timer = asyncio.get_event_loop().call_later(self.latency.total_seconds(), self.run_task) + + def __del__(self): + self.timer.cancel() + + @run_with_operation + async def stream(self, **kwargs) -> AudioStream: + """Generate and stream a sine wave at 440Hz""" + operation = self.get_operation(kwargs) + + length = self.sample_rate * self.latency.total_seconds() # the length of the sample + num_chunks = self.sample_rate / length # the number of chunks needed + angle = math.pi * 2 / (length * num_chunks) + + async def read() -> AsyncIterator[Audio]: + while True: + if await operation.is_cancelled(): + break + output = bytes() + step = int(self.step % num_chunks) # current location on the sine wave + for i in range(int(length)): + value = float(10) * math.sin(angle * 440 * (float(length * step) + i)) # calculate the value at the current location + output += bytes(struct.pack("f", value)) + + yield Audio( + AudioChunkInfo( + sample_format=SampleFormat.SAMPLE_FORMAT_FLOAT32_INTERLEAVED, + channels=self.channel_count, + sampling_rate=self.sample_rate, + ), + AudioChunk( + data=output, + length=int(length), + ), + ) + + await asyncio.sleep(self.latency.total_seconds()) + + return StreamWithIterator(read()) + + async def get_properties(self) -> AudioInput.Properties: + return AudioInput.Properties( + channel_count=self.channel_count, + latency=self.latency, + sample_rate=self.sample_rate, + sample_size=4, + is_big_endian=sys.byteorder != "little", + is_float=True, + is_interleaved=True, + ) + + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + class ExampleBase(Base): def __init__(self, name: str): @@ -86,9 +182,10 @@ def __init__(self, name: str): self.angular_pwr = Vector3(x=0, y=0, z=0) self.linear_vel = Vector3(x=0, y=0, z=0) self.angular_vel = Vector3(x=0, y=0, z=0) + self.props = Base.Properties(1.0, 1.0, 1.0) super().__init__(name) - async def move_straight(self, distance: int, velocity: float, extra: Optional[Dict[str, Any]] = None): + async def move_straight(self, distance: int, velocity: float, extra: Optional[Dict[str, Any]] = None, **kwargs): if distance == 0 or velocity == 0: return await self.stop() @@ -99,7 +196,7 @@ async def move_straight(self, distance: int, velocity: float, extra: Optional[Di self.is_stopped = False - async def spin(self, angle: float, velocity: float, extra: Optional[Dict[str, Any]] = None): + async def spin(self, angle: float, velocity: float, extra: Optional[Dict[str, Any]] = None, **kwargs): if angle == 0 or velocity == 0: return await self.stop() @@ -110,53 +207,47 @@ async def spin(self, angle: float, velocity: float, extra: Optional[Dict[str, An self.is_stopped = False - async def set_power(self, linear: Vector3, angular: Vector3, extra: Optional[Dict[str, Any]] = None): + async def set_power(self, linear: Vector3, angular: Vector3, extra: Optional[Dict[str, Any]] = None, **kwargs): self.linear_pwr = linear - self.anglular_pwr = angular + self.angular_pwr = angular - async def set_velocity(self, linear: Vector3, angular: Vector3, extra: Optional[Dict[str, Any]] = None): + async def set_velocity(self, linear: Vector3, angular: Vector3, extra: Optional[Dict[str, Any]] = None, **kwargs): self.linear_vel = linear - self.anglular_vel = angular + self.angular_vel = angular - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs): self.is_stopped = True async def is_moving(self): return not self.is_stopped + async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> Base.Properties: + return self.props + + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES -class ExampleAnalogReader(Board.AnalogReader): + +class ExampleAnalog(Board.Analog): def __init__(self, name: str, value: int): self.value = value super().__init__(name) - async def read(self, extra: Optional[Dict[str, Any]] = None) -> int: + async def read(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> int: return self.value + async def write(self, value: int, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float], **kwargs): + self.value = value + class ExampleDigitalInterrupt(Board.DigitalInterrupt): def __init__(self, name: str): - self.high = False - self.last_tick = 0 self.num_ticks = 0 - self.callbacks: List[Queue] = [] - self.post_processors: List[PostProcessor] = [] super().__init__(name) - async def value(self, extra: Optional[Dict[str, Any]] = None) -> int: + async def value(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> int: return self.num_ticks - async def tick(self, high: bool, nanos: int): - self.high = high - self.last_tick = nanos - self.num_ticks += 1 - - async def add_callback(self, queue: Queue): - self.callbacks.append(queue) - - async def add_post_processor(self, processor: PostProcessor): - self.post_processors.append(processor) - class ExampleGPIOPin(Board.GPIOPin): def __init__(self, name: str): @@ -165,22 +256,22 @@ def __init__(self, name: str): self.pwm_freq = 0 super().__init__(name) - async def get(self, extra: Optional[Dict[str, Any]] = None) -> bool: + async def get(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> bool: return self.high - async def set(self, high: bool, extra: Optional[Dict[str, Any]] = None): + async def set(self, high: bool, extra: Optional[Dict[str, Any]] = None, **kwargs): self.high = high - async def get_pwm(self, extra: Optional[Dict[str, Any]] = None) -> float: + async def get_pwm(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> float: return self.pwm - async def set_pwm(self, duty_cycle: float, extra: Optional[Dict[str, Any]] = None): + async def set_pwm(self, duty_cycle: float, extra: Optional[Dict[str, Any]] = None, **kwargs): self.pwm = duty_cycle - async def get_pwm_frequency(self, extra: Optional[Dict[str, Any]] = None) -> int: + async def get_pwm_frequency(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> int: return self.pwm_freq - async def set_pwm_frequency(self, frequency: int, extra: Optional[Dict[str, Any]] = None): + async def set_pwm_frequency(self, frequency: int, extra: Optional[Dict[str, Any]] = None, **kwargs): self.pwm_freq = frequency @@ -188,67 +279,70 @@ class ExampleBoard(Board): def __init__( self, name: str, - analog_readers: Dict[str, Board.AnalogReader], + analogs: Dict[str, Board.Analog], digital_interrupts: Dict[str, Board.DigitalInterrupt], gpio_pins: Dict[str, Board.GPIOPin], ): - self.analog_readers = analog_readers + self.analogs = analogs self.digital_interrupts = digital_interrupts self.gpios = gpio_pins super().__init__(name) - async def analog_reader_by_name(self, name: str) -> Board.AnalogReader: + async def analog_by_name(self, name: str) -> Board.Analog: try: - return self.analog_readers[name] + return self.analogs[name] except KeyError: - raise ComponentNotFoundError("Board.AnalogReader", name) + raise ResourceNotFoundError("Board.Analog", name) async def digital_interrupt_by_name(self, name: str) -> Board.DigitalInterrupt: try: return self.digital_interrupts[name] except KeyError: - raise ComponentNotFoundError("Board.DigitalInterrupt", name) + raise ResourceNotFoundError("Board.DigitalInterrupt", name) async def gpio_pin_by_name(self, name: str) -> Board.GPIOPin: try: return self.gpios[name] except KeyError: - raise ComponentNotFoundError("Board.GPIOPin", name) - - async def analog_reader_names(self) -> List[str]: - return [key for key in self.analog_readers.keys()] + raise ResourceNotFoundError("Board.GPIOPin", name) - async def digital_interrupt_names(self) -> List[str]: - return [key for key in self.digital_interrupts.keys()] + async def set_power_mode(self, **kwargs): + raise NotImplementedError() - async def status(self, extra: Optional[Dict[str, Any]] = None) -> BoardStatus: - return BoardStatus( - analogs={name: AnalogStatus(value=await analog.read()) for (name, analog) in self.analog_readers.items()}, - digital_interrupts={name: DigitalInterruptStatus(value=await di.value()) for (name, di) in self.digital_interrupts.items()}, - ) + async def stream_ticks(self, interrupts: List[Board.DigitalInterrupt], *, timeout: Optional[float] = None, **kwargs) -> TickStream: + raise NotImplementedError() - async def model_attributes(self) -> Board.Attributes: - return Board.Attributes(remote=True) + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES class ExampleCamera(Camera): def __init__(self, name: str): p = Path(__file__) - self.image = Image.open(p.parent.absolute().joinpath("viam.webp")) + img = Image.open(p.parent.absolute().joinpath("viam.jpeg")) + buf = BytesIO() + img.copy().save(buf, format="JPEG") + self.image = ViamImage(buf.getvalue(), CameraMimeType.JPEG) + img.close() super().__init__(name) - async def get_image(self, mime_type: str = CameraMimeType.PNG) -> Image.Image: - return self.image.copy() + async def get_image(self, mime_type: str = "", extra: Optional[Dict[str, Any]] = None, **kwargs) -> ViamImage: + return self.image - async def get_point_cloud(self) -> Tuple[bytes, str]: + async def get_images(self, timeout: Optional[float] = None, **kwargs) -> Tuple[List[NamedImage], ResponseMetadata]: raise NotImplementedError() - async def get_properties(self) -> Camera.Properties: + async def get_point_cloud(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[bytes, str]: raise NotImplementedError() + async def get_properties(self, **kwargs) -> Camera.Properties: + raise NotImplementedError() -class ExampleController(Controller): + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + +class ExampleController(Controller): CONTROL_MAP: Dict[int, Control] = { 0: Control.ABSOLUTE_X, 1: Control.ABSOLUTE_Y, @@ -314,7 +408,7 @@ def _execute_callback(self, event): if callback: callback(event) - async def get_controls(self) -> List[Control]: + async def get_controls(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Control]: return [ Control.ABSOLUTE_X, Control.ABSOLUTE_Y, @@ -337,7 +431,7 @@ async def get_controls(self) -> List[Control]: Control.BUTTON_MENU, ] - async def get_events(self) -> Dict[Control, Event]: + async def get_events(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Dict[Control, Event]: with self.lock: return {key: value for (key, value) in self.last_events.items()} @@ -352,6 +446,32 @@ def register_control_callback(self, control: Control, triggers: List[EventType], callbacks[trigger] = function self.callbacks[control] = callbacks + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + + +class ExampleEncoder(Encoder): + def __init__(self, name: str): + self.position: float = 0 + self.position_type = PositionType.POSITION_TYPE_TICKS_COUNT + self.powered = False + self.task = None + super().__init__(name) + + async def reset_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs): + self.position = 0 + + async def get_position( + self, position_type: Optional[PositionType.ValueType], extra: Optional[Dict[str, Any]] = None, **kwargs + ) -> Tuple[float, PositionType.ValueType]: + return self.position, self.position_type + + async def get_properties(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Encoder.Properties: + return Encoder.Properties(ticks_count_supported=True, angle_degrees_supported=False) + + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + class ExampleGantry(Gantry): def __init__(self, name: str, position: List[float], lengths: List[float]): @@ -360,27 +480,34 @@ def __init__(self, name: str, position: List[float], lengths: List[float]): self.is_stopped = True super().__init__(name) - async def get_position(self, extra: Optional[Dict[str, Any]] = None) -> List[float]: + async def get_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[float]: return self.position async def move_to_position( self, positions: List[float], - world_state: Optional[WorldState] = None, + speeds: List[float], extra: Optional[Dict[str, Any]] = None, + **kwargs, ): self.position = positions self.is_stopped = False - async def get_lengths(self, extra: Optional[Dict[str, Any]] = None) -> List[float]: + async def get_lengths(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[float]: return self.lengths - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def home(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> bool: + return True + + async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs): self.is_stopped = True async def is_moving(self): return not self.is_stopped + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + class ExampleGripper(Gripper): def __init__(self, name: str): @@ -388,21 +515,24 @@ def __init__(self, name: str): self.is_stopped = True super().__init__(name) - async def open(self): + async def open(self, extra: Optional[Dict[str, Any]] = None, **kwargs): self.opened = True self.is_stopped = False - async def grab(self) -> bool: + async def grab(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> bool: self.opened = False self.is_stopped = False return random.choice([True, False]) - async def stop(self): + async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs): self.is_stopped = True async def is_moving(self): return not self.is_stopped + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + class ExampleMotor(Motor): def __init__(self, name: str): @@ -418,7 +548,7 @@ async def run_continuously(self, rpm: float): await asyncio.sleep(1) self.position += rps - async def set_power(self, power: float, extra: Optional[Dict[str, Any]] = None): + async def set_power(self, power: float, extra: Optional[Dict[str, Any]] = None, **kwargs): self.power = power self.powered = power != 0 if self.powered: @@ -427,7 +557,7 @@ async def set_power(self, power: float, extra: Optional[Dict[str, Any]] = None): if self.task: self.task.cancel() - async def go_for(self, rpm: float, revolutions: float, extra: Optional[Dict[str, Any]] = None): + async def go_for(self, rpm: float, revolutions: float, extra: Optional[Dict[str, Any]] = None, **kwargs): if self.task: self.task.cancel() target = 0 @@ -442,7 +572,7 @@ async def go_for(self, rpm: float, revolutions: float, extra: Optional[Dict[str, self.position += rps self.powered = False - async def go_to(self, rpm: float, position_revolutions: float, extra: Optional[Dict[str, Any]] = None): + async def go_to(self, rpm: float, position_revolutions: float, extra: Optional[Dict[str, Any]] = None, **kwargs): if self.task: self.task.cancel() distance = position_revolutions - self.position @@ -454,28 +584,37 @@ async def go_to(self, rpm: float, position_revolutions: float, extra: Optional[D self.position += rps self.powered = False - async def reset_zero_position(self, offset: float, extra: Optional[Dict[str, Any]] = None): + async def set_rpm(self, rpm: float, extra: Optional[Dict[str, Any]] = None, **kwargs): + if self.task: + self.task.cancel() + self.powered = True + self.task = asyncio.create_task(self.run_continuously(rpm)) + + async def reset_zero_position(self, offset: float, extra: Optional[Dict[str, Any]] = None, **kwargs): if (self.position > 0 and offset > 0) or (self.position < 0 and offset < 0): self.position = offset else: self.position += offset self.powered = False - async def get_position(self, extra: Optional[Dict[str, Any]] = None) -> float: + async def get_position(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> float: return self.position - async def get_properties(self, extra: Optional[Dict[str, Any]] = None) -> Motor.Properties: + async def get_properties(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Motor.Properties: return Motor.Properties(position_reporting=True) - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop(self, extra: Optional[Dict[str, Any]] = None, **kwargs): await self.set_power(0) - async def is_powered(self, extra: Optional[Dict[str, Any]] = None) -> bool: - return self.powered + async def is_powered(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Tuple[bool, float]: + return self.powered, self.power async def is_moving(self): return self.powered + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + class ExampleMovementSensor(MovementSensor): def __init__( @@ -485,60 +624,78 @@ def __init__( altitude: float, lin_vel: Vector3, ang_vel: Vector3, + lin_acc: Vector3, heading: float, orientation: Orientation, properties: MovementSensor.Properties, accuracy: Mapping[str, float], ): super().__init__(name) + self.num_readings = random.randint(1, 10) self.coordinates = coordinates self.altitude = altitude self.lin_vel = lin_vel self.ang_vel = ang_vel + self.lin_acc = lin_acc self.heading = heading self.orientation = orientation self.properties = properties self.accuracy = accuracy - async def get_position(self) -> Tuple[GeoPoint, float]: + async def get_readings(self, **kwargs) -> Mapping[str, SensorReading]: + return {"abcdefghij"[idx]: random.random() for idx in range(self.num_readings)} + + async def get_position(self, **kwargs) -> Tuple[GeoPoint, float]: return (self.coordinates, self.altitude) - async def get_linear_velocity(self) -> Vector3: + async def get_linear_velocity(self, **kwargs) -> Vector3: return self.lin_vel - async def get_angular_velocity(self) -> Vector3: + async def get_angular_velocity(self, **kwargs) -> Vector3: return self.ang_vel - async def get_compass_heading(self) -> float: + async def get_linear_acceleration(self, **kwargs) -> Vector3: + return self.lin_acc + + async def get_compass_heading(self, **kwargs) -> float: return self.heading - async def get_orientation(self) -> Orientation: + async def get_orientation(self, **kwargs) -> Orientation: return self.orientation - async def get_properties(self) -> MovementSensor.Properties: + async def get_properties(self, **kwargs) -> MovementSensor.Properties: return self.properties - async def get_accuracy(self) -> Mapping[str, float]: + async def get_accuracy(self, **kwargs) -> Mapping[str, float]: return self.accuracy + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + class ExamplePoseTracker(PoseTracker): - async def get_poses(self, body_names: List[str]) -> Dict[str, PoseInFrame]: + async def get_poses(self, body_names: List[str], **kwargs) -> Dict[str, PoseInFrame]: all_poses = { "body1": PoseInFrame(reference_frame="0", pose=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)), "body2": PoseInFrame(reference_frame="0", pose=Pose(x=3, y=2, z=3, o_x=4, o_y=3, o_z=4, theta=40)), } return {k: v for k, v in all_poses.items() if k in body_names} + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + class ExampleSensor(Sensor): def __init__(self, name: str): self.num_readings = random.randint(1, 10) super().__init__(name) - async def get_readings(self) -> Mapping[str, Any]: + async def get_readings(self, **kwargs) -> Mapping[str, SensorReading]: return {"abcdefghij"[idx]: random.random() for idx in range(self.num_readings)} + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES + class ExampleServo(Servo): def __init__(self, name: str): @@ -546,15 +703,18 @@ def __init__(self, name: str): self.is_stopped = True super().__init__(name) - async def move(self, angle: int): + async def move(self, angle: int, **kwargs): self.angle = angle self.is_stopped = False - async def get_position(self) -> int: + async def get_position(self, **kwargs) -> int: return self.angle - async def stop(self): + async def stop(self, **kwargs): self.is_stopped = True async def is_moving(self): return not self.is_stopped + + async def get_geometries(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> List[Geometry]: + return GEOMETRIES diff --git a/examples/server/v1/server.py b/examples/server/v1/server.py index 9b0428efa..f1f7dfc2e 100644 --- a/examples/server/v1/server.py +++ b/examples/server/v1/server.py @@ -6,12 +6,14 @@ from viam.rpc.server import Server from .components import ( - ExampleAnalogReader, + ExampleAnalog, ExampleArm, + ExampleAudioInput, ExampleBase, ExampleBoard, ExampleCamera, ExampleDigitalInterrupt, + ExampleEncoder, ExampleGantry, ExampleGPIOPin, ExampleGripper, @@ -22,15 +24,17 @@ ExampleServo, MovementSensor, ) +from .services import ExampleMLModel, ExampleSLAM async def run(host: str, port: int, log_level: int): my_arm = ExampleArm("arm0") + my_audio_input = ExampleAudioInput("audio_input0") my_base = ExampleBase("base0") my_board = ExampleBoard( name="board", - analog_readers={ - "reader1": ExampleAnalogReader("reader1", 3), + analogs={ + "reader1": ExampleAnalog("reader1", 3), }, digital_interrupts={ "interrupt1": ExampleDigitalInterrupt("interrupt1"), @@ -40,8 +44,10 @@ async def run(host: str, port: int, log_level: int): }, ) my_camera = ExampleCamera("camera0") + my_encoder = ExampleEncoder("encoder0") my_gantry = ExampleGantry("gantry0", [1, 2, 3], [4, 5, 6]) my_gripper = ExampleGripper("gripper0") + my_mlmodel = ExampleMLModel("mlmodel0") my_motor = ExampleMotor("motor0") my_movement_sensor = ExampleMovementSensor( "movement_sensor0", @@ -49,9 +55,11 @@ async def run(host: str, port: int, log_level: int): 15, Vector3(x=1, y=2, z=3), Vector3(x=1, y=2, z=3), + Vector3(x=1, y=2, z=3), 182, Orientation(o_x=1, o_y=2, o_z=3, theta=5), MovementSensor.Properties( + linear_acceleration_supported=False, linear_velocity_supported=False, angular_velocity_supported=True, orientation_supported=False, @@ -63,19 +71,24 @@ async def run(host: str, port: int, log_level: int): my_pose_tracker = ExamplePoseTracker("pose_tracker0") my_sensor = ExampleSensor("sensor0") my_servo = ExampleServo("servo0") + my_slam = ExampleSLAM("slam0") server = Server( - components=[ + resources=[ my_arm, + my_audio_input, my_base, my_board, my_camera, + my_encoder, my_gantry, my_gripper, + my_mlmodel, my_motor, my_movement_sensor, my_pose_tracker, my_sensor, my_servo, + my_slam, ] ) await server.serve(host=host, port=port, log_level=log_level) diff --git a/examples/server/v1/services.py b/examples/server/v1/services.py new file mode 100644 index 000000000..511f8b10a --- /dev/null +++ b/examples/server/v1/services.py @@ -0,0 +1,41 @@ +from numpy.typing import NDArray +from typing import Dict, List, Optional, Tuple +from tests.mocks.services import MockMLModel, MockSLAM +from viam.services.slam import Pose, MappingMode, SLAM +from viam.services.mlmodel import Metadata, MLModel + + +class ExampleMLModel(MLModel): + def __init__(self, name: str): + self.input_data = MockMLModel.EMPTY_NDARRAYS + self.output_data = MockMLModel.EMPTY_NDARRAYS + self.meta = MockMLModel.META + super().__init__(name) + + async def infer(self, input_tensors: Dict[str, NDArray], *, timeout: Optional[float] = None) -> Dict[str, NDArray]: + return self.output_data + + async def metadata(self, *, timeout: Optional[float] = None) -> Metadata: + return self.meta + + +class ExampleSLAM(SLAM): + def __init__(self, name: str): + self.position = MockSLAM.POSITION + self.internal_chunks = MockSLAM.INTERNAL_STATE_CHUNKS + self.point_cloud_chunks = MockSLAM.POINT_CLOUD_PCD_CHUNKS + self.cloud_slam = MockSLAM.CLOUD_SLAM + self.mapping_mode = MockSLAM.MAPPING_MODE + super().__init__(name) + + async def get_internal_state(self, **kwargs) -> List[bytes]: + return self.internal_chunks + + async def get_point_cloud_map(self, **kwargs) -> List[bytes]: + return self.point_cloud_chunks + + async def get_position(self, **kwargs) -> Pose: + return self.position + + async def get_properties(self, **kwargs) -> Tuple[bool, MappingMode.ValueType]: + return (self.cloud_slam, self.mapping_mode) diff --git a/examples/server/v1/viam.jpeg b/examples/server/v1/viam.jpeg new file mode 100644 index 000000000..cef78c2db Binary files /dev/null and b/examples/server/v1/viam.jpeg differ diff --git a/examples/server/v1/viam.webp b/examples/server/v1/viam.webp deleted file mode 100644 index 25b2d983e..000000000 Binary files a/examples/server/v1/viam.webp and /dev/null differ diff --git a/examples/simple_module/README.md b/examples/simple_module/README.md new file mode 100644 index 000000000..48d99daa7 --- /dev/null +++ b/examples/simple_module/README.md @@ -0,0 +1,60 @@ +# Viam Simple Module Example +This example goes through how to create custom modular resources using Viam's Python SDK, and how to connect it to a machine. + +This is a limited document. For a more in-depth understanding of modules, see the [documentation](https://docs.viam.com/operate/get-started/other-hardware/). + +> [!NOTE] +> You can auto-generate the stub files for your module using the Viam CLI. +> [Install the CLI and authenticate](https://docs.viam.com/dev/tools/cli/#install), then run `viam module generate` and follow the prompts. + +## Purpose +Modular resources allow you to define custom components and services, and add them to your robot. Viam ships with many component models, but you're not limited to only using those models -- you can add support for other models of hardware and software using modules. + +For more information, see the [documentation](hhttps://docs.viam.com/operate/get-started/other-hardware/). For a more complex example, take a look at the [complex module example](https://github.com/viamrobotics/viam-python-sdk/tree/main/examples/complex_module), which contains multiple new APIs and custom resource models. + +For a fully fleshed-out example of a Python module that uses Github CI to upload to the Viam Registry, take a look at [python-example-module](https://github.com/viam-labs/python-example-module). For an example that uses Docker to manage dependencies, take a look at [python-container-module](https://github.com/viamrobotics/python-container-module). For a list of example modules in different Viam SDKs, take a look [here](https://github.com/viamrobotics/upload-module/#example-repos). + +## Project structure +The definition of the new resources are in the `main` file of the `src` directory. + +The `run.sh` script is the entrypoint for a module and calls the `main.py` file. The `main.py` file contains the definition of a new sensor model and code to register it. + +It also has the optional validator and reconfiguration functions. The validator function can raise errors that are triggered because of the configuration. It also returns a sequence of strings representing the implicit dependencies of the resource. The reconfiguration function reconfigures the resource based on the new configuration passed in. + +When called, the program creates and starts the module. Read further to learn how to connect this module to your robot. + +Outside the `src` directory, there is a `client.py` file. You can use this file to test the module once you have connected to your robot and configured the module. You will have to update the credentials and robot address in that file. + +## Configuring and using the module +These steps assume that you have a robot available at [app.viam.com](app.viam.com). + +The `run.sh` script is the entrypoint for this module. To connect this module with your robot, you must add this module's entrypoint to the robot's config. For example, the entrypoint file may be at `/home/viam-python-sdk/examples/simple_module/run.sh` and you must add this file path to your configuration. See the [documentation](https://docs.viam.com/operate/get-started/other-hardware/#upload-your-module) for more details. + +Once the module has been added to your robot, add a new component that uses the `MySensor` model. See the [documentation](https://docs.viam.com/operate/get-started/other-hardware/#add-your-new-modular-resource-to-your-machines) for more details. + +Models are uniquely namespaced as colon-delimited-triplets in the form `namespace:family:name`, and are named according to the Viam API that your model implements. A model with the `viam` namespace is always Viam-provided. Read more about making custom namespaces [here](https://docs.viam.com/operate/reference/naming-modules/#create-a-namespace-for-your-organization). + +An example configuration for a Sensor component could look like this: +```json +{ + "components": [ + { + "name": "sensor1", + "type": "sensor", + "model": "viam:sensor:mysensor", + "attributes": { + "multiplier": 2 + }, + "depends_on": [] + } + ], + "modules": [ + { + "name": "my-module", + "executable_path": "/home/viam-python-sdk/examples/simple_module/run.sh" + } + ] +} +``` + +After the robot has started and connected to the module, you can use the provided `client.py` to connect to your robot and make calls to your custom, modular resources. diff --git a/examples/simple_module/client.py b/examples/simple_module/client.py new file mode 100644 index 000000000..ba48b9240 --- /dev/null +++ b/examples/simple_module/client.py @@ -0,0 +1,29 @@ +import asyncio + +from viam.robot.client import RobotClient +from viam.components.sensor import Sensor + + +async def connect(): + opts = RobotClient.Options.with_api_key(api_key="", api_key_id="") + return await RobotClient.at_address("", opts) + + +async def main(): + robot = await connect() + + print("Resources:") + print(robot.resource_names) + + sensor = Sensor.from_robot(robot, name="sensor1") + reading = await sensor.get_readings() + print(f"The reading is {reading}") + + response = await sensor.do_command({"hello": "world"}) + print(f"The response is {response}") + + await robot.close() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/simple_module/requirements.txt b/examples/simple_module/requirements.txt new file mode 100644 index 000000000..df1db24ca --- /dev/null +++ b/examples/simple_module/requirements.txt @@ -0,0 +1,6 @@ +# Uncomment the following line if you would like to use a release version of the SDK +# viam-sdk + +# This line points to the version of the SDK that lives at this project's root. +# In production, you would likely want to use the above `viam-sdk` requirement. +../.. diff --git a/examples/simple_module/run.sh b/examples/simple_module/run.sh new file mode 100755 index 000000000..ad38bed9d --- /dev/null +++ b/examples/simple_module/run.sh @@ -0,0 +1,42 @@ +#!/bin/sh +cd `dirname $0` + +# Create a virtual environment to run our code +VENV_NAME="venv" +PYTHON="$VENV_NAME/bin/python" +ENV_ERROR="This module requires Python >=3.8, pip, and virtualenv to be installed." + +if ! python3 -m venv $VENV_NAME >/dev/null 2>&1; then + echo "Failed to create virtualenv." + if command -v apt-get >/dev/null; then + echo "Detected Debian/Ubuntu, attempting to install python3-venv automatically." + SUDO="sudo" + if ! command -v $SUDO >/dev/null; then + SUDO="" + fi + if ! apt info python3-venv >/dev/null 2>&1; then + echo "Package info not found, trying apt update" + $SUDO apt -qq update >/dev/null + fi + $SUDO apt install -qqy python3-venv >/dev/null 2>&1 + if ! python3 -m venv $VENV_NAME >/dev/null 2>&1; then + echo $ENV_ERROR >&2 + exit 1 + fi + else + echo $ENV_ERROR >&2 + exit 1 + fi +fi + +# remove -U if viam-sdk should not be upgraded whenever possible +# -qq suppresses extraneous output from pip +echo "Virtualenv found/created. Installing/upgrading Python packages..." +if ! $PYTHON -m pip install -r requirements.txt -Uqq; then + exit 1 +fi + +# Be sure to use `exec` so that termination signals reach the python process, +# or handle forwarding termination signals manually +echo "Starting module..." +exec $PYTHON src/main.py $@ diff --git a/examples/simple_module/src/main.py b/examples/simple_module/src/main.py new file mode 100644 index 000000000..0af1d44c7 --- /dev/null +++ b/examples/simple_module/src/main.py @@ -0,0 +1,74 @@ +import asyncio +from typing import Any, ClassVar, Dict, Mapping, Optional, Sequence + +from typing_extensions import Self + +from viam.components.sensor import Sensor +from viam.logging import getLogger +from viam.module.module import Module +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ResourceName +from viam.resource.base import ResourceBase +from viam.resource.registry import Registry, ResourceCreatorRegistration +from viam.resource.types import Model, ModelFamily +from viam.utils import SensorReading, ValueTypes + +LOGGER = getLogger(__name__) + + +class MySensor(Sensor): + # Subclass the Viam Sensor component and implement the required functions + MODEL: ClassVar[Model] = Model(ModelFamily("viam", "sensor"), "mysensor") + + def __init__(self, name: str): + super().__init__(name) + + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: + sensor = cls(config.name) + sensor.reconfigure(config, dependencies) + return sensor + + @classmethod + def validate_config(cls, config: ComponentConfig) -> Sequence[str]: + if "multiplier" in config.attributes.fields: + if not config.attributes.fields["multiplier"].HasField("number_value"): + raise Exception("Multiplier must be a float.") + multiplier = config.attributes.fields["multiplier"].number_value + if multiplier == 0: + raise Exception("Multiplier cannot be 0.") + return [] + + async def get_readings(self, extra: Optional[Dict[str, Any]] = None, **kwargs) -> Mapping[str, SensorReading]: + return {"signal": 1 * self.multiplier} + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + LOGGER.info(f"received {command=}.") + return command + + def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]): + if "multiplier" in config.attributes.fields: + multiplier = config.attributes.fields["multiplier"].number_value + else: + multiplier = 1.0 + self.multiplier = multiplier + + async def close(self): + # This is a completely optional function to include. This will be called when the resource is removed from the config or the module + # is shutting down. + LOGGER.info(f"{self.name} is closed.") + + +async def main(): + """This function creates and starts a new module, after adding all desired resource models. + Resource creators must be registered to the resource registry before the module adds the resource model. + """ + Registry.register_resource_creator(Sensor.API, MySensor.MODEL, ResourceCreatorRegistration(MySensor.new, MySensor.validate_config)) + + module = Module.from_args() + module.add_model_from_registry(Sensor.API, MySensor.MODEL) + await module.start() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/stubbed_model/Makefile b/examples/stubbed_model/Makefile new file mode 100644 index 000000000..038ba1db4 --- /dev/null +++ b/examples/stubbed_model/Makefile @@ -0,0 +1,2 @@ +dist/main: main.py + pyinstaller --onefile --hidden-import="googleapiclient" $^ diff --git a/examples/stubbed_model/README.md b/examples/stubbed_model/README.md new file mode 100644 index 000000000..1670b70c1 --- /dev/null +++ b/examples/stubbed_model/README.md @@ -0,0 +1,23 @@ +# stub_model + +This is an example using the `stub_model` class decorator, a helper for creating Viam modules. + +`stub_model` adds stub functions for unimplemented abstract methods so that your class can be instantiated. This is a helper for development, so you can gradually fill in a new model implementation rather than doing it all at once before you can test. Avoid using this in modules that are published to the registry unless you really understand the maintainability tradeoffs. + +Take an example Motor class: + +```python +@stub_model +class MyMotor(Motor, EasyResource): + MODEL = "org:examples:stubbed-motor" +``` + +Without the stub_model decorator, this would fail on instantiation with an error like: + +``` +TypeError: Can't instantiate abstract class MyMotor with abstract methods get_position, get_properties, go_for, go_to, is_moving, is_powered, reset_zero_position, set_power, set_rpm, stop +``` + +## Instructions + +Follow the instructions in the [easy_resource example](../easy_resource). diff --git a/examples/stubbed_model/main.py b/examples/stubbed_model/main.py new file mode 100755 index 000000000..041b7513c --- /dev/null +++ b/examples/stubbed_model/main.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +import asyncio +from viam.components.motor import Motor +from viam.resource.easy_resource import EasyResource, stub_model +from viam.module.module import Module + + +@stub_model +class MyMotor(Motor, EasyResource): + MODEL = "viam:motor:easy-resource-example" + + async def is_moving(self) -> bool: + return False + + async def stop(self, **kwargs): + pass + + +if __name__ == '__main__': + asyncio.run(Module.run_from_registry()) diff --git a/plugin/main.py b/plugin/main.py new file mode 100755 index 000000000..878766b47 --- /dev/null +++ b/plugin/main.py @@ -0,0 +1,279 @@ +import os +import re +import sys + +from typing import List, Any, Collection, Iterator, NamedTuple, cast +from typing import Dict, Tuple, Optional, Deque +from contextlib import contextmanager +from collections import deque + +from google.protobuf.descriptor_pb2 import FileDescriptorProto, DescriptorProto +from google.protobuf.compiler.plugin_pb2 import CodeGeneratorRequest +from google.protobuf.compiler.plugin_pb2 import CodeGeneratorResponse + +from grpclib import const +from grpclib import client +from grpclib import server +from grpclib import exceptions + + +_CARDINALITY = { + (False, False): const.Cardinality.UNARY_UNARY, + (True, False): const.Cardinality.STREAM_UNARY, + (False, True): const.Cardinality.UNARY_STREAM, + (True, True): const.Cardinality.STREAM_STREAM, +} + + +class Method(NamedTuple): + name: str + cardinality: const.Cardinality + request_type: str + reply_type: str + + +class Service(NamedTuple): + name: str + methods: List[Method] + + +class Buffer: + def __init__(self) -> None: + self._lines: List[str] = [] + self._indent = 0 + + def add(self, string: str, *args: Any, **kwargs: Any) -> None: + line = " " * self._indent * 4 + string.format(*args, **kwargs) + self._lines.append(line.rstrip(" ")) + + @contextmanager + def indent(self) -> Iterator[None]: + self._indent += 1 + try: + yield + finally: + self._indent -= 1 + + def content(self) -> str: + return "\n".join(self._lines) + "\n" + + +def render( + proto_file: str, + package: str, + imports: Collection[str], + services: Collection[Service], +) -> str: + buf = Buffer() + buf.add("# Generated by the Protocol Buffers compiler. DO NOT EDIT!") + buf.add("# source: {}", proto_file) + buf.add("# plugin: {}", __name__) + if not services: + return buf.content() + + buf.add("import abc") + buf.add("import typing") + buf.add("") + buf.add("import {}", const.__name__) + buf.add("import {}", client.__name__) + buf.add("import {}", exceptions.__name__) + buf.add("if typing.TYPE_CHECKING:") + with buf.indent(): + buf.add("import {}", server.__name__) + + buf.add("") + for mod in imports: + buf.add("import {}", mod) + for service in services: + if package: + service_name = "{}.{}".format(package, service.name) + else: + service_name = service.name + buf.add("") + buf.add("") + buf.add("class {}Base(abc.ABC):", service.name) + with buf.indent(): + for name, _, request_type, reply_type in service.methods: + buf.add("") + buf.add("@abc.abstractmethod") + buf.add( + "async def {}(self, stream: '{}.{}[{}, {}]') -> None:", + name, + server.__name__, + server.Stream.__name__, + request_type, + reply_type, + ) + with buf.indent(): + buf.add("pass") + buf.add("") + buf.add("def __mapping__(self) -> typing.Dict[str, {}.{}]:", const.__name__, const.Handler.__name__) + with buf.indent(): + buf.add("return {{") + with buf.indent(): + for method in service.methods: + name, cardinality, request_type, reply_type = method + full_name = "/{}/{}".format(service_name, name) + buf.add("'{}': {}.{}(", full_name, const.__name__, const.Handler.__name__) + with buf.indent(): + buf.add("self.{},", name) + buf.add("{}.{}.{},", const.__name__, const.Cardinality.__name__, cardinality.name) + buf.add("{},", request_type) + buf.add("{},", reply_type) + buf.add("),") + buf.add("}}") + + buf.add("") + buf.add("") + buf.add("class Unimplemented{}Base({}Base):", service.name, service.name) + with buf.indent(): + for name, _, request_type, reply_type in service.methods: + buf.add("") + buf.add( + "async def {}(self, stream: '{}.{}[{}, {}]') -> None:", + name, + server.__name__, + server.Stream.__name__, + request_type, + reply_type, + ) + with buf.indent(): + buf.add("raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED)") + + buf.add("") + buf.add("") + buf.add("class {}Stub:", service.name) + with buf.indent(): + buf.add("") + buf.add("def __init__(self, channel: {}.{}) -> None:".format(client.__name__, client.Channel.__name__)) + with buf.indent(): + if len(service.methods) == 0: + buf.add("pass") + for method in service.methods: + name, cardinality, request_type, reply_type = method + full_name = "/{}/{}".format(service_name, name) + method_cls: type + if cardinality is const.Cardinality.UNARY_UNARY: + method_cls = client.UnaryUnaryMethod + elif cardinality is const.Cardinality.UNARY_STREAM: + method_cls = client.UnaryStreamMethod + elif cardinality is const.Cardinality.STREAM_UNARY: + method_cls = client.StreamUnaryMethod + elif cardinality is const.Cardinality.STREAM_STREAM: + method_cls = client.StreamStreamMethod + else: + raise TypeError(cardinality) + method_cls = cast(type, method_cls) # FIXME: redundant + buf.add("self.{} = {}.{}(".format(name, client.__name__, method_cls.__name__)) + with buf.indent(): + buf.add("channel,") + buf.add("{!r},".format(full_name)) + buf.add("{},", request_type) + buf.add("{},", reply_type) + buf.add(")") + return buf.content() + + +def _get_proto(request: CodeGeneratorRequest, name: str) -> FileDescriptorProto: + return next(f for f in request.proto_file if f.name == name) + + +def _strip_proto(proto_file_path: str) -> str: + for suffix in [".protodevel", ".proto"]: + if proto_file_path.endswith(suffix): + return proto_file_path[: -len(suffix)] + + return proto_file_path + + +def _base_module_name(proto_file_path: str) -> str: + basename = _strip_proto(proto_file_path) + return basename.replace("-", "_").replace("/", ".") + + +def _proto2pb2_module_name(proto_file_path: str) -> str: + return _base_module_name(proto_file_path) + "_pb2" + + +def _proto2grpc_module_name(proto_file_path: str) -> str: + return _base_module_name(proto_file_path) + "_grpc" + + +def _type_names( + proto_file: FileDescriptorProto, + message_type: DescriptorProto, + parents: Optional[Deque[str]] = None, +) -> Iterator[Tuple[str, str]]: + if parents is None: + parents = deque() + + proto_name_parts = [""] + if proto_file.package: + proto_name_parts.append(proto_file.package) + proto_name_parts.extend(parents) + proto_name_parts.append(message_type.name) + + py_name_parts = [_proto2pb2_module_name(proto_file.name)] + py_name_parts.extend(parents) + py_name_parts.append(message_type.name) + + yield ".".join(proto_name_parts), ".".join(py_name_parts) + + parents.append(message_type.name) + for nested in message_type.nested_type: + yield from _type_names(proto_file, nested, parents=parents) + parents.pop() + + +def main() -> None: + with os.fdopen(sys.stdin.fileno(), "rb") as inp: + request = CodeGeneratorRequest.FromString(inp.read()) + + types_map: Dict[str, str] = {} + for pf in request.proto_file: + for mt in pf.message_type: + types_map.update(_type_names(pf, mt)) + + response = CodeGeneratorResponse() + + # See https://github.com/protocolbuffers/protobuf/blob/v3.12.0/docs/implementing_proto3_presence.md # noqa + if hasattr(CodeGeneratorResponse, "Feature"): + response.supported_features = CodeGeneratorResponse.FEATURE_PROTO3_OPTIONAL + + for file_to_generate in request.file_to_generate: + proto_file = _get_proto(request, file_to_generate) + + imports = [_proto2pb2_module_name(dep) for dep in list(proto_file.dependency) + [file_to_generate]] + + services = [] + for service in proto_file.service: + methods = [] + for method in service.method: + cardinality = _CARDINALITY[(method.client_streaming, method.server_streaming)] # type: ignore + methods.append( + Method( + name=method.name, + cardinality=cardinality, + request_type=types_map[method.input_type], + reply_type=types_map[method.output_type], + ) + ) + services.append(Service(name=service.name, methods=methods)) + + file = response.file.add() + module_name = _proto2grpc_module_name(file_to_generate) + file.name = module_name.replace(".", "/") + ".py" + file.content = render( + proto_file=proto_file.name, + package=proto_file.package, + imports=imports, + services=services, + ) + + with os.fdopen(sys.stdout.fileno(), "wb") as out: + out.write(response.SerializeToString()) + + +if __name__ == "__main__": + sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0]) + sys.exit(main()) diff --git a/poetry.lock b/poetry.lock deleted file mode 100644 index e74e85d3c..000000000 --- a/poetry.lock +++ /dev/null @@ -1,2395 +0,0 @@ -[[package]] -name = "alabaster" -version = "0.7.12" -description = "A configurable sidebar-enabled Sphinx theme" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "appnope" -version = "0.1.3" -description = "Disable App Nap on macOS >= 10.9" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "argon2-cffi" -version = "21.3.0" -description = "The secure Argon2 password hashing algorithm." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -argon2-cffi-bindings = "*" - -[package.extras] -dev = ["pre-commit", "cogapp", "tomli", "coverage[toml] (>=5.0.2)", "hypothesis", "pytest", "sphinx", "sphinx-notfound-page", "furo"] -docs = ["sphinx", "sphinx-notfound-page", "furo"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pytest"] - -[[package]] -name = "argon2-cffi-bindings" -version = "21.2.0" -description = "Low-level CFFI bindings for Argon2" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = ">=1.0.1" - -[package.extras] -dev = ["pytest", "cogapp", "pre-commit", "wheel"] -tests = ["pytest"] - -[[package]] -name = "astroid" -version = "2.12.10" -description = "An abstract syntax tree for Python with inference support." -category = "dev" -optional = false -python-versions = ">=3.7.2" - -[package.dependencies] -lazy-object-proxy = ">=1.4.0" -typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} -wrapt = {version = ">=1.11,<2", markers = "python_version < \"3.11\""} - -[[package]] -name = "asttokens" -version = "2.0.8" -description = "Annotate AST trees with source code positions" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" - -[package.extras] -test = ["astroid (<=2.5.3)", "pytest"] - -[[package]] -name = "attrs" -version = "22.1.0" -description = "Classes Without Boilerplate" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] -docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] - -[[package]] -name = "autopep8" -version = "1.6.0" -description = "A tool that automatically formats Python code to conform to the PEP 8 style guide" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -pycodestyle = ">=2.8.0" -toml = "*" - -[[package]] -name = "babel" -version = "2.10.3" -description = "Internationalization utilities" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pytz = ">=2015.7" - -[[package]] -name = "backcall" -version = "0.2.0" -description = "Specifications for callback functions passed in to an API" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "beautifulsoup4" -version = "4.11.1" -description = "Screen-scraping library" -category = "dev" -optional = false -python-versions = ">=3.6.0" - -[package.dependencies] -soupsieve = ">1.2" - -[package.extras] -html5lib = ["html5lib"] -lxml = ["lxml"] - -[[package]] -name = "black" -version = "22.8.0" -description = "The uncompromising code formatter." -category = "dev" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "bleach" -version = "5.0.1" -description = "An easy safelist-based HTML-sanitizing tool." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -six = ">=1.9.0" -webencodings = "*" - -[package.extras] -css = ["tinycss2 (>=1.1.0,<1.2)"] -dev = ["build (==0.8.0)", "flake8 (==4.0.1)", "hashin (==0.17.0)", "pip-tools (==6.6.2)", "pytest (==7.1.2)", "Sphinx (==4.3.2)", "tox (==3.25.0)", "twine (==4.0.1)", "wheel (==0.37.1)", "black (==22.3.0)", "mypy (==0.961)"] - -[[package]] -name = "certifi" -version = "2022.9.14" -description = "Python package for providing Mozilla's CA Bundle." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "cffi" -version = "1.15.1" -description = "Foreign Function Interface for Python calling C code." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "charset-normalizer" -version = "2.1.1" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" -optional = false -python-versions = ">=3.6.0" - -[package.extras] -unicode_backport = ["unicodedata2"] - -[[package]] -name = "click" -version = "8.1.3" -description = "Composable command line interface toolkit" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "colorama" -version = "0.4.5" -description = "Cross-platform colored terminal text." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "coverage" -version = "6.4.4" -description = "Code coverage measurement for Python" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -toml = ["tomli"] - -[[package]] -name = "debugpy" -version = "1.6.3" -description = "An implementation of the Debug Adapter Protocol for Python" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "decorator" -version = "5.1.1" -description = "Decorators for Humans" -category = "dev" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "defusedxml" -version = "0.7.1" -description = "XML bomb protection for Python stdlib modules" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "docutils" -version = "0.17.1" -description = "Docutils -- Python Documentation Utilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "entrypoints" -version = "0.4" -description = "Discover and load entry points from installed packages." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "executing" -version = "1.0.0" -description = "Get the currently executing AST node of a frame, and other information" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "fastjsonschema" -version = "2.16.2" -description = "Fastest Python implementation of JSON schema" -category = "dev" -optional = false -python-versions = "*" - -[package.extras] -devel = ["colorama", "jsonschema", "json-spec", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] - -[[package]] -name = "flake8" -version = "4.0.1" -description = "the modular source code checker: pep8 pyflakes and co" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.8.0,<2.9.0" -pyflakes = ">=2.4.0,<2.5.0" - -[[package]] -name = "googleapis-common-protos" -version = "1.56.4" -description = "Common protobufs used in Google APIs" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -protobuf = ">=3.15.0,<5.0.0dev" - -[package.extras] -grpc = ["grpcio (>=1.0.0,<2.0.0dev)"] - -[[package]] -name = "greenlet" -version = "1.1.3" -description = "Lightweight in-process concurrent programming" -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" - -[package.extras] -docs = ["sphinx"] - -[[package]] -name = "grpclib" -version = "0.4.3" -description = "Pure-Python gRPC implementation for asyncio" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -h2 = ">=3.1.0,<5" -multidict = "*" - -[package.extras] -protobuf = ["protobuf (>=3.15.0)"] - -[[package]] -name = "h2" -version = "4.1.0" -description = "HTTP/2 State-Machine based protocol implementation" -category = "main" -optional = false -python-versions = ">=3.6.1" - -[package.dependencies] -hpack = ">=4.0,<5" -hyperframe = ">=6.0,<7" - -[[package]] -name = "hpack" -version = "4.0.0" -description = "Pure-Python HPACK header compression" -category = "main" -optional = false -python-versions = ">=3.6.1" - -[[package]] -name = "hyperframe" -version = "6.0.1" -description = "HTTP/2 framing layer for Python" -category = "main" -optional = false -python-versions = ">=3.6.1" - -[[package]] -name = "idna" -version = "3.4" -description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "imagesize" -version = "1.4.1" -description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "importlib-metadata" -version = "4.12.0" -description = "Read metadata from Python packages" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] -perf = ["ipython"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] - -[[package]] -name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "ipykernel" -version = "6.15.3" -description = "IPython Kernel for Jupyter" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -appnope = {version = "*", markers = "platform_system == \"Darwin\""} -debugpy = ">=1.0" -ipython = ">=7.23.1" -jupyter-client = ">=6.1.12" -matplotlib-inline = ">=0.1" -nest-asyncio = "*" -packaging = "*" -psutil = "*" -pyzmq = ">=17" -tornado = ">=6.1" -traitlets = ">=5.1.0" - -[package.extras] -test = ["flaky", "ipyparallel", "pre-commit", "pytest-cov", "pytest-timeout", "pytest (>=6.0)"] - -[[package]] -name = "ipython" -version = "8.5.0" -description = "IPython: Productive Interactive Computing" -category = "dev" -optional = false -python-versions = ">=3.8" - -[package.dependencies] -appnope = {version = "*", markers = "sys_platform == \"darwin\""} -backcall = "*" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\""} -pickleshare = "*" -prompt-toolkit = ">3.0.1,<3.1.0" -pygments = ">=2.4.0" -stack-data = "*" -traitlets = ">=5" - -[package.extras] -all = ["black", "Sphinx (>=1.3)", "ipykernel", "nbconvert", "nbformat", "ipywidgets", "notebook", "ipyparallel", "qtconsole", "pytest (<7.1)", "pytest-asyncio", "testpath", "curio", "matplotlib (!=3.2.0)", "numpy (>=1.19)", "pandas", "trio"] -black = ["black"] -doc = ["Sphinx (>=1.3)"] -kernel = ["ipykernel"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["pytest (<7.1)", "pytest-asyncio", "testpath"] -test_extra = ["pytest (<7.1)", "pytest-asyncio", "testpath", "curio", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.19)", "pandas", "trio"] - -[[package]] -name = "ipython-genutils" -version = "0.2.0" -description = "Vestigial utilities from IPython" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "ipywidgets" -version = "8.0.2" -description = "Jupyter interactive widgets" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -ipykernel = ">=4.5.1" -ipython = ">=6.1.0" -jupyterlab-widgets = ">=3.0,<4.0" -traitlets = ">=4.3.1" -widgetsnbextension = ">=4.0,<5.0" - -[package.extras] -test = ["jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] - -[[package]] -name = "jedi" -version = "0.18.1" -description = "An autocompletion tool for Python that can be used for text editors." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -parso = ">=0.8.0,<0.9.0" - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"] - -[[package]] -name = "jinja2" -version = "3.1.2" -description = "A very fast and expressive template engine." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "jsonschema" -version = "4.16.0" -description = "An implementation of JSON Schema validation for Python" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -attrs = ">=17.4.0" -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "jupyter" -version = "1.0.0" -description = "Jupyter metapackage. Install all the Jupyter components in one go." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -ipykernel = "*" -ipywidgets = "*" -jupyter-console = "*" -nbconvert = "*" -notebook = "*" -qtconsole = "*" - -[[package]] -name = "jupyter-cache" -version = "0.5.0" -description = "A defined interface for working with a cache of jupyter notebooks." -category = "dev" -optional = false -python-versions = "~=3.7" - -[package.dependencies] -attrs = "*" -click = "*" -importlib-metadata = "*" -nbclient = ">=0.2,<0.6" -nbformat = "*" -pyyaml = "*" -sqlalchemy = ">=1.3.12,<1.5" -tabulate = "*" - -[package.extras] -cli = ["click-log"] -code_style = ["pre-commit (>=2.12,<3.0)"] -rtd = ["nbdime", "jupytext", "myst-nb (>=0.12.3,<0.13.0)", "sphinx-book-theme (>=0.1.1,<0.2.0)", "sphinx-copybutton"] -testing = ["nbdime", "coverage", "ipykernel", "jupytext", "matplotlib", "nbformat (>=5.1)", "numpy", "pandas", "pytest (>=6,<7)", "pytest-cov", "pytest-regressions", "sympy"] - -[[package]] -name = "jupyter-client" -version = "7.3.5" -description = "Jupyter protocol implementation and client libraries" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -entrypoints = "*" -jupyter-core = ">=4.9.2" -nest-asyncio = ">=1.5.4" -python-dateutil = ">=2.8.2" -pyzmq = ">=23.0" -tornado = ">=6.2" -traitlets = "*" - -[package.extras] -doc = ["ipykernel", "myst-parser", "sphinx-rtd-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt"] -test = ["codecov", "coverage", "ipykernel (>=6.5)", "ipython", "mypy", "pre-commit", "pytest", "pytest-asyncio (>=0.18)", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "jupyter-console" -version = "6.4.4" -description = "Jupyter terminal console" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -ipykernel = "*" -ipython = "*" -jupyter-client = ">=7.0.0" -prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0" -pygments = "*" - -[package.extras] -test = ["pexpect"] - -[[package]] -name = "jupyter-core" -version = "4.11.1" -description = "Jupyter core package. A base package on which Jupyter projects rely." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} -traitlets = "*" - -[package.extras] -test = ["ipykernel", "pre-commit", "pytest", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "jupyterlab-pygments" -version = "0.2.2" -description = "Pygments theme using JupyterLab CSS variables" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "jupyterlab-widgets" -version = "3.0.3" -description = "Jupyter interactive widgets for JupyterLab" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "lazy-object-proxy" -version = "1.7.1" -description = "A fast and thorough lazy object proxy." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "lxml" -version = "4.9.1" -description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" - -[package.extras] -source = ["Cython (>=0.29.7)"] -htmlsoup = ["beautifulsoup4"] -html5 = ["html5lib"] -cssselect = ["cssselect (>=0.7)"] - -[[package]] -name = "markdown-it-py" -version = "2.1.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark (>=3.2,<4.0)"] -code_style = ["pre-commit (==2.6)"] -compare = ["commonmark (>=0.9.1,<0.10.0)", "markdown (>=3.3.6,<3.4.0)", "mistletoe (>=0.8.1,<0.9.0)", "mistune (>=2.0.2,<2.1.0)", "panflute (>=2.1.3,<2.2.0)"] -linkify = ["linkify-it-py (>=1.0,<2.0)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx-book-theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "markupsafe" -version = "2.1.1" -description = "Safely add untrusted strings to HTML/XML markup." -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "matplotlib-inline" -version = "0.1.6" -description = "Inline Matplotlib backend for Jupyter" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.dependencies] -traitlets = "*" - -[[package]] -name = "mccabe" -version = "0.6.1" -description = "McCabe checker, plugin for flake8" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "mdit-py-plugins" -version = "0.3.0" -description = "Collection of plugins for markdown-it-py" -category = "dev" -optional = false -python-versions = "~=3.6" - -[package.dependencies] -markdown-it-py = ">=1.0.0,<3.0.0" - -[package.extras] -code_style = ["pre-commit (==2.6)"] -rtd = ["myst-parser (>=0.14.0,<0.15.0)", "sphinx-book-theme (>=0.1.0,<0.2.0)"] -testing = ["coverage", "pytest (>=3.6,<4)", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "mistune" -version = "2.0.4" -description = "A sane Markdown parser with useful plugins and renderers" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "multidict" -version = "6.0.2" -description = "multidict implementation" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "mypy-protobuf" -version = "3.3.0" -description = "Generate mypy stub files from protobuf specs" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -protobuf = ">=3.19.4" -types-protobuf = ">=3.19.12" - -[[package]] -name = "myst-nb" -version = "0.16.0" -description = "A Jupyter Notebook Sphinx reader built on top of the MyST markdown parser." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -importlib_metadata = "*" -ipykernel = "*" -ipython = "*" -jupyter-cache = ">=0.5.0,<0.6.0" -myst-parser = ">=0.18.0,<0.19.0" -nbclient = "*" -nbformat = ">=5.0,<6.0" -pyyaml = "*" -sphinx = ">=4,<6" -sphinx-togglebutton = ">=0.3.0,<0.4.0" -typing-extensions = "*" - -[package.extras] -code_style = ["pre-commit (>=2.12,<3.0)"] -rtd = ["alabaster", "altair", "bokeh", "coconut (>=1.4.3,<1.5.0)", "ipykernel (>=5.5,<6.0)", "ipywidgets", "jupytext (>=1.11.2,<1.12.0)", "matplotlib", "numpy", "pandas", "plotly", "sphinx-book-theme (>=0.3.0,<0.4.0)", "sphinx-copybutton", "sphinx-design (>=0.1.0,<0.2.0)", "sphinxcontrib-bibtex", "sympy"] -testing = ["coverage (>=6.4,<7.0)", "beautifulsoup4", "ipykernel (>=5.5,<6.0)", "ipython (!=8.1.0)", "ipywidgets", "jupytext (>=1.11.2,<1.12.0)", "matplotlib", "nbdime", "numpy", "pandas", "pytest (>=7.1,<8.0)", "pytest-cov (>=3.0,<4.0)", "pytest-regressions", "pytest-param-files (>=0.3.3,<0.4.0)", "sympy"] - -[[package]] -name = "myst-parser" -version = "0.18.0" -description = "An extended commonmark compliant parser, with bridges to docutils & sphinx." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -docutils = ">=0.15,<0.19" -jinja2 = "*" -markdown-it-py = ">=1.0.0,<3.0.0" -mdit-py-plugins = ">=0.3.0,<0.4.0" -pyyaml = "*" -sphinx = ">=4,<6" -typing-extensions = "*" - -[package.extras] -code_style = ["pre-commit (>=2.12,<3.0)"] -linkify = ["linkify-it-py (>=1.0,<2.0)"] -rtd = ["ipython", "sphinx-book-theme", "sphinx-design", "sphinxext-rediraffe (>=0.2.7,<0.3.0)", "sphinxcontrib.mermaid (>=0.7.1,<0.8.0)", "sphinxext-opengraph (>=0.6.3,<0.7.0)"] -testing = ["beautifulsoup4", "coverage", "pytest (>=6,<7)", "pytest-cov", "pytest-regressions", "pytest-param-files (>=0.3.4,<0.4.0)", "sphinx-pytest"] - -[[package]] -name = "nbclient" -version = "0.5.13" -description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." -category = "dev" -optional = false -python-versions = ">=3.7.0" - -[package.dependencies] -jupyter-client = ">=6.1.5" -nbformat = ">=5.0" -nest-asyncio = "*" -traitlets = ">=5.0.0" - -[package.extras] -sphinx = ["Sphinx (>=1.7)", "sphinx-book-theme", "mock", "moto", "myst-parser"] -test = ["ipython (<8.0.0)", "ipykernel", "ipywidgets (<8.0.0)", "pytest (>=4.1)", "pytest-asyncio", "pytest-cov (>=2.6.1)", "check-manifest", "flake8", "mypy", "xmltodict", "black", "pip (>=18.1)", "wheel (>=0.31.0)", "setuptools (>=38.6.0)", "twine (>=1.11.0)"] - -[[package]] -name = "nbconvert" -version = "7.0.0" -description = "Converting Jupyter Notebooks" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -beautifulsoup4 = "*" -bleach = "*" -defusedxml = "*" -importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} -jinja2 = ">=3.0" -jupyter-core = ">=4.7" -jupyterlab-pygments = "*" -lxml = "*" -markupsafe = ">=2.0" -mistune = ">=2.0.3,<3" -nbclient = ">=0.5.0" -nbformat = ">=5.1" -packaging = "*" -pandocfilters = ">=1.4.1" -pygments = ">=2.4.1" -tinycss2 = "*" -traitlets = ">=5.0" - -[package.extras] -all = ["ipykernel", "ipython", "ipywidgets (>=7)", "nbsphinx (>=0.2.12)", "pre-commit", "pyppeteer (>=1,<1.1)", "pyqtwebengine (>=5.15)", "pytest", "pytest-cov", "pytest-dependency", "sphinx-rtd-theme", "sphinx (==5.0.2)", "tornado (>=6.1)"] -docs = ["ipython", "nbsphinx (>=0.2.12)", "sphinx-rtd-theme", "sphinx (==5.0.2)"] -qtpdf = ["pyqtwebengine (>=5.15)"] -qtpng = ["pyqtwebengine (>=5.15)"] -serve = ["tornado (>=6.1)"] -test = ["ipykernel", "ipywidgets (>=7)", "pre-commit", "pyppeteer (>=1,<1.1)", "pytest", "pytest-cov", "pytest-dependency"] -webpdf = ["pyppeteer (>=1,<1.1)"] - -[[package]] -name = "nbformat" -version = "5.5.0" -description = "The Jupyter Notebook format" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -fastjsonschema = "*" -jsonschema = ">=2.6" -jupyter_core = "*" -traitlets = ">=5.1" - -[package.extras] -test = ["check-manifest", "testpath", "pytest", "pre-commit", "pep440"] - -[[package]] -name = "nbmake" -version = "1.3.0" -description = "Pytest plugin for testing notebooks" -category = "dev" -optional = false -python-versions = ">=3.7.0,<4.0.0" - -[package.dependencies] -ipykernel = ">=5.4.0" -nbclient = ">=0.5.13,<0.6.0" -nbformat = ">=5.0.8,<6.0.0" -pydantic = ">=1.7.2,<2.0.0" -Pygments = ">=2.7.3,<3.0.0" -pytest = ">=6.1.0" - -[[package]] -name = "nest-asyncio" -version = "1.5.5" -description = "Patch asyncio to allow nested event loops" -category = "dev" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "notebook" -version = "6.4.12" -description = "A web-based notebook environment for interactive computing" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -argon2-cffi = "*" -ipykernel = "*" -ipython-genutils = "*" -jinja2 = "*" -jupyter-client = ">=5.3.4" -jupyter-core = ">=4.6.1" -nbconvert = ">=5" -nbformat = "*" -nest-asyncio = ">=1.5" -prometheus-client = "*" -pyzmq = ">=17" -Send2Trash = ">=1.8.0" -terminado = ">=0.8.3" -tornado = ">=6.1" -traitlets = ">=4.2.1" - -[package.extras] -docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt", "sphinx-rtd-theme", "myst-parser"] -json-logging = ["json-logging"] -test = ["pytest", "coverage", "requests", "testpath", "nbval", "selenium", "pytest-cov", "requests-unixsocket"] - -[[package]] -name = "packaging" -version = "21.3" -description = "Core utilities for Python packages" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" - -[[package]] -name = "pandocfilters" -version = "1.5.0" -description = "Utilities for writing pandoc filters in python" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "parso" -version = "0.8.3" -description = "A Python Parser" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] -testing = ["docopt", "pytest (<6.0.0)"] - -[[package]] -name = "pathspec" -version = "0.10.1" -description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "pexpect" -version = "4.8.0" -description = "Pexpect allows easy control of interactive console applications." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "pickleshare" -version = "0.7.5" -description = "Tiny 'shelve'-like database with concurrency support" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "pillow" -version = "9.2.0" -description = "Python Imaging Library (Fork)" -category = "main" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "platformdirs" -version = "2.5.2" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] -test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] - -[[package]] -name = "pluggy" -version = "1.0.0" -description = "plugin and hook calling mechanisms for python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "prometheus-client" -version = "0.14.1" -description = "Python client for the Prometheus monitoring system." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -twisted = ["twisted"] - -[[package]] -name = "prompt-toolkit" -version = "3.0.31" -description = "Library for building powerful interactive command lines in Python" -category = "dev" -optional = false -python-versions = ">=3.6.2" - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "protobuf" -version = "3.20.1" -description = "Protocol Buffers" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "protoletariat" -version = "0.9.4" -description = "Python protocol buffers for the rest of us" -category = "dev" -optional = false -python-versions = ">=3.7,<3.11" - -[package.dependencies] -click = ">=7.1.2,<9" -protobuf = ">=3.19.1,<4.0.0" - -[[package]] -name = "psutil" -version = "5.9.2" -description = "Cross-platform lib for process and system monitoring in Python." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.extras] -test = ["ipaddress", "mock", "enum34", "pywin32", "wmi"] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "pure-eval" -version = "0.2.2" -description = "Safely evaluate AST nodes without side effects" -category = "dev" -optional = false -python-versions = "*" - -[package.extras] -tests = ["pytest"] - -[[package]] -name = "py" -version = "1.11.0" -description = "library with cross-python path, ini-parsing, io, code, log facilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "pycodestyle" -version = "2.8.0" -description = "Python style guide checker" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[[package]] -name = "pycparser" -version = "2.21" -description = "C parser in Python" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pydantic" -version = "1.10.2" -description = "Data validation and settings management using python type hints" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -typing-extensions = ">=4.1.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - -[[package]] -name = "pyflakes" -version = "2.4.0" -description = "passive checker of Python programs" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[[package]] -name = "pygments" -version = "2.13.0" -description = "Pygments is a syntax highlighting package written in Python." -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -plugins = ["importlib-metadata"] - -[[package]] -name = "pyparsing" -version = "3.0.9" -description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "dev" -optional = false -python-versions = ">=3.6.8" - -[package.extras] -diagrams = ["railroad-diagrams", "jinja2"] - -[[package]] -name = "pyrsistent" -version = "0.18.1" -description = "Persistent/Functional/Immutable data structures" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "pytest" -version = "7.1.3" -description = "pytest: simple powerful testing with Python" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -attrs = ">=19.2.0" -colorama = {version = "*", markers = "sys_platform == \"win32\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=0.12,<2.0" -py = ">=1.8.2" -tomli = ">=1.0.0" - -[package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] - -[[package]] -name = "pytest-asyncio" -version = "0.18.3" -description = "Pytest support for asyncio" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -pytest = ">=6.1.0" - -[package.extras] -testing = ["coverage (==6.2)", "hypothesis (>=5.7.1)", "flaky (>=3.5.0)", "mypy (==0.931)", "pytest-trio (>=0.7.0)"] - -[[package]] -name = "python-dateutil" -version = "2.8.2" -description = "Extensions to the standard Python datetime module" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "pytz" -version = "2022.2.1" -description = "World timezone definitions, modern and historical" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "pywin32" -version = "304" -description = "Python for Window Extensions" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "pywinpty" -version = "2.0.8" -description = "Pseudo terminal support for Windows from Python." -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "pyyaml" -version = "6.0" -description = "YAML parser and emitter for Python" -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "pyzmq" -version = "24.0.0" -description = "Python bindings for 0MQ" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -cffi = {version = "*", markers = "implementation_name == \"pypy\""} -py = {version = "*", markers = "implementation_name == \"pypy\""} - -[[package]] -name = "qtconsole" -version = "5.3.2" -description = "Jupyter Qt console" -category = "dev" -optional = false -python-versions = ">= 3.7" - -[package.dependencies] -ipykernel = ">=4.1" -ipython-genutils = "*" -jupyter-client = ">=4.1" -jupyter-core = "*" -pygments = "*" -pyzmq = ">=17.1" -qtpy = ">=2.0.1" -traitlets = "<5.2.1 || >5.2.1,<5.2.2 || >5.2.2" - -[package.extras] -doc = ["Sphinx (>=1.3)"] -test = ["flaky", "pytest", "pytest-qt"] - -[[package]] -name = "qtpy" -version = "2.2.0" -description = "Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6)." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -packaging = "*" - -[package.extras] -test = ["pytest-qt", "pytest-cov (>=3.0.0)", "pytest (>=6,!=7.0.0,!=7.0.1)"] - -[[package]] -name = "requests" -version = "2.28.1" -description = "Python HTTP for Humans." -category = "dev" -optional = false -python-versions = ">=3.7, <4" - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<3" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "send2trash" -version = "1.8.0" -description = "Send file to trash natively under Mac OS X, Windows and Linux." -category = "dev" -optional = false -python-versions = "*" - -[package.extras] -nativelib = ["pyobjc-framework-cocoa", "pywin32"] -objc = ["pyobjc-framework-cocoa"] -win32 = ["pywin32"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "soupsieve" -version = "2.3.2.post1" -description = "A modern CSS selector implementation for Beautiful Soup." -category = "dev" -optional = false -python-versions = ">=3.6" - -[[package]] -name = "sphinx" -version = "5.1.1" -description = "Python documentation generator" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -alabaster = ">=0.7,<0.8" -babel = ">=1.3" -colorama = {version = ">=0.3.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.20" -imagesize = "*" -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} -Jinja2 = ">=2.3" -packaging = "*" -Pygments = ">=2.0" -requests = ">=2.5.0" -snowballstemmer = ">=1.1" -sphinxcontrib-applehelp = "*" -sphinxcontrib-devhelp = "*" -sphinxcontrib-htmlhelp = ">=2.0.0" -sphinxcontrib-jsmath = "*" -sphinxcontrib-qthelp = "*" -sphinxcontrib-serializinghtml = ">=1.1.5" - -[package.extras] -docs = ["sphinxcontrib-websupport"] -lint = ["flake8 (>=3.5.0)", "flake8-comprehensions", "flake8-bugbear", "isort", "mypy (>=0.971)", "sphinx-lint", "docutils-stubs", "types-typed-ast", "types-requests"] -test = ["pytest (>=4.6)", "html5lib", "cython", "typed-ast"] - -[[package]] -name = "sphinx-autoapi" -version = "1.9.0" -description = "Sphinx API documentation generator" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -astroid = ">=2.7" -Jinja2 = "*" -PyYAML = "*" -sphinx = ">=3.0" -unidecode = "*" - -[package.extras] -docs = ["sphinx", "sphinx-rtd-theme"] -dotnet = ["sphinxcontrib-dotnetdomain"] -go = ["sphinxcontrib-golangdomain"] - -[[package]] -name = "sphinx-rtd-theme" -version = "1.0.0" -description = "Read the Docs theme for Sphinx" -category = "dev" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" - -[package.dependencies] -docutils = "<0.18" -sphinx = ">=1.6" - -[package.extras] -dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"] - -[[package]] -name = "sphinx-togglebutton" -version = "0.3.2" -description = "Toggle page content and collapse admonitions in Sphinx." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -docutils = "*" -sphinx = "*" - -[package.extras] -sphinx = ["sphinx-examples", "sphinx-design", "sphinx-book-theme", "myst-nb", "numpy", "matplotlib"] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "1.0.2" -description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "1.0.2" -description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.0.0" -description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest", "html5lib"] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -test = ["pytest", "flake8", "mypy"] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "1.0.3" -description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest"] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "1.1.5" -description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -category = "dev" -optional = false -python-versions = ">=3.5" - -[package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest"] - -[[package]] -name = "sqlalchemy" -version = "1.4.41" -description = "Database Abstraction Library" -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version >= \"3\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} - -[package.extras] -aiomysql = ["greenlet (!=0.4.17)", "aiomysql"] -aiosqlite = ["typing_extensions (!=3.10.0.1)", "greenlet (!=0.4.17)", "aiosqlite"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["greenlet (!=0.4.17)", "asyncmy (>=0.2.3,!=0.2.4)"] -mariadb_connector = ["mariadb (>=1.0.1,!=1.1.2)"] -mssql = ["pyodbc"] -mssql_pymssql = ["pymssql"] -mssql_pyodbc = ["pyodbc"] -mypy = ["sqlalchemy2-stubs", "mypy (>=0.910)"] -mysql = ["mysqlclient (>=1.4.0,<2)", "mysqlclient (>=1.4.0)"] -mysql_connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=7,<8)", "cx_oracle (>=7)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql_asyncpg = ["greenlet (!=0.4.17)", "asyncpg"] -postgresql_pg8000 = ["pg8000 (>=1.16.6,!=1.29.0)"] -postgresql_psycopg2binary = ["psycopg2-binary"] -postgresql_psycopg2cffi = ["psycopg2cffi"] -pymysql = ["pymysql (<1)", "pymysql"] -sqlcipher = ["sqlcipher3-binary"] - -[[package]] -name = "stack-data" -version = "0.5.0" -description = "Extract data from python stack frames and tracebacks for informative displays" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -asttokens = "*" -executing = "*" -pure-eval = "*" - -[package.extras] -tests = ["cython", "littleutils", "pygments", "typeguard", "pytest"] - -[[package]] -name = "tabulate" -version = "0.8.10" -description = "Pretty-print tabular data" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" - -[package.extras] -widechars = ["wcwidth"] - -[[package]] -name = "terminado" -version = "0.15.0" -description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.dependencies] -ptyprocess = {version = "*", markers = "os_name != \"nt\""} -pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""} -tornado = ">=6.1.0" - -[package.extras] -test = ["pre-commit", "pytest-timeout", "pytest (>=6.0)"] - -[[package]] -name = "tinycss2" -version = "1.1.1" -description = "A tiny CSS parser" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -webencodings = ">=0.4" - -[package.extras] -doc = ["sphinx", "sphinx-rtd-theme"] -test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"] - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "tornado" -version = "6.2" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "dev" -optional = false -python-versions = ">= 3.7" - -[[package]] -name = "traitlets" -version = "5.4.0" -description = "" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -test = ["pre-commit", "pytest"] - -[[package]] -name = "types-protobuf" -version = "3.20.4" -description = "Typing stubs for protobuf" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "typing-extensions" -version = "4.3.0" -description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "unidecode" -version = "1.3.4" -description = "ASCII transliterations of Unicode text" -category = "dev" -optional = false -python-versions = ">=3.5" - -[[package]] -name = "urllib3" -version = "1.26.12" -description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" - -[package.extras] -brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] - -[[package]] -name = "wcwidth" -version = "0.2.5" -description = "Measures the displayed width of unicode strings in a terminal" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "webencodings" -version = "0.5.1" -description = "Character encoding aliases for legacy web content" -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "widgetsnbextension" -version = "4.0.3" -description = "Jupyter interactive widgets for Jupyter Notebook" -category = "dev" -optional = false -python-versions = ">=3.7" - -[[package]] -name = "wrapt" -version = "1.14.1" -description = "Module for decorators, wrappers and monkey patching." -category = "dev" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" - -[[package]] -name = "zipp" -version = "3.8.1" -description = "Backport of pathlib-compatible object wrapper for zip files" -category = "dev" -optional = false -python-versions = ">=3.7" - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] - -[metadata] -lock-version = "1.1" - python-versions = ">=3.9,<3.11" -content-hash = "68252a63b4f9833991c1d804166b4f40cbef35eb55c43b4a81f6f8c9d473d81f" - -[metadata.files] -alabaster = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, -] -appnope = [ - {file = "appnope-0.1.3-py2.py3-none-any.whl", hash = "sha256:265a455292d0bd8a72453494fa24df5a11eb18373a60c7c0430889f22548605e"}, - {file = "appnope-0.1.3.tar.gz", hash = "sha256:02bd91c4de869fbb1e1c50aafc4098827a7a54ab2f39d9dcba6c9547ed920e24"}, -] -argon2-cffi = [ - {file = "argon2-cffi-21.3.0.tar.gz", hash = "sha256:d384164d944190a7dd7ef22c6aa3ff197da12962bd04b17f64d4e93d934dba5b"}, - {file = "argon2_cffi-21.3.0-py3-none-any.whl", hash = "sha256:8c976986f2c5c0e5000919e6de187906cfd81fb1c72bf9d88c01177e77da7f80"}, -] -argon2-cffi-bindings = [ - {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, - {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, -] -astroid = [] -asttokens = [] -attrs = [] -autopep8 = [ - {file = "autopep8-1.6.0-py2.py3-none-any.whl", hash = "sha256:ed77137193bbac52d029a52c59bec1b0629b5a186c495f1eb21b126ac466083f"}, - {file = "autopep8-1.6.0.tar.gz", hash = "sha256:44f0932855039d2c15c4510d6df665e4730f2b8582704fa48f9c55bd3e17d979"}, -] -babel = [ - {file = "Babel-2.10.3-py3-none-any.whl", hash = "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"}, - {file = "Babel-2.10.3.tar.gz", hash = "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51"}, -] -backcall = [ - {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"}, - {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, -] -beautifulsoup4 = [ - {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, - {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, -] -black = [] -bleach = [] -certifi = [] -cffi = [] -charset-normalizer = [] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] -colorama = [ - {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, - {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, -] -coverage = [] -debugpy = [] -decorator = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, -] -defusedxml = [ - {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, - {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, -] -docutils = [ - {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, - {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, -] -entrypoints = [ - {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, - {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, -] -executing = [] -fastjsonschema = [] -flake8 = [ - {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, - {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, -] -googleapis-common-protos = [] -greenlet = [] -grpclib = [] -h2 = [ - {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, - {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, -] -hpack = [ - {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, - {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, -] -hyperframe = [ - {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, - {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, -] -idna = [] -imagesize = [] -importlib-metadata = [] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -ipykernel = [] -ipython = [] -ipython-genutils = [ - {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, - {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, -] -ipywidgets = [] -jedi = [ - {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"}, - {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"}, -] -jinja2 = [ - {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, - {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, -] -jsonschema = [] -jupyter = [ - {file = "jupyter-1.0.0-py2.py3-none-any.whl", hash = "sha256:5b290f93b98ffbc21c0c7e749f054b3267782166d72fa5e3ed1ed4eaf34a2b78"}, - {file = "jupyter-1.0.0.tar.gz", hash = "sha256:d9dc4b3318f310e34c82951ea5d6683f67bed7def4b259fafbfe4f1beb1d8e5f"}, - {file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"}, -] -jupyter-cache = [ - {file = "jupyter-cache-0.5.0.tar.gz", hash = "sha256:87408030a4c8c14fe3f8fe62e6ceeb24c84e544c7ced20bfee45968053d07801"}, - {file = "jupyter_cache-0.5.0-py3-none-any.whl", hash = "sha256:642e434b9b75c4b94dc8346eaf5a639c8926a0673b87e5e8ef6460d5cf2c9516"}, -] -jupyter-client = [] -jupyter-console = [ - {file = "jupyter_console-6.4.4-py3-none-any.whl", hash = "sha256:756df7f4f60c986e7bc0172e4493d3830a7e6e75c08750bbe59c0a5403ad6dee"}, - {file = "jupyter_console-6.4.4.tar.gz", hash = "sha256:172f5335e31d600df61613a97b7f0352f2c8250bbd1092ef2d658f77249f89fb"}, -] -jupyter-core = [] -jupyterlab-pygments = [ - {file = "jupyterlab_pygments-0.2.2-py2.py3-none-any.whl", hash = "sha256:2405800db07c9f770863bcf8049a529c3dd4d3e28536638bd7c1c01d2748309f"}, - {file = "jupyterlab_pygments-0.2.2.tar.gz", hash = "sha256:7405d7fde60819d905a9fa8ce89e4cd830e318cdad22a0030f7a901da705585d"}, -] -jupyterlab-widgets = [] -lazy-object-proxy = [ - {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win32.whl", hash = "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9"}, - {file = "lazy_object_proxy-1.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win32.whl", hash = "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb"}, - {file = "lazy_object_proxy-1.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win32.whl", hash = "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69"}, - {file = "lazy_object_proxy-1.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win32.whl", hash = "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55"}, - {file = "lazy_object_proxy-1.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win32.whl", hash = "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f"}, - {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, - {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, -] -lxml = [] -markdown-it-py = [ - {file = "markdown-it-py-2.1.0.tar.gz", hash = "sha256:cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da"}, - {file = "markdown_it_py-2.1.0-py3-none-any.whl", hash = "sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27"}, -] -markupsafe = [ - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, - {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, - {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, - {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, - {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, - {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, -] -matplotlib-inline = [] -mccabe = [ - {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, - {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, -] -mdit-py-plugins = [ - {file = "mdit-py-plugins-0.3.0.tar.gz", hash = "sha256:ecc24f51eeec6ab7eecc2f9724e8272c2fb191c2e93cf98109120c2cace69750"}, - {file = "mdit_py_plugins-0.3.0-py3-none-any.whl", hash = "sha256:b1279701cee2dbf50e188d3da5f51fee8d78d038cdf99be57c6b9d1aa93b4073"}, -] -mdurl = [] -mistune = [] -multidict = [ - {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b9e95a740109c6047602f4db4da9949e6c5945cefbad34a1299775ddc9a62e2"}, - {file = "multidict-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ac0e27844758d7177989ce406acc6a83c16ed4524ebc363c1f748cba184d89d3"}, - {file = "multidict-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:041b81a5f6b38244b34dc18c7b6aba91f9cdaf854d9a39e5ff0b58e2b5773b9c"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fdda29a3c7e76a064f2477c9aab1ba96fd94e02e386f1e665bca1807fc5386f"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3368bf2398b0e0fcbf46d85795adc4c259299fec50c1416d0f77c0a843a3eed9"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4f052ee022928d34fe1f4d2bc743f32609fb79ed9c49a1710a5ad6b2198db20"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:225383a6603c086e6cef0f2f05564acb4f4d5f019a4e3e983f572b8530f70c88"}, - {file = "multidict-6.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50bd442726e288e884f7be9071016c15a8742eb689a593a0cac49ea093eef0a7"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:47e6a7e923e9cada7c139531feac59448f1f47727a79076c0b1ee80274cd8eee"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0556a1d4ea2d949efe5fd76a09b4a82e3a4a30700553a6725535098d8d9fb672"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:626fe10ac87851f4cffecee161fc6f8f9853f0f6f1035b59337a51d29ff3b4f9"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8064b7c6f0af936a741ea1efd18690bacfbae4078c0c385d7c3f611d11f0cf87"}, - {file = "multidict-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2d36e929d7f6a16d4eb11b250719c39560dd70545356365b494249e2186bc389"}, - {file = "multidict-6.0.2-cp310-cp310-win32.whl", hash = "sha256:fcb91630817aa8b9bc4a74023e4198480587269c272c58b3279875ed7235c293"}, - {file = "multidict-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:8cbf0132f3de7cc6c6ce00147cc78e6439ea736cee6bca4f068bcf892b0fd658"}, - {file = "multidict-6.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:05f6949d6169878a03e607a21e3b862eaf8e356590e8bdae4227eedadacf6e51"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2c2e459f7050aeb7c1b1276763364884595d47000c1cddb51764c0d8976e608"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d0509e469d48940147e1235d994cd849a8f8195e0bca65f8f5439c56e17872a3"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:514fe2b8d750d6cdb4712346a2c5084a80220821a3e91f3f71eec11cf8d28fd4"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19adcfc2a7197cdc3987044e3f415168fc5dc1f720c932eb1ef4f71a2067e08b"}, - {file = "multidict-6.0.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9d153e7f1f9ba0b23ad1568b3b9e17301e23b042c23870f9ee0522dc5cc79e8"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aef9cc3d9c7d63d924adac329c33835e0243b5052a6dfcbf7732a921c6e918ba"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4571f1beddff25f3e925eea34268422622963cd8dc395bb8778eb28418248e43"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:d48b8ee1d4068561ce8033d2c344cf5232cb29ee1a0206a7b828c79cbc5982b8"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:45183c96ddf61bf96d2684d9fbaf6f3564d86b34cb125761f9a0ef9e36c1d55b"}, - {file = "multidict-6.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:75bdf08716edde767b09e76829db8c1e5ca9d8bb0a8d4bd94ae1eafe3dac5e15"}, - {file = "multidict-6.0.2-cp37-cp37m-win32.whl", hash = "sha256:a45e1135cb07086833ce969555df39149680e5471c04dfd6a915abd2fc3f6dbc"}, - {file = "multidict-6.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6f3cdef8a247d1eafa649085812f8a310e728bdf3900ff6c434eafb2d443b23a"}, - {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0327292e745a880459ef71be14e709aaea2f783f3537588fb4ed09b6c01bca60"}, - {file = "multidict-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e875b6086e325bab7e680e4316d667fc0e5e174bb5611eb16b3ea121c8951b86"}, - {file = "multidict-6.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feea820722e69451743a3d56ad74948b68bf456984d63c1a92e8347b7b88452d"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cc57c68cb9139c7cd6fc39f211b02198e69fb90ce4bc4a094cf5fe0d20fd8b0"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:497988d6b6ec6ed6f87030ec03280b696ca47dbf0648045e4e1d28b80346560d"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:89171b2c769e03a953d5969b2f272efa931426355b6c0cb508022976a17fd376"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684133b1e1fe91eda8fa7447f137c9490a064c6b7f392aa857bba83a28cfb693"}, - {file = "multidict-6.0.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd9fc9c4849a07f3635ccffa895d57abce554b467d611a5009ba4f39b78a8849"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e07c8e79d6e6fd37b42f3250dba122053fddb319e84b55dd3a8d6446e1a7ee49"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:4070613ea2227da2bfb2c35a6041e4371b0af6b0be57f424fe2318b42a748516"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:47fbeedbf94bed6547d3aa632075d804867a352d86688c04e606971595460227"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5774d9218d77befa7b70d836004a768fb9aa4fdb53c97498f4d8d3f67bb9cfa9"}, - {file = "multidict-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2957489cba47c2539a8eb7ab32ff49101439ccf78eab724c828c1a54ff3ff98d"}, - {file = "multidict-6.0.2-cp38-cp38-win32.whl", hash = "sha256:e5b20e9599ba74391ca0cfbd7b328fcc20976823ba19bc573983a25b32e92b57"}, - {file = "multidict-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:8004dca28e15b86d1b1372515f32eb6f814bdf6f00952699bdeb541691091f96"}, - {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2e4a0785b84fb59e43c18a015ffc575ba93f7d1dbd272b4cdad9f5134b8a006c"}, - {file = "multidict-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6701bf8a5d03a43375909ac91b6980aea74b0f5402fbe9428fc3f6edf5d9677e"}, - {file = "multidict-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a007b1638e148c3cfb6bf0bdc4f82776cef0ac487191d093cdc316905e504071"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:07a017cfa00c9890011628eab2503bee5872f27144936a52eaab449be5eaf032"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c207fff63adcdf5a485969131dc70e4b194327666b7e8a87a97fbc4fd80a53b2"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:373ba9d1d061c76462d74e7de1c0c8e267e9791ee8cfefcf6b0b2495762c370c"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfba7c6d5d7c9099ba21f84662b037a0ffd4a5e6b26ac07d19e423e6fdf965a9"}, - {file = "multidict-6.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19d9bad105dfb34eb539c97b132057a4e709919ec4dd883ece5838bcbf262b80"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:de989b195c3d636ba000ee4281cd03bb1234635b124bf4cd89eeee9ca8fcb09d"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7c40b7bbece294ae3a87c1bc2abff0ff9beef41d14188cda94ada7bcea99b0fb"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:d16cce709ebfadc91278a1c005e3c17dd5f71f5098bfae1035149785ea6e9c68"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:a2c34a93e1d2aa35fbf1485e5010337c72c6791407d03aa5f4eed920343dd360"}, - {file = "multidict-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:feba80698173761cddd814fa22e88b0661e98cb810f9f986c54aa34d281e4937"}, - {file = "multidict-6.0.2-cp39-cp39-win32.whl", hash = "sha256:23b616fdc3c74c9fe01d76ce0d1ce872d2d396d8fa8e4899398ad64fb5aa214a"}, - {file = "multidict-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:4bae31803d708f6f15fd98be6a6ac0b6958fcf68fda3c77a048a4f9073704aae"}, - {file = "multidict-6.0.2.tar.gz", hash = "sha256:5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013"}, -] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -mypy-protobuf = [] -myst-nb = [ - {file = "myst-nb-0.16.0.tar.gz", hash = "sha256:9c7ab37929da72f78569a37bcccbc5d49fd679fd7935bf6c9fa36365eb58783a"}, - {file = "myst_nb-0.16.0-py3-none-any.whl", hash = "sha256:bc1073a1fb707e9be4711bce3222ffd4bee30de0cf51aed3c8b852ceccbc4ff0"}, -] -myst-parser = [ - {file = "myst-parser-0.18.0.tar.gz", hash = "sha256:739a4d96773a8e55a2cacd3941ce46a446ee23dcd6b37e06f73f551ad7821d86"}, - {file = "myst_parser-0.18.0-py3-none-any.whl", hash = "sha256:4965e51918837c13bf1c6f6fe2c6bddddf193148360fbdaefe743a4981358f6a"}, -] -nbclient = [ - {file = "nbclient-0.5.13-py3-none-any.whl", hash = "sha256:47ac905af59379913c1f8f541098d2550153cf8dc58553cbe18c702b181518b0"}, - {file = "nbclient-0.5.13.tar.gz", hash = "sha256:40c52c9b5e3c31faecaee69f202b3f53e38d7c1c563de0fadde9d7eda0fdafe8"}, -] -nbconvert = [] -nbformat = [] -nbmake = [] -nest-asyncio = [ - {file = "nest_asyncio-1.5.5-py3-none-any.whl", hash = "sha256:b98e3ec1b246135e4642eceffa5a6c23a3ab12c82ff816a92c612d68205813b2"}, - {file = "nest_asyncio-1.5.5.tar.gz", hash = "sha256:e442291cd942698be619823a17a86a5759eabe1f8613084790de189fe9e16d65"}, -] -notebook = [ - {file = "notebook-6.4.12-py3-none-any.whl", hash = "sha256:8c07a3bb7640e371f8a609bdbb2366a1976c6a2589da8ef917f761a61e3ad8b1"}, - {file = "notebook-6.4.12.tar.gz", hash = "sha256:6268c9ec9048cff7a45405c990c29ac9ca40b0bc3ec29263d218c5e01f2b4e86"}, -] -packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, -] -pandocfilters = [ - {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"}, - {file = "pandocfilters-1.5.0.tar.gz", hash = "sha256:0b679503337d233b4339a817bfc8c50064e2eff681314376a47cb582305a7a38"}, -] -parso = [ - {file = "parso-0.8.3-py2.py3-none-any.whl", hash = "sha256:c001d4636cd3aecdaf33cbb40aebb59b094be2a74c556778ef5576c175e19e75"}, - {file = "parso-0.8.3.tar.gz", hash = "sha256:8c07be290bb59f03588915921e29e8a50002acaf2cdc5fa0e0114f91709fafa0"}, -] -pathspec = [] -pexpect = [ - {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, - {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, -] -pickleshare = [ - {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, - {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, -] -pillow = [] -platformdirs = [ - {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, - {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, -] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -prometheus-client = [ - {file = "prometheus_client-0.14.1-py3-none-any.whl", hash = "sha256:522fded625282822a89e2773452f42df14b5a8e84a86433e3f8a189c1d54dc01"}, - {file = "prometheus_client-0.14.1.tar.gz", hash = "sha256:5459c427624961076277fdc6dc50540e2bacb98eebde99886e59ec55ed92093a"}, -] -prompt-toolkit = [] -protobuf = [] -protoletariat = [ - {file = "protoletariat-0.9.4-py3-none-any.whl", hash = "sha256:25f2dec490ef66a3a577c6956d03835ffbcb769865ecefd709798a81ffd46b30"}, - {file = "protoletariat-0.9.4.tar.gz", hash = "sha256:f34633c9b1d4b0c4efccadd09a4f2a3c3dc1ddb3d38240dfdbafad2553f85fe3"}, -] -psutil = [] -ptyprocess = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] -pure-eval = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, -] -py = [ - {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, - {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, -] -pycodestyle = [ - {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, - {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, -] -pycparser = [ - {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, - {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, -] -pydantic = [] -pyflakes = [ - {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, - {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, -] -pygments = [] -pyparsing = [ - {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, - {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, -] -pyrsistent = [ - {file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"}, - {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"}, - {file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"}, - {file = "pyrsistent-0.18.1-cp310-cp310-win32.whl", hash = "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6"}, - {file = "pyrsistent-0.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec"}, - {file = "pyrsistent-0.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b"}, - {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc"}, - {file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22"}, - {file = "pyrsistent-0.18.1-cp37-cp37m-win32.whl", hash = "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8"}, - {file = "pyrsistent-0.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286"}, - {file = "pyrsistent-0.18.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"}, - {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec"}, - {file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c"}, - {file = "pyrsistent-0.18.1-cp38-cp38-win32.whl", hash = "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca"}, - {file = "pyrsistent-0.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a"}, - {file = "pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5"}, - {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045"}, - {file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c"}, - {file = "pyrsistent-0.18.1-cp39-cp39-win32.whl", hash = "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc"}, - {file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"}, - {file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"}, -] -pytest = [] -pytest-asyncio = [ - {file = "pytest-asyncio-0.18.3.tar.gz", hash = "sha256:7659bdb0a9eb9c6e3ef992eef11a2b3e69697800ad02fb06374a210d85b29f91"}, - {file = "pytest_asyncio-0.18.3-1-py3-none-any.whl", hash = "sha256:16cf40bdf2b4fb7fc8e4b82bd05ce3fbcd454cbf7b92afc445fe299dabb88213"}, - {file = "pytest_asyncio-0.18.3-py3-none-any.whl", hash = "sha256:8fafa6c52161addfd41ee7ab35f11836c5a16ec208f93ee388f752bea3493a84"}, -] -python-dateutil = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, -] -pytz = [] -pywin32 = [ - {file = "pywin32-304-cp310-cp310-win32.whl", hash = "sha256:3c7bacf5e24298c86314f03fa20e16558a4e4138fc34615d7de4070c23e65af3"}, - {file = "pywin32-304-cp310-cp310-win_amd64.whl", hash = "sha256:4f32145913a2447736dad62495199a8e280a77a0ca662daa2332acf849f0be48"}, - {file = "pywin32-304-cp310-cp310-win_arm64.whl", hash = "sha256:d3ee45adff48e0551d1aa60d2ec066fec006083b791f5c3527c40cd8aefac71f"}, - {file = "pywin32-304-cp311-cp311-win32.whl", hash = "sha256:30c53d6ce44c12a316a06c153ea74152d3b1342610f1b99d40ba2795e5af0269"}, - {file = "pywin32-304-cp311-cp311-win_amd64.whl", hash = "sha256:7ffa0c0fa4ae4077e8b8aa73800540ef8c24530057768c3ac57c609f99a14fd4"}, - {file = "pywin32-304-cp311-cp311-win_arm64.whl", hash = "sha256:cbbe34dad39bdbaa2889a424d28752f1b4971939b14b1bb48cbf0182a3bcfc43"}, - {file = "pywin32-304-cp36-cp36m-win32.whl", hash = "sha256:be253e7b14bc601718f014d2832e4c18a5b023cbe72db826da63df76b77507a1"}, - {file = "pywin32-304-cp36-cp36m-win_amd64.whl", hash = "sha256:de9827c23321dcf43d2f288f09f3b6d772fee11e809015bdae9e69fe13213988"}, - {file = "pywin32-304-cp37-cp37m-win32.whl", hash = "sha256:f64c0377cf01b61bd5e76c25e1480ca8ab3b73f0c4add50538d332afdf8f69c5"}, - {file = "pywin32-304-cp37-cp37m-win_amd64.whl", hash = "sha256:bb2ea2aa81e96eee6a6b79d87e1d1648d3f8b87f9a64499e0b92b30d141e76df"}, - {file = "pywin32-304-cp38-cp38-win32.whl", hash = "sha256:94037b5259701988954931333aafd39cf897e990852115656b014ce72e052e96"}, - {file = "pywin32-304-cp38-cp38-win_amd64.whl", hash = "sha256:ead865a2e179b30fb717831f73cf4373401fc62fbc3455a0889a7ddac848f83e"}, - {file = "pywin32-304-cp39-cp39-win32.whl", hash = "sha256:25746d841201fd9f96b648a248f731c1dec851c9a08b8e33da8b56148e4c65cc"}, - {file = "pywin32-304-cp39-cp39-win_amd64.whl", hash = "sha256:d24a3382f013b21aa24a5cfbfad5a2cd9926610c0affde3e8ab5b3d7dbcf4ac9"}, -] -pywinpty = [] -pyyaml = [ - {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, - {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, - {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, - {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, - {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, - {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, - {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, - {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, - {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, - {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, - {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, - {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, - {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, - {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, - {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, - {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, - {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, - {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, - {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, - {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, - {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, - {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, -] -pyzmq = [] -qtconsole = [] -qtpy = [] -requests = [] -send2trash = [ - {file = "Send2Trash-1.8.0-py3-none-any.whl", hash = "sha256:f20eaadfdb517eaca5ce077640cb261c7d2698385a6a0f072a4a5447fd49fa08"}, - {file = "Send2Trash-1.8.0.tar.gz", hash = "sha256:d2c24762fd3759860a0aff155e45871447ea58d2be6bdd39b5c8f966a0c99c2d"}, -] -six = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, -] -snowballstemmer = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] -soupsieve = [ - {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, - {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, -] -sphinx = [] -sphinx-autoapi = [] -sphinx-rtd-theme = [ - {file = "sphinx_rtd_theme-1.0.0-py2.py3-none-any.whl", hash = "sha256:4d35a56f4508cfee4c4fb604373ede6feae2a306731d533f409ef5c3496fdbd8"}, - {file = "sphinx_rtd_theme-1.0.0.tar.gz", hash = "sha256:eec6d497e4c2195fa0e8b2016b337532b8a699a68bcb22a512870e16925c6a5c"}, -] -sphinx-togglebutton = [] -sphinxcontrib-applehelp = [ - {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, - {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, -] -sphinxcontrib-devhelp = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] -sphinxcontrib-htmlhelp = [ - {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, - {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, -] -sphinxcontrib-jsmath = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] -sphinxcontrib-qthelp = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] -sphinxcontrib-serializinghtml = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] -sqlalchemy = [] -stack-data = [] -tabulate = [ - {file = "tabulate-0.8.10-py3-none-any.whl", hash = "sha256:0ba055423dbaa164b9e456abe7920c5e8ed33fcc16f6d1b2f2d152c8e1e8b4fc"}, - {file = "tabulate-0.8.10-py3.8.egg", hash = "sha256:436f1c768b424654fce8597290d2764def1eea6a77cfa5c33be00b1bc0f4f63d"}, - {file = "tabulate-0.8.10.tar.gz", hash = "sha256:6c57f3f3dd7ac2782770155f3adb2db0b1a269637e42f27599925e64b114f519"}, -] -terminado = [ - {file = "terminado-0.15.0-py3-none-any.whl", hash = "sha256:0d5f126fbfdb5887b25ae7d9d07b0d716b1cc0ccaacc71c1f3c14d228e065197"}, - {file = "terminado-0.15.0.tar.gz", hash = "sha256:ab4eeedccfcc1e6134bfee86106af90852c69d602884ea3a1e8ca6d4486e9bfe"}, -] -tinycss2 = [ - {file = "tinycss2-1.1.1-py3-none-any.whl", hash = "sha256:fe794ceaadfe3cf3e686b22155d0da5780dd0e273471a51846d0a02bc204fec8"}, - {file = "tinycss2-1.1.1.tar.gz", hash = "sha256:b2e44dd8883c360c35dd0d1b5aad0b610e5156c2cb3b33434634e539ead9d8bf"}, -] -toml = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] -tomli = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] -tornado = [] -traitlets = [] -types-protobuf = [] -typing-extensions = [] -unidecode = [ - {file = "Unidecode-1.3.4-py3-none-any.whl", hash = "sha256:afa04efcdd818a93237574791be9b2817d7077c25a068b00f8cff7baa4e59257"}, - {file = "Unidecode-1.3.4.tar.gz", hash = "sha256:8e4352fb93d5a735c788110d2e7ac8e8031eb06ccbfe8d324ab71735015f9342"}, -] -urllib3 = [] -wcwidth = [ - {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, - {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, -] -webencodings = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] -widgetsnbextension = [] -wrapt = [ - {file = "wrapt-1.14.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59"}, - {file = "wrapt-1.14.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462"}, - {file = "wrapt-1.14.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1"}, - {file = "wrapt-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320"}, - {file = "wrapt-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069"}, - {file = "wrapt-1.14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656"}, - {file = "wrapt-1.14.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c"}, - {file = "wrapt-1.14.1-cp310-cp310-win32.whl", hash = "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8"}, - {file = "wrapt-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3"}, - {file = "wrapt-1.14.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d"}, - {file = "wrapt-1.14.1-cp35-cp35m-win32.whl", hash = "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7"}, - {file = "wrapt-1.14.1-cp35-cp35m-win_amd64.whl", hash = "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00"}, - {file = "wrapt-1.14.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1"}, - {file = "wrapt-1.14.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1"}, - {file = "wrapt-1.14.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569"}, - {file = "wrapt-1.14.1-cp36-cp36m-win32.whl", hash = "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed"}, - {file = "wrapt-1.14.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471"}, - {file = "wrapt-1.14.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d"}, - {file = "wrapt-1.14.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015"}, - {file = "wrapt-1.14.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a"}, - {file = "wrapt-1.14.1-cp37-cp37m-win32.whl", hash = "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853"}, - {file = "wrapt-1.14.1-cp37-cp37m-win_amd64.whl", hash = "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c"}, - {file = "wrapt-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456"}, - {file = "wrapt-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1"}, - {file = "wrapt-1.14.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0"}, - {file = "wrapt-1.14.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57"}, - {file = "wrapt-1.14.1-cp38-cp38-win32.whl", hash = "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5"}, - {file = "wrapt-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d"}, - {file = "wrapt-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383"}, - {file = "wrapt-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735"}, - {file = "wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3"}, - {file = "wrapt-1.14.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe"}, - {file = "wrapt-1.14.1-cp39-cp39-win32.whl", hash = "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5"}, - {file = "wrapt-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb"}, - {file = "wrapt-1.14.1.tar.gz", hash = "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d"}, -] -zipp = [] diff --git a/pyproject.toml b/pyproject.toml index 2639ab39d..f6290190e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,32 +1,64 @@ -[tool.poetry] -name = "viam" -version = "0.1.0-beta.0" -description = "Viam Robotics python SDK" -authors = [ "Viam Inc" ] -license = "None" +[project] +name = "viam-sdk" +description = "Viam Robotics Python SDK" +authors = [ + {name = "Naveed Jooma", email = "naveed@viam.com" } +] +license = "Apache-2.0" readme = "README.md" +requires-python = ">=3.8.1" +dynamic = [ + 'version', +] +dependencies = [ + "googleapis-common-protos>=1.65.0", + "grpclib>=0.4.7", + "protobuf==5.29.2", + "typing-extensions>=4.12.2", + "pymongo>=4.10.1", +] - [tool.poetry.dependencies] - python = ">=3.9,<3.11" - grpclib = "^0.4.2" - googleapis-common-protos = "^1.56.3" - typing-extensions = "^4.2.0" - Pillow = "^9.1.1" - - [tool.poetry.dev-dependencies] - pytest = "^7.1.2" - pytest-asyncio = "^0.18.3" - coverage = "^6.4.1" - mypy-protobuf = "^3.2.0" - protoletariat = "^0.9.4" - jupyter = "^1.0.0" - flake8 = "^4.0.1" - myst-nb = "^0.16.0" - sphinx-autoapi = "^1.8.4" - sphinx-rtd-theme = "^1.0.0" - autopep8 = "^1.6.0" - black = "^22.3.0" - nbmake = "1.3.0" +[project.urls] +Homepage = "https://www.viam.com" +Documentation = "https://python.viam.dev" +Repository = "https://github.com/viamrobotics/viam-python-sdk" + +[project.optional-dependencies] +mlmodel = [ + "numpy" +] + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.version] +path = "src/viam/version_metadata.py" + +[tool.hatch.build.targets.wheel] +packages = ["src/viam"] +artifacts = ["src/viam/rpc/libviam_rust_utils.*"] + +[tool.uv] +dev-dependencies = [ + "coverage>=7.6.1", + "mypy-protobuf>=3.6.0", + "myst-nb<1.0.0; python_version<'3.9'", + "myst-nb>=1.0.0; python_version>='3.9'", + "nbmake>=1.5.4", + "numpy<1.25.0; python_version<'3.9'", + "numpy>=1.26.2; python_version>='3.9'", + "pillow>=10.4.0", + "pyright>=1.1.382.post1", + "pytest-asyncio>=0.24.0", + "pytest-mock>=3.14.0", + "pytest>=8.3.3", + "ruff>=0.6.8", + "sphinx-autoapi<3.0.0; python_version<'3.9'", + "sphinx-autoapi>=3.0.0; python_version>='3.9'", + "sphinx-rtd-theme>=2.0.0", + "types-pillow>=10.2.0.20240822", +] [tool.pytest.ini_options] addopts = "-ra" @@ -34,17 +66,35 @@ testpaths = "tests" asyncio_mode = "auto" [tool.coverage.run] -omit = [ "*/gen/*" ] +omit = [ "*/gen/*", "*/proto/*" ] -[tool.coverage.report] -exclude_lines = [ "pragma: no\\s*cover", "\\.\\.\\." ] +[tool.pyright] +include = [ "src" ] +exclude = [ "**/gen", "**/proto" ] -[tool.black] +[tool.ruff] line-length = 140 +exclude = [ + ".direnv", + ".git", + ".git-rewrite", + ".ipynb_checkpoints", + ".mypy_cache", + ".pytest_cache", + ".ruff_cache", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "build", + "dist", + "venv", + "gen", + "*_grpc.py", + "*_pb2.py", + "*.pyi", +] -[tool.isort] -profile = "black" - -[build-system] -requires = [ "poetry-core>=1.0.0" ] -build-backend = "poetry.core.masonry.api" +[tool.ruff.lint.per-file-ignores] +"__init__.py" = ["F401"] diff --git a/requirements-test.txt b/requirements-test.txt new file mode 100644 index 000000000..1cb93a2e3 --- /dev/null +++ b/requirements-test.txt @@ -0,0 +1,6 @@ + grpclib~=0.4.6 + googleapis-common-protos~=1.63.2 + typing-extensions~=4.8.0 + Pillow~=10.4.0 + protobuf==5.28.2 + numpy~=1.21 diff --git a/src/viam/__init__.py b/src/viam/__init__.py index 5b1c331c5..a8e9ffdd5 100644 --- a/src/viam/__init__.py +++ b/src/viam/__init__.py @@ -2,7 +2,7 @@ from importlib.metadata import PackageNotFoundError, version from uuid import uuid4 -from viam.proto.common import ResourceName as _ResourceName +from viam.gen.common.v1.common_pb2 import ResourceName as _ResourceName from .logging import getLogger as _getLogger @@ -10,7 +10,7 @@ # VERSIONING # ############## try: - __version__ = version("viam") + __version__ = version("viam-sdk") except PackageNotFoundError: pass @@ -41,4 +41,31 @@ def _log_exceptions(exctype, value, traceback): ################## # MONKEY PATCHES # ################## -_ResourceName.__hash__ = lambda self: hash(f"{self.namespace}/{self.type}/{self.subtype}/{self.name}") +def _rname_str(self: _ResourceName) -> str: + return f"{self.namespace}:{self.type}:{self.subtype}/{self.name}" + + +_ResourceName.__str__ = _rname_str + + +def _rname_repr(self: _ResourceName) -> str: + return f"" + + +_ResourceName.__repr__ = _rname_repr + + +def _rname_hash(self: _ResourceName) -> int: + return hash(str(self)) + + +_ResourceName.__hash__ = _rname_hash # type: ignore + + +def _rname_eq(self: _ResourceName, other: object) -> bool: + if isinstance(other, _ResourceName): + return self.__hash__() == other.__hash__() # type: ignore + return False + + +_ResourceName.__eq__ = _rname_eq # type: ignore diff --git a/src/viam/app/__init__.py b/src/viam/app/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/app/_logs.py b/src/viam/app/_logs.py new file mode 100644 index 000000000..407186b0f --- /dev/null +++ b/src/viam/app/_logs.py @@ -0,0 +1,34 @@ +import sys + +if sys.version_info >= (3, 9): + from collections.abc import AsyncIterator +else: + from typing import AsyncIterator + +from typing import Protocol, TypeVar + +LogsType = TypeVar("LogsType", covariant=True) + + +class _LogsStream(Protocol[LogsType]): + async def next(self) -> LogsType: ... + + def __aiter__(self) -> AsyncIterator: ... + + async def __anext__(self) -> LogsType: ... + + +class _LogsStreamWithIterator(_LogsStream[LogsType]): + _stream: AsyncIterator[LogsType] + + def __init__(self, stream: AsyncIterator[LogsType]): + self._stream = stream + + async def next(self) -> LogsType: + return await self._stream.__anext__() + + def __aiter__(self): + return self._stream + + async def __anext__(self) -> LogsType: + return await self._stream.__anext__() diff --git a/src/viam/app/app_client.py b/src/viam/app/app_client.py new file mode 100644 index 000000000..be3b490b6 --- /dev/null +++ b/src/viam/app/app_client.py @@ -0,0 +1,2687 @@ +import json +from datetime import datetime +from enum import Enum +from typing import Any, AsyncIterator, List, Literal, Mapping, Optional, Tuple, Union + +from grpclib.client import Channel +from typing_extensions import Self + +from viam import logging +from viam.app._logs import _LogsStream, _LogsStreamWithIterator +from viam.proto.app import ( + AddRoleRequest, + APIKeyWithAuthorizations, + AppServiceStub, + AuthenticatorInfo, + Authorization, + AuthorizedPermissions, + ChangeRoleRequest, + CheckPermissionsRequest, + CheckPermissionsResponse, + CreateFragmentRequest, + CreateFragmentResponse, + CreateKeyFromExistingKeyAuthorizationsRequest, + CreateKeyFromExistingKeyAuthorizationsResponse, + CreateKeyRequest, + CreateKeyResponse, + CreateLocationRequest, + CreateLocationResponse, + CreateLocationSecretRequest, + CreateLocationSecretResponse, + CreateModuleRequest, + CreateModuleResponse, + CreateOrganizationInviteRequest, + CreateOrganizationInviteResponse, + CreateOrganizationRequest, + CreateOrganizationResponse, + CreateRegistryItemRequest, + CreateRobotPartSecretRequest, + CreateRobotPartSecretResponse, + DeleteFragmentRequest, + DeleteKeyRequest, + DeleteLocationRequest, + DeleteLocationSecretRequest, + DeleteOrganizationInviteRequest, + DeleteOrganizationMemberRequest, + DeleteOrganizationRequest, + DeleteRegistryItemRequest, + DeleteRobotPartRequest, + DeleteRobotPartSecretRequest, + DeleteRobotRequest, + GetFragmentHistoryRequest, + GetFragmentHistoryResponse, + GetFragmentRequest, + GetFragmentResponse, + GetLocationRequest, + GetLocationResponse, + GetLocationMetadataRequest, + GetLocationMetadataResponse, + GetModuleRequest, + GetModuleResponse, + GetOrganizationNamespaceAvailabilityRequest, + GetOrganizationNamespaceAvailabilityResponse, + GetOrganizationRequest, + GetOrganizationResponse, + GetOrganizationMetadataRequest, + GetOrganizationMetadataResponse, + GetOrganizationsWithAccessToLocationRequest, + GetOrganizationsWithAccessToLocationResponse, + GetRegistryItemRequest, + GetRegistryItemResponse, + GetRobotAPIKeysRequest, + GetRobotAPIKeysResponse, + GetRobotPartHistoryRequest, + GetRobotPartHistoryResponse, + GetRobotPartLogsRequest, + GetRobotPartLogsResponse, + GetRobotPartMetadataRequest, + GetRobotPartMetadataResponse, + GetRobotPartRequest, + GetRobotPartResponse, + GetRobotPartsRequest, + GetRobotPartsResponse, + GetRobotRequest, + GetRobotResponse, + GetRobotMetadataRequest, + GetRobotMetadataResponse, + GetRoverRentalRobotsRequest, + GetRoverRentalRobotsResponse, + GetUserIDByEmailRequest, + GetUserIDByEmailResponse, + ListAuthorizationsRequest, + ListAuthorizationsResponse, + ListFragmentsRequest, + ListFragmentsResponse, + ListKeysRequest, + ListKeysResponse, + ListLocationsRequest, + ListLocationsResponse, + ListModulesRequest, + ListModulesResponse, + ListOrganizationMembersRequest, + ListOrganizationMembersResponse, + ListOrganizationsByUserRequest, + ListOrganizationsByUserResponse, + ListOrganizationsRequest, + ListOrganizationsResponse, + ListRegistryItemsRequest, + ListRegistryItemsResponse, + ListRobotsRequest, + ListRobotsResponse, + Location, + LocationAuth, + LocationAuthRequest, + LocationAuthResponse, + MarkPartAsMainRequest, + MarkPartForRestartRequest, + Model, + Module, + ModuleFileInfo, + NewRobotPartRequest, + NewRobotPartResponse, + NewRobotRequest, + NewRobotResponse, + Organization, + OrganizationIdentity, + OrganizationInvite, + OrganizationMember, + OrgDetails, + RegistryItem, + RegistryItemStatus, + RemoveRoleRequest, + ResendOrganizationInviteRequest, + ResendOrganizationInviteResponse, + Robot, + RotateKeyRequest, + RotateKeyResponse, + RoverRentalRobot, + SharedSecret, + ShareLocationRequest, + TailRobotPartLogsRequest, + TailRobotPartLogsResponse, + UnshareLocationRequest, + UpdateFragmentRequest, + UpdateFragmentResponse, + UpdateLocationMetadataRequest, + UpdateLocationMetadataResponse, + UpdateLocationRequest, + UpdateLocationResponse, + UpdateModuleRequest, + UpdateModuleResponse, + UpdateOrganizationInviteAuthorizationsRequest, + UpdateOrganizationInviteAuthorizationsResponse, + UpdateOrganizationMetadataRequest, + UpdateOrganizationMetadataResponse, + UpdateOrganizationRequest, + UpdateOrganizationResponse, + UpdateRegistryItemRequest, + UpdateRobotMetadataRequest, + UpdateRobotMetadataResponse, + UpdateRobotPartMetadataRequest, + UpdateRobotPartMetadataResponse, + UpdateRobotPartRequest, + UpdateRobotPartResponse, + UpdateRobotRequest, + UpdateRobotResponse, + UploadModuleFileRequest, + Visibility, +) +from viam.proto.app import Fragment as FragmentPB +from viam.proto.app import FragmentHistoryEntry as FragmentHistoryEntryPB +from viam.proto.app import FragmentVisibility as FragmentVisibilityPB +from viam.proto.app import RobotPart as RobotPartPB +from viam.proto.app import RobotPartHistoryEntry as RobotPartHistoryEntryPB +from viam.proto.app.packages import PackageType +from viam.proto.common import LogEntry as LogEntryPB +from viam.utils import datetime_to_timestamp, dict_to_struct, struct_to_dict + +LOGGER = logging.getLogger(__name__) + + +class RobotPart: + """A class that mirrors the `RobotPart` proto message. + + Use this class to make the attributes of a `viam.proto.app.RobotPart` more accessible and easier to read/interpret. + """ + + @classmethod + def from_proto(cls, robot_part: RobotPartPB) -> Self: + """Create a `RobotPart` from the .proto defined `RobotPart`. + + Args: + robot_part (viam.proto.app.RobotPart): The object to copy from. + + Returns: + RobotPart: The `RobotPart`. + """ + self = cls() + self.id = robot_part.id + self.name = robot_part.name + self.dns_name = robot_part.dns_name + self.secret = robot_part.secret + self.robot = robot_part.robot + self.location_id = robot_part.location_id + self.robot_config = struct_to_dict(robot_part.robot_config) if robot_part.HasField("robot_config") else None + self.last_access = robot_part.last_access.ToDatetime() if robot_part.HasField("last_access") else None + self.user_supplied_info = struct_to_dict(robot_part.user_supplied_info) if robot_part.HasField("user_supplied_info") else None + self.main_part = robot_part.main_part + self.fqdn = robot_part.fqdn + self.local_fqdn = robot_part.local_fqdn + self.created_on = robot_part.created_on.ToDatetime() if robot_part.HasField("created_on") else None + self.secrets = list(robot_part.secrets) + self.last_updated = robot_part.last_updated.ToDatetime() if robot_part.HasField("last_updated") else None + return self + + id: str + name: str + dns_name: str + secret: str + robot: str + location_id: str + robot_config: Optional[Mapping[str, Any]] + last_access: Optional[datetime] + user_supplied_info: Optional[Mapping[str, Any]] + main_part: bool + fqdn: str + local_fqdn: str + created_on: Optional[datetime] + secrets: Optional[List[SharedSecret]] + last_updated: Optional[datetime] + + @property + def proto(self) -> RobotPartPB: + return RobotPartPB( + id=self.id, + name=self.name, + dns_name=self.dns_name, + secret=self.secret, + robot=self.robot, + location_id=self.location_id, + robot_config=dict_to_struct(self.robot_config) if self.robot_config else None, + last_access=datetime_to_timestamp(self.last_access) if self.last_access else None, + user_supplied_info=dict_to_struct(self.user_supplied_info) if self.user_supplied_info else None, + main_part=self.main_part, + fqdn=self.fqdn, + local_fqdn=self.local_fqdn, + created_on=datetime_to_timestamp(self.created_on) if self.created_on else None, + secrets=self.secrets, + last_updated=datetime_to_timestamp(self.last_updated) if self.last_updated else None, + ) + + +class LogEntry: + """A class that mirrors the `LogEntry` proto message. + + Use this class to make the attributes of a `viam.proto.app.LogEntry` more accessible and easier to read/interpret. + """ + + @classmethod + def from_proto(cls, log_entry: LogEntryPB) -> Self: + """Create a `LogEntry` from the .proto defined `LogEntry`. + + Args: + log_entry (viam.proto.app.LogEntry): The object to copy from. + + Returns: + LogEntry: The `LogEntry`. + """ + self = cls() + self.host = log_entry.host + self.level = log_entry.level + self.time = log_entry.time.ToDatetime() if log_entry.HasField("time") else None + self.logger_name = log_entry.logger_name + self.message = log_entry.message + self.caller = struct_to_dict(log_entry.caller) if log_entry.HasField("caller") else None + self.stack = log_entry.stack + self.fields = [struct_to_dict(field) for field in log_entry.fields] + return self + + host: str + level: str + time: Optional[datetime] + logger_name: str + message: str + caller: Optional[Mapping[str, Any]] + stack: str + fields: Optional[List[Mapping[str, Any]]] + + @property + def proto(self) -> LogEntryPB: + return LogEntryPB( + host=self.host, + level=self.level, + time=datetime_to_timestamp(self.time) if self.time else None, + logger_name=self.logger_name, + message=self.message, + caller=dict_to_struct(self.caller) if self.caller else None, + stack=self.stack, + fields=[dict_to_struct(field) for field in self.fields] if self.fields else None, + ) + + +class Fragment: + """A class that mirrors the `Fragment` proto message. + + Use this class to make the attributes of a `viam.proto.app.RobotPart` more accessible and easier to read/interpret. + """ + + class Visibility(str, Enum): + """ + FragmentVisibility specifies who is permitted to view the fragment. + """ + + PRIVATE = "private" + """ + Only visible to members in the fragment's organization. + """ + + PUBLIC = "public" + """ + Visible to anyone and appears on the fragments page. + """ + + PUBLIC_UNLISTED = "public_unlisted" + """ + Visible to anyone but does not appear on the fragments page. + """ + + UNSPECIFIED = "unspecified" + """ + Uninitialized visibility. + """ + + @classmethod + def from_proto(cls, visibility: FragmentVisibilityPB.ValueType) -> "Fragment.Visibility": + if visibility == FragmentVisibilityPB.FRAGMENT_VISIBILITY_PRIVATE: + return Fragment.Visibility.PRIVATE + if visibility == FragmentVisibilityPB.FRAGMENT_VISIBILITY_PUBLIC: + return Fragment.Visibility.PUBLIC + if visibility == FragmentVisibilityPB.FRAGMENT_VISIBILITY_PUBLIC_UNLISTED: + return Fragment.Visibility.PUBLIC_UNLISTED + return Fragment.Visibility.UNSPECIFIED + + def to_proto(self) -> FragmentVisibilityPB.ValueType: + if self == self.PRIVATE: + return FragmentVisibilityPB.FRAGMENT_VISIBILITY_PRIVATE + if self == self.PUBLIC: + return FragmentVisibilityPB.FRAGMENT_VISIBILITY_PUBLIC + if self == self.PUBLIC_UNLISTED: + return FragmentVisibilityPB.FRAGMENT_VISIBILITY_PUBLIC_UNLISTED + return FragmentVisibilityPB.FRAGMENT_VISIBILITY_UNSPECIFIED + + @classmethod + def from_proto(cls, fragment: FragmentPB) -> Self: + """Create a `Fragment` from the .proto defined `Fragment`. + + Args: + fragment (viam.proto.app.Fragment): The object to copy from. + + Returns: + Fragment: The `Fragment`. + """ + self = cls() + self.id = fragment.id + self.name = fragment.name + self.fragment = struct_to_dict(fragment.fragment) if fragment.HasField("fragment") else None + self.organization_owner = fragment.organization_owner + self.public = fragment.public + self.created_on = fragment.created_on.ToDatetime() if fragment.HasField("created_on") else None + self.organization_name = fragment.organization_name + self.robot_part_count = fragment.robot_part_count + self.organization_count = fragment.organization_count + self.only_used_by_owner = fragment.only_used_by_owner + self.visibility = Fragment.Visibility.from_proto(fragment.visibility) + self.last_updated = fragment.last_updated.ToDatetime() if fragment.HasField("last_updated") else None + return self + + id: str + name: str + fragment: Optional[Mapping[str, Any]] + organization_owner: str + public: bool + created_on: Optional[datetime] + organization_name: str + robot_part_count: int + organization_count: int + only_used_by_owner: bool + visibility: Visibility + last_updated: Optional[datetime] + + @property + def proto(self) -> FragmentPB: + return FragmentPB( + id=self.id, + name=self.name, + fragment=dict_to_struct(self.fragment) if self.fragment else None, + organization_owner=self.organization_owner, + public=self.public, + created_on=datetime_to_timestamp(self.created_on) if self.created_on else None, + organization_name=self.organization_name, + robot_part_count=self.robot_part_count, + organization_count=self.organization_count, + only_used_by_owner=self.only_used_by_owner, + visibility=self.visibility.to_proto(), + last_updated=datetime_to_timestamp(self.last_updated) if self.last_updated else None, + ) + + +class FragmentHistoryEntry: + """A class that mirrors the `FragmentHistoryEntry` proto message. + + Use this class to make the attributes of a `viam.proto.app.FragmentHistoryEntry` more accessible and easier to read/interpret. + """ + + @classmethod + def from_proto(cls, fragment_history_entry: FragmentHistoryEntryPB) -> Self: + """Create a `FragmentHistoryEntry` from the .proto defined `FragmentHistoryEntry`. + + Args: + fragment_history_entry (viam.proto.app.FragmentHistoryEntry): The object to copy from. + + Returns: + FragmentHistoryEntry: The `FragmentHistoryEntry`. + """ + self = cls() + self.fragment = fragment_history_entry.fragment + self.edited_on = fragment_history_entry.edited_on.ToDatetime() + self.old = Fragment.from_proto(fragment_history_entry.old) + self.edited_by = fragment_history_entry.edited_by + return self + + fragment: str + edited_on: datetime + old: Fragment + edited_by: AuthenticatorInfo + + @property + def proto(self) -> FragmentHistoryEntryPB: + return FragmentHistoryEntryPB( + fragment=self.fragment, + edited_on=datetime_to_timestamp(self.edited_on), + edited_by=self.edited_by, + old=self.old.proto if self.old else None, + ) + + +class RobotPartHistoryEntry: + """A class that mirrors the `RobotPartHistoryEntry` proto message. + + Use this class to make the attributes of a `viam.proto.app.RobotPartHistoryEntry` more accessible and easier to read/interpret. + """ + + @classmethod + def from_proto(cls, robot_part_history_entry: RobotPartHistoryEntryPB) -> Self: + """Create a `RobotPartHistoryEntry` from the .proto defined `RobotPartHistoryEntry`. + + Args: + robot_part_history_entry (viam.proto.app.RobotPartHistoryEntry): The object to copy from. + + Returns: + RobotPartHistoryEntry: The `RobotPartHistoryEntry`. + """ + self = cls() + self.part = robot_part_history_entry.part + self.robot = robot_part_history_entry.robot + self.when = robot_part_history_entry.when.ToDatetime() if robot_part_history_entry.HasField("when") else None + self.old = RobotPart.from_proto(robot_part_history_entry.old) if robot_part_history_entry.HasField("old") else None + return self + + part: str + robot: str + when: Optional[datetime] + old: Optional[RobotPart] + + @property + def proto(self) -> RobotPartHistoryEntryPB: + return RobotPartHistoryEntryPB( + part=self.part, + robot=self.robot, + when=datetime_to_timestamp(self.when) if self.when else None, + old=self.old.proto if self.old else None, + ) + + +class APIKeyAuthorization: + """A class with the necessary authorization data for creating an API key. + + Use this class when constructing API key authorizations to minimize the risk of malformed or missing data. + """ + + def __init__( + self, + role: Union[Literal["owner"], Literal["operator"]], + resource_type: Union[Literal["organization"], Literal["location"], Literal["robot"]], + resource_id: str, + ): + """role (Union[Literal["owner"], Literal["operator"]]): The role to add. + resource_type (Union[Literal["organization"], Literal["location"], Literal["robot"]]): Type of the resource to add role to. + Must match `resource_id`. + resource_id (str): ID of the resource the role applies to (that is, either an organization, location, or robot ID). + """ + self._role = role + self._resource_type = resource_type + self._resource_id = resource_id + + _role: str + _resource_type: str + _resource_id: str + + +class AppClient: + """gRPC client for method calls to app. + + Constructor is used by `ViamClient` to instantiate relevant service stub. Calls to `AppClient` methods should be made through + `ViamClient`. + + Establish a Connection:: + + import asyncio + + from viam.rpc.dial import DialOptions, Credentials + from viam.app.viam_client import ViamClient + + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + + async def main(): + + # Make a ViamClient + viam_client = await connect() + # Instantiate an AppClient called "cloud" to run cloud app API methods on + cloud = viam_client.app_client + + viam_client.close() + + if __name__ == '__main__': + asyncio.run(main()) + + For more information, see `Fleet Management API `_. + """ + + def __init__(self, channel: Channel, metadata: Mapping[str, str], location_id: Optional[str] = None): + """Create an `AppClient` that maintains a connection to app. + + Args: + channel (grpclib.client.Channel): connection to app. + metadata (Mapping[str, str]): Required authorization token to send requests to app. + location_id (Optional[str]): Default location ID. + """ + self._metadata = metadata + self._app_client = AppServiceStub(channel) + self._location_id = location_id + self._channel = channel + + _app_client: AppServiceStub + _metadata: Mapping[str, str] + _location_id: Optional[str] + _channel: Channel + _organization_id: Optional[str] = None + + async def _create_authorization( + self, + organization_id: str, + identity_id: str, + identity_type: str, + role: Union[Literal["owner"], Literal["operator"]], + resource_type: Union[Literal["organization"], Literal["location"], Literal["robot"]], + resource_id: str, + ) -> Authorization: + return Authorization( + authorization_type="role", + identity_id=identity_id, + identity_type=identity_type, + authorization_id=f"{resource_type}_{role}", + resource_type=resource_type, + resource_id=resource_id, + organization_id=organization_id, + ) + + async def _create_authorization_for_new_api_key(self, org_id: str, auth: APIKeyAuthorization) -> Authorization: + """Creates a new Authorization specifically for creating an API key.""" + return await self._create_authorization( + organization_id=org_id, + identity_id="", # setting `identity_id` when creating an API key results in an error + identity_type="api-key", + role=auth._role, # type: ignore -- Ignoring because this is technically a `string` + resource_type=auth._resource_type, # type: ignore -- Ignoring because this is technically a `string` + resource_id=auth._resource_id, + ) + + async def get_user_id_by_email(self, email: str) -> str: + """Get the ID of a user by email. + + :: + + id = await cloud.get_user_id_by_email("youremail@email.com") + + Args: + email (str): The email of the user. + + Returns: + str: The ID of the user. + + For more information, see `Fleet Management API `_. + """ + request = GetUserIDByEmailRequest(email=email) + response: GetUserIDByEmailResponse = await self._app_client.GetUserIDByEmail(request, metadata=self._metadata) + return response.user_id + + async def create_organization(self, name: str) -> Organization: + """Create an organization. + + :: + + organization = await cloud.create_organization("name") + + Args: + name (str): The name of the organization. + + Returns: + Organization: The created organization. + + For more information, see `Fleet Management API `_. + """ + request = CreateOrganizationRequest(name=name) + response: CreateOrganizationResponse = await self._app_client.CreateOrganization(request, metadata=self._metadata) + return response.organization + + async def list_organizations(self) -> List[Organization]: + """List the organization(s) the user is an authorized owner of. + + :: + + org_list = await cloud.list_organizations() + + Returns: + List[viam.proto.app.Organization]: The list of organizations. + + For more information, see `Fleet Management API `_. + """ + request = ListOrganizationsRequest() + response: ListOrganizationsResponse = await self._app_client.ListOrganizations(request, metadata=self._metadata) + return list(response.organizations) + + async def get_organizations_with_access_to_location(self, location_id: str) -> List[OrganizationIdentity]: + """Get all organizations that have access to a location. + + :: + + org_list = await cloud.get_organizations_with_access_to_location("location-id") + + Args: + location_id (str): The ID of the location. + + Returns: + List[viam.proto.app.OrganizationIdentity]: The list of organizations. + + For more information, see `Fleet Management API `_. + """ + request = GetOrganizationsWithAccessToLocationRequest(location_id=location_id) + response: GetOrganizationsWithAccessToLocationResponse = await self._app_client.GetOrganizationsWithAccessToLocation( + request, metadata=self._metadata + ) + return list(response.organization_identities) + + async def list_organizations_by_user(self, user_id: str) -> List[OrgDetails]: + """List the organizations a user belongs to. + + :: + + org_list = await cloud.list_organizations_by_user("") + + Args: + user_id (str): The ID of the user. You can retrieve this with the get_user_id_by_email() method. + + Returns: + List[OrgDetails]: The list of organizations. + + For more information, see `Fleet Management API `_. + """ + request = ListOrganizationsByUserRequest(user_id=user_id) + response: ListOrganizationsByUserResponse = await self._app_client.ListOrganizationsByUser(request, metadata=self._metadata) + return list(response.orgs) + + async def get_organization(self, org_id: str) -> Organization: + """Retrieve the organization object for the requested organization containing the organization's ID, + name, public namespace, and more. + + :: + + org = await cloud.get_organization("") + + Args: + org_id (str): The ID of the organization to query. You can retrieve this from the organization settings page. + + Raises: + GRPCError: If the provided org_id is invalid, or not currently authed to. + + Returns: + viam.proto.app.Organization: The requested organization. + + For more information, see `Fleet Management API `_. + """ + request = GetOrganizationRequest(organization_id=org_id) + response: GetOrganizationResponse = await self._app_client.GetOrganization(request, metadata=self._metadata) + return response.organization + + async def get_organization_namespace_availability(self, public_namespace: str) -> bool: + """Check the availability of an organization namespace. + + :: + + available = await cloud.get_organization_namespace_availability( + public_namespace="my-cool-organization") + + Args: + public_namespace (str): Organization namespace to check. Namespaces can only contain lowercase alphanumeric and dash + characters. + + Raises: + GRPCError: If an invalid namespace (for example, "") is provided. + + Returns: + bool: True if the provided namespace is available. + + For more information, see `Fleet Management API `_. + """ + request = GetOrganizationNamespaceAvailabilityRequest(public_namespace=public_namespace) + response: GetOrganizationNamespaceAvailabilityResponse = await self._app_client.GetOrganizationNamespaceAvailability( + request, metadata=self._metadata + ) + return response.available + + async def update_organization( + self, + org_id: str, + name: Optional[str] = None, + public_namespace: Optional[str] = None, + region: Optional[str] = None, + cid: Optional[str] = None, + ) -> Organization: + """Updates organization details. + + :: + + organization = await cloud.update_organization( + org_id="", + name="Artoo's Org", + public_namespace="artoo" + ) + + Args: + org_id (str): The ID of the organization to update. + name (Optional[str]): If provided, updates the org's name. + public_namespace (Optional[str]): If provided, sets the org's namespace if it hasn't already been set. + region (Optional[str]): If provided, updates the org's region. + cid (Optional[str]): If provided, update's the org's CRM ID. + + Raises: + GRPCError: If the org's namespace has already been set, or if the provided namespace is already taken. + + Returns: + viam.proto.app.Organization: The updated organization. + + For more information, see `Fleet Management API `_. + """ + request = UpdateOrganizationRequest( + organization_id=org_id, + public_namespace=public_namespace, + region=region, + cid=cid, + name=name, + ) + response: UpdateOrganizationResponse = await self._app_client.UpdateOrganization(request, metadata=self._metadata) + return response.organization + + async def delete_organization(self, org_id: str) -> None: + """Delete an organization + + :: + + await cloud.delete_organization("") + + Args: + org_id (str): The ID of the organization. + You can obtain your organization ID from the Viam app's organization settings page. + + For more information, see `Fleet Management API `_. + """ + request = DeleteOrganizationRequest(organization_id=org_id) + await self._app_client.DeleteOrganization(request, metadata=self._metadata) + + async def list_organization_members(self, org_id: str) -> Tuple[List[OrganizationMember], List[OrganizationInvite]]: + """List the members and invites of the currently authed-to organization. + + :: + + member_list, invite_list = await cloud.list_organization_members("") + + Args: + org_id (str): The ID of the organization to list members of. + You can obtain your organization ID from the Viam app's organization settings page. + + Returns: + Tuple[List[viam.proto.app.OrganizationMember], List[viam.proto.app.OrganizationInvite]]: A tuple containing two lists; the first + [0] of organization members, and the second [1] of organization invites. + + For more information, see `Fleet Management API `_. + """ + request = ListOrganizationMembersRequest(organization_id=org_id) + response: ListOrganizationMembersResponse = await self._app_client.ListOrganizationMembers(request, metadata=self._metadata) + return list(response.members), list(response.invites) + + async def create_organization_invite( + self, + org_id: str, + email: str, + authorizations: Optional[List[Authorization]] = None, + send_email_invite: bool = True, + ) -> OrganizationInvite: + """Creates an organization invite and sends it via email. + + :: + + await cloud.create_organization_invite("", "youremail@email.com") + + Args: + org_id (str): The ID of the organization to create an invite for. + You can obtain your organization ID from the Viam app's organization settings page. + email (str): The email address to send the invite to. + authorizations (Optional[List[viam.proto.app.Authorization]]): Specifications of the + authorizations to include in the invite. If not provided, full owner permissions will + be granted. + send_email_invite (Optional[bool]): Whether or not an email should be sent to the recipient of an invite. + The user must accept the email to be added to the associated authorizations. + When set to false, the user automatically receives the associated authorization + on the next login of the user with the associated email address. + + Raises: + GRPCError: if an invalid email is provided, or if the user is already a member of the org. + + Returns: + OrganizationInvite: The organization invite. + + For more information, see `Fleet Management API `_. + """ + request = CreateOrganizationInviteRequest( + organization_id=org_id, email=email, authorizations=authorizations, send_email_invite=send_email_invite + ) + response: CreateOrganizationInviteResponse = await self._app_client.CreateOrganizationInvite(request, metadata=self._metadata) + return response.invite + + async def update_organization_invite_authorizations( + self, + org_id: str, + email: str, + add_authorizations: Optional[List[Authorization]] = None, + remove_authorizations: Optional[List[Authorization]] = None, + ) -> OrganizationInvite: + """Update the authorizations attached to an organization invite that has already been created. + + Note that an invite can only have one authorization at each resource (for example, organization, location, robot, etc.) level and + must have at least one authorization overall. + + :: + + from viam.proto.app import Authorization + + auth = Authorization( + authorization_type="role", + authorization_id="location_owner", + resource_type="location", # "robot", "location", or "organization" + resource_id="012456lni0", # machine id, location id or org id + identity_id="", + organization_id="", + identity_type="" + ) + + update_invite = await cloud.update_organization_invite_authorizations( + org_id="", + email="notarealemail@viam.com", + add_authorizations=[auth] + ) + + Args: + org_id (str): The ID of the organization that the invite is for. + You can obtain your organization ID from the Viam app's organization settings page. + email (str): Email of the user the invite was sent to. + add_authorizations (Optional[List[viam.proto.app.Authorization]]): Optional list of authorizations to add to the invite. + remove_authorizations (Optional[List[viam.proto.app.Authorization]]): Optional list of authorizations to remove from the invite. + + Raises: + GRPCError: If no authorizations are passed or if an invalid combination of authorizations is passed (for example an + authorization to remove when the invite only contains one authorization). + + Returns: + viam.proto.app.OrganizationInvite: The updated invite. + + For more information, see `Fleet Management API `_. + """ + request = UpdateOrganizationInviteAuthorizationsRequest( + organization_id=org_id, email=email, add_authorizations=add_authorizations, remove_authorizations=remove_authorizations + ) + response: UpdateOrganizationInviteAuthorizationsResponse = await self._app_client.UpdateOrganizationInviteAuthorizations( + request, metadata=self._metadata + ) + return response.invite + + async def delete_organization_member(self, org_id: str, user_id: str) -> None: + """Remove a member from the organization. + + :: + + member_list, invite_list = await cloud.list_organization_members(org_id="") + first_user_id = member_list[0].user_id + + await cloud.delete_organization_member(org_id="org_id", user_id=first_user_id) + + Args: + org_id (str): The ID of the org to remove the user from. + You can obtain your organization ID from the Viam app's organization settings page. + user_id (str): The ID of the user to remove. + + For more information, see `Fleet Management API `_. + """ + request = DeleteOrganizationMemberRequest(organization_id=org_id, user_id=user_id) + await self._app_client.DeleteOrganizationMember(request, metadata=self._metadata) + + async def delete_organization_invite(self, org_id: str, email: str) -> None: + """Deletes a pending organization invite. + + :: + + await cloud.delete_organization_invite("", "youremail@email.com") + + Args: + org_id (str): The ID of the organization that the invite to delete was for. + You can obtain your organization ID from the Viam app's organization settings page. + email (str): The email address the pending invite was sent to. + + Raises: + GRPCError: If no pending invite is associated with the provided email address. + + For more information, see `Fleet Management API `_. + """ + request = DeleteOrganizationInviteRequest(organization_id=org_id, email=email) + await self._app_client.DeleteOrganizationInvite(request, metadata=self._metadata) + + async def resend_organization_invite(self, org_id: str, email: str) -> OrganizationInvite: + """Re-sends a pending organization invite email. + + :: + + org_invite = await cloud.resend_organization_invite("", "youremail@email.com") + + Args: + org_id (str): The ID of the organization that the invite to resend was for. + You can obtain your organization ID from the Viam app's organization settings page. + email (str): The email address associated with the invite. + + Raises: + GRPCError: If no pending invite is associated with the provided email address. + + Returns: + viam.proto.app.OrganizationInvite: The organization invite sent. + + For more information, see `Fleet Management API `_. + """ + request = ResendOrganizationInviteRequest(organization_id=org_id, email=email) + response: ResendOrganizationInviteResponse = await self._app_client.ResendOrganizationInvite(request, metadata=self._metadata) + return response.invite + + async def create_location(self, org_id: str, name: str, parent_location_id: Optional[str] = None) -> Location: + """Create and name a location under the currently authed-to organization and the specified parent location. + + :: + + my_new_location = await cloud.create_location(org_id="", name="Robotville", parent_location_id="111ab12345") + + Args: + org_id (str): The ID of the organization to create the location under. + You can obtain your organization ID from the Viam app's organization settings page. + name (str): Name of the location. + parent_location_id (Optional[str]): Optional parent location to put the location under. Defaults to a root-level location if no + location ID is provided. + + Raises: + GRPCError: If either an invalid name (for example, ""), or parent location ID (for example, a nonexistent ID) is passed. + + Returns: + viam.proto.app.Location: The newly created location. + + For more information, see `Fleet Management API `_. + """ + request = CreateLocationRequest(organization_id=org_id, name=name, parent_location_id=parent_location_id) + response: CreateLocationResponse = await self._app_client.CreateLocation(request, metadata=self._metadata) + return response.location + + async def get_location(self, location_id: Optional[str] = None) -> Location: + """Get a location. + + :: + + location = await cloud.get_location(location_id="123ab12345") + + Args: + location_id (Optional[str]): ID of the location to get. Defaults to the location ID provided at `AppClient` instantiation. + + Raises: + GRPCError: If an invalid location ID is passed or if one isn't passed and there was no location ID provided at `AppClient` + instantiation. + + Returns: + viam.proto.app.Location: The location. + + For more information, see `Fleet Management API `_. + """ + request = GetLocationRequest(location_id=location_id if location_id else self._location_id if self._location_id else "") + response: GetLocationResponse = await self._app_client.GetLocation(request, metadata=self._metadata) + return response.location + + async def update_location(self, location_id: str, name: Optional[str] = None, parent_location_id: Optional[str] = None) -> Location: + """Change the name of a location and/or assign it a new parent location. + + :: + + # The following line takes the location with ID "abc12abcde" and moves it to be a + # sub-location of the location with ID "xyz34xxxxx" + my_updated_location = await cloud.update_location( + location_id="abc12abcde", + name="", + parent_location_id="xyz34xxxxx", + ) + + # The following line changes the name of the location without changing its parent location + my_updated_location = await cloud.update_location( + location_id="abc12abcde", + name="Land Before Robots" + ) + + # The following line moves the location back up to be a top level location without changing its name + my_updated_location = await cloud.update_location( + location_id="abc12abcde", + name="", + parent_location_id="" + ) + + Args: + location_id (str): ID of the location to update. Must be specified. + name (Optional[str]): Optional new name to be updated on the location. Defaults to the empty string "" (that is, the name + doesn't change). + parent_location_id(Optional[str]): Optional ID of new parent location to move the location under. Defaults to the empty string + "" (that is, no new parent location is assigned). + + Raises: + GRPCError: If either an invalid location ID, name, or parent location ID is passed. + + Returns: + viam.proto.app.Location: The newly updated location. + + For more information, see `Fleet Management API `_. + """ + request = UpdateLocationRequest(location_id=location_id, name=name, parent_location_id=parent_location_id) + response: UpdateLocationResponse = await self._app_client.UpdateLocation(request, metadata=self._metadata) + return response.location + + async def delete_location(self, location_id: str) -> None: + """Delete a location. + + :: + + await cloud.delete_location(location_id="abc12abcde") + + Args: + location_id (str): ID of the location to delete. Must be specified. + + Raises: + GRPCError: If an invalid location ID is passed. + + For more information, see `Fleet Management API `_. + """ + request = DeleteLocationRequest(location_id=location_id) + await self._app_client.DeleteLocation(request, metadata=self._metadata) + + async def list_locations(self, org_id: str) -> List[Location]: + """Get a list of all locations under the currently authed-to organization. + + :: + + locations = await cloud.list_locations("") + + Args: + org_id (str): The ID of the org to list locations for. + You can obtain your organization ID from the Viam app's organization settings page. + + Returns: + List[viam.proto.app.Location]: The list of locations. + + For more information, see `Fleet Management API `_. + """ + request = ListLocationsRequest(organization_id=org_id) + response: ListLocationsResponse = await self._app_client.ListLocations(request, metadata=self._metadata) + return list(response.locations) + + async def share_location(self, organization_id: str, location_id: str) -> None: + """Share a location with an organization. + + :: + + await cloud.share_location("", "") + + Args: + organization_id (str): The ID of the organization. + location_id (str): The ID of the location. + + For more information, see `Fleet Management API `_. + """ + request = ShareLocationRequest(location_id=location_id, organization_id=organization_id) + await self._app_client.ShareLocation(request, metadata=self._metadata) + + async def unshare_location(self, organization_id: str, location_id: str) -> None: + """Stop sharing a location with an organization. + + :: + + await cloud.unshare_location("", "") + + Args: + organization_id (str): The ID of the organization. + location_id (str): The ID of the location. + + For more information, see `Fleet Management API `_. + """ + request = UnshareLocationRequest(location_id=location_id, organization_id=organization_id) + await self._app_client.UnshareLocation(request, metadata=self._metadata) + + async def location_auth(self, location_id: Optional[str] = None) -> LocationAuth: + """Get a location's `LocationAuth` (location secret(s)). + + :: + + loc_auth = await cloud.location_auth(location_id="123xy12345") + + Args: + location_id (str): ID of the location to retrieve `LocationAuth` from. Defaults to the location ID provided at `AppClient` + instantiation. + + Raises: + GRPCError: If an invalid location ID is passed or if one isn't passed and there was no location ID provided at `AppClient` + instantiation. + + Returns: + viam.proto.app.LocationAuth: The `LocationAuth` containing location secrets. + + For more information, see `Fleet Management API `_. + """ + request = LocationAuthRequest(location_id=location_id if location_id else self._location_id if self._location_id else "") + response: LocationAuthResponse = await self._app_client.LocationAuth(request, metadata=self._metadata) + return response.auth + + async def create_location_secret(self, location_id: Optional[str] = None) -> LocationAuth: + """Create a new location secret. + + :: + + new_loc_auth = await cloud.create_location_secret(location_id="123xy12345") + + Args: + location_id (Optional[str]): ID of the location to generate a new secret for. Defaults to the location ID provided at + `AppClient` instantiation. + + Raises: + GRPCError: If an invalid location ID is passed or one isn't passed and there was no location ID provided at `AppClient` + instantiation. + + Returns: + viam.proto.app.LocationAuth: The specified location's `LocationAuth` containing the newly created secret. + + For more information, see `Fleet Management API `_. + """ + request = CreateLocationSecretRequest(location_id=location_id if location_id else self._location_id if self._location_id else "") + response: CreateLocationSecretResponse = await self._app_client.CreateLocationSecret(request, metadata=self._metadata) + return response.auth + + async def delete_location_secret(self, secret_id: str, location_id: Optional[str] = None) -> None: + """Delete a location secret. + + :: + + await cloud.delete_location_secret( + secret_id="abcd123-456-7890ab-cxyz98-989898xyzxyz", + location_id="123xy12345" + ) + + Args: + location_id (str): ID of the location to delete secret from. Defaults to the location ID provided at `AppClient` instantiation. + secret_id (str): ID of the secret to delete. + + Raises: + GRPCError: If either an invalid location ID or secret ID is passed or a location ID isn't passed and there was no location + ID provided at `AppClient` instantiation. + + For more information, see `Fleet Management API `_. + """ + request = DeleteLocationSecretRequest( + location_id=location_id if location_id else self._location_id if self._location_id else "", secret_id=secret_id + ) + await self._app_client.DeleteLocationSecret(request, metadata=self._metadata) + + async def get_robot(self, robot_id: str) -> Robot: + """Get a machine. + + :: + + machine = await cloud.get_robot(robot_id="1a123456-x1yz-0ab0-a12xyzabc") + + Args: + robot_id (str): ID of the machine to get. You can copy this value from the URL of the machine's page. + + Raises: + GRPCError: If an invalid machine ID is passed. + + Returns: + viam.proto.app.Robot: The machine. + + For more information, see `Fleet Management API `_. + """ + request = GetRobotRequest(id=robot_id) + response: GetRobotResponse = await self._app_client.GetRobot(request, metadata=self._metadata) + return response.robot + + async def get_rover_rental_robots(self, org_id: str) -> List[RoverRentalRobot]: + """Returns a list of rover rental robots within an org. + + :: + + rental_robots = await cloud.get_rover_rental_robots() + + Args: + org_id (str): The ID of the organization to list rover rental robots for. + You can obtain your organization ID from the Viam app's organization settings page. + + Returns: + List[viam.proto.app.RoverRentalRobot]: The list of rover rental robots. + + For more information, see `Fleet Management API `_. + """ + request = GetRoverRentalRobotsRequest(org_id=org_id) + response: GetRoverRentalRobotsResponse = await self._app_client.GetRoverRentalRobots(request, metadata=self._metadata) + return list(response.robots) + + async def get_robot_parts(self, robot_id: str) -> List[RobotPart]: + """Get a list of all the parts under a specific machine. + + :: + + list_of_parts = await cloud.get_robot_parts( + robot_id="1a123456-x1yz-0ab0-a12xyzabc" + ) + + Args: + robot_id (str): ID of the machine to get parts from. + + Raises: + GRPCError: If an invalid machine ID is passed. + + Returns: + List[viam.app.app_client.RobotPart]: The list of machine parts. + + For more information, see `Fleet Management API `_. + """ + request = GetRobotPartsRequest(robot_id=robot_id) + response: GetRobotPartsResponse = await self._app_client.GetRobotParts(request, metadata=self._metadata) + return [RobotPart.from_proto(robot_part=part) for part in response.parts] + + async def get_robot_part(self, robot_part_id: str, dest: Optional[str] = None, indent: int = 4) -> RobotPart: + """Get a machine part. + + :: + + my_robot_part = await cloud.get_robot_part( + robot_part_id="abc12345-1a23-1234-ab12-a22a22a2aa22" + ) + # Check if machine is live (last access time less than 10 sec ago) + if (time.time() - my_robot_part.last_access.timestamp()) <= 10000: + print("Machine is live.") + + Args: + robot_part_id (str): ID of the machine part to get. You can retrieve this value by navigating to the machine's page, + clicking on the part status dropdown, and clicking the copy icon next to Part ID. + dest (Optional[str]): Optional filepath to write the machine part's config file in JSON format to. + indent (int): Size (in number of spaces) of indent when writing config to `dest`. Defaults to 4. + + Raises: + GRPCError: If an invalid machine part ID is passed. + + Returns: + viam.app.app_client.RobotPart: The machine part. + + For more information, see `Fleet Management API `_. + """ + request = GetRobotPartRequest(id=robot_part_id) + response: GetRobotPartResponse = await self._app_client.GetRobotPart(request, metadata=self._metadata) + + if dest: + try: + file = open(dest, "w") + file.write(f"{json.dumps(json.loads(response.config_json), indent=indent)}") + file.flush() + except Exception as e: + LOGGER.error(f"Failed to write config JSON to file {dest}", exc_info=e) + + return RobotPart.from_proto(robot_part=response.part) + + async def get_robot_part_logs( + self, + robot_part_id: str, + filter: Optional[str] = None, + dest: Optional[str] = None, + log_levels: List[str] = [], + num_log_entries: int = 100, + ) -> List[LogEntry]: + """Get the logs associated with a robot part. + + :: + + part_logs = await cloud.get_robot_part_logs( + robot_part_id="abc12345-1a23-1234-ab12-a22a22a2aa22", + num_log_entries=20 + ) + + Args: + robot_part_id (str): ID of the machine part to get logs from. + filter (Optional[str]): Only include logs with messages that contain the string `filter`. Defaults to empty string "" (that is, + no filter). + dest (Optional[str]): Optional filepath to write the log entries to. + log_levels (List[str]): List of log levels for which entries should be returned. Defaults to empty list, which returns all logs. + num_log_entries (int): Number of log entries to return. Passing 0 returns all logs. Defaults to 100. All logs or the first + `num_log_entries` logs will be returned, whichever comes first. + + Raises: + GRPCError: If an invalid robot part ID is passed. + + Returns: + List[viam.app.app_client.LogEntry]: The list of log entries. + + For more information, see `Fleet Management API `_. + """ + if num_log_entries < 0: + raise ValueError("'num_log_entries must be at least 0.") + logs_left = num_log_entries + page_token = "" + logs = [] + + while True: + new_logs, next_page_token = await self._get_robot_part_logs( + robot_part_id=robot_part_id, filter=filter if filter else "", page_token=page_token, log_levels=log_levels + ) + if num_log_entries != 0 and len(new_logs) > logs_left: + logs += new_logs[0:logs_left] + break + logs += new_logs + logs_left -= len(new_logs) + if not next_page_token or next_page_token == "" or logs_left == 0: + break + page_token = next_page_token + + if dest: + try: + file = open(dest, "w") + for log in logs: + time = log.time + level = log.level.upper() + logger_name = log.logger_name.split(".")[0] + file_name = log.caller["File"] + ":" + str(int(log.caller["Line"])) + message = log.message + file.write(f"{time}\t{level}\t{logger_name}\t{file_name:<64}{message}\n") + file.flush() + except Exception as e: + LOGGER.error(f"Failed to write robot part from robot part with ID [{robot_part_id}]logs to file {dest}", exc_info=e) + + return logs + + async def _get_robot_part_logs( + self, robot_part_id: str, filter: str, page_token: str, log_levels: List[str] + ) -> Tuple[List[LogEntry], str]: + request = GetRobotPartLogsRequest(id=robot_part_id, filter=filter, page_token=page_token, levels=log_levels) + response: GetRobotPartLogsResponse = await self._app_client.GetRobotPartLogs(request, metadata=self._metadata) + return [LogEntry.from_proto(log) for log in response.logs], response.next_page_token + + async def tail_robot_part_logs( + self, robot_part_id: str, errors_only: bool = True, filter: Optional[str] = None + ) -> _LogsStream[List[LogEntry]]: + """Get an asynchronous iterator that receives live machine part logs. + + :: + + logs_stream = await cloud.tail_robot_part_logs( + robot_part_id="abc12345-1a23-1234-ab12-a22a22a2aa22" + ) + + Args: + robot_part_id (str): ID of the machine part to retrieve logs from. + errors_only (bool): Boolean specifying whether or not to only include error logs. Defaults to True. + filter (Optional[str]): Only include logs with messages that contain the string `filter`. Defaults to empty string "" (that is, + no filter). + + Returns: + _LogsStream[List[LogEntry]]: The asynchronous iterator receiving live machine part logs. + """ + + async def read() -> AsyncIterator[List[LogEntry]]: + async with self._app_client.TailRobotPartLogs.open(metadata=self._metadata) as stream: + await stream.send_message( + TailRobotPartLogsRequest(id=robot_part_id, errors_only=errors_only, filter=filter if filter else "") + ) + + while True: + response: Optional[TailRobotPartLogsResponse] = await stream.recv_message() + if response is None or len(response.logs) == 0: + break + logs = [LogEntry.from_proto(log) for log in response.logs] + yield logs + + return _LogsStreamWithIterator(read()) + + async def get_robot_part_history(self, robot_part_id: str) -> List[RobotPartHistoryEntry]: + """Get a list containing the history of a machine part. + + :: + + part_history = await cloud.get_robot_part_history( + robot_part_id="abc12345-1a23-1234-ab12-a22a22a2aa22" + ) + + Args: + robot_part_id (str): ID of the machine part to retrieve history from. + + Raises: + GRPCError: If an invalid machine part ID is provided. + + Returns: + List[viam.app.app_client.RobotPartHistoryEntry]: The list of the machine part's history. + + For more information, see `Fleet Management API `_. + """ + request = GetRobotPartHistoryRequest(id=robot_part_id) + response: GetRobotPartHistoryResponse = await self._app_client.GetRobotPartHistory(request, metadata=self._metadata) + return [RobotPartHistoryEntry.from_proto(part_history) for part_history in response.history] + + async def update_robot_part(self, robot_part_id: str, name: str, robot_config: Optional[Mapping[str, Any]] = None, + last_known_update: Optional[datetime] = None) -> RobotPart: + """Change the name and assign an optional new configuration to a machine part. + + :: + + my_machine_part = await cloud.update_robot_part( + robot_part_id="abc12345-1a23-1234-ab12-a22a22a2aa22" + ) + + Args: + robot_part_id (str): ID of the robot part to update. + name (str): New name to be updated on the robot part. + robot_config (Mapping[str, Any]): Optional new config represented as a dictionary to be updated on the machine part. The machine + part's config will remain as is (no change) if one isn't passed. + last_known_update (datetime): Optional time of the last known update to this part's config. If provided, this will result in a + GRPCError if the upstream config has changed since this time, indicating that the local config is out of date. Omitting this + parameter will result in an overwrite of the upstream config. + Raises: + GRPCError: If either an invalid machine part ID, name, or config is passed, or if the upstream config has changed since + last_known_update. + Returns: + viam.app.app_client.RobotPart: The newly updated robot part. + + For more information, see `Fleet Management API `_. + """ + request = UpdateRobotPartRequest(id=robot_part_id, name=name, robot_config=dict_to_struct(robot_config) if robot_config else None, + last_known_update=datetime_to_timestamp(last_known_update)) + response: UpdateRobotPartResponse = await self._app_client.UpdateRobotPart(request, metadata=self._metadata) + return RobotPart.from_proto(robot_part=response.part) + + async def new_robot_part(self, robot_id: str, part_name: str) -> str: + """Create a new machine part. + + :: + + new_part_id = await cloud.new_robot_part( + robot_id="1a123456-x1yz-0ab0-a12xyzabc", part_name="myNewSubPart" + ) + + Args: + robot_id (str): ID of the machine to create a new part for. + part_name (str): Name of the new part. + + Raises: + GRPCError: If either an invalid machine ID or name is passed. + + Returns: + str: The new machine part's ID. + + For more information, see `Fleet Management API `_. + """ + request = NewRobotPartRequest(robot_id=robot_id, part_name=part_name) + response: NewRobotPartResponse = await self._app_client.NewRobotPart(request, metadata=self._metadata) + return response.part_id + + async def delete_robot_part(self, robot_part_id: str) -> None: + """Delete the specified machine part. + + :: + + await cloud.delete_robot_part( + robot_part_id="abc12345-1a23-1234-ab12-a22a22a2aa22" + ) + + Args: + robot_part_id (str): ID of the machine part to delete. Must be specified. + + Raises: + GRPCError: If an invalid machine part ID is passed. + + For more information, see `Fleet Management API `_. + """ + request = DeleteRobotPartRequest(part_id=robot_part_id) + await self._app_client.DeleteRobotPart(request, metadata=self._metadata) + + async def get_robot_api_keys(self, robot_id: str) -> List[APIKeyWithAuthorizations]: + """Gets the API Keys for the machine. + + :: + + api_keys = await cloud.get_robot_api_keys(robot_id="robot-id") + + Args: + robot_id (str): The ID of the machine. + + Returns: + List[APIKeyWithAuthorizations]: The list of API keys. + + For more information, see `Fleet Management API `_. + """ + request = GetRobotAPIKeysRequest(robot_id=robot_id) + response: GetRobotAPIKeysResponse = await self._app_client.GetRobotAPIKeys(request, metadata=self._metadata) + return list(response.api_keys) + + async def mark_part_as_main(self, robot_part_id: str) -> None: + """Mark a machine part as the main part of a machine. + + :: + + await cloud.mark_part_as_main( + robot_part_id="abc12345-1a23-1234-ab12-a22a22a2aa22") + + Args: + robot_part_id (str): ID of the machine part to mark as main. + + Raises: + GRPCError: If an invalid machine part ID is passed. + + For more information, see `Fleet Management API `_. + """ + request = MarkPartAsMainRequest(part_id=robot_part_id) + await self._app_client.MarkPartAsMain(request, metadata=self._metadata) + + async def mark_part_for_restart(self, robot_part_id: str) -> None: + """Mark the specified machine part for restart. + + :: + + await cloud.mark_part_for_restart( + robot_part_id="abc12345-1a23-1234-ab12-a22a22a2aa22") + + Args: + robot_part_id (str): ID of the machine part to mark for restart. + + Raises: + GRPCError: If an invalid machine part ID is passed. + + For more information, see `Fleet Management API `_. + """ + request = MarkPartForRestartRequest(part_id=robot_part_id) + await self._app_client.MarkPartForRestart(request, metadata=self._metadata) + + async def create_robot_part_secret(self, robot_part_id: str) -> RobotPart: + """Create a machine part secret. + + :: + + part_with_new_secret = await cloud.create_robot_part_secret( + robot_part_id="abc12345-1a23-1234-ab12-a22a22a2aa22") + + Args: + robot_part_id (str): ID of the machine part to create a secret for. + + Raises: + GRPCError: If an invalid machine part ID is passed. + + Returns: + viam.app.app_client.RobotPart: The machine part the new secret was generated for. + + For more information, see `Fleet Management API `_. + """ + request = CreateRobotPartSecretRequest(part_id=robot_part_id) + response: CreateRobotPartSecretResponse = await self._app_client.CreateRobotPartSecret(request, metadata=self._metadata) + return RobotPart.from_proto(response.part) + + async def delete_robot_part_secret(self, robot_part_id: str, secret_id: str) -> None: + """Delete a machine part secret. + + :: + + await cloud.delete_robot_part_secret( + robot_part_id="abc12345-1a23-1234-ab12-a22a22a2aa22", + secret_id="123xyz12-abcd-4321-12ab-12xy1xyz12xy") + + Args: + robot_part_id (str): ID of the machine part to delete the secret from. + secret_id (str): ID of the secret to delete. + + Raises: + GRPCError: If an invalid machine part ID or secret ID is passed. + + For more information, see `Fleet Management API `_. + """ + request = DeleteRobotPartSecretRequest(part_id=robot_part_id, secret_id=secret_id) + await self._app_client.DeleteRobotPartSecret(request, metadata=self._metadata) + + async def list_robots(self, location_id: Optional[str] = None) -> List[Robot]: + """Get a list of all machines under the specified location. + + :: + + list_of_machines = await cloud.list_robots(location_id="123ab12345") + + Args: + location_id (Optional[str]): ID of the location to retrieve the machines from. Defaults to the location ID provided at + `AppClient` instantiation. + + Raises: + GRPCError: If an invalid location ID is passed or one isn't passed and there was no location ID provided at `AppClient` + instantiation. + + Returns: + List[viam.proto.app.Robot]: The list of robots. + + For more information, see `Fleet Management API `_. + """ + request = ListRobotsRequest(location_id=location_id if location_id else self._location_id if self._location_id else "") + response: ListRobotsResponse = await self._app_client.ListRobots(request, metadata=self._metadata) + return list(response.robots) + + async def new_robot(self, name: str, location_id: Optional[str] = None) -> str: + """Create a new machine. + + :: + + new_machine_id = await cloud.new_robot(name="beepboop", location_id="my-location-id") + + Args: + name (str): Name of the new machine. + location_id (Optional[str]): ID of the location under which to create the machine. Defaults to the current authorized location. + + Raises: + GRPCError: If an invalid location ID is passed or one isn't passed and there was no location ID provided at `AppClient` + instantiation. + + Returns: + str: The new robot's ID. + + For more information, see `Fleet Management API `_. + """ + request = NewRobotRequest(location=location_id if location_id else self._location_id if self._location_id else "", name=name) + response: NewRobotResponse = await self._app_client.NewRobot(request, metadata=self._metadata) + return response.id + + async def update_robot(self, robot_id: str, name: str, location_id: Optional[str] = None) -> Robot: + """Change the name of an existing machine. + + :: + + updated_robot = await cloud.update_robot( + robot_id="1a123456-x1yz-0ab0-a12xyzabc", + name="Orange-Robot", + location_id="23ab12345" + ) + + Args: + robot_id (str): ID of the machine to update. + name (str): New name to be updated on the machine. + location_id (Optional[str]): ID of the location under which the machine exists. Defaults to the location ID provided at + `AppClient` instantiation + + Raises: + GRPCError: If either an invalid machine ID, name, or location ID is passed or a location ID isn't passed and there was no + location ID provided at `AppClient` instantiation. + + Returns: + viam.proto.app.Robot: The newly updated machine. + + For more information, see `Fleet Management API `_. + """ + request = UpdateRobotRequest( + id=robot_id, name=name, location=location_id if location_id else self._location_id if self._location_id else "" + ) + response: UpdateRobotResponse = await self._app_client.UpdateRobot(request, metadata=self._metadata) + return response.robot + + async def delete_robot(self, robot_id: str) -> None: + """Delete the specified machine. + + :: + + await cloud.delete_robot(robot_id="1a123456-x1yz-0ab0-a12xyzabc") + + Args: + robot_id (str): ID of the machine to delete. + + Raises: + GRPCError: If an invalid machine ID is passed. + + For more information, see `Fleet Management API `_. + """ + request = DeleteRobotRequest(id=robot_id) + await self._app_client.DeleteRobot(request, metadata=self._metadata) + + async def list_fragments( + self, org_id: str, show_public: bool = True, visibilities: Optional[List[Fragment.Visibility]] = None + ) -> List[Fragment]: + """Get a list of fragments under the currently authed-to organization. + + :: + + fragments_list = await cloud.list_fragments(org_id="org-id", visibilities=[]) + + Args: + org_id (str): The ID of the organization to list fragments for. + You can obtain your organization ID from the Viam app's organization settings page. + show_public (bool): Optional boolean specifying whether or not to only show public fragments. If True, only public fragments + will return. If False, only private fragments will return. Defaults to True. + + .. deprecated:: 0.25.0 + Use ``visibilities`` instead. + visibilities (Optional[List[Fragment.Visibility]]): List of FragmentVisibilities specifying which types of fragments to include + in the results. If empty, by default only public fragments will be returned. + + Returns: + List[viam.app.app_client.Fragment]: The list of fragments. + + For more information, see `Fleet Management API `_. + """ + request = ListFragmentsRequest( + organization_id=org_id, + fragment_visibility=map(Fragment.Visibility.to_proto, visibilities if visibilities else []), + show_public=show_public, + ) + response: ListFragmentsResponse = await self._app_client.ListFragments(request, metadata=self._metadata) + return [Fragment.from_proto(fragment=fragment) for fragment in response.fragments] + + async def get_fragment(self, fragment_id: str) -> Fragment: + """Get a fragment. + + :: + + # Get a fragment and print its name and when it was created. + the_fragment = await cloud.get_fragment( + fragment_id="12a12ab1-1234-5678-abcd-abcd01234567") + print("Name: ", the_fragment.name, "\\nCreated on: ", the_fragment.created_on) + + Args: + fragment_id (str): ID of the fragment to get. + + Raises: + GRPCError: If an invalid fragment ID is passed. + + Returns: + viam.app.app_client.Fragment: The fragment. + + For more information, see `Fleet Management API `_. + """ + request = GetFragmentRequest(id=fragment_id) + response: GetFragmentResponse = await self._app_client.GetFragment(request, metadata=self._metadata) + return Fragment.from_proto(fragment=response.fragment) + + async def create_fragment(self, org_id: str, name: str, config: Optional[Mapping[str, Any]] = None) -> Fragment: + """Create a new private fragment. + + :: + + new_fragment = await cloud.create_fragment(org_id="org-id", name="cool_smart_machine_to_configure_several_of") + + Args: + org_id (str): The ID of the organization to create the fragment within. + You can obtain your organization ID from the Viam app's organization settings page. + name (str): Name of the fragment. + config (Optional[Mapping[str, Any]]): Optional Dictionary representation of new config to assign to specified fragment. Can be + assigned by updating the fragment. + + Raises: + GRPCError: If an invalid name is passed. + + Returns: + viam.app.app_client.Fragment: The newly created fragment. + + For more information, see `Fleet Management API `_. + """ + request = CreateFragmentRequest(name=name, config=dict_to_struct(config) if config else None, organization_id=org_id) + response: CreateFragmentResponse = await self._app_client.CreateFragment(request, metadata=self._metadata) + return Fragment.from_proto(response.fragment) + + async def update_fragment( + self, + fragment_id: str, + name: str, + config: Optional[Mapping[str, Any]] = None, + public: Optional[bool] = None, + visibility: Optional[Fragment.Visibility] = None, + last_known_update: Optional[datetime] = None, + ) -> Fragment: + """Update a fragment name AND its config and/or visibility. + + :: + + updated_fragment = await cloud.update_fragment( + fragment_id="12a12ab1-1234-5678-abcd-abcd01234567", + name="better_name") + + Args: + fragment_id (str): ID of the fragment to update. + name (str): New name to associate with the fragment. + config (Optional[Mapping[str, Any]]): Optional Dictionary representation of new config to assign to specified fragment. Not + passing this parameter will leave the fragment's config unchanged. + public (bool): Boolean specifying whether the fragment is public. Not passing this parameter will leave the fragment's + visibility unchanged. A fragment is private by default when created. + + .. deprecated:: 0.25.0 + Use ``visibility`` instead. + visibility (Optional[FragmentVisibility]): Optional FragmentVisibility list specifying who should be allowed + to view the fragment. Not passing this parameter will leave the fragment's visibility unchanged. + A fragment is private by default when created. + last_known_update (datetime): Optional time of the last known update to this fragment's config. If provided, this will result in + a GRPCError if the upstream config has changed since this time, indicating that the local config is out of date. Omitting + this parameter will result in an overwrite of the upstream config. + Raises: + GRPCError: if an invalid ID, name, or config is passed, or if the upstream fragment config has changed since last_known_update. + + Returns: + viam.app.app_client.Fragment: The newly updated fragment. + + For more information, see `Fleet Management API `_. + """ + request = UpdateFragmentRequest( + id=fragment_id, + name=name, + config=dict_to_struct(config) if config else None, + public=public, + visibility=visibility.to_proto() if visibility else None, + last_known_update=datetime_to_timestamp(last_known_update), + ) + response: UpdateFragmentResponse = await self._app_client.UpdateFragment(request, metadata=self._metadata) + return Fragment.from_proto(response.fragment) + + async def delete_fragment(self, fragment_id: str) -> None: + """Delete a fragment. + + :: + + await cloud.delete_fragment( + fragment_id="12a12ab1-1234-5678-abcd-abcd01234567") + + Args: + fragment_id (str): ID of the fragment to delete. + + Raises: + GRPCError: If an invalid fragment ID is passed. + + For more information, see `Fleet Management API `_. + """ + request = DeleteFragmentRequest(id=fragment_id) + await self._app_client.DeleteFragment(request, metadata=self._metadata) + + async def get_fragment_history( + self, id: str, page_token: Optional[str] = "", page_limit: Optional[int] = 10 + ) -> List[FragmentHistoryEntry]: + """Get fragment history. + + :: + + fragment_history = await cloud.get_fragment_history( + id = "12a12ab1-1234-5678-abcd-abcd01234567", + page_token = "pg-token", + page_limit = 10 + ) + + Args: + id (str): ID of the fragment to fetch history for. + page_token (Optional[str]): the page token for the fragment history collection + page_limit (Optional[int]): the number of fragment history documents to return in the result. + The default page limit is 10. + + Raises: + GRPCError: if an invalid fragment id, page token or page limit is passed. + + Returns: + viam.app.app_client.FragmentHistoryResponse: A list of documents with the fragment history. + + For more information, see `Fleet Management API `_. + """ + + request = GetFragmentHistoryRequest(id=id, page_token=page_token, page_limit=page_limit) + response: GetFragmentHistoryResponse = await self._app_client.GetFragmentHistory(request, metadata=self._metadata) + return [FragmentHistoryEntry.from_proto(fragment_history) for fragment_history in response.history] + + async def add_role( + self, + org_id: str, + identity_id: str, + role: Union[Literal["owner"], Literal["operator"]], + resource_type: Union[Literal["organization"], Literal["location"], Literal["robot"]], + resource_id: str, + ) -> None: + """Add a role under the currently authed-to organization. + + :: + + await cloud.add_role( + org_id="", + identity_id="abc01234-0123-4567-ab12-a11a00a2aa22", + role="owner", + resource_type="location", + resource_id="111ab12345" + ) + + Args: + org_id (str): The ID of the organization to create the role in. + You can obtain your organization ID from the Viam app's organization settings page. + identity_id (str): ID of the entity the role belongs to (for example, a user ID). + role (Union[Literal["owner"], Literal["operator"]]): The role to add. + resource_type (Union[Literal["organization"], Literal["location"], Literal["robot"]]): Type of the resource to add role to. + Must match `resource_id`. + resource_id (str): ID of the resource the role applies to (that is, either an organization, location, or robot ID). + + Raises: + GRPCError: If either an invalid identity ID, role ID, resource type, or resource ID is passed. + + For more information, see `Fleet Management API `_. + """ + authorization = await self._create_authorization( + organization_id=org_id, + identity_id=identity_id, + identity_type="", + role=role, + resource_type=resource_type, + resource_id=resource_id, + ) + request = AddRoleRequest(authorization=authorization) + await self._app_client.AddRole(request, metadata=self._metadata) + + async def remove_role( + self, + org_id: str, + identity_id: str, + role: Union[Literal["owner"], Literal["operator"]], + resource_type: Union[Literal["organization"], Literal["location"], Literal["robot"]], + resource_id: str, + ) -> None: + """Remove a role under the currently authed-to organization. + + :: + + await cloud.remove_role( + org_id="", + identity_id="abc01234-0123-4567-ab12-a11a00a2aa22", + role="owner", + resource_type="location", + resource_id="111ab12345" + ) + + Args: + org_id (str): The ID of the organization the role exists in. + You can obtain your organization ID from the Viam app's organization settings page. + identity_id (str): ID of the entity the role belongs to (for example, a user ID). + role (Union[Literal["owner"], Literal["operator"]]): The role to remove. + resource_type (Union[Literal["organization"], Literal["location"], Literal["robot"]]): Type of the resource the role is being + removed from. Must match `resource_id`. + resource_id (str): ID of the resource the role applies to (that is, either an organization, location, or robot ID). + + Raises: + GRPCError: If either an invalid identity ID, role ID, resource type, or resource ID or is passed. + + For more information, see `Fleet Management API `_. + """ + authorization = await self._create_authorization( + organization_id=org_id, + identity_id=identity_id, + identity_type="", + role=role, + resource_type=resource_type, + resource_id=resource_id, + ) + request = RemoveRoleRequest(authorization=authorization) + await self._app_client.RemoveRole(request, metadata=self._metadata) + + async def change_role( + self, + organization_id: str, + old_identity_id: str, + old_role: Union[Literal["owner"], Literal["operator"]], + old_resource_type: Union[Literal["organization"], Literal["location"], Literal["robot"]], + old_resource_id: str, + new_identity_id: str, + new_role: Union[Literal["owner"], Literal["operator"]], + new_resource_type: Union[Literal["organization"], Literal["location"], Literal["robot"]], + new_resource_id: str, + ) -> None: + """Changes a role to a new role. + + :: + + await cloud.change_role( + organization_id="", + old_identity_id="abc01234-0123-4567-ab12-a11a00a2aa22", + old_role="operator", + old_resource_type="location", + old_resource_id="111ab12345", + new_identity_id="abc01234-0123-4567-ab12-a11a00a2aa22", + new_role="owner", + new_resource_type="organization", + new_resource_id="abc12345" + ) + + Args: + organization_id (str): ID of the organization + old_identity_id (str): ID of the entity the role belongs to (for example, a user ID). + old_role (Union[Literal["owner"], Literal["operator"]]): The role to be changed. + old_resource_type (Union[Literal["organization"], Literal["location"], Literal["robot"]]): Type of the resource the role is + added to. Must match `old_resource_id`. + old_resource_id (str): ID of the resource the role applies to (that is, either an organization, location, or robot ID). + + new_identity_id (str): New ID of the entity the role belongs to (for example, a user ID). + new_role (Union[Literal["owner"], Literal["operator"]]): The new role. + new_resource_type (Union[Literal["organization"], Literal["location"], Literal["robot"]]): Type of the resource to add role to. + Must match `new_resource_id`. + new_resource_id (str): New ID of the resource the role applies to (that is, either an organization, location, or robot ID). + + For more information, see `Fleet Management API `_. + """ + old_authorization = await self._create_authorization( + organization_id=organization_id, + identity_id=old_identity_id, + identity_type="", + role=old_role, + resource_type=old_resource_type, + resource_id=old_resource_id, + ) + new_authorization = await self._create_authorization( + organization_id=organization_id, + identity_id=new_identity_id, + identity_type="", + role=new_role, + resource_type=new_resource_type, + resource_id=new_resource_id, + ) + request = ChangeRoleRequest(old_authorization=old_authorization, new_authorization=new_authorization) + await self._app_client.ChangeRole(request, metadata=self._metadata) + + async def list_authorizations(self, org_id: str, resource_ids: Optional[List[str]] = None) -> List[Authorization]: + """List all authorizations under a specific resource (or resources) within the currently authed-to organization. If no resource IDs + are provided, all resource authorizations within the organizations are returned. + + :: + + list_of_auths = await cloud.list_authorizations( + org_id="", + resource_ids=["1a123456-x1yz-0ab0-a12xyzabc"]) + + Args: + org_id: The ID of the organization to list authorizations for. + resource_ids (Optional[List[str]]): IDs of the resources to retrieve authorizations from. + If None, defaults to all resources. + + Raises: + GRPCError: If an invalid resource ID is passed. + + Returns: + List[viam.proto.app.Authorization]: The list of authorizations. + + For more information, see `Fleet Management API `_. + """ + request = ListAuthorizationsRequest(organization_id=org_id, resource_ids=resource_ids) + response: ListAuthorizationsResponse = await self._app_client.ListAuthorizations(request, metadata=self._metadata) + return list(response.authorizations) + + async def check_permissions(self, permissions: List[AuthorizedPermissions]) -> List[AuthorizedPermissions]: + """Checks validity of a list of permissions. + + :: + + from viam.proto.app import AuthorizedPermissions + + # Check whether the entity you're currently authenticated to has permission to control and/or + # read logs from robots in the "organization-identifier123" org + permissions = [AuthorizedPermissions(resource_type="organization", + resource_id="", + permissions=["control_robot", + "read_robot_logs"])] + + filtered_permissions = await cloud.check_permissions(permissions) + + Args: + permissions (List[viam.proto.app.AuthorizedPermissions]): the permissions to validate + (for example, "read_organization", "control_robot") + + Raises: + GRPCError: If the list of permissions to validate is empty. + + Returns: + List[viam.proto.app.AuthorizedPermissions]: The permissions argument, with invalid permissions filtered out. + + For more information, see `Fleet Management API `_. + """ + request = CheckPermissionsRequest(permissions=permissions) + response: CheckPermissionsResponse = await self._app_client.CheckPermissions(request, metadata=self._metadata) + return list(response.authorized_permissions) + + async def get_registry_item(self, item_id: str, include_markdown_documentation: bool = False) -> RegistryItem: + """Get registry item by ID. + + :: + + item = await cloud.get_registry_item("item-id") + + Args: + item_id (str): The ID of the registry item. This is the namespace and name of the item in the + form `namespace:name`. For example, `Viam's csi-cam-pi module's `_ item ID + would be `viam:csi-cam-pi`. You can also use `org-id:name`. For example, + `abc01234-0123-4567-ab12-a11a00a2aa22:training-script`. + + Returns: + RegistryItem: The registry item. + + For more information, see `Fleet Management API `_. + """ + request = GetRegistryItemRequest(item_id=item_id, include_markdown_documentation=include_markdown_documentation) + response: GetRegistryItemResponse = await self._app_client.GetRegistryItem(request, metadata=self._metadata) + return response.item + + async def create_registry_item(self, organization_id: str, name: str, type: PackageType.ValueType) -> None: + """Create a registry item + + :: + + from viam.proto.app.packages import PackageType + + await cloud.create_registry_item("", "name", PackageType.PACKAGE_TYPE_ML_MODEL) + + Args: + organization_id (str): The organization to create the registry item under. + name (str): The name of the registry item, which must be unique within your org. + type (PackageType.ValueType): The type of the item in the registry. + + For more information, see `Fleet Management API `_. + """ + request = CreateRegistryItemRequest(organization_id=organization_id, name=name, type=type) + await self._app_client.CreateRegistryItem(request, metadata=self._metadata) + + async def update_registry_item( + self, item_id: str, type: PackageType.ValueType, description: str, visibility: Visibility.ValueType + ) -> None: + """Update a registry item. + + :: + + from viam.proto.app.packages import PackageType + from viam.proto.app import Visibility + + await cloud.update_registry_item( + "your-namespace:your-name", + PackageType.PACKAGE_TYPE_ML_TRAINING, + "description", + Visibility.VISIBILITY_PUBLIC + ) + + Args: + item_id (str): The ID of the registry item, containing either the namespace and module name + (for example, `my-org:my-module`) or organization ID and module name (`org-id:my-module`). + type (PackageType.ValueType): The type of the item in the registry. + description (str): The description of the registry item. + visibility (Visibility.ValueType): The visibility of the registry item. + + For more information, see `Fleet Management API `_. + """ + + request = UpdateRegistryItemRequest(item_id=item_id, type=type, description=description, visibility=visibility) + await self._app_client.UpdateRegistryItem(request, metadata=self._metadata) + + async def list_registry_items( + self, + organization_id: str, + types: List[PackageType.ValueType], + visibilities: List[Visibility.ValueType], + platforms: List[str], + statuses: List[RegistryItemStatus.ValueType], + search_term: Optional[str] = None, + page_token: Optional[str] = None, + ) -> List[RegistryItem]: + """List the registry items in an organization. + + :: + + from viam.proto.app.packages import PackageType + from viam.proto.app import Visibility, RegistryItemStatus + + # List private, published ml training scripts in your organization + registry_items = await cloud.list_registry_items( + organization_id="", + types=[PackageType.PACKAGE_TYPE_ML_TRAINING], + visibilities=[Visibility.VISIBILITY_PRIVATE], + platforms=[""], + statuses=[RegistryItemStatus.REGISTRY_ITEM_STATUS_PUBLISHED] + ) + + # List public, published linux modules in all organizations + registry_items = await cloud.list_registry_items( + organization_id="", + types=[PackageType.PACKAGE_TYPE_MODULE], + visibilities=[Visibility.VISIBILITY_PUBLIC], + platforms=["linux/any"], + statuses=[RegistryItemStatus.REGISTRY_ITEM_STATUS_PUBLISHED] + ) + + Args: + organization_id (str): The ID of the organization to return registry items for. + types (List[PackageType.ValueType]): The types of registry items. + visibilities (List[Visibility.ValueType]): The visibilities of registry items. + platforms (List[str]): The platforms of registry items. + statuses (List[RegistryItemStatus.ValueType]): The types of the items in the registry. + search_term (Optional[str]): The search term of the registry items. + page_token (Optional[str]): The page token of the registry items. + + Returns: + List[RegistryItem]: The list of registry items. + + For more information, see `Fleet Management API `_. + """ + request = ListRegistryItemsRequest( + organization_id=organization_id, + types=types, + visibilities=visibilities, + platforms=platforms, + statuses=statuses, + search_term=search_term if search_term is not None else "", + page_token=page_token if page_token is not None else "", + ) + response: ListRegistryItemsResponse = await self._app_client.ListRegistryItems(request, metadata=self._metadata) + return list(response.items) + + async def delete_registry_item(self, item_id: str) -> None: + """Delete a registry item + + :: + + await cloud.delete_registry_item("your-namespace:your-name") + + Args: + item_id (str): The ID of the deleted registry item, containing either the namespace and module name + (for example, `my-org:my-module`) or organization ID and module name (`org-id:my-module`). + + For more information, see `Fleet Management API `_. + """ + request = DeleteRegistryItemRequest(item_id=item_id) + await self._app_client.DeleteRegistryItem(request, metadata=self._metadata) + + async def create_module(self, org_id: str, name: str) -> Tuple[str, str]: + """Create a module under the currently authed-to organization. + + :: + + new_module = await cloud.create_module(org_id="org-id", name="cool_new_hoverboard_module") + print("Module ID:", new_module[0]) + + Args: + org_id (str): The ID of the organization to create the module under. + You can obtain your organization ID from the Viam app's organization settings page. + name (str): The name of the module. Must be unique within your organization. + + Raises: + GRPCError: If an invalid name (for example, "") is passed. + + Returns: + Tuple[str, str]: A tuple containing the ID [0] of the new module and its URL [1]. + + For more information, see `Fleet Management API `_. + """ + request = CreateModuleRequest(organization_id=org_id, name=name) + response: CreateModuleResponse = await self._app_client.CreateModule(request, metadata=self._metadata) + return response.module_id, response.url + + async def update_module( + self, + module_id: str, + url: str, + description: str, + models: Optional[List[Model]], + entrypoint: str, + public: bool = False, + ) -> str: + """Update the documentation URL, description, models, entrypoint, and/or the visibility of a module. + + :: + + from viam.proto.app import Model + + model = Model( + api="rdk:component:base", + model="my-group:cool_new_hoverboard_module:wheeled" + ) + + url_of_my_module = await cloud.update_module( + module_id="my-group:cool_new_hoverboard_module", + url="https://docsformymodule.viam.com", + models=[model], + description="A base to support hoverboards.", + entrypoint="exec" + ) + + Args: + module_id (str): ID of the module being updated, containing either the namespace and module name + (for example, `my-org:my-module`) or organization ID and module name (`org-id:my-module`). + url (str): The url to reference for documentation and code (NOT the url of the module itself). + description (str): A short description of the module that explains its purpose. + models (List[viam.proto.app.Model]): list of models that are available in the module. + entrypoint (str): The executable to run to start the module program. + public (bool): The visibility that should be set for the module. Defaults to False (private). + + Raises: + GRPCError: If either an invalid module ID, URL, list of models, or organization ID is passed. + + Returns: + str: The URL of the newly updated module. + + For more information, see `Fleet Management API `_. + """ + request = UpdateModuleRequest( + module_id=module_id, + visibility=Visibility.VISIBILITY_PUBLIC if public else Visibility.VISIBILITY_PRIVATE, + url=url, + description=description, + models=models, + entrypoint=entrypoint, + ) + response: UpdateModuleResponse = await self._app_client.UpdateModule(request, metadata=self._metadata) + return response.url + + async def upload_module_file(self, module_file_info: Optional[ModuleFileInfo], file: bytes) -> str: + """Upload a module file + + :: + + from viam.proto.app import ModuleFileInfo + + module_file_info = ModuleFileInfo( + module_id = "sierra:cool_new_hoverboard_module", + version = "1.0.0", + platform = "darwin/arm64" + ) + + file_id = await cloud.upload_module_file( + module_file_info=module_file_info, + file=b"" + ) + + Args: + module_file_info (Optional[viam.proto.app.ModuleFileInfo]): Relevant metadata. + file (bytes): Bytes of file to upload. + + Returns: + str: URL of uploaded file. + + For more information, see `Fleet Management API `_. + """ + request_module_file_info = UploadModuleFileRequest(module_file_info=module_file_info) + request_file = UploadModuleFileRequest(file=file) + async with self._app_client.UploadModuleFile.open(metadata=self._metadata) as stream: + await stream.send_message(request_module_file_info) + await stream.send_message(request_file, end=True) + response: Union[UploadModuleFileRequest, None] = await stream.recv_message() + if not response: + await stream.recv_trailing_metadata() # causes us to throw appropriate gRPC error. + raise TypeError("Response cannot be empty") # we should never get here, but for typechecking + return response.url + + async def get_module(self, module_id: str) -> Module: + """Get a module. + + :: + + the_module = await cloud.get_module(module_id="my-group:my-cool-modular-base") + + Args: + module_id (str): ID of the module being retrieved, containing either the namespace and module name + (for example, `my-org:my-module`) or organization ID and module name (`org-id:my-module`). + + Raises: + GRPCError: If an invalid module ID is passed. + + Returns: + viam.proto.app.Module: The module. + + For more information, see `Fleet Management API `_. + """ + request = GetModuleRequest(module_id=module_id) + response: GetModuleResponse = await self._app_client.GetModule(request, metadata=self._metadata) + return response.module + + async def list_modules(self, org_id: str) -> List[Module]: + """List the modules under the currently authed-to organization. + + :: + + modules_list = await cloud.list_modules("") + + Args: + org_id (str): The ID of the organization to list modules for. + You can obtain your organization ID from the Viam app's organization settings page. + + Returns: + List[viam.proto.app.Module]: The list of modules. + + For more information, see `Fleet Management API `_. + """ + request = ListModulesRequest(organization_id=org_id) + response: ListModulesResponse = await self._app_client.ListModules(request, metadata=self._metadata) + return list(response.modules) + + # TODO(RSDK-5569): when user-based auth exists, make `name` default to `None` and let + # app deal with setting a default. + async def create_key(self, org_id: str, authorizations: List[APIKeyAuthorization], name: Optional[str] = None) -> Tuple[str, str]: + """Creates a new API key. + + :: + + from viam.app.app_client import APIKeyAuthorization + + auth = APIKeyAuthorization( + role="owner", + resource_type="robot", + resource_id="your-machine-id123" + ) + + api_key, api_key_id = cloud.create_key( + org_id="", + authorizations=[auth], + name="my_key" + ) + + Args: + org_id (str): The ID of the organization to create the key for. + You can obtain your organization ID from the Viam app's organization settings page. + authorizations (List[viam.proto.app.Authorization]): A list of authorizations to associate + with the key. + name (Optional[str]): A name for the key. If None, defaults to the current timestamp. + + Raises: + GRPCError: If the authorizations list is empty. + + Returns: + Tuple[str, str]: The api key and api key ID. + + For more information, see `Fleet Management API `_. + """ + name = name if name is not None else str(datetime.now()) + authorizations_pb = [await self._create_authorization_for_new_api_key(org_id, auth) for auth in authorizations] + request = CreateKeyRequest(authorizations=authorizations_pb, name=name) + response: CreateKeyResponse = await self._app_client.CreateKey(request, metadata=self._metadata) + return (response.key, response.id) + + async def delete_key(self, id: str) -> None: + """Delete a API key. + + :: + + await cloud.delete_key("key-id") + + Args: + id (str): The ID of the API key. + + For more information, see `Fleet Management API `_. + """ + request = DeleteKeyRequest(id=id) + await self._app_client.DeleteKey(request, metadata=self._metadata) + + async def create_key_from_existing_key_authorizations(self, id: str) -> Tuple[str, str]: + """Creates a new API key with an existing key's authorizations + + :: + + api_key, api_key_id = await cloud.create_key_from_existing_key_authorizations( + id="INSERT YOUR API KEY ID") + + Args: + id (str): the ID of the API key to duplication authorizations from + + Returns: + Tuple[str, str]: The API key and API key id + + For more information, see `Fleet Management API `_. + """ + request = CreateKeyFromExistingKeyAuthorizationsRequest(id=id) + response: CreateKeyFromExistingKeyAuthorizationsResponse = await self._app_client.CreateKeyFromExistingKeyAuthorizations( + request, + metadata=self._metadata, + ) + return (response.key, response.id) + + async def list_keys(self, org_id: str) -> List[APIKeyWithAuthorizations]: + """Lists all keys for the currently-authed-to org. + + :: + + keys = await cloud.list_keys(org_id="") + + Args: + org_id (str): The ID of the organization to list API keys for. + You can obtain your organization ID from the Viam app's organization settings page. + + Returns: + List[viam.proto.app.APIKeyWithAuthorizations]: The existing API keys and authorizations. + + For more information, see `Fleet Management API `_. + """ + request = ListKeysRequest(org_id=org_id) + response: ListKeysResponse = await self._app_client.ListKeys(request, metadata=self._metadata) + return list(response.api_keys) + + async def rotate_key(self, id: str) -> Tuple[str, str]: + """Rotate an API key. + + :: + + id, key = await cloud.rotate_key("key-id") + + Args: + id (str): The ID of the key to be rotated. + + Returns: + Tuple[str, str]: The API key and API key id + + For more information, see `Fleet Management API `_. + """ + request = RotateKeyRequest(id=id) + response: RotateKeyResponse = await self._app_client.RotateKey(request, metadata=self._metadata) + return response.key, response.id + + async def get_organization_metadata(self, org_id: str) -> Mapping[str, Any]: + """Get an organization's user-defined metadata. + + :: + + metadata = await cloud.get_organization_metadata(org_id="") + + Args: + org_id (str): The ID of the organization with which the user-defined metadata is associated. + You can obtain your organization ID from the Viam app's organization settings page. + + Returns: + Mapping[str, Any]: The user-defined metadata converted from JSON to a Python dictionary + """ + request = GetOrganizationMetadataRequest(organization_id=org_id) + response: GetOrganizationMetadataResponse = await self._app_client.GetOrganizationMetadata(request) + return struct_to_dict(response.data) + + async def update_organization_metadata(self, org_id: str, metadata: Mapping[str, Any]) -> None: + """Update an organization's user-defined metadata. + + :: + + await cloud.update_organization_metadata(org_id="", metadata=) + + Args: + organization_id (str): The ID of the organization with which to associate the user-defined metadata. + You can obtain your organization ID from the Viam app's organization settings page. + metadata (Mapping[str, Any]): The user-defined metadata to upload as a Python dictionary. + """ + request = UpdateOrganizationMetadataRequest(organization_id=org_id, data=dict_to_struct(metadata)) + _: UpdateOrganizationMetadataResponse = await self._app_client.UpdateOrganizationMetadata(request) + + async def get_location_metadata(self, location_id: str) -> Mapping[str, Any]: + """Get a location's user-defined metadata. + + :: + + metadata = await cloud.get_location_metadata(location_id="") + + Args: + location_id (str): The ID of the location with which the user-defined metadata is associated. + You can obtain your location ID from the Viam app's locations page. + + Returns: + Mapping[str, Any]: The user-defined metadata converted from JSON to a Python dictionary. + """ + request = GetLocationMetadataRequest(location_id=location_id) + response: GetLocationMetadataResponse = await self._app_client.GetLocationMetadata(request) + return struct_to_dict(response.data) + + async def update_location_metadata(self, location_id: str, metadata: Mapping[str, Any]) -> None: + """Update a location's user-defined metadata. + + :: + + await cloud.update_location_metadata(location_id="", metadata=) + + Args: + location_id (str): The ID of the location with which to associate the user-defined metadata. + You can obtain your location ID from the Viam app's locations page. + metadata (Mapping[str, Any]): The user-defined metadata converted from JSON to a Python dictionary. + """ + request = UpdateLocationMetadataRequest(location_id=location_id, data=dict_to_struct(metadata)) + _: UpdateLocationMetadataResponse = await self._app_client.UpdateLocationMetadata(request) + + async def get_robot_metadata(self, robot_id: str) -> Mapping[str, Any]: + """Get a robot's user-defined metadata. + + :: + + metadata = await cloud.get_robot_metadata(robot_id="") + + Args: + robot_id (str): The ID of the robot with which the user-defined metadata is associated. + You can obtain your robot ID from the Viam app's machine page. + + Returns: + Mapping[str, Any]: The user-defined metadata converted from JSON to a Python dictionary. + """ + request = GetRobotMetadataRequest(id=robot_id) + response: GetRobotMetadataResponse = await self._app_client.GetRobotMetadata(request) + return struct_to_dict(response.data) + + async def update_robot_metadata(self, robot_id: str, metadata: Mapping[str, Any]) -> None: + """Update a robot's user-defined metadata. + + :: + + await cloud.update_robot_metadata(robot_id="", metadata=) + + Args: + robot_id (str): The ID of the robot with which to associate the user-defined metadata. + You can obtain your robot ID from the Viam app's machine page. + metadata (Mapping[str, Any]): The user-defined metadata converted from JSON to a Python dictionary. + """ + request = UpdateRobotMetadataRequest(id=robot_id, data=dict_to_struct(metadata)) + _: UpdateRobotMetadataResponse = await self._app_client.UpdateRobotMetadata(request) + + async def get_robot_part_metadata(self, robot_part_id: str) -> Mapping[str, Any]: + """Get a robot part's user-defined metadata. + + :: + + metadata = await cloud.get_robot_part_metadata(robot_part_id="") + + Args: + robot_part_id (str): The ID of the robot part with which the user-defined metadata is associated. + You can obtain your robot part ID from the Viam app's machine page. + + Returns: + Mapping[str, Any]: The user-defined metadata converted from JSON to a Python dictionary. + """ + request = GetRobotPartMetadataRequest(id=robot_part_id) + response: GetRobotPartMetadataResponse = await self._app_client.GetRobotPartMetadata(request) + return struct_to_dict(response.data) + + async def update_robot_part_metadata(self, robot_part_id: str, metadata: Mapping[str, Any]) -> None: + """Update a robot part's user-defined metadata. + + :: + + await cloud.update_robot_part_metadata(robot_part_id="", metadata=) + + Args: + robot_id (str): The ID of the robot part with which to associate the user-defined metadata. + You can obtain your robot part ID from the Viam app's machine page. + metadata (Mapping[str, Any]): The user-defined metadata converted from JSON to a Python dictionary. + """ + request = UpdateRobotPartMetadataRequest(id=robot_part_id, data=dict_to_struct(metadata)) + _: UpdateRobotPartMetadataResponse = await self._app_client.UpdateRobotPartMetadata(request) diff --git a/src/viam/app/billing_client.py b/src/viam/app/billing_client.py new file mode 100644 index 000000000..b49f1254c --- /dev/null +++ b/src/viam/app/billing_client.py @@ -0,0 +1,143 @@ +from typing import Mapping, Optional + +from grpclib.client import Channel, Stream + +from viam import logging +from viam.proto.app.billing import ( + BillingServiceStub, + GetCurrentMonthUsageRequest, + GetCurrentMonthUsageResponse, + GetInvoicePdfRequest, + GetInvoicePdfResponse, + GetInvoicesSummaryRequest, + GetInvoicesSummaryResponse, + GetOrgBillingInformationRequest, + GetOrgBillingInformationResponse, +) + +LOGGER = logging.getLogger(__name__) + + +class BillingClient: + """gRPC client for retrieving billing data from app. + + Constructor is used by `ViamClient` to instantiate relevant service stubs. Calls to + `BillingClient` methods should be made through `ViamClient`. + + Establish a Connection:: + + import asyncio + + from viam.rpc.dial import DialOptions, Credentials + from viam.app.viam_client import ViamClient + + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + + async def main(): + # Make a ViamClient + viam_client = await connect() + # Instantiate a BillingClient to run billing client API methods on + billing_client = viam_client.billing_client + + viam_client.close() + + if __name__ == '__main__': + asyncio.run(main()) + + For more information, see `Billing Client API `_. + """ + + def __init__(self, channel: Channel, metadata: Mapping[str, str]): + """Create a `BillingClient` that maintains a connection to app. + + Args: + channel (grpclib.client.Channel): Connection to app. + metadata (Mapping[str, str]): Required authorization token to send requests to app. + """ + self._metadata = metadata + self._billing_client = BillingServiceStub(channel) + self._channel = channel + + _billing_client: BillingServiceStub + _channel: Channel + _metadata: Mapping[str, str] + + async def get_current_month_usage(self, org_id: str, timeout: Optional[float] = None) -> GetCurrentMonthUsageResponse: + """Access data usage information for the current month for a given organization. + + :: + + usage = await billing_client.get_current_month_usage("") + + Args: + org_id (str): the ID of the organization to request usage data for + + Returns: + viam.proto.app.billing.GetCurrentMonthUsageResponse: the current month usage information + + For more information, see `Billing Client API `_. + """ + request = GetCurrentMonthUsageRequest(org_id=org_id) + return await self._billing_client.GetCurrentMonthUsage(request, metadata=self._metadata, timeout=timeout) + + async def get_invoice_pdf(self, invoice_id: str, org_id: str, dest: str, timeout: Optional[float] = None) -> None: + """Access invoice PDF data and optionally save it to a provided file path. + + :: + + await billing_client.get_invoice_pdf("", "", "invoice.pdf") + + Args: + invoice_id (str): the ID of the invoice being requested + org_id (str): the ID of the org to request data from + dest (str): the filepath to save the invoice to + + For more information, see `Billing Client API `_. + """ + stream: Stream[GetInvoicePdfRequest, GetInvoicePdfResponse] + async with self._billing_client.GetInvoicePdf.open(timeout=timeout, metadata=self._metadata) as stream: + await stream.send_message(GetInvoicePdfRequest(id=invoice_id, org_id=org_id), end=True) + with open(dest, "wb") as file: + async for response in stream: + file.write(response.chunk) + + async def get_invoices_summary(self, org_id: str, timeout: Optional[float] = None) -> GetInvoicesSummaryResponse: + """Access total outstanding balance plus invoice summaries for a given org. + + :: + + summary = await billing_client.get_invoices_summary("") + + Args: + org_id (str): the ID of the org to request data for + + Returns: + viam.proto.app.billing.GetInvoicesSummaryResponse: the summaries of all org invoices + + For more information, see `Billing Client API `_. + """ + request = GetInvoicesSummaryRequest(org_id=org_id) + return await self._billing_client.GetInvoicesSummary(request, metadata=self._metadata, timeout=timeout) + + async def get_org_billing_information(self, org_id: str, timeout: Optional[float] = None) -> GetOrgBillingInformationResponse: + """Access billing information (payment method, billing tier, etc.) for a given org. + + :: + + information = await billing_client.get_org_billing_information("") + + Args: + org_id (str): the ID of the org to request data for + + Returns: + viam.proto.app.billing.GetOrgBillingInformationResponse: the org billing information + + For more information, see `Billing Client API `_. + """ + request = GetOrgBillingInformationRequest(org_id=org_id) + return await self._billing_client.GetOrgBillingInformation(request, metadata=self._metadata, timeout=timeout) diff --git a/src/viam/app/data_client.py b/src/viam/app/data_client.py new file mode 100644 index 000000000..d39eacbdb --- /dev/null +++ b/src/viam/app/data_client.py @@ -0,0 +1,1734 @@ +import warnings +from dataclasses import dataclass +from datetime import datetime +from pathlib import Path +from typing import Any, Dict, List, Mapping, Optional, Sequence, Tuple, Union, cast + +import bson +from google.protobuf.struct_pb2 import Struct +from grpclib.client import Channel, Stream + +from viam import logging +from viam.proto.app.data import ( + AddBinaryDataToDatasetByIDsRequest, + AddBoundingBoxToImageByIDRequest, + AddBoundingBoxToImageByIDResponse, + AddTagsToBinaryDataByFilterRequest, + AddTagsToBinaryDataByIDsRequest, + BinaryData, + BinaryDataByFilterRequest, + BinaryDataByFilterResponse, + BinaryDataByIDsRequest, + BinaryDataByIDsResponse, + BinaryID, + BoundingBoxLabelsByFilterRequest, + BoundingBoxLabelsByFilterResponse, + CaptureInterval, + CaptureMetadata, + ConfigureDatabaseUserRequest, + DataRequest, + DataServiceStub, + DeleteBinaryDataByFilterRequest, + DeleteBinaryDataByFilterResponse, + DeleteBinaryDataByIDsRequest, + DeleteBinaryDataByIDsResponse, + DeleteTabularDataRequest, + DeleteTabularDataResponse, + ExportTabularDataRequest, + ExportTabularDataResponse, + Filter, + GetDatabaseConnectionRequest, + GetDatabaseConnectionResponse, + GetLatestTabularDataRequest, + GetLatestTabularDataResponse, + Order, + RemoveBinaryDataFromDatasetByIDsRequest, + RemoveBoundingBoxFromImageByIDRequest, + RemoveTagsFromBinaryDataByFilterRequest, + RemoveTagsFromBinaryDataByFilterResponse, + RemoveTagsFromBinaryDataByIDsRequest, + RemoveTagsFromBinaryDataByIDsResponse, + TabularDataByFilterRequest, + TabularDataByFilterResponse, + TabularDataByMQLRequest, + TabularDataByMQLResponse, + TabularDataBySQLRequest, + TabularDataBySQLResponse, + TagsByFilterRequest, + TagsByFilterResponse, +) +from viam.proto.app.dataset import ( + CreateDatasetRequest, + CreateDatasetResponse, + Dataset, + DatasetServiceStub, + DeleteDatasetRequest, + ListDatasetsByIDsRequest, + ListDatasetsByIDsResponse, + ListDatasetsByOrganizationIDRequest, + ListDatasetsByOrganizationIDResponse, + RenameDatasetRequest, +) +from viam.proto.app.datasync import ( + DataCaptureUploadMetadata, + DataCaptureUploadRequest, + DataCaptureUploadResponse, + DataSyncServiceStub, + DataType, + FileData, + FileUploadRequest, + FileUploadResponse, + SensorData, + SensorMetadata, + StreamingDataCaptureUploadRequest, + StreamingDataCaptureUploadResponse, + UploadMetadata, +) +from viam.utils import ValueTypes, _alias_param, create_filter, datetime_to_timestamp, struct_to_dict + +LOGGER = logging.getLogger(__name__) + + +class DataClient: + """gRPC client for uploading and retrieving data from app. + + This class's constructor instantiates relevant service stubs. Always make :class:`DataClient` method calls through an instance of + :class:`ViamClient`. + + Establish a connection:: + + import asyncio + + from viam.rpc.dial import DialOptions, Credentials + from viam.app.viam_client import ViamClient + + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + + async def main(): + # Make a ViamClient + viam_client = await connect() + # Instantiate a DataClient to run data client API methods on + data_client = viam_client.data_client + + viam_client.close() + + if __name__ == '__main__': + asyncio.run(main()) + + + For more information, see `Data Client API `_. + """ + + @dataclass + class TabularData: + """Class representing a piece of tabular data and associated metadata.""" + + data: Mapping[str, Any] + """The requested data""" + + metadata: CaptureMetadata + """The metadata associated with the data""" + + time_requested: datetime + """The time the data were requested""" + + time_received: datetime + """The time the data were received""" + + def __str__(self) -> str: + return f"{self.data}\n{self.metadata}Time requested: {self.time_requested}\nTime received: {self.time_received}\n" + + def __eq__(self, other: object) -> bool: + if isinstance(other, DataClient.TabularData): + return str(self) == str(other) + return False + + @dataclass + class TabularDataPoint: + """Represents a tabular data point and its associated metadata.""" + + part_id: str + """The robot part ID""" + + resource_name: str + """The resource name""" + + resource_api: str + """The resource API. For example, rdk:component:sensor""" + + method_name: str + """The method used for data capture. For example, Readings""" + + time_captured: datetime + """The time at which the data point was captured""" + + organization_id: str + """The organization ID""" + + location_id: str + """The location ID""" + + robot_name: str + """The robot name""" + + robot_id: str + """The robot ID""" + + part_name: str + """The robot part name""" + + method_parameters: Mapping[str, ValueTypes] + """Additional parameters associated with the data capture method""" + + tags: List[str] + """A list of tags associated with the data point""" + + payload: Mapping[str, ValueTypes] + """The captured data""" + + def __str__(self) -> str: + return ( + f"TabularDataPoint(" + f"robot='{self.robot_name}' (id={self.robot_id}), " + f"part='{self.part_name}' (id={self.part_id}), " + f"resource='{self.resource_name}' ({self.resource_api}), " + f"method='{self.method_name}', " + f"org={self.organization_id}, " + f"location={self.location_id}, " + f"time='{self.time_captured.isoformat()}', " + f"params={self.method_parameters}, " + f"tags={self.tags}, " + f"payload={self.payload})" + ) + + def __eq__(self, other: object) -> bool: + if isinstance(other, DataClient.TabularDataPoint): + return str(self) == str(other) + return False + + @property + def resource_subtype(self) -> str: + warnings.warn( + "`TabularDataPoint.resource_subtype` is deprecated. Use `TabularDataPoint.resource_api` instead.", + DeprecationWarning, + stacklevel=2, + ) + return self.resource_api + + def __init__(self, channel: Channel, metadata: Mapping[str, str]): + """Create a :class:`DataClient` that maintains a connection to app. + + Args: + channel (grpclib.client.Channel): Connection to app. + metadata (Mapping[str, str]): Required authorization token to send requests to app. + """ + self._metadata = metadata + self._data_client = DataServiceStub(channel) + self._data_sync_client = DataSyncServiceStub(channel) + self._dataset_client = DatasetServiceStub(channel) + self._channel = channel + + _data_client: DataServiceStub + _data_sync_client: DataSyncServiceStub + _dataset_client: DatasetServiceStub + _metadata: Mapping[str, str] + _channel: Channel + + async def tabular_data_by_filter( + self, + filter: Optional[Filter] = None, + limit: Optional[int] = None, + sort_order: Optional[Order.ValueType] = None, + last: Optional[str] = None, + count_only: bool = False, + include_internal_data: bool = False, + dest: Optional[str] = None, + ) -> Tuple[List[TabularData], int, str]: + """Filter and download tabular data. The data will be paginated into pages of ``limit`` items; the returned tuple will include + the pagination ID. If a destination is provided, this method saves returned data to that file, overwriting any existing file content. + + :: + + from viam.utils import create_filter + + my_data = [] + my_filter = create_filter(component_name="motor-1") + last = None + while True: + tabular_data, count, last = await data_client.tabular_data_by_filter(my_filter, last=last) + if not tabular_data: + break + my_data.extend(tabular_data) + + print(f"My data: {my_data}") + + Args: + filter (~viam.proto.app.data.Filter): Optional, specifies tabular data to retrieve. If missing, matches all tabular data. + limit (int): The maximum number of entries to include in a page. Defaults to 50 if unspecified. + sort_order (~viam.proto.app.data.Order): The desired sort order of the data. + last (str): Optional string indicating the object identifier of the last-returned data. + Returned by calls to :class:`TabularDataByFilter` as the ``last`` value. + If provided, the server returns the next data entries after the last object identifier. + count_only (bool): Whether to return only the total count of entries. + include_internal_data (bool): Whether to return the internal data. Internal data is used for Viam-specific data ingestion, + like cloud SLAM. Defaults to ``False``. + dest (str): Optional filepath for writing retrieved data. + + Returns: + Tuple[List[TabularData], int, str]: A tuple containing the following: + + - ``tabular_data`` (*List[TabularData]*): The tabular data. + - ``count`` (*int*): The count (number of entries). + - ``last`` (*str*): The last-returned page ID. + + For more information, see `Data Client API `_. + """ + filter = filter if filter else Filter() + + data_request = DataRequest(filter=filter) + if limit: + data_request.limit = limit + if sort_order: + data_request.sort_order = sort_order + if last: + data_request.last = last + request = TabularDataByFilterRequest(data_request=data_request, count_only=count_only, include_internal_data=include_internal_data) + response: TabularDataByFilterResponse = await self._data_client.TabularDataByFilter(request, metadata=self._metadata) + data = [ + DataClient.TabularData( + struct_to_dict(struct.data), + response.metadata[struct.metadata_index], + struct.time_requested.ToDatetime(), + struct.time_received.ToDatetime(), + ) + for struct in response.data + ] + + if dest: + try: + file = open(dest, "w") + file.write(f"{[str(d) for d in data]}") + file.flush() + except Exception as e: + LOGGER.error(f"Failed to write tabular data to file {dest}", exc_info=e) + return data, response.count, response.last + + async def tabular_data_by_sql(self, organization_id: str, sql_query: str) -> List[Dict[str, Union[ValueTypes, datetime]]]: + """Obtain unified tabular data and metadata, queried with SQL. + Make sure your API key has permissions at the organization level in order to use this. + + :: + + data = await data_client.tabular_data_by_sql( + organization_id="", + sql_query="SELECT * FROM readings LIMIT 5" + ) + + Args: + organization_id (str): The ID of the organization that owns the data. + To find your organization ID, visit the organization settings page in the Viam app. + sql_query (str): The SQL query to run. + + Returns: + List[Dict[str, Union[ValueTypes, datetime]]]: An array of decoded BSON data objects. + + For more information, see `Data Client API `_. + """ + request = TabularDataBySQLRequest(organization_id=organization_id, sql_query=sql_query) + response: TabularDataBySQLResponse = await self._data_client.TabularDataBySQL(request, metadata=self._metadata) + return [bson.decode(bson_bytes) for bson_bytes in response.raw_data] + + @_alias_param("query", param_alias="mql_binary") + async def tabular_data_by_mql( + self, organization_id: str, query: Union[List[bytes], List[Dict[str, Any]]], use_recent_data: Optional[bool] = None + ) -> List[Dict[str, Union[ValueTypes, datetime]]]: + """Obtain unified tabular data and metadata, queried with MQL. + + :: + + import bson + + tabular_data = await data_client.tabular_data_by_mql(organization_id="", query=[ + { '$match': { 'location_id': '' } }, + { "$limit": 5 } + ]) + + print(f"Tabular Data: {tabular_data}") + + Args: + organization_id (str): The ID of the organization that owns the data. + To find your organization ID, visit the organization settings page in the Viam app. + query (Union[List[bytes], List[Dict[str, Any]]]): The MQL query to run, as a list of MongoDB aggregation pipeline stages. + Each stage can be provided as either a dictionary or raw BSON bytes, but support for bytes will be removed in the + future, so prefer the dictionary option. + use_recent_data (bool): Whether to query blob storage or your recent data store. Defaults to ``False``. + + Returns: + List[Dict[str, Union[ValueTypes, datetime]]]: An array of decoded BSON data objects. + + For more information, see `Data Client API `_. + """ + binary: List[bytes] = [bson.encode(query) for query in query] if isinstance(query[0], dict) else query # type: ignore + request = TabularDataByMQLRequest(organization_id=organization_id, mql_binary=binary, use_recent_data=use_recent_data) + response: TabularDataByMQLResponse = await self._data_client.TabularDataByMQL(request, metadata=self._metadata) + return [bson.decode(bson_bytes) for bson_bytes in response.raw_data] + + @_alias_param("resource_api", param_alias="resource_subtype") + async def get_latest_tabular_data( + self, part_id: str, resource_name: str, resource_api: str, method_name: str + ) -> Optional[Tuple[datetime, datetime, Dict[str, ValueTypes]]]: + """Gets the most recent tabular data captured from the specified data source, as long as it was synced within the last year. + + :: + + tabular_data = await data_client.get_latest_tabular_data( + part_id="77ae3145-7b91-123a-a234-e567cdca8910", + resource_name="camera-1", + resource_api="rdk:component:camera", + method_name="GetImage" + ) + + if tabular_data: + time_captured, time_synced, payload = tabular_data + print(f"Time Captured: {time_captured}") + print(f"Time Synced: {time_synced}") + print(f"Payload: {payload}") + else: + print(f"No data returned: {tabular_data}") + + Args: + part_id (str): The ID of the part that owns the data. + resource_name (str): The name of the requested resource that captured the data. For example, "my-sensor". + resource_api (str): The API of the requested resource that captured the data. For example, "rdk:component:sensor". + method_name (str): The data capture method name. For exampe, "Readings". + + Returns: + Optional[Tuple[datetime, datetime, Dict[str, ValueTypes]]]: + A return value of ``None`` means that this data source + has not synced data in the last year. Otherwise, the data source has synced some data in the last year, so the returned + tuple contains the following: + + - ``time_captured`` (*datetime*): The time captured. + - ``time_synced`` (*datetime*): The time synced. + - ``payload`` (*Dict[str, ValueTypes]*): The latest tabular data captured from the specified data source. + + For more information, see `Data Client API `_. + """ + + request = GetLatestTabularDataRequest( + part_id=part_id, resource_name=resource_name, resource_subtype=resource_api, method_name=method_name + ) + response: GetLatestTabularDataResponse = await self._data_client.GetLatestTabularData(request, metadata=self._metadata) + if not response.payload: + return None + return response.time_captured.ToDatetime(), response.time_synced.ToDatetime(), struct_to_dict(response.payload) + + @_alias_param("resource_api", param_alias="resource_subtype") + async def export_tabular_data( + self, + part_id: str, + resource_name: str, + resource_api: str, + method_name: str, + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + ) -> List[TabularDataPoint]: + """Obtain unified tabular data and metadata from the specified data source. + + :: + + tabular_data = await data_client.export_tabular_data( + part_id="", + resource_name="", + resource_api="", + method_name="", + start_time="" + end_time="" + ) + + print(f"My data: {tabular_data}") + + Args: + part_id (str): The ID of the part that owns the data. + resource_name (str): The name of the requested resource that captured the data. + resource_api (str): The API of the requested resource that captured the data. + method_name (str): The data capture method name. + start_time (datetime): Optional start time for requesting a specific range of data. + end_time (datetime): Optional end time for requesting a specific range of data. + + Returns: + List[TabularDataPoint]: The unified tabular data and metadata. + + For more information, see `Data Client API `_. + """ + + interval = CaptureInterval(start=datetime_to_timestamp(start_time), end=datetime_to_timestamp(end_time)) + request = ExportTabularDataRequest( + part_id=part_id, resource_name=resource_name, resource_subtype=resource_api, method_name=method_name, interval=interval + ) + response: List[ExportTabularDataResponse] = await self._data_client.ExportTabularData(request, metadata=self._metadata) + + return [ + DataClient.TabularDataPoint( + part_id=resp.part_id, + resource_name=resp.resource_name, + resource_api=resp.resource_subtype, + method_name=resp.method_name, + time_captured=resp.time_captured.ToDatetime(), + organization_id=resp.organization_id, + location_id=resp.location_id, + robot_name=resp.robot_name, + robot_id=resp.robot_id, + part_name=resp.part_name, + method_parameters=struct_to_dict(resp.method_parameters), + tags=list(resp.tags), + payload=struct_to_dict(resp.payload), + ) + for resp in response + ] + + async def binary_data_by_filter( + self, + filter: Optional[Filter] = None, + limit: Optional[int] = None, + sort_order: Optional[Order.ValueType] = None, + last: Optional[str] = None, + include_binary_data: bool = True, + count_only: bool = False, + include_internal_data: bool = False, + dest: Optional[str] = None, + ) -> Tuple[List[BinaryData], int, str]: + """Filter and download binary data. The data will be paginated into pages of ``limit`` items, and the pagination ID will be included + in the returned tuple as ``last``. If a destination is provided, this method saves returned data to that file, + overwriting any existing file content. + + :: + + from viam.utils import create_filter + from viam.proto.app.data import Filter, TagsFilter, TagsFilterType + + # Get data captured from camera components + my_data = [] + last = None + my_filter = create_filter(component_name="camera-1") + + while True: + data, count, last = await data_client.binary_data_by_filter( + my_filter, limit=1, last=last) + if not data: + break + my_data.extend(data) + + print(f"My data: {my_data}") + + # Get untagged data from a dataset + + my_untagged_data = [] + last = None + tags_filter = TagsFilter(type=TagsFilterType.TAGS_FILTER_TYPE_UNTAGGED) + my_filter = Filter( + dataset_id="66db6fe7d93d1ade24cd1dc3", + tags_filter=tags_filter + ) + + while True: + data, count, last = await data_client.binary_data_by_filter( + my_filter, last=last, include_binary_data=False) + if not data: + break + my_untagged_data.extend(data) + + Args: + filter (~viam.proto.app.data.Filter): Optional, specifies tabular data to retrieve. An empty filter matches all binary data. + limit (int): The maximum number of entries to include in a page. Defaults to 50 if unspecified. + sort_order (~viam.proto.app.data.Order): The desired sort order of the data. + last (str): Optional string indicating the object identifier of the last-returned data. + This object identifier is returned by calls to :meth:`binary_data_by_filter` as the ``last`` value. + If provided, the server will return the next data entries after the last object identifier. + include_binary_data (bool): Boolean specifying whether to actually include the binary file data with each retrieved file. + Defaults to true (that is, both the files' data and metadata are returned). + count_only (bool): Whether to return only the total count of entries. + include_internal_data (bool): Whether to return the internal data. Internal data is used for Viam-specific data ingestion, + like cloud SLAM. Defaults to ``False``. + dest (str): Optional filepath for writing retrieved data. + + Returns: + Tuple[List[~viam.proto.app.data.BinaryData], int, str]: A tuple containing the following: + + - ``data`` (*List[* :class:`~viam.proto.app.data.BinaryData` *]*): The binary data. + - ``count`` (*int*): The count (number of entries). + - ``last`` (*str*): The last-returned page ID. + + For more information, see `Data Client API `_. + """ + + data_request = DataRequest(filter=filter) + if limit: + data_request.limit = limit + if sort_order: + data_request.sort_order = sort_order + if last: + data_request.last = last + request = BinaryDataByFilterRequest( + data_request=data_request, + include_binary=include_binary_data, + count_only=count_only, + include_internal_data=include_internal_data, + ) + response: BinaryDataByFilterResponse = await self._data_client.BinaryDataByFilter(request, metadata=self._metadata) + data = list(response.data) + if dest: + try: + file = open(dest, "w") + file.write(f"{[str(d) for d in data]}") + file.flush() + except Exception as e: + LOGGER.error(f"Failed to write binary data to file {dest}", exc_info=e) + + return data, response.count, response.last + + async def binary_data_by_ids( + self, + binary_ids: Union[List[BinaryID], List[str]], + dest: Optional[str] = None, + ) -> List[BinaryData]: + """Filter and download binary data. + + :: + + binary_metadata, count, last = await data_client.binary_data_by_filter( + include_binary_data=False + ) + + my_ids = [] + + for obj in binary_metadata: + my_ids.append(obj.metadata.binary_data_id) + + binary_data = await data_client.binary_data_by_ids(my_ids) + + Args: + binary_ids (Union[List[~viam.proto.app.data.BinaryID], List[str]]): Binary data ID strings specifying the desired data or + :class:`BinaryID` objects. Must be non-empty. + *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a + list of strings.* + dest (str): Optional filepath for writing retrieved data. + + Raises: + GRPCError: If no binary data ID strings or :class:`BinaryID` objects are provided. + + Returns: + List[~viam.proto.app.data.BinaryData]: The binary data. + + For more information, see `Data Client API `_. + """ + request = BinaryDataByIDsRequest() + if len(binary_ids) > 0 and isinstance(binary_ids[0], str): + binary_data_ids = cast(List[str], binary_ids) + request = BinaryDataByIDsRequest(binary_data_ids=binary_data_ids, include_binary=True) + else: + bin_ids = cast(List[BinaryID], binary_ids) + request = BinaryDataByIDsRequest(binary_ids=bin_ids, include_binary=True) + response: BinaryDataByIDsResponse = await self._data_client.BinaryDataByIDs(request, metadata=self._metadata) + if dest: + try: + file = open(dest, "w") + file.write(f"{response.data}") + file.flush() + except Exception as e: + LOGGER.error(f"Failed to write binary data to file {dest}", exc_info=e) + return list(response.data) + + async def delete_tabular_data(self, organization_id: str, delete_older_than_days: int) -> int: + """Delete tabular data older than a specified number of days. + + :: + + tabular_data = await data_client.delete_tabular_data( + organization_id="", + delete_older_than_days=150 + ) + + Args: + organization_id (str): The ID of the organization to delete the data from. + To find your organization ID, visit the organization settings page in the Viam app. + delete_older_than_days (int): Delete data that was captured up to *this many* days ago. For example, a value of + 10 deletes any data that was captured up to 10 days ago. A value of 0 deletes *all* existing data. + + Returns: + int: The number of items deleted. + + For more information, see `Data Client API `_. + """ + request = DeleteTabularDataRequest(organization_id=organization_id, delete_older_than_days=delete_older_than_days) + response: DeleteTabularDataResponse = await self._data_client.DeleteTabularData(request, metadata=self._metadata) + return response.deleted_count + + async def delete_tabular_data_by_filter(self, filter: Optional[Filter]) -> int: + """Deprecated: use :meth:`delete_tabular_data` instead.""" + raise NotImplementedError() + + async def delete_binary_data_by_filter(self, filter: Optional[Filter]) -> int: + """Filter and delete binary data. + + :: + + from viam.utils import create_filter + + my_filter = create_filter(component_name="left_motor", organization_ids=[""]) + + res = await data_client.delete_binary_data_by_filter(my_filter) + + Args: + filter (~viam.proto.app.data.Filter): Optional, specifies binary data to delete. + **CAUTION: Passing an empty** ``Filter`` **deletes all binary data!** + You must specify an organization ID with ``organization_ids`` when using this option. + To find your organization ID, visit the organization settings page in the Viam app. + + Returns: + int: The number of items deleted. + + For more information, see `Data Client API `_. + """ + filter = filter if filter else Filter() + request = DeleteBinaryDataByFilterRequest(filter=filter) + response: DeleteBinaryDataByFilterResponse = await self._data_client.DeleteBinaryDataByFilter(request, metadata=self._metadata) + return response.deleted_count + + async def delete_binary_data_by_ids(self, binary_ids: Union[List[BinaryID], List[str]]) -> int: + """Filter and delete binary data. + + :: + + from viam.proto.app.data import BinaryID + from viam.utils import create_filter + + my_filter = create_filter(component_name="camera-1", organization_ids=[""]) + binary_metadata, count, last = await data_client.binary_data_by_filter( + filter=my_filter, + limit=20, + include_binary_data=False + ) + + my_ids = [] + + for obj in binary_metadata: + my_ids.append( + obj.metadata.binary_data_id + ) + + binary_data = await data_client.delete_binary_data_by_ids(my_ids) + + Args: + binary_ids (Union[List[~viam.proto.app.data.BinaryID], List[str]]): Binary data ID strings specifying the data to be deleted or + :class:`BinaryID` objects. Must be non-empty. + *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a + list of strings.* + + Raises: + GRPCError: If no binary data ID strings or :class:`BinaryID` objects are provided. + + Returns: + int: The number of items deleted. + + For more information, see `Data Client API `_. + """ + request = DeleteBinaryDataByIDsRequest() + if len(binary_ids) > 0 and isinstance(binary_ids[0], str): + binary_data_ids = cast(List[str], binary_ids) + request = DeleteBinaryDataByIDsRequest(binary_data_ids=binary_data_ids) + else: + bin_ids = cast(List[BinaryID], binary_ids) + request = DeleteBinaryDataByIDsRequest(binary_ids=bin_ids) + response: DeleteBinaryDataByIDsResponse = await self._data_client.DeleteBinaryDataByIDs(request, metadata=self._metadata) + return response.deleted_count + + async def add_tags_to_binary_data_by_ids(self, tags: List[str], binary_ids: Union[List[BinaryID], List[str]]) -> None: + """Add tags to binary data. + + :: + + from viam.utils import create_filter + + tags = ["tag1", "tag2"] + + my_filter = create_filter(component_name="camera-1", organization_ids=[""]) + binary_metadata, count, last = await data_client.binary_data_by_filter( + filter=my_filter, + limit=20, + include_binary_data=False + ) + + my_ids = [] + + for obj in binary_metadata: + my_ids.append( + obj.metadata.binary_data_id + ) + + binary_data = await data_client.add_tags_to_binary_data_by_ids(tags, my_ids) + + Args: + tags (List[str]): List of tags to add to specified binary data. Must be non-empty. + binary_ids (Union[List[~viam.proto.app.data.BinaryID], List[str]]): Binary data ID strings specifying the data to be tagged or + :class:`BinaryID` objects. Must be non-empty. + *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a + list of strings.* + + Raises: + GRPCError: If no binary data ID strings or :class:`BinaryID` objects are provided. + + For more information, see `Data Client API `_. + """ + request = AddTagsToBinaryDataByIDsRequest() + if len(binary_ids) > 0 and isinstance(binary_ids[0], str): + binary_data_ids = cast(List[str], binary_ids) + request = AddTagsToBinaryDataByIDsRequest(binary_data_ids=binary_data_ids, tags=tags) + else: + bin_ids = cast(List[BinaryID], binary_ids) + request = AddTagsToBinaryDataByIDsRequest(binary_ids=bin_ids, tags=tags) + await self._data_client.AddTagsToBinaryDataByIDs(request, metadata=self._metadata) + + async def add_tags_to_binary_data_by_filter(self, tags: List[str], filter: Optional[Filter] = None) -> None: + """Add tags to binary data. + + :: + + from viam.utils import create_filter + + my_filter = create_filter(component_name="my_camera") + tags = ["tag1", "tag2"] + await data_client.add_tags_to_binary_data_by_filter(tags, my_filter) + + Args: + tags (List[str]): List of tags to add to specified binary data. Must be non-empty. + filter (~viam.proto.app.data.Filter): Specifies binary data to tag. If none is provided, tags all data. + + Raises: + GRPCError: If no tags are provided. + + For more information, see `Data Client API `_. + """ + filter = filter if filter else Filter() + request = AddTagsToBinaryDataByFilterRequest(filter=filter, tags=tags) + await self._data_client.AddTagsToBinaryDataByFilter(request, metadata=self._metadata) + + async def remove_tags_from_binary_data_by_ids(self, tags: List[str], binary_ids: Union[List[BinaryID], List[str]]) -> int: + """Remove tags from binary data by IDs. + + :: + + from viam.utils import create_filter + + tags = ["tag1", "tag2"] + + my_filter = create_filter(component_name="camera-1") + + binary_metadata, count, last = await data_client.binary_data_by_filter( + filter=my_filter, + limit=50, + include_binary_data=False + ) + + my_ids = [] + + for obj in binary_metadata: + my_ids.append( + obj.metadata.binary_data_id + ) + + binary_data = await data_client.remove_tags_from_binary_data_by_ids( + tags, my_ids) + + Args: + tags (List[str]): List of tags to remove from specified binary data. Must be non-empty. + binary_ids (Union[List[~viam.proto.app.data.BinaryID], List[str]]): Binary data ID strings specifying the data to be untagged + or `BinaryID` objects. Must be non-empty. + *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a + list of strings.* + + Raises: + GRPCError: If no binary data ID strings, :class:`BinaryID` objects, or tags are provided. + + Returns: + int: The number of tags removed. + + For more information, see `Data Client API `_. + """ + request = RemoveTagsFromBinaryDataByIDsRequest(tags=tags) + if len(binary_ids) > 0 and isinstance(binary_ids[0], str): + binary_data_ids = cast(List[str], binary_ids) + request = RemoveTagsFromBinaryDataByIDsRequest(binary_data_ids=binary_data_ids, tags=tags) + else: + bin_ids = cast(List[BinaryID], binary_ids) + request = RemoveTagsFromBinaryDataByIDsRequest(binary_ids=bin_ids, tags=tags) + response: RemoveTagsFromBinaryDataByIDsResponse = await self._data_client.RemoveTagsFromBinaryDataByIDs( + request, metadata=self._metadata + ) + return response.deleted_count + + async def remove_tags_from_binary_data_by_filter(self, tags: List[str], filter: Optional[Filter] = None) -> int: + """Remove tags from binary data. + + :: + + from viam.utils import create_filter + + my_filter = create_filter(component_name="my_camera") + tags = ["tag1", "tag2"] + res = await data_client.remove_tags_from_binary_data_by_filter(tags, my_filter) + + Args: + tags (List[str]): List of tags to remove from specified binary data. + filter (~viam.proto.app.data.Filter): Specifies binary data to untag. If none is provided, removes tags from all data. + + Raises: + GRPCError: If no tags are provided. + + Returns: + int: The number of tags removed. + + For more information, see `Data Client API `_. + """ + filter = filter if filter else Filter() + request = RemoveTagsFromBinaryDataByFilterRequest(filter=filter, tags=tags) + response: RemoveTagsFromBinaryDataByFilterResponse = await self._data_client.RemoveTagsFromBinaryDataByFilter( + request, metadata=self._metadata + ) + return response.deleted_count + + async def tags_by_filter(self, filter: Optional[Filter] = None) -> List[str]: + """Get a list of tags using a filter. + + :: + + from viam.utils import create_filter + + my_filter = create_filter(component_name="my_camera") + tags = await data_client.tags_by_filter(my_filter) + + Args: + filter (~viam.proto.app.data.Filter): Specifies subset ofdata to retrieve tags from. If none is provided, returns all tags. + + Returns: + List[str]: The list of tags. + + For more information, see `Data Client API `_. + """ + filter = filter if filter else Filter() + request = TagsByFilterRequest(filter=filter) + response: TagsByFilterResponse = await self._data_client.TagsByFilter(request, metadata=self._metadata) + return list(response.tags) + + async def add_bounding_box_to_image_by_id( + self, + binary_id: Union[BinaryID, str], + label: str, + x_min_normalized: float, + y_min_normalized: float, + x_max_normalized: float, + y_max_normalized: float, + ) -> str: + """Add a bounding box to an image. + + :: + + bbox_id = await data_client.add_bounding_box_to_image_by_id( + binary_id="", + label="label", + x_min_normalized=0, + y_min_normalized=.1, + x_max_normalized=.2, + y_max_normalized=.3 + ) + + print(bbox_id) + + Args: + binary_id (Union[~viam.proto.app.data.BinaryID, str]): The binary data ID or :class:`BinaryID` of the image to add the bounding + box to. *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data + IDs as a list of strings.* + label (str): A label for the bounding box. + x_min_normalized (float): Min X value of the bounding box normalized from 0 to 1. + y_min_normalized (float): Min Y value of the bounding box normalized from 0 to 1. + x_max_normalized (float): Max X value of the bounding box normalized from 0 to 1. + y_max_normalized (float): Max Y value of the bounding box normalized from 0 to 1. + + Raises: + GRPCError: If the X or Y values are outside of the [0, 1] range. + + Returns: + str: The bounding box ID. + + For more information, see `Data Client API `_. + """ + request = AddBoundingBoxToImageByIDRequest() + if isinstance(binary_id, str): + request = AddBoundingBoxToImageByIDRequest( + binary_data_id=binary_id, + label=label, + x_max_normalized=x_max_normalized, + x_min_normalized=x_min_normalized, + y_max_normalized=y_max_normalized, + y_min_normalized=y_min_normalized, + ) + else: + request = AddBoundingBoxToImageByIDRequest( + binary_id=binary_id, + label=label, + x_max_normalized=x_max_normalized, + x_min_normalized=x_min_normalized, + y_max_normalized=y_max_normalized, + y_min_normalized=y_min_normalized, + ) + response: AddBoundingBoxToImageByIDResponse = await self._data_client.AddBoundingBoxToImageByID(request, metadata=self._metadata) + return response.bbox_id + + async def remove_bounding_box_from_image_by_id(self, bbox_id: str, binary_id: Union[BinaryID, str]) -> None: + """Removes a bounding box from an image. + + :: + + await data_client.remove_bounding_box_from_image_by_id( + binary_id="", + bbox_id="your-bounding-box-id-to-delete" + ) + + Args: + bbox_id (str): The ID of the bounding box to remove. + binary_id (Union[~viam.proto.app.data.BinaryID, str]): The binary data ID or :class:`BinaryID` of the image to remove the + bounding box from. + *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a + list of strings.* + + For more information, see `Data Client API `_. + """ + request = RemoveBoundingBoxFromImageByIDRequest() + if isinstance(binary_id, str): + request = RemoveBoundingBoxFromImageByIDRequest(binary_data_id=binary_id, bbox_id=bbox_id) + else: + request = RemoveBoundingBoxFromImageByIDRequest(binary_id=binary_id, bbox_id=bbox_id) + + await self._data_client.RemoveBoundingBoxFromImageByID(request, metadata=self._metadata) + + async def bounding_box_labels_by_filter(self, filter: Optional[Filter] = None) -> List[str]: + """Get a list of bounding box labels using a `Filter`. + + :: + + from viam.utils import create_filter + + my_filter = create_filter(component_name="my_camera") + bounding_box_labels = await data_client.bounding_box_labels_by_filter( + my_filter) + + print(bounding_box_labels) + + Args: + filter (~viam.proto.app.data.Filter): Specifies data to retrieve bounding box labels from. If none is provided, returns labels + from all data. + + Returns: + List[str]: The list of bounding box labels. + + For more information, see `Data Client API `_. + """ + filter = filter if filter else Filter() + request = BoundingBoxLabelsByFilterRequest(filter=filter) + response: BoundingBoxLabelsByFilterResponse = await self._data_client.BoundingBoxLabelsByFilter(request, metadata=self._metadata) + return list(response.labels) + + async def get_database_connection(self, organization_id: str) -> str: + """Get a connection to access a MongoDB Atlas Data federation instance. + + :: + + hostname = await data_client.get_database_connection(organization_id="") + + Args: + organization_id (str): The ID of the organization you'd like to connect to. + To find your organization ID, visit the organization settings page in the Viam app. + + Returns: + str: The hostname of the federated database. + + For more information, see `Data Client API `_. + """ + request = GetDatabaseConnectionRequest(organization_id=organization_id) + response: GetDatabaseConnectionResponse = await self._data_client.GetDatabaseConnection(request, metadata=self._metadata) + return response.hostname + + async def configure_database_user(self, organization_id: str, password: str) -> None: + """Configure a database user for the Viam organization's MongoDB Atlas Data Federation instance. It can also be used to reset the + password of the existing database user. + + :: + + await data_client.configure_database_user( + organization_id="", + password="Your_Password@1234" + ) + + Args: + organization_id (str): The ID of the organization you'd like to configure a database user for. + To find your organization ID, visit the organization settings page in the Viam app. + password (str): The password of the user. + + For more information, see `Data Client API `_. + """ + request = ConfigureDatabaseUserRequest(organization_id=organization_id, password=password) + await self._data_client.ConfigureDatabaseUser(request, metadata=self._metadata) + + async def create_dataset(self, name: str, organization_id: str) -> str: + """Create a new dataset. + + :: + + dataset_id = await data_client.create_dataset( + name="", + organization_id="" + ) + print(dataset_id) + + Args: + name (str): The name of the dataset being created. + organization_id (str): The ID of the organization where the dataset is being created. + To find your organization ID, visit the organization settings page in the Viam app. + + Returns: + str: The dataset ID of the created dataset. + + For more information, see `Data Client API `_. + """ + request = CreateDatasetRequest(name=name, organization_id=organization_id) + response: CreateDatasetResponse = await self._dataset_client.CreateDataset(request, metadata=self._metadata) + return response.id + + async def list_dataset_by_ids(self, ids: List[str]) -> Sequence[Dataset]: + """Get a list of datasets using their IDs. + + :: + + datasets = await data_client.list_dataset_by_ids( + ids=[", "] + ) + print(datasets) + + Args: + ids (List[str]): The IDs of the datasets that you would like to retrieve information about. To retrieve a dataset ID: + + - Navigate to the **DATASETS** tab of the **DATA** page. + - Click on the dataset. + - Click the **...** menu. + - Select **Copy dataset ID**. + + Returns: + Sequence[Dataset]: The list of datasets. + + For more information, see `Data Client API `_. + """ + request = ListDatasetsByIDsRequest(ids=ids) + response: ListDatasetsByIDsResponse = await self._dataset_client.ListDatasetsByIDs(request, metadata=self._metadata) + + return response.datasets + + async def list_datasets_by_organization_id(self, organization_id: str) -> Sequence[Dataset]: + """Get the datasets in an organization. + + :: + + datasets = await data_client.list_datasets_by_organization_id( + organization_id="" + ) + print(datasets) + + Args: + organization_id (str): The ID of the organization you'd like to retrieve datasets from. + To find your organization ID, visit the organization settings page in the Viam app. + + Returns: + Sequence[Dataset]: The list of datasets in the organization. + + For more information, see `Data Client API `_. + """ + request = ListDatasetsByOrganizationIDRequest(organization_id=organization_id) + response: ListDatasetsByOrganizationIDResponse = await self._dataset_client.ListDatasetsByOrganizationID( + request, metadata=self._metadata + ) + + return response.datasets + + async def rename_dataset(self, id: str, name: str) -> None: + """Rename a dataset specified by the dataset ID. + + :: + + await data_client.rename_dataset( + id="", + name="MyDataset" + ) + + Args: + id (str): The ID of the dataset. To retrieve the dataset ID: + + - Navigate to the **DATASETS** tab of the **DATA** page. + - Click on the dataset. + - Click the **...** menu. + - Select **Copy dataset ID**. + name (str): The new name of the dataset. + + For more information, see `Data Client API `_. + """ + request = RenameDatasetRequest(id=id, name=name) + await self._dataset_client.RenameDataset(request, metadata=self._metadata) + + async def delete_dataset(self, id: str) -> None: + """Delete a dataset. + + :: + + await data_client.delete_dataset( + id="" + ) + + Args: + id (str): The ID of the dataset. To retrieve the dataset ID: + + - Navigate to the **DATASETS** tab of the **DATA** page. + - Click on the dataset. + - Click the **...** menu. + - Select **Copy dataset ID**. + + For more information, see `Data Client API `_. + """ + request = DeleteDatasetRequest(id=id) + await self._dataset_client.DeleteDataset(request, metadata=self._metadata) + + async def add_binary_data_to_dataset_by_ids(self, binary_ids: Union[List[BinaryID], List[str]], dataset_id: str) -> None: + """Add the BinaryData to the provided dataset. + + This BinaryData will be tagged with the VIAM_DATASET_{id} label. + + :: + + binary_metadata, count, last = await data_client.binary_data_by_filter( + include_binary_data=False + ) + + my_binary_data_ids = [] + + for obj in binary_metadata: + my_binary_data_ids.append( + obj.metadata.binary_data_id + ) + + await data_client.add_binary_data_to_dataset_by_ids( + binary_ids=my_binary_data_ids, + dataset_id="abcd-1234xyz-8765z-123abc" + ) + + Args: + binary_ids (List[~viam.proto.app.data.BinaryID]): Unique identifiers for binary data to add to the dataset. To retrieve these IDs, + navigate to the DATA page, click on an image, and copy its Binary Data ID from the details tab. + dataset_id (str): The ID of the dataset to be added to. To retrieve the dataset ID: + + - Navigate to the **DATASETS** tab of the **DATA** page. + - Click on the dataset. + - Click the **...** menu. + - Select **Copy dataset ID**. + + For more information, see `Data Client API `_. + """ + request = AddBinaryDataToDatasetByIDsRequest() + if len(binary_ids) > 0 and isinstance(binary_ids[0], str): + binary_data_ids = cast(List[str], binary_ids) + request = AddBinaryDataToDatasetByIDsRequest(binary_data_ids=binary_data_ids, dataset_id=dataset_id) + else: + bin_ids = cast(List[BinaryID], binary_ids) + request = AddBinaryDataToDatasetByIDsRequest(binary_ids=bin_ids, dataset_id=dataset_id) + await self._data_client.AddBinaryDataToDatasetByIDs(request, metadata=self._metadata) + + async def remove_binary_data_from_dataset_by_ids(self, binary_ids: Union[List[BinaryID], List[str]], dataset_id: str) -> None: + """Remove the BinaryData from the provided dataset. + + This BinaryData will lose the VIAM_DATASET_{id} tag. + + :: + + binary_metadata, count, last = await data_client.binary_data_by_filter( + include_binary_data=False + ) + + my_binary_data_ids = [] + + for obj in binary_metadata: + my_binary_data_ids.append( + obj.metadata.binary_data_id + ) + + await data_client.remove_binary_data_from_dataset_by_ids( + binary_ids=my_binary_data_ids, + dataset_id="abcd-1234xyz-8765z-123abc" + ) + + Args: + binary_ids (Union[List[~viam.proto.app.data.BinaryID], List[str]]): Unique identifiers for the binary data to remove from the dataset. To retrieve these IDs, + navigate to the DATA page, click on an image and copy its Binary Data ID from the details tab. + *DEPRECATED:* :class:`BinaryID` *is deprecated and will be removed in a future release. Instead, pass binary data IDs as a + list of strings.* + dataset_id (str): The ID of the dataset to be removed from. To retrieve the dataset ID: + + - Navigate to the **DATASETS** tab of the **DATA** page. + - Click on the dataset. + - Click the **...** menu. + - Select **Copy dataset ID**. + + For more information, see `Data Client API `_. + """ + request = RemoveBinaryDataFromDatasetByIDsRequest() + if len(binary_ids) > 0 and isinstance(binary_ids[0], str): + binary_data_ids = cast(List[str], binary_ids) + request = RemoveBinaryDataFromDatasetByIDsRequest(binary_data_ids=binary_data_ids, dataset_id=dataset_id) + else: + bin_ids = cast(List[BinaryID], binary_ids) + request = RemoveBinaryDataFromDatasetByIDsRequest(binary_ids=bin_ids, dataset_id=dataset_id) + await self._data_client.RemoveBinaryDataFromDatasetByIDs(request, metadata=self._metadata) + + async def binary_data_capture_upload( + self, + binary_data: bytes, + part_id: str, + component_type: str, + component_name: str, + method_name: str, + file_extension: str, + method_parameters: Optional[Mapping[str, Any]] = None, + tags: Optional[List[str]] = None, + data_request_times: Optional[Tuple[datetime, datetime]] = None, + ) -> str: + """Upload binary sensor data. + + Upload binary data collected on a robot through a specific component (for example, a motor), along with the relevant metadata. + Binary data can be found on the **DATA** page of the Viam app. + + :: + + time_requested = datetime(2023, 6, 5, 11) + time_received = datetime(2023, 6, 5, 11, 0, 3) + + file_id = await data_client.binary_data_capture_upload( + part_id="INSERT YOUR PART ID", + component_type='camera', + component_name='my_camera', + method_name='GetImages', + method_parameters=None, + tags=["tag_1", "tag_2"], + data_request_times=[time_requested, time_received], + file_extension=".jpg", + binary_data=b"Encoded image bytes" + ) + + Args: + binary_data (bytes): The data to be uploaded, represented in bytes. + part_id (str): Part ID of the component used to capture the data. + component_type (str): Type of the component used to capture the data (for example, "movement_sensor"). + component_name (str): Name of the component used to capture the data. + method_name (str): Name of the method used to capture the data. + file_extension (str): The file extension of binary data, *including the period*, for example ``.jpg``, ``.png``, ``.pcd``. + The backend routes the binary to its corresponding mime type based on this extension. Files with a ``.jpeg``, ``.jpg``, + or ``.png`` extension will appear in the **Images** tab. + method_parameters (Optional[Mapping[str, Any]]): Optional dictionary of method parameters. No longer in active use. + tags (Optional[List[str]]): Optional list of tags to allow for tag-based data filtering when retrieving data. + data_request_times (Optional[Tuple[datetime.datetime, datetime.datetime]]): Optional tuple containing datetime objects + denoting the times this data was requested ``[0]`` by the robot and received ``[1]`` from the appropriate sensor. + + Raises: + GRPCError: If an invalid part ID is passed. + + Returns: + str: The binary data ID of the uploaded data. + + For more information, see `Data Client API `_. + """ + sensor_contents = SensorData( + metadata=( + SensorMetadata( + time_requested=datetime_to_timestamp(data_request_times[0]) if data_request_times else None, + time_received=datetime_to_timestamp(data_request_times[1]) if data_request_times else None, + ) + if data_request_times + else None + ), + struct=None, # Used for tabular data. + binary=binary_data, + ) + metadata = UploadMetadata( + part_id=part_id, + component_type=component_type, + component_name=component_name, + method_name=method_name, + type=DataType.DATA_TYPE_BINARY_SENSOR, + method_parameters=method_parameters, + tags=tags, + ) + if file_extension: + metadata.file_extension = file_extension if file_extension[0] == "." else f".{file_extension}" + response = await self._data_capture_upload(metadata=metadata, sensor_contents=[sensor_contents]) + return response.binary_data_id + + async def tabular_data_capture_upload( + self, + tabular_data: List[Mapping[str, Any]], + part_id: str, + component_type: str, + component_name: str, + method_name: str, + data_request_times: List[Tuple[datetime, datetime]], + method_parameters: Optional[Mapping[str, Any]] = None, + tags: Optional[List[str]] = None, + ) -> str: + """Upload tabular sensor data. + + Upload tabular data collected on a robot through a specific component (for example, a motor), along with the relevant metadata. + Tabular data can be found under the **Sensors** tab of the **DATA** page. + + :: + + from datetime import datetime + + time_requested = datetime(2023, 6, 5, 11) + time_received = datetime(2023, 6, 5, 11, 0, 3) + file_id = await data_client.tabular_data_capture_upload( + part_id="INSERT YOUR PART ID", + component_type='rdk:component:movement_sensor', + component_name='my_movement_sensor', + method_name='Readings', + tags=["sensor_data"], + data_request_times=[(time_requested, time_received)], + tabular_data=[{ + 'readings': { + 'linear_velocity': {'x': 0.5, 'y': 0.0, 'z': 0.0}, + 'angular_velocity': {'x': 0.0, 'y': 0.0, 'z': 0.1} + } + }] + ) + + Args: + tabular_data (List[Mapping[str, Any]]): List of the data to be uploaded, represented tabularly as a collection of dictionaries. + Must include the key ``readings`` for sensors. + part_id (str): Part ID of the component used to capture the data. + component_type (str): Type of the component used to capture the data (for example, ``rdk:component:movement_sensor``). + component_name (str): Name of the component used to capture the data. + method_name (str): Name of the method used to capture the data. + data_request_times (List[Tuple[datetime.datetime, datetime.datetime]]): List of tuples, each containing ``datetime`` objects + denoting the times this data was requested ``[0]`` by the robot and received ``[1]`` from the appropriate sensor. + Pass a list of tabular data and timestamps with length ``n > 1`` to upload ``n`` datapoints, all with the same metadata. + method_parameters (Optional[Mapping[str, Any]]): Optional dictionary of method parameters. No longer in active use. + tags (Optional[List[str]]): Optional list of tags to allow for tag-based data filtering when retrieving data. + + Raises: + GRPCError: If an invalid part ID is passed. + ValueError: If the provided list of `Timestamp` objects has a length that does not match the length of the list of tabular + data. + + Returns: + str: The file ID of the uploaded data. + + For more information, see `Data Client API `_. + """ + sensor_contents = [] + if len(data_request_times) != len(tabular_data): + raise ValueError("data_request_times and tabular_data lengths must be equal.") + + for idx, tab in enumerate(tabular_data): + s = Struct() + s.update(tab) + sensor_contents.append( + SensorData( + metadata=( + SensorMetadata( + time_requested=datetime_to_timestamp(data_request_times[idx][0]) if data_request_times else None, + time_received=datetime_to_timestamp(data_request_times[idx][1]) if data_request_times else None, + ) + if data_request_times[idx] + else None + ) + if data_request_times + else None, + struct=s, + ) + ) + + metadata = UploadMetadata( + part_id=part_id, + component_type=component_type, + component_name=component_name, + method_name=method_name, + type=DataType.DATA_TYPE_TABULAR_SENSOR, + method_parameters=method_parameters, + tags=tags, + ) + response = await self._data_capture_upload(metadata=metadata, sensor_contents=sensor_contents) + return response.file_id + + async def _data_capture_upload(self, metadata: UploadMetadata, sensor_contents: List[SensorData]) -> DataCaptureUploadResponse: + request = DataCaptureUploadRequest(metadata=metadata, sensor_contents=sensor_contents) + response: DataCaptureUploadResponse = await self._data_sync_client.DataCaptureUpload(request, metadata=self._metadata) + return response + + async def streaming_data_capture_upload( + self, + data: bytes, + part_id: str, + file_ext: str, + component_type: Optional[str] = None, + component_name: Optional[str] = None, + method_name: Optional[str] = None, + method_parameters: Optional[Mapping[str, Any]] = None, + data_request_times: Optional[Tuple[datetime, datetime]] = None, + tags: Optional[List[str]] = None, + ) -> str: + """Uploads the metadata and contents of streaming binary data. + + :: + + time_requested = datetime(2023, 6, 5, 11) + time_received = datetime(2023, 6, 5, 11, 0, 3) + + file_id = await data_client.streaming_data_capture_upload( + data="byte-data-to-upload", + part_id="INSERT YOUR PART ID", + file_ext="png", + component_type='motor', + component_name='left_motor', + method_name='IsPowered', + data_request_times=[time_requested, time_received], + tags=["tag_1", "tag_2"] + ) + + Args: + data (bytes): The data to be uploaded. + part_id (str): Part ID of the resource associated with the file. + file_ext (str): File extension type for the data. required for determining MIME type. + component_type (Optional[str]): Optional type of the component associated with the file (for example, "movement_sensor"). + component_name (Optional[str]): Optional name of the component associated with the file. + method_name (Optional[str]): Optional name of the method associated with the file. + method_parameters (Optional[str]): Optional dictionary of the method parameters. No longer in active use. + data_request_times (Optional[Tuple[datetime.datetime, datetime.datetime]]): Optional tuple containing datetime objects + denoting the times this data was requested ``[0]`` by the robot and received ``[1]`` from the appropriate sensor. + tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data. + + Raises: + GRPCError: If an invalid part ID is passed. + + Returns: + str: The binary data ID of the uploaded data. + + For more information, see `Data Client API `_. + """ + + upload_metadata = UploadMetadata( + part_id=part_id, + component_type=component_type if component_type else "", + component_name=component_name if component_name else "", + method_name=method_name if method_name else "", + method_parameters=method_parameters, + type=DataType.DATA_TYPE_BINARY_SENSOR, + file_extension=file_ext if file_ext[0] == "." else f".{file_ext}", + tags=tags, + ) + sensor_metadata = SensorMetadata( + time_requested=datetime_to_timestamp(data_request_times[0]) if data_request_times else None, + time_received=datetime_to_timestamp(data_request_times[1]) if data_request_times else None, + ) + metadata = DataCaptureUploadMetadata(upload_metadata=upload_metadata, sensor_metadata=sensor_metadata) + request_metadata = StreamingDataCaptureUploadRequest(metadata=metadata) + stream: Stream[StreamingDataCaptureUploadRequest, StreamingDataCaptureUploadResponse] + async with self._data_sync_client.StreamingDataCaptureUpload.open(metadata=self._metadata) as stream: + await stream.send_message(request_metadata) + await stream.send_message(StreamingDataCaptureUploadRequest(data=data), end=True) + response = await stream.recv_message() + if not response: + await stream.recv_trailing_metadata() # causes us to throw appropriate gRPC error + raise TypeError("Response cannot be empty") + return response.binary_data_id + + async def file_upload( + self, + part_id: str, + data: bytes, + component_type: Optional[str] = None, + component_name: Optional[str] = None, + method_name: Optional[str] = None, + file_name: Optional[str] = None, + method_parameters: Optional[Mapping[str, Any]] = None, + file_extension: Optional[str] = None, + tags: Optional[List[str]] = None, + ) -> str: + """Upload arbitrary file data. + + Upload file data that may be stored on a robot along with the relevant metadata. File data can be found in the + **Files** tab of the **DATA** page. + + :: + + file_id = await data_client.file_upload( + data=b"Encoded image bytes", + part_id="INSERT YOUR PART ID", + tags=["tag_1", "tag_2"], + file_name="your-file", + file_extension=".txt" + ) + + Args: + part_id (str): Part ID of the resource associated with the file. + data (bytes): Bytes representing file data to upload. + component_type (Optional[str]): Optional type of the component associated with the file (for example, "movement_sensor"). + component_name (Optional[str]): Optional name of the component associated with the file. + method_name (Optional[str]): Optional name of the method associated with the file. + file_name (Optional[str]): Optional name of the file. The empty string ``""`` will be assigned as the file name if one isn't + provided. + method_parameters (Optional[str]): Optional dictionary of the method parameters. No longer in active use. + file_extension (Optional[str]): Optional file extension. The empty string ``""`` will be assigned as the file extension if one + isn't provided. Files with a ``.jpeg``, ``.jpg``, or ``.png`` extension will be saved to the **Images** tab. + tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data. + + Raises: + GRPCError: If an invalid part ID is passed. + + Returns: + str: Binary data ID of the new file. + + For more information, see `Data Client API `_. + """ + metadata = UploadMetadata( + part_id=part_id, + component_type=component_type if component_type else "", + component_name=component_name if component_name else "", + method_name=method_name if method_name else "", + type=DataType.DATA_TYPE_FILE, + file_name=file_name if file_name else "", + method_parameters=method_parameters, + file_extension=file_extension if file_extension else "", + tags=tags, + ) + response: FileUploadResponse = await self._file_upload(metadata=metadata, file_contents=FileData(data=data)) + return response.binary_data_id + + async def file_upload_from_path( + self, + filepath: str, + part_id: str, + component_type: Optional[str] = None, + component_name: Optional[str] = None, + method_name: Optional[str] = None, + method_parameters: Optional[Mapping[str, Any]] = None, + tags: Optional[List[str]] = None, + ) -> str: + """Upload arbitrary file data. + + Upload file data that may be stored on a robot along with the relevant metadata. File data can be found in the + **Files** tab of the **DATA** page. + + :: + + file_id = await data_client.file_upload_from_path( + part_id="INSERT YOUR PART ID", + tags=["tag_1", "tag_2"], + filepath="/Users///" + ) + + Args: + filepath (str): Absolute filepath of file to be uploaded. + part_id (str): Part ID of the component associated with the file. + component_type (Optional[str]): Optional type of the component associated with the file (for example, "movement_sensor"). + component_name (Optional[str]): Optional name of the component associated with the file. + method_name (Optional[str]): Optional name of the method associated with the file. + method_parameters (Optional[str]): Optional dictionary of the method parameters. No longer in active use. + tags (Optional[List[str]]): Optional list of tags to allow for tag-based filtering when retrieving data. + + + Raises: + GRPCError: If an invalid part ID is passed. + FileNotFoundError: If the provided filepath is not found. + + Returns: + str: Binary data ID of the new file. + + For more information, see `Data Client API `_. + """ + path = Path(filepath) + file_name = path.stem + file_extension = path.suffix if path.suffix != "" else None + f = open(filepath, "rb") + data = f.read() + f.close() + + metadata = UploadMetadata( + part_id=part_id, + component_type=component_type if component_type else "", + component_name=component_name if component_name else "", + method_name=method_name if method_name else "", + type=DataType.DATA_TYPE_FILE, + file_name=file_name, + method_parameters=method_parameters, + file_extension=file_extension if file_extension else "", + tags=tags, + ) + response: FileUploadResponse = await self._file_upload(metadata=metadata, file_contents=FileData(data=data if data else bytes())) + return response.binary_data_id + + async def _file_upload(self, metadata: UploadMetadata, file_contents: FileData) -> FileUploadResponse: + request_metadata = FileUploadRequest(metadata=metadata) + request_file_contents = FileUploadRequest(file_contents=file_contents) + stream: Stream[FileUploadRequest, FileUploadResponse] + async with self._data_sync_client.FileUpload.open(metadata=self._metadata) as stream: + await stream.send_message(request_metadata) + await stream.send_message(request_file_contents, end=True) + response = await stream.recv_message() + if not response: + await stream.recv_trailing_metadata() # causes us to throw appropriate gRPC error. + raise TypeError("Response cannot be empty") + return response + + @staticmethod + def create_filter( + component_name: Optional[str] = None, + component_type: Optional[str] = None, + method: Optional[str] = None, + robot_name: Optional[str] = None, + robot_id: Optional[str] = None, + part_name: Optional[str] = None, + part_id: Optional[str] = None, + location_ids: Optional[List[str]] = None, + organization_ids: Optional[List[str]] = None, + mime_type: Optional[List[str]] = None, + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + tags: Optional[List[str]] = None, + bbox_labels: Optional[List[str]] = None, + dataset_id: Optional[str] = None, + ) -> Filter: + warnings.warn("DataClient.create_filter is deprecated. Use utils.create_filter instead.", DeprecationWarning, stacklevel=2) + return create_filter( + component_name, + component_type, + method, + robot_name, + robot_id, + part_name, + part_id, + location_ids, + organization_ids, + mime_type, + start_time, + end_time, + tags, + bbox_labels, + dataset_id, + ) diff --git a/src/viam/app/ml_training_client.py b/src/viam/app/ml_training_client.py new file mode 100644 index 000000000..3166cca27 --- /dev/null +++ b/src/viam/app/ml_training_client.py @@ -0,0 +1,251 @@ +from typing import List, Mapping, Optional + +from grpclib.client import Channel + +from viam import logging +from viam.proto.app.mltraining import ( + CancelTrainingJobRequest, + DeleteCompletedTrainingJobRequest, + GetTrainingJobRequest, + GetTrainingJobResponse, + ListTrainingJobsRequest, + ListTrainingJobsResponse, + MLTrainingServiceStub, + ModelType, + SubmitCustomTrainingJobRequest, + SubmitCustomTrainingJobResponse, + SubmitTrainingJobRequest, + SubmitTrainingJobResponse, + TrainingJobMetadata, + TrainingStatus, +) + +LOGGER = logging.getLogger(__name__) + + +class MLTrainingClient: + """gRPC client for working with ML training jobs. + + Constructor is used by `ViamClient` to instantiate relevant service stubs. + Calls to `MLTrainingClient` methods should be made through `ViamClient`. + + Establish a Connection:: + + import asyncio + + from viam.rpc.dial import DialOptions, Credentials + from viam.app.viam_client import ViamClient + + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + + async def main(): + + # Make a ViamClient + viam_client = await connect() + # Instantiate an MLTrainingClient to run ML training client API methods on + ml_training_client = viam_client.ml_training_client + + viam_client.close() + + if __name__ == '__main__': + asyncio.run(main()) + + For more information, see `ML Training Client API `_. + """ + + def __init__(self, channel: Channel, metadata: Mapping[str, str]): + """Create a `MLTrainingClient` that maintains a connection to app. + + Args: + channel (grpclib.client.Channel): Connection to app. + metadata (Mapping[str, str]): Required authorization token to send requests to app. + + For more information, see `ML Training Client API `_. + """ + self._metadata = metadata + self._ml_training_client = MLTrainingServiceStub(channel) + self._channel = channel + + async def submit_training_job( + self, + org_id: str, + dataset_id: str, + model_name: str, + model_version: str, + model_type: ModelType.ValueType, + tags: List[str], + ) -> str: + """Submit a training job. + + :: + + from viam.proto.app.mltraining import ModelType + + job_id = await ml_training_client.submit_training_job( + org_id="", + dataset_id="", + model_name="", + model_version="1", + model_type=ModelType.MODEL_TYPE_SINGLE_LABEL_CLASSIFICATION, + tags=["tag1", "tag2"] + ) + + Args: + org_id (str): the ID of the org to submit the training job to. + dataset_id (str): the ID of the dataset to train the model on. + model_name (str): the model name. + model_version (str): the model version. + model_type (ModelType.ValueType): the model type. + tags (List[str]): the labels to train the model on. + + Returns: + str: the ID of the training job. + + For more information, see `ML Training Client API `_. + """ + + request = SubmitTrainingJobRequest( + dataset_id=dataset_id, + organization_id=org_id, + model_name=model_name, + model_version=model_version, + model_type=model_type, + tags=tags, + ) + response: SubmitTrainingJobResponse = await self._ml_training_client.SubmitTrainingJob(request, metadata=self._metadata) + return response.id + + async def submit_custom_training_job( + self, org_id: str, dataset_id: str, registry_item_id: str, registry_item_version: str, model_name: str, model_version: str + ) -> str: + """Submit a custom training job. + + :: + + job_id = await ml_training_client.submit_custom_training_job( + org_id="", + dataset_id="", + registry_item_id="viam:classification-tflite", + registry_item_version="2024-08-13T12-11-54", + model_name="", + model_version="1" + ) + + Args: + org_id (str): the ID of the org to submit the training job to. + dataset_id (str): the ID of the dataset to train the model on. + registry_item_id (str): the ID of the training script from the registry. + registry_item_version (str): the version of the training script from the registry. + model_name (str): the model name. + model_version (str): the model version. + + Returns: + str: the ID of the training job. + + For more information, see `ML Training Client API `_. + """ + + request = SubmitCustomTrainingJobRequest( + dataset_id=dataset_id, + registry_item_id=registry_item_id, + registry_item_version=registry_item_version, + organization_id=org_id, + model_name=model_name, + model_version=model_version, + ) + response: SubmitCustomTrainingJobResponse = await self._ml_training_client.SubmitCustomTrainingJob(request, metadata=self._metadata) + return response.id + + async def get_training_job(self, id: str) -> TrainingJobMetadata: + """Gets training job data. + + :: + + job_metadata = await ml_training_client.get_training_job( + id="") + + Args: + id (str): the ID of the requested training job. + + Returns: + viam.proto.app.mltraining.TrainingJobMetadata: the training job data. + + For more information, see `ML Training Client API `_. + """ + + request = GetTrainingJobRequest(id=id) + response: GetTrainingJobResponse = await self._ml_training_client.GetTrainingJob(request, metadata=self._metadata) + + return response.metadata + + async def list_training_jobs( + self, + org_id: str, + training_status: Optional[TrainingStatus.ValueType] = None, + ) -> List[TrainingJobMetadata]: + """Returns training job data for all jobs within an org. + + :: + + jobs_metadata = await ml_training_client.list_training_jobs( + org_id="") + + first_job_id = jobs_metadata[1].id + + Args: + org_id (str): the ID of the org to request training job data from. + training_status (Optional[TrainingStatus]): the status to filter the training jobs list by. + If unspecified, all training jobs will be returned. + + Returns: + List[viam.proto.app.mltraining.TrainingJobMetadata]: the list of training job data. + + For more information, see `ML Training Client API `_. + """ + + training_status = training_status if training_status else TrainingStatus.TRAINING_STATUS_UNSPECIFIED + request = ListTrainingJobsRequest(organization_id=org_id, status=training_status) + response: ListTrainingJobsResponse = await self._ml_training_client.ListTrainingJobs(request, metadata=self._metadata) + + return list(response.jobs) + + async def cancel_training_job(self, id: str) -> None: + """Cancels the specified training job. + + :: + + await ml_training_client.cancel_training_job( + id="") + + Args: + id (str): the ID of the job to cancel. + + Raises: + GRPCError: if no training job exists with the given ID. + + For more information, see `ML Training Client API `_. + """ + + request = CancelTrainingJobRequest(id=id) + await self._ml_training_client.CancelTrainingJob(request, metadata=self._metadata) + + async def delete_completed_training_job(self, id: str) -> None: + """Delete a completed training job from the database, whether the job succeeded or failed. + + :: + + await ml_training_client.delete_completed_training_job( + id="") + + Args: + id (str): the ID of the training job to delete. + + For more information, see `ML Training Client API `_. + """ + request = DeleteCompletedTrainingJobRequest(id=id) + await self._ml_training_client.DeleteCompletedTrainingJob(request, metadata=self._metadata) diff --git a/src/viam/app/provisioning_client.py b/src/viam/app/provisioning_client.py new file mode 100644 index 000000000..924bf722f --- /dev/null +++ b/src/viam/app/provisioning_client.py @@ -0,0 +1,95 @@ +from typing import List, Mapping, Optional + +from grpclib.client import Channel + +from viam import logging +from viam.proto.provisioning import ( + CloudConfig, + GetNetworkListRequest, + GetNetworkListResponse, + GetSmartMachineStatusRequest, + GetSmartMachineStatusResponse, + NetworkInfo, + ProvisioningServiceStub, + SetNetworkCredentialsRequest, + SetSmartMachineCredentialsRequest, +) + +LOGGER = logging.getLogger(__name__) + + +class ProvisioningClient: + """gRPC client for getting and setting smart machine info. + + Constructor is used by `ViamClient` to instantiate relevant service stubs. Calls to + `ProvisioningClient` methods should be made through `ViamClient`. + + Establish a connection:: + + import asyncio + + from viam.rpc.dial import DialOptions, Credentials + from viam.app.viam_client import ViamClient + + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + + async def main(): + + # Make a ViamClient + viam_client = await connect() + # Instantiate a ProvisioningClient to run provisioning client API methods on + provisioning_client = viam_client.provisioning_client + + viam_client.close() + + if __name__ == '__main__': + asyncio.run(main()) + + """ + + def __init__(self, channel: Channel, metadata: Mapping[str, str]): + """Create a `ProvisioningClient` that maintains a connection to app. + + Args: + channel (grpclib.client.Channel): Connection to app. + metadata (Mapping[str, str]): Required authorization token to send requests to app. + """ + self._metadata = metadata + self._provisioning_client = ProvisioningServiceStub(channel) + self._channel = channel + + _provisioning_client: ProvisioningServiceStub + _metadata: Mapping[str, str] + _channel: Channel + + async def get_network_list(self) -> List[NetworkInfo]: + """Returns list of networks that are visible to the Smart Machine.""" + request = GetNetworkListRequest() + resp: GetNetworkListResponse = await self._provisioning_client.GetNetworkList(request, metadata=self._metadata) + return list(resp.networks) + + async def get_smart_machine_status(self) -> GetSmartMachineStatusResponse: + """Returns the status of the smart machine.""" + request = GetSmartMachineStatusRequest() + return await self._provisioning_client.GetSmartMachineStatus(request, metadata=self._metadata) + + async def set_network_credentials(self, network_type: str, ssid: str, psk: str) -> None: + """Sets the network credentials of the Smart Machine. + + Args: + network_type (str): The type of the network. + ssid (str): The SSID of the network. + psk (str): The network's passkey. + """ + + request = SetNetworkCredentialsRequest(type=network_type, ssid=ssid, psk=psk) + await self._provisioning_client.SetNetworkCredentials(request, metadata=self._metadata) + + async def set_smart_machine_credentials(self, cloud_config: Optional[CloudConfig] = None) -> None: + request = SetSmartMachineCredentialsRequest(cloud=cloud_config) + await self._provisioning_client.SetSmartMachineCredentials(request, metadata=self._metadata) diff --git a/src/viam/app/viam_client.py b/src/viam/app/viam_client.py new file mode 100644 index 000000000..2f2e0b4e8 --- /dev/null +++ b/src/viam/app/viam_client.py @@ -0,0 +1,260 @@ +import os +from typing import Mapping, Optional + +from grpclib.client import Channel +from typing_extensions import Self + +from viam import logging +from viam.app.app_client import AppClient +from viam.app.billing_client import BillingClient +from viam.app.data_client import DataClient +from viam.app.ml_training_client import MLTrainingClient +from viam.app.provisioning_client import ProvisioningClient +from viam.robot.client import RobotClient +from viam.rpc.dial import Credentials, DialOptions, _dial_app, _get_access_token + +LOGGER = logging.getLogger(__name__) + + +class ViamClient: + """gRPC client for all communication and interaction with app. + + `ViamClient` class for creating and managing specialized client instances. + There is currently 1 way to instantiate a `ViamClient` object:: + + ViamClient.create_from_dial_options(...) + """ + + @classmethod + async def create_from_env_vars(cls, dial_options: Optional[DialOptions] = None, app_url: Optional[str] = None) -> Self: + """Create `ViamClient` using credentials set in the environment as `VIAM_API_KEY` and `VIAM_API_KEY_ID`. + + :: + + client = await ViamClient.create_from_env_vars() + + Args: + dial_options (Optional[viam.rpc.dial.DialOptions]): Options for authorization and connection to app. + If not provided, default options will be selected. Note that `creds` and `auth_entity` + fields will be overwritten by the values set by a module. + app_url: (Optional[str]): URL of app. Uses app.viam.com if not specified. + + Raises: + ValueError: If there are no env vars set by the module, or if they are set improperly + + """ + dial_options = dial_options if dial_options else DialOptions() + api_key = os.environ.get("VIAM_API_KEY") + if api_key is None: + raise ValueError("api key cannot be None") + api_key_id = os.environ.get("VIAM_API_KEY_ID") + if api_key_id is None: + raise ValueError("api key ID cannot be None") + credentials = Credentials(type="api-key", payload=api_key) + dial_options.credentials = credentials + dial_options.auth_entity = api_key_id + + return await cls.create_from_dial_options(dial_options, app_url) + + @classmethod + async def create_from_dial_options(cls, dial_options: DialOptions, app_url: Optional[str] = None) -> Self: + """Create `ViamClient` that establishes a connection to the Viam app. + + :: + + dial_options = DialOptions.with_api_key("", "") + client = await ViamClient.create_from_dial_options(dial_options) + + Args: + dial_options (viam.rpc.dial.DialOptions): Required information for authorization and connection to app. + `creds` and `auth_entity` fields are required. + app_url: (Optional[str]): URL of app. Uses app.viam.com if not specified. + + Raises: + ValueError: If the input parameters are missing a required field or simply invalid. + + Returns: + Self: The `ViamClient`. + """ + if dial_options.credentials is None: + raise ValueError("dial_options.credentials cannot be None.") + if dial_options.credentials.type == "robot-secret": + raise ValueError("dial_options.credentials.type cannot be 'robot-secret'") + if dial_options.auth_entity is None: + raise ValueError("dial_options.auth_entity cannot be None.") + + self = cls() + self._dial_options = dial_options + self._location_id = None + if dial_options.credentials.type == "robot-location-secret": + self._location_id = dial_options.auth_entity.split(".")[1] + if app_url is None: + app_url = "app.viam.com" + self._channel = await _dial_app(app_url) + access_token = await _get_access_token(self._channel, dial_options.auth_entity, dial_options) + self._metadata = {"authorization": f"Bearer {access_token}"} + return self + + _channel: Channel + _metadata: Mapping[str, str] + _closed: bool = False + _location_id: Optional[str] + _dial_options: DialOptions + + @property + def data_client(self) -> DataClient: + """Instantiate and return a `DataClient` object used to make `data` and `data_sync` method calls. + To use the `DataClient`, you must first instantiate a `ViamClient`. + + :: + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + async def main(): + viam_client = await connect() + + # Instantiate a DataClient to run data client API methods on + data_client = viam_client.data_client + """ + return DataClient(self._channel, self._metadata) + + @property + def app_client(self) -> AppClient: + """Instantiate and return an `AppClient` used to make `app` method calls. + To use the `AppClient`, you must first instantiate a `ViamClient`. + + :: + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + + async def main(): + viam_client = await connect() + + # Instantiate an AppClient called "fleet" to run fleet management API methods on + fleet = viam_client.app_client + """ + return AppClient(self._channel, self._metadata, self._location_id) + + @property + def ml_training_client(self) -> MLTrainingClient: + """Instantiate and return a `MLTrainingClient` used to make `ml_training` method calls. + To use the `MLTrainingClient`, you must first instantiate a `ViamClient`. + + :: + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + + async def main(): + viam_client = await connect() + + # Instantiate an MLTrainingClient to run ML training client API methods on + ml_training_client = viam_client.ml_training_client + """ + return MLTrainingClient(self._channel, self._metadata) + + @property + def billing_client(self) -> BillingClient: + """Instantiate and return a `BillingClient` used to make `billing` method calls. + To use the `BillingClient`, you must first instantiate a `ViamClient`. + + :: + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + + async def main(): + viam_client = await connect() + + # Instantiate a BillingClient to run billing client API methods on + billing_client = viam_client.billing_client + """ + + return BillingClient(self._channel, self._metadata) + + @property + def provisioning_client(self) -> ProvisioningClient: + """Instantiate and return a `ProvisioningClient` used to make `provisioning` method calls. + To use the `ProvisioningClient`, you must first instantiate a `ViamClient`. + + :: + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + + async def main(): + viam_client = await connect() + + # Instantiate a ProvisioningClient to run provisioning API methods on + provisioning_client = viam_client.provisioning_client + """ + return ProvisioningClient(self._channel, self._metadata) + + def close(self): + """Close opened channels used for the various service stubs initialized.""" + if self._closed: + LOGGER.debug("ViamClient is already closed.") + return + LOGGER.debug("Closing gRPC channel to app.") + self._channel.close() + self._closed = True + + async def connect_to_machine(self, *, address: Optional[str] = None, id: Optional[str] = None) -> RobotClient: + """Connect to a machine using existing credentials. + + A connection can be attempted using either the machine's address or its ID. + If both an address and ID are provided, the address will take precedence and the ID will be ignored. + + :: + + async def connect() -> ViamClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + dial_options = DialOptions.with_api_key("", "") + return await ViamClient.create_from_dial_options(dial_options) + + + async def main(): + viam_client = await connect() + + # Connect to a machine and obtain a RobotClient + # Replace "" (including brackets) with your machine's connection address + machine = await viam_client.connect_to_machine(address="") + + Args: + address (Optional[str]): The address (FQDN) of the machine. Defaults to None. + id (Optional[str]): The ID (as a UUID) of the machine. Defaults to None. + + Raises: + ValueError: If neither an address nor ID is provided. + + Returns: + RobotClient: The active connection to the machine. + """ + if address is None and id is None: + raise ValueError("Either a machine address or ID must be provided") + + if id is not None and address is None: + parts = await self.app_client.get_robot_parts(id) + main_part = next(p for p in parts if p.main_part) + address = main_part.fqdn + + opts = RobotClient.Options(dial_options=self._dial_options) + + assert address is not None + return await RobotClient.at_address(address, opts) diff --git a/src/viam/components/arm/__init__.py b/src/viam/components/arm/__init__.py index 407d0ceff..60278e410 100644 --- a/src/viam/components/arm/__init__.py +++ b/src/viam/components/arm/__init__.py @@ -1,27 +1,16 @@ -from viam.proto.component.arm import Status as ArmStatus -from viam.proto.robot import Status -from viam.registry import ComponentRegistration, Registry -from viam.utils import message_to_struct +from viam.proto.common import KinematicsFileFormat, Pose +from viam.proto.component.arm import JointPositions +from viam.resource.registry import Registry, ResourceRegistration -from .arm import Arm, JointPositions, Pose, WorldState +from .arm import Arm from .client import ArmClient -from .service import ArmService +from .service import ArmRPCService __all__ = [ "Arm", "JointPositions", + "KinematicsFileFormat", "Pose", - "WorldState", ] - -async def create_status(component: Arm) -> Status: - s = ArmStatus( - end_position=await component.get_end_position(), - joint_positions=await component.get_joint_positions(), - is_moving=await component.is_moving(), - ) - return Status(name=Arm.get_resource_name(component.name), status=message_to_struct(s)) - - -Registry.register(ComponentRegistration(Arm, "arm", ArmService, lambda name, channel: ArmClient(name, channel), create_status)) +Registry.register_api(ResourceRegistration(Arm, ArmRPCService, lambda name, channel: ArmClient(name, channel))) diff --git a/src/viam/components/arm/arm.py b/src/viam/components/arm/arm.py index 397f00d32..a61d52136 100644 --- a/src/viam/components/arm/arm.py +++ b/src/viam/components/arm/arm.py @@ -1,11 +1,10 @@ import abc -from typing import Any, Dict, Optional +from typing import Any, Dict, Final, Optional, Tuple -from viam.errors import NotSupportedError -from viam.proto.common import Pose, WorldState -from viam.proto.component.arm import JointPositions +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT from ..component_base import ComponentBase +from . import JointPositions, KinematicsFileFormat, Pose class Arm(ComponentBase): @@ -13,66 +12,212 @@ class Arm(ComponentBase): Arm represents a physical robot arm that exists in three-dimensional space. This acts as an abstract base class for any drivers representing specific - arm implementations. This cannot be used on its own. If the `__init__()` function is - overriden, it must call the `super().__init__()` function. + arm implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.arm import Arm + # To use move_to_position: + from viam.proto.common import Pose + # To use move_to_joint_positions: + from viam.proto.component.arm import JointPositions + + For more information, see `Arm component `_. """ + API: Final = API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "arm") # pyright: ignore [reportIncompatibleVariableOverride] + @abc.abstractmethod - async def get_end_position(self, extra: Optional[Dict[str, Any]] = None) -> Pose: + async def get_end_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Pose: """ - Get the current position of the end of the arm expressed as a Pose. + Get the current position of the end of the arm expressed as a ``Pose``. + + :: + + my_arm = Arm.from_robot(robot=machine, name="my_arm") - Returns: The location and orientation of the arm described as a Pose. + # Get the end position of the arm as a Pose. + pos = await my_arm.get_end_position() + + Returns: + Pose: A representation of the arm's current position as a 6 DOF (six degrees of freedom) pose. + The ``Pose`` is composed of values for location and orientation with respect to the origin. + Location is expressed as distance, which is represented by x, y, and z coordinate values. + Orientation is expressed as an orientation vector, which is represented by o_x, o_y, o_z, and theta values. + + For more information, see `Arm component `_. """ ... @abc.abstractmethod - async def move_to_position(self, pose: Pose, world_state: Optional[WorldState] = None, extra: Optional[Dict[str, Any]] = None): + async def move_to_position( + self, + pose: Pose, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ - Move the end of the arm to the Pose specified in `pose`. - When obstacles are specified in `world_state`, the motion plan of the arm will avoid them. + Move the end of the arm to the Pose specified in ``pose``. - Args: + :: + + my_arm = Arm.from_robot(robot=machine, name="my_arm") + + # Create a Pose for the arm. + examplePose = Pose(x=5, y=5, z=5, o_x=5, o_y=5, o_z=5, theta=20) - pose (Pose): The destination Pose for the arm. + # Move your arm to the Pose. + await my_arm.move_to_position(pose=examplePose) - world_state (WorldState): The obstacles for the arm to avoid on its way to `pose`. + Args: + pose (Pose): The destination ``Pose`` for the arm. The ``Pose`` is composed of values for location and orientation + with respect to the origin. + Location is expressed as distance, which is represented by x, y, and z coordinate values. + Orientation is expressed as an orientation vector, which is represented by o_x, o_y, o_z, and theta values. + + For more information, see `Arm component `_. """ ... @abc.abstractmethod - async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None): + async def move_to_joint_positions( + self, + positions: JointPositions, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ - Move each joint on the arm to the corresponding angle specified in `positions`. + Move each joint on the arm to the corresponding angle specified in ``positions``. + + :: + + my_arm = Arm.from_robot(robot=machine, name="my_arm") + + # Declare a list of values with your desired rotational value for each joint on + # the arm. This example is for a 5dof arm. + degrees = [0.0, 45.0, 0.0, 0.0, 0.0] + + # Declare a new JointPositions with these values. + jointPos = JointPositions(values=degrees) + + # Move each joint of the arm to the position these values specify. + await my_arm.move_to_joint_positions(positions=jointPos) Args: + positions (JointPositions): The destination ``JointPositions`` for the arm. - positions (JointPositions): The destination `JointPositions` for the arm. + For more information, see `Arm component `_. """ ... @abc.abstractmethod - async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None) -> JointPositions: + async def get_joint_positions( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> JointPositions: """ Get the JointPositions representing the current position of the arm. + :: + + my_arm = Arm.from_robot(robot=machine, name="my_arm") + + # Get the current position of each joint on the arm as JointPositions. + pos = await my_arm.get_joint_positions() + Returns: - JointPositions: The current JointPositions for the arm. + JointPositions: The current ``JointPositions`` for the arm. + ``JointPositions`` can have one attribute, ``values``, a list of joint positions with rotational values (degrees) + and translational values (mm). + + For more information, see `Arm component `_. """ ... @abc.abstractmethod - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ Stop all motion of the arm. It is assumed that the arm stops immediately. + + :: + + my_arm = Arm.from_robot(robot=machine, name="my_arm") + + # Stop all motion of the arm. It is assumed that the arm stops immediately. + await my_arm.stop() + + For more information, see `Arm component `_. """ ... + @abc.abstractmethod async def is_moving(self) -> bool: """ Get if the arm is currently moving. + :: + + my_arm = Arm.from_robot(robot=machine, name="my_arm") + + # Stop all motion of the arm. It is assumed that the arm stops immediately. + await my_arm.stop() + + # Print if the arm is currently moving. + print(await my_arm.is_moving()) + Returns: bool: Whether the arm is moving. + + For more information, see `Arm component `_. """ - raise NotSupportedError(f"Arm named {self.name} does not support returning whether it is moving") + ... + + @abc.abstractmethod + async def get_kinematics( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None + ) -> Tuple[KinematicsFileFormat.ValueType, bytes]: + """ + Get the kinematics information associated with the arm. + + :: + + my_arm = Arm.from_robot(robot=machine, name="my_arm") + + # Get the kinematics information associated with the arm. + kinematics = await my_arm.get_kinematics() + + # Get the format of the kinematics file. + k_file = kinematics[0] + + # Get the byte contents of the file. + k_bytes = kinematics[1] + + Returns: + Tuple[KinematicsFileFormat.ValueType, bytes]: A tuple containing two values; the first [0] value represents the format of the + file, either in URDF format (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_URDF``) or + Viam's kinematic parameter format (spatial vector algebra) (``KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA``), + and the second [1] value represents the byte contents of the file. + + For more information, see `Arm component `_. + """ + ... diff --git a/src/viam/components/arm/client.py b/src/viam/components/arm/client.py index 60023531d..37ad25e08 100644 --- a/src/viam/components/arm/client.py +++ b/src/viam/components/arm/client.py @@ -1,29 +1,32 @@ -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Mapping, Optional, Tuple from grpclib.client import Channel -from viam.components.generic.client import do_command -from viam.proto.common import Pose, WorldState + +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, GetKinematicsRequest, GetKinematicsResponse from viam.proto.component.arm import ( ArmServiceStub, GetEndPositionRequest, GetEndPositionResponse, GetJointPositionsRequest, GetJointPositionsResponse, + IsMovingRequest, + IsMovingResponse, JointPositions, MoveToJointPositionsRequest, MoveToPositionRequest, StopRequest, ) -from viam.utils import dict_to_struct +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict -from .arm import Arm +from . import Arm, KinematicsFileFormat, Pose -class ArmClient(Arm): +class ArmClient(Arm, ReconfigurableResourceRPCClientBase): """ gRPC client for an Arm component. - Used to communicate with an existing `Arm` implementation over gRPC. + Used to communicate with an existing ``Arm`` implementation over gRPC. """ def __init__(self, name: str, channel: Channel): @@ -31,42 +34,91 @@ def __init__(self, name: str, channel: Channel): self.client = ArmServiceStub(channel) super().__init__(name) - async def get_end_position(self, extra: Optional[Dict[str, Any]] = None) -> Pose: - if extra is None: - extra = {} + async def get_end_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Pose: + md = kwargs.get("metadata", self.Metadata()).proto request = GetEndPositionRequest(name=self.name, extra=dict_to_struct(extra)) - response: GetEndPositionResponse = await self.client.GetEndPosition(request) + response: GetEndPositionResponse = await self.client.GetEndPosition(request, timeout=timeout, metadata=md) return response.pose async def move_to_position( self, pose: Pose, - world_state: Optional[WorldState] = None, + *, extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, ): - if extra is None: - extra = {} - request = MoveToPositionRequest(name=self.name, to=pose, world_state=world_state, extra=dict_to_struct(extra)) - await self.client.MoveToPosition(request) - - async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None) -> JointPositions: - if extra is None: - extra = {} + md = kwargs.get("metadata", self.Metadata()).proto + request = MoveToPositionRequest(name=self.name, to=pose, extra=dict_to_struct(extra)) + await self.client.MoveToPosition(request, timeout=timeout, metadata=md) + + async def get_joint_positions( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> JointPositions: + md = kwargs.get("metadata", self.Metadata()).proto request = GetJointPositionsRequest(name=self.name, extra=dict_to_struct(extra)) - response: GetJointPositionsResponse = await self.client.GetJointPositions(request) + response: GetJointPositionsResponse = await self.client.GetJointPositions(request, timeout=timeout, metadata=md) return response.positions - async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def move_to_joint_positions( + self, + positions: JointPositions, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = MoveToJointPositionsRequest(name=self.name, positions=positions, extra=dict_to_struct(extra)) - await self.client.MoveToJointPositions(request) + await self.client.MoveToJointPositions(request, timeout=timeout, metadata=md) - async def stop(self, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = StopRequest(name=self.name, extra=dict_to_struct(extra)) - await self.client.Stop(request) + await self.client.Stop(request, timeout=timeout, metadata=md) + + async def is_moving(self, *, timeout: Optional[float] = None, **kwargs) -> bool: + md = kwargs.get("metadata", self.Metadata()).proto + request = IsMovingRequest(name=self.name) + response: IsMovingResponse = await self.client.IsMoving(request, timeout=timeout, metadata=md) + return response.is_moving + + async def do_command( + self, + command: Mapping[str, Any], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) + + async def get_kinematics( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Tuple[KinematicsFileFormat.ValueType, bytes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetKinematicsRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetKinematicsResponse = await self.client.GetKinematics(request, timeout=timeout, metadata=md) + return (response.format, response.kinematics_data) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/arm/service.py b/src/viam/components/arm/service.py index 2d2ee20fe..77661b8ce 100644 --- a/src/viam/components/arm/service.py +++ b/src/viam/components/arm/service.py @@ -1,25 +1,35 @@ from grpclib.server import Stream -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError + +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, + GetGeometriesRequest, + GetGeometriesResponse, + GetKinematicsRequest, + GetKinematicsResponse, +) from viam.proto.component.arm import ( - ArmServiceBase, GetEndPositionRequest, GetEndPositionResponse, GetJointPositionsRequest, GetJointPositionsResponse, + IsMovingRequest, + IsMovingResponse, MoveToJointPositionsRequest, MoveToJointPositionsResponse, MoveToPositionRequest, MoveToPositionResponse, StopRequest, StopResponse, + UnimplementedArmServiceBase, ) -from viam.utils import struct_to_dict +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict from .arm import Arm -class ArmService(ArmServiceBase, ComponentServiceBase[Arm]): +class ArmRPCService(UnimplementedArmServiceBase, ResourceRPCServiceBase[Arm]): """ gRPC Service for an Arm """ @@ -30,11 +40,9 @@ async def GetEndPosition(self, stream: Stream[GetEndPositionRequest, GetEndPosit request = await stream.recv_message() assert request is not None name = request.name - try: - arm = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - position = await arm.get_end_position(extra=struct_to_dict(request.extra)) + arm = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + position = await arm.get_end_position(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetEndPositionResponse(pose=position) await stream.send_message(response) @@ -42,11 +50,9 @@ async def MoveToPosition(self, stream: Stream[MoveToPositionRequest, MoveToPosit request = await stream.recv_message() assert request is not None name = request.name - try: - arm = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await arm.move_to_position(request.to, request.world_state, extra=struct_to_dict(request.extra)) + arm = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await arm.move_to_position(request.to, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = MoveToPositionResponse() await stream.send_message(response) @@ -54,11 +60,9 @@ async def GetJointPositions(self, stream: Stream[GetJointPositionsRequest, GetJo request = await stream.recv_message() assert request is not None name = request.name - try: - arm = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - positions = await arm.get_joint_positions(extra=struct_to_dict(request.extra)) + arm = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + positions = await arm.get_joint_positions(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetJointPositionsResponse(positions=positions) await stream.send_message(response) @@ -66,11 +70,9 @@ async def MoveToJointPositions(self, stream: Stream[MoveToJointPositionsRequest, request = await stream.recv_message() assert request is not None name = request.name - try: - arm = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await arm.move_to_joint_positions(request.positions, extra=struct_to_dict(request.extra)) + arm = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await arm.move_to_joint_positions(request.positions, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = MoveToJointPositionsResponse() await stream.send_message(response) @@ -78,10 +80,44 @@ async def Stop(self, stream: Stream[StopRequest, StopResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - arm = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await arm.stop(extra=struct_to_dict(request.extra)) + arm = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await arm.stop(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = StopResponse() await stream.send_message(response) + + async def IsMoving(self, stream: Stream[IsMovingRequest, IsMovingResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + arm = self.get_resource(name) + is_moving = await arm.is_moving() + response = IsMovingResponse(is_moving=is_moving) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + arm = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await arm.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetKinematics(self, stream: Stream[GetKinematicsRequest, GetKinematicsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + arm = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + format, kinematics_data = await arm.get_kinematics(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetKinematicsResponse(format=format, kinematics_data=kinematics_data) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + arm = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await arm.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/audio_input/__init__.py b/src/viam/components/audio_input/__init__.py new file mode 100644 index 000000000..70bfcfc97 --- /dev/null +++ b/src/viam/components/audio_input/__init__.py @@ -0,0 +1,18 @@ +from viam.resource.registry import Registry, ResourceRegistration + +from .audio_input import AudioInput +from .client import AudioInputClient +from .service import AudioInputRPCService + +__all__ = [ + "AudioInput", +] + + +Registry.register_api( + ResourceRegistration( + AudioInput, + AudioInputRPCService, + lambda name, channel: AudioInputClient(name, channel), + ) +) diff --git a/src/viam/components/audio_input/audio_input.py b/src/viam/components/audio_input/audio_input.py new file mode 100644 index 000000000..f48d31f46 --- /dev/null +++ b/src/viam/components/audio_input/audio_input.py @@ -0,0 +1,81 @@ +import abc +from dataclasses import dataclass +from datetime import timedelta +from typing import Final, Optional + +from google.protobuf.duration_pb2 import Duration +from typing_extensions import Self + +from viam.media.audio import Audio, AudioStream +from viam.proto.component.audioinput import PropertiesResponse +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT +from viam.streams import StreamSource + +from ..component_base import ComponentBase + + +class AudioInput(ComponentBase, StreamSource[Audio]): + """AudioInput represents a component that can capture audio. + + This acts as an abstract base class for any drivers representing specific + audio input implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "audio_input" + ) + + @dataclass + class Properties: + channel_count: int + latency: timedelta + sample_rate: int + sample_size: int + is_big_endian: bool + is_float: bool + is_interleaved: bool + + @property + def proto(self) -> PropertiesResponse: + latency = Duration() + latency.FromTimedelta(self.latency) + return PropertiesResponse( + channel_count=self.channel_count, + latency=latency, + sample_rate=self.sample_rate, + sample_size=self.sample_size, + is_big_endian=self.is_big_endian, + is_float=self.is_float, + is_interleaved=self.is_interleaved, + ) + + @classmethod + def from_proto(cls, proto: PropertiesResponse) -> Self: + return cls( + channel_count=proto.channel_count, + latency=proto.latency.ToTimedelta(), + sample_rate=proto.sample_rate, + sample_size=proto.sample_size, + is_big_endian=proto.is_big_endian, + is_float=proto.is_float, + is_interleaved=proto.is_interleaved, + ) + + @abc.abstractmethod + async def stream(self, *, timeout: Optional[float] = None, **kwargs) -> AudioStream: + """Stream audio samples from the audio input of the underlying robot + + Returns: + Stream[Audio]: The stream of audio chunks + """ + ... + + @abc.abstractmethod + async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> Properties: + """Get the properties of the audio input of the underlying robot + + Returns: + Properties: The audio input properties + """ + ... diff --git a/src/viam/components/audio_input/client.py b/src/viam/components/audio_input/client.py new file mode 100644 index 000000000..5dbb87db5 --- /dev/null +++ b/src/viam/components/audio_input/client.py @@ -0,0 +1,70 @@ +from typing import Any, AsyncIterator, Dict, List, Mapping, Optional, Union + +from grpclib.client import Channel + +from viam.media.audio import Audio +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry +from viam.proto.component.audioinput import ( + AudioInputServiceStub, + ChunksRequest, + ChunksResponse, + PropertiesRequest, + PropertiesResponse, + SampleFormat, +) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.streams import Stream, StreamWithIterator +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict + +from .audio_input import AudioInput + + +class AudioInputClient(AudioInput, ReconfigurableResourceRPCClientBase): + """ + gRPC client for the AudioInput component. + """ + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = AudioInputServiceStub(channel) + super().__init__(name) + + async def stream(self, *, timeout: Optional[float] = None, **kwargs) -> Stream[Audio]: + async def read() -> AsyncIterator[Audio]: + md = kwargs.get("metadata", self.Metadata()).proto + async with self.client.Chunks.open(timeout=timeout, metadata=md) as chunks_stream: + await chunks_stream.send_message( + ChunksRequest(name=self.name, sample_format=SampleFormat.SAMPLE_FORMAT_FLOAT32_INTERLEAVED), end=True + ) + response: Union[ChunksResponse, None] = await chunks_stream.recv_message() + if not response: + await chunks_stream.recv_trailing_metadata() # causes us to throw appropriate gRPC error. + raise TypeError("Response cannot be empty") # we should never get here, but for typechecking + assert response.HasField("info") + info = response.info + + while True: + response = await chunks_stream.recv_message() + if response is None: + break + assert response.HasField("chunk") + audio = Audio(info=info, chunk=response.chunk) + yield audio + + return StreamWithIterator(read()) + + async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> AudioInput.Properties: + md = kwargs.get("metadata", self.Metadata()).proto + request = PropertiesRequest(name=self.name) + response: PropertiesResponse = await self.client.Properties(request, timeout=timeout, metadata=md) + return AudioInput.Properties.from_proto(response) + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/audio_input/service.py b/src/viam/components/audio_input/service.py new file mode 100644 index 000000000..41acd2257 --- /dev/null +++ b/src/viam/components/audio_input/service.py @@ -0,0 +1,114 @@ +import wave +from datetime import timedelta +from io import BytesIO + +from google.api.httpbody_pb2 import HttpBody # type: ignore +from grpclib import GRPCError, Status +from grpclib.server import Stream + +from viam.errors import NotSupportedError +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse +from viam.proto.component.audioinput import ( + AudioInputServiceBase, + ChunksRequest, + ChunksResponse, + PropertiesRequest, + PropertiesResponse, + RecordRequest, + SampleFormat, +) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict + +from .audio_input import AudioInput + + +class AudioInputRPCService(AudioInputServiceBase, ResourceRPCServiceBase[AudioInput]): + """ + gRPC Service for a generic AudioInput + """ + + RESOURCE_TYPE = AudioInput + + async def Chunks(self, stream: Stream[ChunksRequest, ChunksResponse]) -> None: + request = await stream.recv_message() + assert request is not None + audio_input = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + audio_stream = await audio_input.stream(timeout=timeout, metadata=stream.metadata) + first_chunk = await audio_stream.__anext__() + await stream.send_message(ChunksResponse(info=first_chunk.info)) + await stream.send_message(ChunksResponse(chunk=first_chunk.chunk)) + + async for audio in audio_stream: + await stream.send_message(ChunksResponse(chunk=audio.chunk)) + + async def Properties(self, stream: Stream[PropertiesRequest, PropertiesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + audio_input = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + response = (await audio_input.get_properties(timeout=timeout, metadata=stream.metadata)).proto + await stream.send_message(response) + + async def Record(self, stream: Stream[RecordRequest, HttpBody]) -> None: # pyright: ignore [reportInvalidTypeForm] + raise NotSupportedError("Recording audio input is not supported").grpc_error + + # TODO: Eventually implement recording + request = await stream.recv_message() + assert request is not None + duration = request.duration.ToTimedelta() + if duration.total_seconds() == 0: + duration = timedelta(seconds=1) + if duration.total_seconds() > 5: + raise GRPCError(Status.INVALID_ARGUMENT, "Can only record up to 5 seconds") + + audio_input = self.get_resource(request.name) + audio_stream = await audio_input.stream() + first_chunk = await audio_stream.__anext__() + num_chunks = int(duration.total_seconds() * float(first_chunk.info.sampling_rate / first_chunk.chunk.length)) + + sample_width: int + if first_chunk.info.sample_format == SampleFormat.SAMPLE_FORMAT_INT16_INTERLEAVED: + sample_width = 2 + elif first_chunk.info.sample_format == SampleFormat.SAMPLE_FORMAT_FLOAT32_INTERLEAVED: + sample_width = 4 + else: + raise GRPCError(Status.INVALID_ARGUMENT, "Unspecified type of audio buffer") + + output = BytesIO() + wav_file = wave.open(output, "w") + wav_file.setnchannels(first_chunk.info.channels) + wav_file.setframerate(first_chunk.info.sampling_rate) + wav_file.setsampwidth(sample_width) + try: + wav_file.writeframes(first_chunk.chunk.data) + for _ in range(num_chunks - 1): + chunk = await audio_stream.__anext__() + wav_file.writeframes(chunk.chunk.data) + finally: + wav_file.close() + output.close() + + output.seek(0) + response = HttpBody(data=output.read(), content_type="audio/wav") + + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + audio_input = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await audio_input.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + audio_input = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await audio_input.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/base/__init__.py b/src/viam/components/base/__init__.py index 8bc6620be..4641c5bd8 100644 --- a/src/viam/components/base/__init__.py +++ b/src/viam/components/base/__init__.py @@ -1,18 +1,13 @@ -from viam.registry import ComponentRegistration, Registry -from viam.proto.common import ActuatorStatus -from viam.proto.robot import Status -from viam.utils import message_to_struct +from viam.proto.common import Vector3 +from viam.resource.registry import Registry, ResourceRegistration -from .base import Base, Vector3 +from .base import Base from .client import BaseClient -from .service import BaseService +from .service import BaseRPCService -__all__ = ["Base", "Vector3"] +__all__ = [ + "Base", + "Vector3", +] - -async def create_status(component: Base) -> Status: - s = ActuatorStatus(is_moving=await component.is_moving()) - return Status(name=Base.get_resource_name(component.name), status=message_to_struct(s)) - - -Registry.register(ComponentRegistration(Base, "base", BaseService, lambda name, channel: BaseClient(name, channel), create_status)) +Registry.register_api(ResourceRegistration(Base, BaseRPCService, lambda name, channel: BaseClient(name, channel))) diff --git a/src/viam/components/base/base.py b/src/viam/components/base/base.py index 89fe2e114..d867685fd 100644 --- a/src/viam/components/base/base.py +++ b/src/viam/components/base/base.py @@ -1,10 +1,11 @@ import abc -from typing import Any, Dict, Optional +from dataclasses import dataclass +from typing import Any, Dict, Final, Optional -from viam.errors import NotSupportedError -from viam.proto.common import Vector3 +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT from ..component_base import ComponentBase +from . import Vector3 class Base(ComponentBase): @@ -12,82 +13,248 @@ class Base(ComponentBase): Base represents a physical base of a robot. This acts as an abstract base class for any drivers representing specific - base implementations. This cannot be used on its own. If the `__init__()` function is - overriden, it must call the `super().__init__()` function. + base implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.base import Base + + For more information, see `Base component `_. """ + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "base" + ) + + @dataclass + class Properties: + width_meters: float + turning_radius_meters: float + wheel_circumference_meters: float + @abc.abstractmethod - async def move_straight(self, distance: int, velocity: float, extra: Optional[Dict[str, Any]] = None): + async def move_straight( + self, + distance: int, + velocity: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ - Move the base in a straight line the given `distance`, expressed in millimeters, - at the given `velocity`, expressed in millimeters per second. - When `distance` or `velocity` is 0, the base will stop. + Move the base in a straight line the given ``distance``, expressed in millimeters, + at the given ``velocity``, expressed in millimeters per second. + When ``distance`` or ``velocity`` is 0, the base will stop. This method blocks until completed or cancelled. + :: + + my_base = Base.from_robot(robot=machine, name="my_base") + + # Move the base 40 mm at a velocity of 90 mm/s, forward. + await my_base.move_straight(distance=40, velocity=90) + + # Move the base 40 mm at a velocity of -90 mm/s, backward. + await my_base.move_straight(distance=40, velocity=-90) + Args: distance (int): The distance (in millimeters) to move. Negative implies backwards. velocity (float): The velocity (in millimeters per second) to move. Negative implies backwards. + + For more information, see `Base component `_. """ ... @abc.abstractmethod - async def spin(self, angle: float, velocity: float, extra: Optional[Dict[str, Any]] = None): + async def spin( + self, + angle: float, + velocity: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ - Spin the base in place `angle` degrees, at the given angular `velocity`, + Spin the base in place ``angle`` degrees, at the given angular ``velocity``, expressed in degrees per second. - When `velocity` is 0, the base will stop. + When ``velocity`` is 0, the base will stop. This method blocks until completed or cancelled. + :: + + my_base = Base.from_robot(robot=machine, name="my_base") + + # Spin the base 10 degrees at an angular velocity of 15 deg/sec. + await my_base.spin(angle=10, velocity=15) + Args: angle (float): The angle (in degrees) to spin. - Negative implies backwards. - velocity (float): The angular velocity (in degrees per second). - to spin. Negative implies backwards. + velocity (float): The angular velocity (in degrees per second) + to spin. + Given a positive angle and a positive velocity, the base will turn to the left. + + For more information, see `Base component `_. """ ... @abc.abstractmethod - async def set_power(self, linear: Vector3, angular: Vector3, extra: Optional[Dict[str, Any]] = None): + async def set_power( + self, + linear: Vector3, + angular: Vector3, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """Set the linear and angular velocity of the Base - When `linear` is 0, the the base will spin. - When `angular` is 0, the the base will move in a straight line. - When both `linear` and `angular` are 0, the base will stop. - When `linear` and `angular` are both nonzero, the base will move in an arc, + When ``linear`` is 0, the base will spin. + When ``angular`` is 0, the base will move in a straight line. + When both ``linear`` and ``angular`` are 0, the base will stop. + When ``linear`` and ``angular`` are both nonzero, the base will move in an arc, with a tighter radius if angular power is greater than linear power. + :: + + my_base = Base.from_robot(robot=machine, name="my_base") + + # Make your wheeled base move forward. Set linear power to 75%. + print("move forward") + await my_base.set_power( + linear=Vector3(x=0, y=.75, z=0), + angular=Vector3(x=0, y=0, z=0)) + + # Make your wheeled base move backward. Set linear power to -100%. + print("move backward") + await my_base.set_power( + linear=Vector3(x=0, y=-1.0, z=0), + angular=Vector3(x=0, y=0, z=0)) + + # Make your wheeled base spin left. Set angular power to 100%. + print("spin left") + await my_base.set_power( + linear=Vector3(x=0, y=0, z=0), + angular=Vector3(x=0, y=0, z=1)) + + # Make your wheeled base spin right. Set angular power to -75%. + print("spin right") + await my_base.set_power( + linear=Vector3(x=0, y=0, z=0), + angular=Vector3(x=0, y=0, z=-.75)) + Args: linear (Vector3): The linear component. Only the Y component is used - for wheeled base. Negative implies backwards. + for wheeled base. Positive implies forwards. angular (Vector3): The angular component. Only the Z component is used for wheeled base. Positive turns left; negative turns right. + + For more information, see `Base component `_. """ ... @abc.abstractmethod - async def set_velocity(self, linear: Vector3, angular: Vector3, extra: Optional[Dict[str, Any]] = None): + async def set_velocity( + self, + linear: Vector3, + angular: Vector3, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ Set the linear and angular velocities of the base. + :: + + my_base = Base.from_robot(robot=machine, name="my_base") + + # Set the linear velocity to 50 mm/sec and the angular velocity to + # 15 degree/sec. + await my_base.set_velocity( + linear=Vector3(x=0, y=50, z=0), angular=Vector3(x=0, y=0, z=15)) Args: linear (Vector3): Velocity in mm/sec angular (Vector3): Velocity in deg/sec + + For more information, see `Base component `_. """ @abc.abstractmethod - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ Stop the base. + + :: + + my_base = Base.from_robot(robot=machine, name="my_base") + + # Move the base forward 10 mm at a velocity of 50 mm/s. + await my_base.move_straight(distance=10, velocity=50) + + # Stop the base. + await my_base.stop() + + For more information, see `Base component `_. """ ... + @abc.abstractmethod async def is_moving(self) -> bool: """ Get if the base is currently moving. + :: + + my_base = Base.from_robot(robot=machine, name="my_base") + + # Check whether the base is currently moving. + moving = await my_base.is_moving() + print('Moving: ', moving) + Returns: bool: Whether the base is moving. + + For more information, see `Base component `_. """ - raise NotSupportedError(f"Base named {self.name} does not support returning whether it is moving") + ... + + @abc.abstractmethod + async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> Properties: + """ + Get the base width and turning radius + + :: + + my_base = Base.from_robot(robot=machine, name="my_base") + + # Get the width and turning radius of the base + properties = await my_base.get_properties() + + # Get the width + print(f"Width of base: {properties.width_meters}") + + # Get the turning radius + print(f"Turning radius of base: {properties.turning_radius_meters}") + + # Get the wheel circumference + print(f"Wheel circumference of base: {properties.wheel_circumference_meters}") + + Returns: + Properties: The properties of the base + + For more information, see `Base component `_. + """ + ... diff --git a/src/viam/components/base/client.py b/src/viam/components/base/client.py index c58566a65..9a89ae561 100644 --- a/src/viam/components/base/client.py +++ b/src/viam/components/base/client.py @@ -1,22 +1,27 @@ -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Mapping, Optional from grpclib.client import Channel -from viam.components.generic.client import do_command -from viam.proto.common import Vector3 + +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry from viam.proto.component.base import ( BaseServiceStub, + GetPropertiesRequest, + GetPropertiesResponse, + IsMovingRequest, + IsMovingResponse, MoveStraightRequest, SetPowerRequest, SetVelocityRequest, SpinRequest, StopRequest, ) -from viam.utils import dict_to_struct +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict -from .base import Base +from . import Base, Vector3 -class BaseClient(Base): +class BaseClient(Base, ReconfigurableResourceRPCClientBase): """ gRPC client for the Base component. """ @@ -26,50 +31,123 @@ def __init__(self, name: str, channel: Channel): self.client = BaseServiceStub(channel) super().__init__(name) - async def move_straight(self, distance: int, velocity: float, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def move_straight( + self, + distance: int, + velocity: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = MoveStraightRequest( name=self.name, distance_mm=distance, mm_per_sec=velocity, extra=dict_to_struct(extra), ) - await self.client.MoveStraight(request) + await self.client.MoveStraight(request, timeout=timeout, metadata=md) - async def spin(self, angle: float, velocity: float, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def spin( + self, + angle: float, + velocity: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = SpinRequest( name=self.name, angle_deg=angle, degs_per_sec=velocity, extra=dict_to_struct(extra), ) - await self.client.Spin(request) + await self.client.Spin(request, timeout=timeout, metadata=md) - async def set_power(self, linear: Vector3, angular: Vector3, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def set_power( + self, + linear: Vector3, + angular: Vector3, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = SetPowerRequest( name=self.name, linear=linear, angular=angular, extra=dict_to_struct(extra), ) - await self.client.SetPower(request) + await self.client.SetPower(request, timeout=timeout, metadata=md) - async def set_velocity(self, linear: Vector3, angular: Vector3, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def set_velocity( + self, + linear: Vector3, + angular: Vector3, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = SetVelocityRequest(name=self.name, linear=linear, angular=angular, extra=dict_to_struct(extra)) - await self.client.SetVelocity(request) + await self.client.SetVelocity(request, timeout=timeout, metadata=md) - async def stop(self, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = StopRequest(name=self.name, extra=dict_to_struct(extra)) - await self.client.Stop(request) + await self.client.Stop(request, timeout=timeout, metadata=md) + + async def is_moving( + self, + *, + timeout: Optional[float] = None, + **kwargs, + ) -> bool: + md = kwargs.get("metadata", self.Metadata()).proto + request = IsMovingRequest(name=self.name) + response: IsMovingResponse = await self.client.IsMoving(request, timeout=timeout, metadata=md) + return response.is_moving + + async def get_properties( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Base.Properties: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPropertiesRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetPropertiesResponse = await self.client.GetProperties(request, timeout=timeout, metadata=md) + return Base.Properties( + width_meters=response.width_meters, + turning_radius_meters=response.turning_radius_meters, + wheel_circumference_meters=response.wheel_circumference_meters, + ) + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/base/service.py b/src/viam/components/base/service.py index 5563dba68..90563c041 100644 --- a/src/viam/components/base/service.py +++ b/src/viam/components/base/service.py @@ -1,8 +1,12 @@ from grpclib.server import Stream -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError + +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.base import ( BaseServiceBase, + GetPropertiesRequest, + GetPropertiesResponse, + IsMovingRequest, + IsMovingResponse, MoveStraightRequest, MoveStraightResponse, SetPowerRequest, @@ -14,12 +18,13 @@ StopRequest, StopResponse, ) -from viam.utils import struct_to_dict +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict from .base import Base -class BaseService(BaseServiceBase, ComponentServiceBase[Base]): +class BaseRPCService(BaseServiceBase, ResourceRPCServiceBase[Base]): """ gRPC service for a robotic Base """ @@ -30,11 +35,15 @@ async def MoveStraight(self, stream: Stream[MoveStraightRequest, MoveStraightRes request = await stream.recv_message() assert request is not None name = request.name - try: - base = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await base.move_straight(distance=request.distance_mm, velocity=request.mm_per_sec, extra=struct_to_dict(request.extra)) + base = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await base.move_straight( + distance=request.distance_mm, + velocity=request.mm_per_sec, + extra=struct_to_dict(request.extra), + timeout=timeout, + metadata=stream.metadata, + ) response = MoveStraightResponse() await stream.send_message(response) @@ -42,11 +51,15 @@ async def Spin(self, stream: Stream[SpinRequest, SpinResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - base = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await base.spin(angle=request.angle_deg, velocity=request.degs_per_sec, extra=struct_to_dict(request.extra)) + base = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await base.spin( + angle=request.angle_deg, + velocity=request.degs_per_sec, + extra=struct_to_dict(request.extra), + timeout=timeout, + metadata=stream.metadata, + ) response = SpinResponse() await stream.send_message(response) @@ -54,11 +67,11 @@ async def SetPower(self, stream: Stream[SetPowerRequest, SetPowerResponse]) -> N request = await stream.recv_message() assert request is not None name = request.name - try: - base = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await base.set_power(request.linear, request.angular, struct_to_dict(request.extra)) + base = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await base.set_power( + request.linear, request.angular, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata + ) response = SetPowerResponse() await stream.send_message(response) @@ -66,21 +79,60 @@ async def SetVelocity(self, stream: Stream[SetVelocityRequest, SetVelocityRespon request = await stream.recv_message() assert request is not None name = request.name - try: - base = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await base.set_velocity(request.linear, request.angular, struct_to_dict(request.extra)) + base = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await base.set_velocity( + request.linear, request.angular, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata + ) await stream.send_message(SetVelocityResponse()) async def Stop(self, stream: Stream[StopRequest, StopResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - base = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await base.stop(extra=struct_to_dict(request.extra)) + base = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await base.stop(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = StopResponse() await stream.send_message(response) + + async def IsMoving(self, stream: Stream[IsMovingRequest, IsMovingResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + base = self.get_resource(name) + is_moving = await base.is_moving() + response = IsMovingResponse(is_moving=is_moving) + await stream.send_message(response) + + async def GetProperties(self, stream: Stream[GetPropertiesRequest, GetPropertiesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + base = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + properties = await base.get_properties(timeout=timeout, metadata=stream.metadata) + response = GetPropertiesResponse( + width_meters=properties.width_meters, + turning_radius_meters=properties.turning_radius_meters, + wheel_circumference_meters=properties.wheel_circumference_meters, + ) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + base = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await base.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + base = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await base.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/board/__init__.py b/src/viam/components/board/__init__.py index 6f8e3d3a4..0ce67448e 100644 --- a/src/viam/components/board/__init__.py +++ b/src/viam/components/board/__init__.py @@ -1,18 +1,9 @@ -from viam.proto.robot import Status -from viam.registry import ComponentRegistration, Registry -from viam.utils import message_to_struct +from viam.resource.registry import Registry, ResourceRegistration -from .board import Board +from .board import Board, Tick, TickStream from .client import BoardClient -from .service import BoardService +from .service import BoardRPCService -__all__ = [ - "Board", -] +__all__ = ["Board", "Tick", "TickStream"] - -async def create_status(component: Board) -> Status: - return Status(name=Board.get_resource_name(component.name), status=message_to_struct(await component.status())) - - -Registry.register(ComponentRegistration(Board, "board", BoardService, lambda name, channel: BoardClient(name, channel), create_status)) +Registry.register_api(ResourceRegistration(Board, BoardRPCService, lambda name, channel: BoardClient(name, channel))) diff --git a/src/viam/components/board/board.py b/src/viam/components/board/board.py index 463f63ffa..1d8933aa0 100644 --- a/src/viam/components/board/board.py +++ b/src/viam/components/board/board.py @@ -1,244 +1,414 @@ import abc -from dataclasses import dataclass -from multiprocessing import Queue -from typing import Any, Callable, Dict, List, Optional +import sys +from datetime import timedelta +from typing import Any, Dict, Final, List, Optional -from viam.proto.common import BoardStatus +from viam.proto.component.board import PowerMode, ReadAnalogReaderResponse, StreamTicksResponse +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT +from viam.streams import Stream from ..component_base import ComponentBase +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias -PostProcessor = Callable[[int], int] +Tick = StreamTicksResponse +TickStream = Stream[Tick] class Board(ComponentBase): """ Board represents a physical general purpose compute board that contains various - components such as analog readers, and digital interrupts. + components such as analog readers/writers, and digital interrupts. This acts as an abstract base class for any drivers representing specific - board implementations. This cannot be used on its own. If the `__init__()` function is - overriden, it must call the `super().__init__()` function. + board implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.board import Board + + For more information, see `Board component `_. """ - @dataclass - class Attributes: - remote: bool + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "board" + ) + + class Analog: """ - Indicates whether this board is accessed over a remote connection, e.g. gRPC. + Analog represents an analog pin reader or writer that resides on a Board. """ - class AnalogReader(ComponentBase): + name: str + """The name of the analog pin""" + + Value: "TypeAlias" = ReadAnalogReaderResponse """ - AnalogReader represents an analog pin reader that resides on a Board. + Value contains the result of reading an analog reader. It contains the raw data read, + the reader's minimum and maximum possible values, and its step size (the minimum possible + change between values it can read). + + For more information, see `analogs `_. """ + def __init__(self, name: str): + self.name = name + @abc.abstractmethod - async def read(self, extra: Optional[Dict[str, Any]] = None) -> int: + async def read(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Value: """ - Read the current value. + Read the current value from the reader. + + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the Analog "my_example_analog_reader". + reader = await my_board.analog_by_name( + name="my_example_analog_reader") + + # Get the value of the digital signal "my_example_analog_reader" has most + # recently measured. + reading = await reader.read() Returns: - int: The current value. + Value: The current value, including the min, max, and step_size of the reader. + + For more information, see `Board component Analog API `_. """ ... - class DigitalInterrupt(ComponentBase): + @abc.abstractmethod + async def write(self, value: int, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): + """ + Write a value to the Analog writer. + + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the Analog "my_example_analog_writer". + writer = await my_board.analog_by_name( + name="my_example_analog_writer") + + await writer.write(42) + + Args: + value (int): Value to write to the analog writer. + + For more information, see `Board component Analog API `_. + """ + ... + + class DigitalInterrupt: """ DigitalInterrupt represents a configured interrupt on the Board that when interrupted, calls the added callbacks. Post processors can be added to modify what Value it ultimately returns. + + For more information, see `digital_interrupts `_. """ + name: str + """The name of the digital interrupt.""" + + def __init__(self, name: str): + self.name = name + @abc.abstractmethod - async def value(self, extra: Optional[Dict[str, Any]] = None) -> int: + async def value(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> int: """ Get the current value of the interrupt, which is based on the type of interrupt. - Returns: - int: The current value. - """ - ... + :: - @abc.abstractmethod - async def tick(self, high: bool, nanos: int): - """ - This method is to be called either manually if the interrupt - is a proxy to some real hardware interrupt or for tests. - - Args: - high (bool): If the signal of the interrupt is high. - nanos (int): Nanoseconds from an arbitrary point in time, - but always increasing and always needs to be accurate. - Using `time.time_ns()` would be acceptable. - """ - ... + my_board = Board.from_robot(robot=machine, name="my_board") - @abc.abstractmethod - async def add_callback(self, queue: Queue): - """ - Add a callback to be sent the low/high value on `tick()`. + # Get the DigitalInterrupt "my_example_digital_interrupt". + interrupt = await my_board.digital_interrupt_by_name( + name="my_example_digital_interrupt") - Args: - queue (Queue): The receiving queue. - """ - ... + # Get the amount of times this DigitalInterrupt has been interrupted with a + # tick. + count = await interrupt.value() - @abc.abstractmethod - async def add_post_processor(self, processor: PostProcessor): - """ - Add a post processor that should be used to modify what - is returned by `self.value()` + Returns: + int: The current value. - Args: - processor (PostProcessor): The post processor to add. + For more information, see + `Board component DigitalInterrupt API `_. """ ... - class GPIOPin(ComponentBase): + class GPIOPin: """ - Abstract representation of an individual GPIO pin on a board - - Args: - ComponentBase (_type_): _description_ + Abstract representation of an individual GPIO pin on a board. """ + name: str + """The name of the GPIO pin.""" + + def __init__(self, name: str): + self.name = name + @abc.abstractmethod - async def set(self, high: bool, extra: Optional[Dict[str, Any]] = None): + async def set(self, high: bool, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): """ Set the pin to either low or high. + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the GPIOPin with pin number 15. + pin = await my_board.gpio_pin_by_name(name="15") + + # Set the pin to high. + await pin.set(high=True) + Args: high (bool): When true, sets the pin to high. When false, sets the pin to low. + + For more information, see `GPIOPin API `_. """ ... @abc.abstractmethod - async def get(self, extra: Optional[Dict[str, Any]] = None) -> bool: + async def get(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> bool: """ Get the high/low state of the pin. + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the GPIOPin with pin number 15. + pin = await my_board.gpio_pin_by_name(name="15") + + # Get if it is true or false that the state of the pin is high. + high = await pin.get() + Returns: bool: Indicates if the state of the pin is high. + + For more information, see `GPIOPin API `_. """ ... @abc.abstractmethod - async def get_pwm(self, extra: Optional[Dict[str, Any]] = None) -> float: + async def get_pwm(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> float: """ Get the pin's given duty cycle. + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the GPIOPin with pin number 15. + pin = await my_board.gpio_pin_by_name(name="15") + + # Get if it is true or false that the state of the pin is high. + duty_cycle = await pin.get_pwm() + Returns: float: The duty cycle. + + For more information, see `GPIOPin API `_. """ ... @abc.abstractmethod - async def set_pwm(self, duty_cycle: float, extra: Optional[Dict[str, Any]] = None): + async def set_pwm(self, duty_cycle: float, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): """ - Set the pin to the given `duty_cycle`. + Set the pin to the given ``duty_cycle``. + + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the GPIOPin with pin number 15. + pin = await my_board.gpio_pin_by_name(name="15") + + # Set the duty cycle to .6, meaning that this pin will be in the high state for + # 60% of the duration of the PWM interval period. + await pin.set_pwm(duty_cycle=.6) Args: duty_cycle (float): The duty cycle. + + For more information, see `GPIOPin API `_. """ ... @abc.abstractmethod - async def get_pwm_frequency(self, extra: Optional[Dict[str, Any]] = None) -> int: + async def get_pwm_frequency(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> int: """ Get the PWM frequency of the pin. + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the GPIOPin with pin number 15. + pin = await my_board.gpio_pin_by_name(name="15") + + # Get the PWM frequency of this pin. + freq = await pin.get_pwm_frequency() + Returns: int: The PWM frequency. + + For more information, see `GPIOPin API `_. """ ... @abc.abstractmethod - async def set_pwm_frequency(self, frequency: int, extra: Optional[Dict[str, Any]] = None): + async def set_pwm_frequency( + self, + frequency: int, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ - Set the pin to the given PWM `frequency` (in Hz). - When `frequency` is 0, it will use the board's default PWM frequency. + Set the pin to the given PWM ``frequency`` (in Hz). + When ``frequency`` is 0, it will use the board's default PWM frequency. + + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the GPIOPin with pin number 15. + pin = await my_board.gpio_pin_by_name(name="15") + + # Set the PWM frequency of this pin to 1600 Hz. + high = await pin.set_pwm_frequency(frequency=1600) Args: frequency (int): The frequency, in Hz. + + For more information, see `GPIOPin API `_. """ ... @abc.abstractmethod - async def analog_reader_by_name(self, name: str) -> AnalogReader: + async def analog_by_name(self, name: str) -> Analog: """ - Get an AnalogReader by `name`. + Get an Analog (reader or writer) by ``name``. + + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the Analog "my_example_analog_reader". + reader = await my_board.analog_by_name(name="my_example_analog_reader") Args: name (str): Name of the analog reader to be retrieved. Returns: - AnalogReader: The analog reader. + Analog: The analog reader or writer. + + For more information, see `Board component `_. """ ... @abc.abstractmethod async def digital_interrupt_by_name(self, name: str) -> DigitalInterrupt: """ - Get a DigitalInterrupt by `name`. + Get a DigitalInterrupt by ``name``. + + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the DigitalInterrupt "my_example_digital_interrupt". + interrupt = await my_board.digital_interrupt_by_name( + name="my_example_digital_interrupt") Args: name (str): Name of the digital interrupt. Returns: - DigitalInterrupt: the digital interrupt. + DigitalInterrupt: The digital interrupt. + + For more information, see `Board component `_. """ ... @abc.abstractmethod async def gpio_pin_by_name(self, name: str) -> GPIOPin: """ - Get a GPIO Pin by `name`. + Get a GPIO Pin by ``name``. + + :: + + my_board = Board.from_robot(robot=machine, name="my_board") + + # Get the GPIOPin with pin number 15. + pin = await my_board.gpio_pin_by_name(name="15") Args: name (str): Name of the GPIO pin. Returns: - GPIOPin: the pin. + GPIOPin: The pin. + + For more information, see `Board component `_. """ ... @abc.abstractmethod - async def analog_reader_names(self) -> List[str]: + async def set_power_mode( + self, mode: PowerMode.ValueType, duration: Optional[timedelta] = None, *, timeout: Optional[float] = None, **kwargs + ): """ - Get the names of all known analog readers. + Set the board to the indicated power mode. - Returns: - List[str]: The names of the analog readers.. - """ - ... + :: - @abc.abstractmethod - async def digital_interrupt_names(self) -> List[str]: - """ - Get the names of all known digital interrupts. + my_board = Board.from_robot(robot=machine, name="my_board") - Returns: - List[str]: The names of the digital interrupts. + # Set the power mode of the board to OFFLINE_DEEP. + status = await my_board.set_power_mode(mode=PowerMode.POWER_MODE_OFFLINE_DEEP) + + Args: + mode (PowerMode): The desired power mode. + duration (Optional[timedelta]): Requested duration to stay in power mode. + + For more information, see `Board component `_. """ ... @abc.abstractmethod - async def status(self, extra: Optional[Dict[str, Any]] = None) -> BoardStatus: + async def stream_ticks(self, interrupts: List[DigitalInterrupt], *, timeout: Optional[float] = None, **kwargs) -> TickStream: """ - Return the current status of the board. + Stream digital interrupt ticks. - Returns: - BoardStatus: the status. - """ + :: + + + my_board = Board.from_robot(robot=machine, name="my_board") + di8 = await my_board.digital_interrupt_by_name(name="8") + di11 = await my_board.digital_interrupt_by_name(name="11") + + # Iterate over stream of ticks from pins 8 and 11. + async for tick in await my_board.stream_ticks([di8, di11]): + print(f"Pin {tick.pin_name} changed to {'high' if tick.high else 'low'} at {tick.time}") - @abc.abstractmethod - async def model_attributes(self) -> Attributes: - """ - Get the attributes related to the model of this board. + + Args: + interrupts (List[DigitalInterrupt]) : list of digital interrupts to receive ticks from. Returns: - Attributes: The attributes. + TickStream: stream of ticks. + + For more information, see `Board component `_. """ ... diff --git a/src/viam/components/board/client.py b/src/viam/components/board/client.py index c99d42234..5377225da 100644 --- a/src/viam/components/board/client.py +++ b/src/viam/components/board/client.py @@ -1,43 +1,65 @@ -from multiprocessing import Queue -from typing import Any, Dict, List, Optional +from datetime import timedelta +from typing import Any, Dict, List, Mapping, Optional +from google.protobuf.duration_pb2 import Duration from grpclib.client import Channel -from viam.components.generic.client import do_command -from viam.proto.common import BoardStatus +from grpclib.client import Stream as ClientStream + +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry from viam.proto.component.board import ( BoardServiceStub, GetDigitalInterruptValueRequest, GetDigitalInterruptValueResponse, GetGPIORequest, GetGPIOResponse, + PowerMode, PWMFrequencyRequest, PWMFrequencyResponse, PWMRequest, PWMResponse, ReadAnalogReaderRequest, - ReadAnalogReaderResponse, SetGPIORequest, + SetPowerModeRequest, SetPWMFrequencyRequest, SetPWMRequest, - StatusRequest, - StatusResponse, + StreamTicksRequest, + StreamTicksResponse, + WriteAnalogRequest, ) -from viam.utils import dict_to_struct +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase, ResourceRPCClientBase +from viam.streams import StreamWithIterator +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict -from .board import Board, PostProcessor +from .board import Board, TickStream -class AnalogReaderClient(Board.AnalogReader): +class AnalogClient(Board.Analog): def __init__(self, name: str, board: "BoardClient"): self.board = board super().__init__(name) - async def read(self, extra: Optional[Dict[str, Any]] = None) -> int: - if extra is None: - extra = {} + async def read( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Board.Analog.Value: + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto request = ReadAnalogReaderRequest(board_name=self.board.name, analog_reader_name=self.name, extra=dict_to_struct(extra)) - response: ReadAnalogReaderResponse = await self.board.client.ReadAnalogReader(request) - return response.value + return await self.board.client.ReadAnalogReader(request, timeout=timeout, metadata=md) + + async def write( + self, + value: int, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto + request = WriteAnalogRequest(name=self.board.name, pin=self.name, value=value, extra=dict_to_struct(extra)) + await self.board.client.WriteAnalog(request, timeout=timeout, metadata=md) class DigitalInterruptClient(Board.DigitalInterrupt): @@ -45,112 +67,175 @@ def __init__(self, name: str, board: "BoardClient"): self.board = board super().__init__(name) - async def value(self, extra: Optional[Dict[str, Any]] = None) -> int: - if extra is None: - extra = {} + async def value( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> int: + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto request = GetDigitalInterruptValueRequest(board_name=self.board.name, digital_interrupt_name=self.name, extra=dict_to_struct(extra)) - response: GetDigitalInterruptValueResponse = await self.board.client.GetDigitalInterruptValue(request) + response: GetDigitalInterruptValueResponse = await self.board.client.GetDigitalInterruptValue(request, timeout=timeout, metadata=md) return response.value - async def tick(self, high: bool, nanos: int): - raise NotImplementedError() - - async def add_callback(self, queue: Queue): - raise NotImplementedError() - - async def add_post_processor(self, processor: PostProcessor): - raise NotImplementedError() - class GPIOPinClient(Board.GPIOPin): def __init__(self, name: str, board: "BoardClient"): self.board = board super().__init__(name) - async def get(self, extra: Optional[Dict[str, Any]] = None) -> bool: - if extra is None: - extra = {} + async def get( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> bool: + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto request = GetGPIORequest(name=self.board.name, pin=self.name, extra=dict_to_struct(extra)) - response: GetGPIOResponse = await self.board.client.GetGPIO(request) + response: GetGPIOResponse = await self.board.client.GetGPIO(request, timeout=timeout, metadata=md) return response.high - async def set(self, high: bool, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def set( + self, + high: bool, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto request = SetGPIORequest(name=self.board.name, pin=self.name, high=high, extra=dict_to_struct(extra)) - await self.board.client.SetGPIO(request) - - async def get_pwm(self, extra: Optional[Dict[str, Any]] = None) -> float: - if extra is None: - extra = {} + await self.board.client.SetGPIO(request, timeout=timeout, metadata=md) + + async def get_pwm( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> float: + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto request = PWMRequest(name=self.board.name, pin=self.name, extra=dict_to_struct(extra)) - response: PWMResponse = await self.board.client.PWM(request) + response: PWMResponse = await self.board.client.PWM(request, timeout=timeout, metadata=md) return response.duty_cycle_pct - async def set_pwm(self, duty_cycle: float, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def set_pwm( + self, + duty_cycle: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto request = SetPWMRequest(name=self.board.name, pin=self.name, duty_cycle_pct=duty_cycle, extra=dict_to_struct(extra)) - await self.board.client.SetPWM(request) - - async def get_pwm_frequency(self, extra: Optional[Dict[str, Any]] = None) -> int: - if extra is None: - extra = {} + await self.board.client.SetPWM(request, timeout=timeout, metadata=md) + + async def get_pwm_frequency( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> int: + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto request = PWMFrequencyRequest(name=self.board.name, pin=self.name, extra=dict_to_struct(extra)) - response: PWMFrequencyResponse = await self.board.client.PWMFrequency(request) + response: PWMFrequencyResponse = await self.board.client.PWMFrequency(request, timeout=timeout, metadata=md) return response.frequency_hz - async def set_pwm_frequency(self, frequency: int, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def set_pwm_frequency( + self, + frequency: int, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto request = SetPWMFrequencyRequest(name=self.board.name, pin=self.name, frequency_hz=frequency, extra=dict_to_struct(extra)) - await self.board.client.SetPWMFrequency(request) + await self.board.client.SetPWMFrequency(request, timeout=timeout, metadata=md) -class BoardClient(Board): +class BoardClient(Board, ReconfigurableResourceRPCClientBase): """ gRPC client for the Board component. """ + _analog_names: List[str] + _digital_interrupt_names: List[str] + def __init__(self, name: str, channel: Channel): self.channel = channel self.client = BoardServiceStub(channel) - self._analog_reader_names: Optional[List[str]] = None - self._digital_interrupt_names: Optional[List[str]] = None + self._analog_names = [] + self._digital_interrupt_names = [] super().__init__(name) - async def analog_reader_by_name(self, name: str) -> Board.AnalogReader: - return AnalogReaderClient(name, self) + async def analog_by_name(self, name: str) -> Board.Analog: + self._analog_names.append(name) + return AnalogClient(name, self) async def digital_interrupt_by_name(self, name: str) -> Board.DigitalInterrupt: + self._digital_interrupt_names.append(name) return DigitalInterruptClient(name, self) async def gpio_pin_by_name(self, name: str) -> Board.GPIOPin: return GPIOPinClient(name, self) - async def analog_reader_names(self) -> List[str]: - if self._analog_reader_names is None: - status = await self.status() - names = [name for name in status.analogs.keys()] - self._analog_reader_names = names - return self._analog_reader_names - - async def digital_interrupt_names(self) -> List[str]: - if self._digital_interrupt_names is None: - status = await self.status() - names = [name for name in status.digital_interrupts.keys()] - self._digital_interrupt_names = names - return self._digital_interrupt_names - - async def status(self, extra: Optional[Dict[str, Any]] = None) -> BoardStatus: - if extra is None: - extra = {} - request = StatusRequest(name=self.name, extra=dict_to_struct(extra)) - response: StatusResponse = await self.client.Status(request) - return response.status - - async def model_attributes(self) -> Board.Attributes: - return Board.Attributes(remote=True) - - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) + + async def set_power_mode( + self, + mode: PowerMode.ValueType, + duration: Optional[timedelta] = None, + *, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto + duration_pb: Optional[Duration] = None + if duration is not None: + duration_pb = [(d, d.FromTimedelta(duration)) for d in [Duration()]][0][0] + request = SetPowerModeRequest(name=self.name, power_mode=mode, duration=duration_pb) + await self.client.SetPowerMode(request, timeout=timeout, metadata=md) + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) + + async def stream_ticks( + self, + interrupts: List[Board.DigitalInterrupt], + *, + extra: Optional[Dict[str, Any]] = None, + **kwargs, + ) -> TickStream: + names = [] + for di in interrupts: + names.append(di.name) + request = StreamTicksRequest(name=self.name, pin_names=names, extra=dict_to_struct(extra)) + + async def read(): + md = kwargs.get("metadata", self.Metadata()).proto + tick_stream: ClientStream[StreamTicksRequest, StreamTicksResponse] + async with self.client.StreamTicks.open(metadata=md) as tick_stream: + try: + await tick_stream.send_message(request, end=True) + async for tick in tick_stream: + yield tick + except Exception as e: + raise (e) + + return StreamWithIterator(read()) diff --git a/src/viam/components/board/service.py b/src/viam/components/board/service.py index f6cffbd31..dbc837d80 100644 --- a/src/viam/components/board/service.py +++ b/src/viam/components/board/service.py @@ -1,6 +1,9 @@ from grpclib.server import Stream -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError +from h2.exceptions import StreamClosedError + +from viam.errors import ResourceNotFoundError +from viam.logging import getLogger +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.board import ( BoardServiceBase, GetDigitalInterruptValueRequest, @@ -15,47 +18,43 @@ ReadAnalogReaderResponse, SetGPIORequest, SetGPIOResponse, + SetPowerModeRequest, + SetPowerModeResponse, SetPWMFrequencyRequest, SetPWMFrequencyResponse, SetPWMRequest, SetPWMResponse, - StatusRequest, - StatusResponse, + StreamTicksRequest, + StreamTicksResponse, + WriteAnalogRequest, + WriteAnalogResponse, ) -from viam.utils import struct_to_dict +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict from .board import Board +LOGGER = getLogger(__name__) + -class BoardService(BoardServiceBase, ComponentServiceBase[Board]): +class BoardRPCService(BoardServiceBase, ResourceRPCServiceBase[Board]): """ gRPC Service for a Board """ RESOURCE_TYPE = Board - async def Status(self, stream: Stream[StatusRequest, StatusResponse]) -> None: - request = await stream.recv_message() - assert request is not None - name = request.name - try: - board = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - status = await board.status(extra=struct_to_dict(request.extra)) - response = StatusResponse(status=status) - await stream.send_message(response) - async def SetGPIO(self, stream: Stream[SetGPIORequest, SetGPIOResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name + board = self.get_resource(name) try: - board = self.get_component(name) pin = await board.gpio_pin_by_name(request.pin) - except ComponentNotFoundError as e: + except ResourceNotFoundError as e: raise e.grpc_error - await pin.set(request.high, extra=struct_to_dict(request.extra)) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await pin.set(request.high, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = SetGPIOResponse() await stream.send_message(response) @@ -63,12 +62,13 @@ async def GetGPIO(self, stream: Stream[GetGPIORequest, GetGPIOResponse]) -> None request = await stream.recv_message() assert request is not None name = request.name + board = self.get_resource(name) try: - board = self.get_component(name) pin = await board.gpio_pin_by_name(request.pin) - except ComponentNotFoundError as e: + except ResourceNotFoundError as e: raise e.grpc_error - high = await pin.get(extra=struct_to_dict(request.extra)) + timeout = stream.deadline.time_remaining() if stream.deadline else None + high = await pin.get(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetGPIOResponse(high=high) await stream.send_message(response) @@ -76,12 +76,13 @@ async def PWM(self, stream: Stream[PWMRequest, PWMResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name + board = self.get_resource(name) try: - board = self.get_component(name) pin = await board.gpio_pin_by_name(request.pin) - except ComponentNotFoundError as e: + except ResourceNotFoundError as e: raise e.grpc_error - pwm = await pin.get_pwm(extra=struct_to_dict(request.extra)) + timeout = stream.deadline.time_remaining() if stream.deadline else None + pwm = await pin.get_pwm(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = PWMResponse(duty_cycle_pct=pwm) await stream.send_message(response) @@ -89,12 +90,13 @@ async def SetPWM(self, stream: Stream[SetPWMRequest, SetPWMResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name + board = self.get_resource(name) try: - board = self.get_component(name) pin = await board.gpio_pin_by_name(request.pin) - except ComponentNotFoundError as e: + except ResourceNotFoundError as e: raise e.grpc_error - await pin.set_pwm(request.duty_cycle_pct, extra=struct_to_dict(request.extra)) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await pin.set_pwm(request.duty_cycle_pct, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = SetPWMResponse() await stream.send_message(response) @@ -102,12 +104,13 @@ async def PWMFrequency(self, stream: Stream[PWMFrequencyRequest, PWMFrequencyRes request = await stream.recv_message() assert request is not None name = request.name + board = self.get_resource(name) try: - board = self.get_component(name) pin = await board.gpio_pin_by_name(request.pin) - except ComponentNotFoundError as e: + except ResourceNotFoundError as e: raise e.grpc_error - frequency = await pin.get_pwm_frequency(extra=struct_to_dict(request.extra)) + timeout = stream.deadline.time_remaining() if stream.deadline else None + frequency = await pin.get_pwm_frequency(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = PWMFrequencyResponse(frequency_hz=frequency) await stream.send_message(response) @@ -115,12 +118,13 @@ async def SetPWMFrequency(self, stream: Stream[SetPWMFrequencyRequest, SetPWMFre request = await stream.recv_message() assert request is not None name = request.name + board = self.get_resource(name) try: - board = self.get_component(name) pin = await board.gpio_pin_by_name(request.pin) - except ComponentNotFoundError as e: + except ResourceNotFoundError as e: raise e.grpc_error - await pin.set_pwm_frequency(request.frequency_hz, extra=struct_to_dict(request.extra)) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await pin.set_pwm_frequency(request.frequency_hz, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = SetPWMFrequencyResponse() await stream.send_message(response) @@ -128,24 +132,92 @@ async def ReadAnalogReader(self, stream: Stream[ReadAnalogReaderRequest, ReadAna request = await stream.recv_message() assert request is not None name = request.board_name + board = self.get_resource(name) try: - board = self.get_component(name) - analog_reader = await board.analog_reader_by_name(request.analog_reader_name) - except ComponentNotFoundError as e: + analog_reader = await board.analog_by_name(request.analog_reader_name) + except ResourceNotFoundError as e: raise e.grpc_error - value = await analog_reader.read(extra=struct_to_dict(request.extra)) - response = ReadAnalogReaderResponse(value=value) + timeout = stream.deadline.time_remaining() if stream.deadline else None + response = await analog_reader.read(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) await stream.send_message(response) async def GetDigitalInterruptValue(self, stream: Stream[GetDigitalInterruptValueRequest, GetDigitalInterruptValueResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.board_name + board = self.get_resource(name) try: - board = self.get_component(name) interrupt = await board.digital_interrupt_by_name(request.digital_interrupt_name) - except ComponentNotFoundError as e: + except ResourceNotFoundError as e: raise e.grpc_error - value = await interrupt.value(extra=struct_to_dict(request.extra)) + timeout = stream.deadline.time_remaining() if stream.deadline else None + value = await interrupt.value(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetDigitalInterruptValueResponse(value=value) await stream.send_message(response) + + async def SetPowerMode(self, stream: Stream[SetPowerModeRequest, SetPowerModeResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + board = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await board.set_power_mode( + mode=request.power_mode, + duration=request.duration.ToTimedelta(), + timeout=timeout, + metadata=stream.metadata, + ) + response = SetPowerModeResponse() + await stream.send_message(response) + + async def WriteAnalog(self, stream: Stream[WriteAnalogRequest, WriteAnalogResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + board = self.get_resource(name) + try: + analog_writer = await board.analog_by_name(request.pin) + except ResourceNotFoundError as e: + raise e.grpc_error + timeout = stream.deadline.time_remaining() if stream.deadline else None + await analog_writer.write(value=request.value, timeout=timeout, metadata=stream.metadata, extra=struct_to_dict(request.extra)) + response = WriteAnalogResponse() + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + board = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await board.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + arm = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await arm.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) + + async def StreamTicks(self, stream: Stream[StreamTicksRequest, StreamTicksResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + board = self.get_resource(name) + + dis = [] + for name in request.pin_names: + dis.append(await board.digital_interrupt_by_name(name)) + + tick_stream = await board.stream_ticks(interrupts=dis, metadata=stream.metadata) + async for tick in tick_stream: + try: + await stream.send_message(tick) + except StreamClosedError: + return + except Exception as e: + LOGGER.error(e) + return diff --git a/src/viam/components/button/__init__.py b/src/viam/components/button/__init__.py new file mode 100644 index 000000000..066ef4f94 --- /dev/null +++ b/src/viam/components/button/__init__.py @@ -0,0 +1,10 @@ +import viam.gen.component.button.v1.button_pb2 +from viam.resource.registry import Registry, ResourceRegistration + +from .button import Button +from .client import ButtonClient +from .service import ButtonRPCService + +__all__ = ["Button"] + +Registry.register_api(ResourceRegistration(Button, ButtonRPCService, lambda name, channel: ButtonClient(name, channel))) diff --git a/src/viam/components/button/button.py b/src/viam/components/button/button.py new file mode 100644 index 000000000..42e5ca2a6 --- /dev/null +++ b/src/viam/components/button/button.py @@ -0,0 +1,41 @@ +import abc +from typing import Any, Final, Mapping, Optional + +from viam.components.component_base import ComponentBase +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT + + +class Button(ComponentBase): + """ + Button represents a device that can be pushed. + + This acts as an abstract base class for any drivers representing specific + button implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.button import Button + + For more information, see `Button component ` _. + """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "button" + ) + + @abc.abstractmethod + async def push(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> None: + """ + Push the button. + + :: + + my_button = Button.from_robot(robot=machine, name="my_button") + + # Push the button + await my_button.push() + + For more information, see `Button component ` _. + """ + ... diff --git a/src/viam/components/button/client.py b/src/viam/components/button/client.py new file mode 100644 index 000000000..e342e7435 --- /dev/null +++ b/src/viam/components/button/client.py @@ -0,0 +1,52 @@ +from typing import Any, Mapping, Optional + +from grpclib.client import Channel + +from viam.gen.component.button.v1.button_pb2 import PushRequest +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, +) +from viam.proto.component.button import ButtonServiceStub +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ( + ValueTypes, + dict_to_struct, + struct_to_dict, +) + +from .button import Button + + +class ButtonClient(Button, ReconfigurableResourceRPCClientBase): + """ + gRPC client for Button component + """ + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = ButtonServiceStub(channel) + super().__init__(name) + + async def push( + self, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> None: + md = kwargs.get("metadata", self.Metadata()).proto + request = PushRequest(name=self.name, extra=dict_to_struct(extra)) + await self.client.Push(request, timeout=timeout, metadata=md) + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) diff --git a/src/viam/components/button/service.py b/src/viam/components/button/service.py new file mode 100644 index 000000000..88e9821de --- /dev/null +++ b/src/viam/components/button/service.py @@ -0,0 +1,46 @@ +from grpclib.server import Stream + +from viam.gen.component.button.v1.button_pb2 import ( + PushRequest, + PushResponse, +) +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, +) +from viam.proto.component.button import ButtonServiceBase +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict + +from .button import Button + + +class ButtonRPCService(ButtonServiceBase, ResourceRPCServiceBase[Button]): + """ + gRPC Service for a generic Button + """ + + RESOURCE_TYPE = Button + + async def Push(self, stream: Stream[PushRequest, PushResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + button = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await button.push(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + await stream.send_message(PushResponse()) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + button = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await button.do_command( + command=struct_to_dict(request.command), + timeout=timeout, + metadata=stream.metadata, + ) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) diff --git a/src/viam/components/camera/__init__.py b/src/viam/components/camera/__init__.py index e1d0ed01d..578ddf262 100644 --- a/src/viam/components/camera/__init__.py +++ b/src/viam/components/camera/__init__.py @@ -1,20 +1,22 @@ -from viam.registry import ComponentRegistration, Registry +from viam.media.video import ViamImage +from viam.proto.component.camera import DistortionParameters, IntrinsicParameters +from viam.resource.registry import Registry, ResourceRegistration -from .camera import Camera, IntrinsicParameters, DistortionParameters +from .camera import Camera from .client import CameraClient -from .service import CameraService +from .service import CameraRPCService __all__ = [ "Camera", "IntrinsicParameters", "DistortionParameters", + "ViamImage", ] -Registry.register( - ComponentRegistration( +Registry.register_api( + ResourceRegistration( Camera, - "camera", - CameraService, + CameraRPCService, lambda name, channel: CameraClient(name, channel), ) ) diff --git a/src/viam/components/camera/camera.py b/src/viam/components/camera/camera.py index 6b83194c2..9faeae9d5 100644 --- a/src/viam/components/camera/camera.py +++ b/src/viam/components/camera/camera.py @@ -1,49 +1,92 @@ import abc -from typing import NamedTuple, Tuple, Union +import sys +from typing import Any, Dict, Final, List, Optional, Tuple -from PIL.Image import Image -from viam.components.types import CameraMimeType, RawImage -from viam.proto.component.camera import IntrinsicParameters, DistortionParameters +from viam.media.video import NamedImage, ViamImage +from viam.proto.common import ResponseMetadata +from viam.proto.component.camera import GetPropertiesResponse +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT from ..component_base import ComponentBase +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + class Camera(ComponentBase): """ Camera represents any physical hardware that can capture frames. This acts as an abstract base class for any drivers representing specific - camera implementations. This cannot be used on its own. If the `__init__()` function is - overridden, it must call the `super().__init__()` function. - """ + camera implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. - class Properties(NamedTuple): - """The camera's supported features and settings""" + :: - supports_pcd: bool - """Whether the camera has a valid implementation of `get_point_cloud`""" + from viam.components.camera import Camera + + For more information, see `Camera component `_. + """ - intrinsic_parameters: IntrinsicParameters - """The properties of the camera""" + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "camera" + ) - distortion_parameters: DistortionParameters - """The distortion parameters of the camera""" + Properties: "TypeAlias" = GetPropertiesResponse @abc.abstractmethod - async def get_image(self, mime_type: str = CameraMimeType.PNG) -> Union[Image, RawImage]: - """Get the next image from the camera as an Image or RawImage. + async def get_image( + self, mime_type: str = "", *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> ViamImage: + """Get the next image from the camera as a ViamImage. Be sure to close the image when finished. + NOTE: If the mime type is ``image/vnd.viam.dep`` you can use :func:`viam.media.video.ViamImage.bytes_to_depth_array` + to convert the data to a standard representation. + + :: + + my_camera = Camera.from_robot(machine, "my_camera") + frame = await my_camera.get_image() + print(f"Frame: {frame}") + Args: mime_type (str): The desired mime type of the image. This does not guarantee output type Returns: - Image | RawImage: The frame + ViamImage: The frame. + + For more information, see `Camera component `_. """ ... @abc.abstractmethod - async def get_point_cloud(self) -> Tuple[bytes, str]: + async def get_images(self, *, timeout: Optional[float] = None, **kwargs) -> Tuple[List[NamedImage], ResponseMetadata]: + """Get simultaneous images from different imagers, along with associated metadata. + This should not be used for getting a time series of images from the same imager. + + :: + + my_camera = Camera.from_robot(robot=machine, name="my_camera") + + images, metadata = await my_camera.get_images() + first_image = images[0] + timestamp = metadata.captured_at + + Returns: + Tuple[List[NamedImage], ResponseMetadata]: A tuple containing two values; the first [0] a list of images + returned from the camera system, and the second [1] the metadata associated with this response. + + For more information, see `Camera component `_. + """ + ... + + @abc.abstractmethod + async def get_point_cloud( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Tuple[bytes, str]: """ Get the next point cloud from the camera. This will be returned as bytes with a mimetype describing @@ -51,18 +94,45 @@ async def get_point_cloud(self) -> Tuple[bytes, str]: should encode the bytes into the formatted suggested by the mimetype. + To deserialize the returned information into a numpy array, use the Open3D library. + + :: + + import numpy as np + import open3d as o3d + + my_camera = Camera.from_robot(robot=machine, name="my_camera") + + data, _ = await my_camera.get_point_cloud() + + # write the point cloud into a temporary file + with open("/tmp/pointcloud_data.pcd", "wb") as f: + f.write(data) + pcd = o3d.io.read_point_cloud("/tmp/pointcloud_data.pcd") + points = np.asarray(pcd.points) + Returns: - bytes: The pointcloud data. - str: The mimetype of the pointcloud (e.g. PCD). + Tuple[bytes, str]: A tuple containing two values; the first [0] the pointcloud data, + and the second [1] the mimetype of the pointcloud (for example, PCD). + + For more information, see `Camera component `_. """ ... @abc.abstractmethod - async def get_properties(self) -> Properties: + async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> Properties: """ Get the camera intrinsic parameters and camera distortion parameters + :: + + my_camera = Camera.from_robot(robot=machine, name="my_camera") + + properties = await my_camera.get_properties() + Returns: - Properties: The properties of the camera + Properties: The properties of the camera. + + For more information, see `Camera component `_. """ ... diff --git a/src/viam/components/camera/client.py b/src/viam/components/camera/client.py index 2678f8473..45feacf32 100644 --- a/src/viam/components/camera/client.py +++ b/src/viam/components/camera/client.py @@ -1,25 +1,26 @@ -from io import BytesIO -from typing import Any, Dict, Tuple, Union +from typing import Any, Dict, List, Mapping, Optional, Tuple from grpclib.client import Channel -from PIL import Image -from viam.components.generic.client import do_command -from viam.components.types import CameraMimeType, RawImage +from viam.media.video import CameraMimeType, NamedImage, ViamImage +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, ResponseMetadata from viam.proto.component.camera import ( CameraServiceStub, GetImageRequest, GetImageResponse, + GetImagesRequest, + GetImagesResponse, GetPointCloudRequest, GetPointCloudResponse, GetPropertiesRequest, - GetPropertiesResponse, ) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict -from .camera import Camera +from . import Camera -class CameraClient(Camera): +class CameraClient(Camera, ReconfigurableResourceRPCClientBase): """ gRPC client for the Camera component """ @@ -29,28 +30,69 @@ def __init__(self, name: str, channel: Channel): self.client = CameraServiceStub(channel) super().__init__(name) - async def get_image(self, mime_type: str = CameraMimeType.PNG) -> Union[Image.Image, RawImage]: - request = GetImageRequest(name=self.name, mime_type=mime_type) - response: GetImageResponse = await self.client.GetImage(request) - try: - mimetype = CameraMimeType(response.mime_type) - if mimetype == CameraMimeType.RAW: - raise ValueError("Raw mime type should go return a RawImage") - except ValueError: - # If the mime type is raw or unknown, return a RawImage - image = RawImage(response.image, response.mime_type, response.width_px, response.height_px) - return image - - return Image.open(BytesIO(response.image), formats=["JPEG", "PNG"]) - - async def get_point_cloud(self) -> Tuple[bytes, str]: - request = GetPointCloudRequest(name=self.name, mime_type=CameraMimeType.PCD) - response: GetPointCloudResponse = await self.client.GetPointCloud(request) + async def get_image( + self, + mime_type: str = "", + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> ViamImage: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetImageRequest(name=self.name, mime_type=mime_type, extra=dict_to_struct(extra)) + response: GetImageResponse = await self.client.GetImage(request, timeout=timeout, metadata=md) + return ViamImage(response.image, CameraMimeType.from_string(response.mime_type)) + + async def get_images( + self, + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[List[NamedImage], ResponseMetadata]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetImagesRequest(name=self.name) + response: GetImagesResponse = await self.client.GetImages(request, timeout=timeout, metadata=md) + imgs = [] + for img_data in response.images: + mime_type = CameraMimeType.from_proto(img_data.format) + img = NamedImage(img_data.source_name, img_data.image, mime_type) + imgs.append(img) + resp_metadata: ResponseMetadata = response.response_metadata + return imgs, resp_metadata + + async def get_point_cloud( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[bytes, str]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPointCloudRequest(name=self.name, mime_type=CameraMimeType.PCD, extra=dict_to_struct(extra)) + response: GetPointCloudResponse = await self.client.GetPointCloud(request, timeout=timeout, metadata=md) return (response.point_cloud, response.mime_type) - async def get_properties(self) -> Camera.Properties: - response: GetPropertiesResponse = await self.client.GetProperties(GetPropertiesRequest(name=self.name)) - return Camera.Properties(response.supports_pcd, response.intrinsic_parameters, response.distortion_parameters) + async def get_properties( + self, + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Camera.Properties: + md = kwargs.get("metadata", self.Metadata()).proto + return await self.client.GetProperties(GetPropertiesRequest(name=self.name), timeout=timeout, metadata=md) + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/camera/service.py b/src/viam/components/camera/service.py index 67baac4e5..936271491 100644 --- a/src/viam/components/camera/service.py +++ b/src/viam/components/camera/service.py @@ -1,24 +1,29 @@ -from google.api.httpbody_pb2 import HttpBody +# TODO: Update type checking based with RSDK-4089 +# pyright: reportGeneralTypeIssues=false +from google.api.httpbody_pb2 import HttpBody # type: ignore from grpclib.server import Stream -from viam.components.service_base import ComponentServiceBase -from viam.components.types import CameraMimeType, RawImage -from viam.errors import ComponentNotFoundError +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.camera import ( CameraServiceBase, GetImageRequest, GetImageResponse, + GetImagesRequest, + GetImagesResponse, GetPointCloudRequest, GetPointCloudResponse, GetPropertiesRequest, GetPropertiesResponse, + Image, RenderFrameRequest, ) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict -from .camera import Camera +from . import Camera -class CameraService(CameraServiceBase, ComponentServiceBase[Camera]): +class CameraRPCService(CameraServiceBase, ResourceRPCServiceBase[Camera]): """ gRPC Service for a generic Camera """ @@ -29,56 +34,46 @@ async def GetImage(self, stream: Stream[GetImageRequest, GetImageResponse]) -> N request = await stream.recv_message() assert request is not None name = request.name - try: - camera = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - image = await camera.get_image(request.mime_type) - try: - try: - mimetype = CameraMimeType(request.mime_type) - except ValueError: - mimetype = CameraMimeType.RAW - response = GetImageResponse( - mime_type=image.mime_type if isinstance(image, RawImage) else mimetype, - width_px=image.width, - height_px=image.height, - ) - img_bytes = mimetype.encode_image(image) - finally: - image.close() - response.image = img_bytes + camera = self.get_resource(name) + + timeout = stream.deadline.time_remaining() if stream.deadline else None + image = await camera.get_image(request.mime_type, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = GetImageResponse(mime_type=image.mime_type, image=image.data) + await stream.send_message(response) + + async def GetImages(self, stream: Stream[GetImagesRequest, GetImagesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + camera = self.get_resource(name) + + timeout = stream.deadline.time_remaining() if stream.deadline else None + images, metadata = await camera.get_images(timeout=timeout, metadata=stream.metadata) + img_bytes_lst = [] + for img in images: + fmt = img.mime_type.to_proto() + img_bytes = img.data + img_bytes_lst.append(Image(source_name=name, format=fmt, image=img_bytes)) + response = GetImagesResponse(images=img_bytes_lst, response_metadata=metadata) await stream.send_message(response) - async def RenderFrame(self, stream: Stream[RenderFrameRequest, HttpBody]) -> None: + async def RenderFrame(self, stream: Stream[RenderFrameRequest, HttpBody]) -> None: # pyright: ignore [reportInvalidTypeForm] request = await stream.recv_message() assert request is not None name = request.name - try: - camera = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - try: - mimetype = CameraMimeType(request.mime_type) - except ValueError: - mimetype = CameraMimeType.JPEG - image = await camera.get_image(mimetype) - try: - img = mimetype.encode_image(image) - finally: - image.close() - response = HttpBody(data=img, content_type=image.mime_type if isinstance(image, RawImage) else mimetype) + camera = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + image = await camera.get_image(request.mime_type, timeout=timeout, metadata=stream.metadata) + response = HttpBody(data=image.data, content_type=image.mime_type) # type: ignore await stream.send_message(response) async def GetPointCloud(self, stream: Stream[GetPointCloudRequest, GetPointCloudResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - camera = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - pc, mimetype = await camera.get_point_cloud() + camera = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + pc, mimetype = await camera.get_point_cloud(timeout=timeout, extra=struct_to_dict(request.extra), metadata=stream.metadata) response = GetPointCloudResponse(mime_type=mimetype, point_cloud=pc) await stream.send_message(response) @@ -86,14 +81,25 @@ async def GetProperties(self, stream: Stream[GetPropertiesRequest, GetProperties request = await stream.recv_message() assert request is not None name = request.name - try: - camera = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - properties = await camera.get_properties() - response = GetPropertiesResponse( - supports_pcd=properties.supports_pcd, - intrinsic_parameters=properties.intrinsic_parameters, - distortion_parameters=properties.distortion_parameters, - ) + camera = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + properties = await camera.get_properties(timeout=timeout, metadata=stream.metadata) + await stream.send_message(properties) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + camera = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await camera.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + camera = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await camera.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) await stream.send_message(response) diff --git a/src/viam/components/component_base.py b/src/viam/components/component_base.py index 4fb025ad6..728b80761 100644 --- a/src/viam/components/component_base.py +++ b/src/viam/components/component_base.py @@ -1,47 +1,37 @@ import abc -from typing import Any, Dict, cast, TYPE_CHECKING +from logging import Logger +from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Mapping, Optional, SupportsBytes, SupportsFloat, Union, cast + from typing_extensions import Self -from viam.proto.common import ResourceName +from viam.errors import MethodNotImplementedError +from viam.logging import getLogger +from viam.proto.common import Geometry +from viam.resource.base import ResourceBase if TYPE_CHECKING: + from viam.resource.types import API from viam.robot.client import RobotClient -class ComponentBase(abc.ABC): +ValueTypes = Union[bool, SupportsBytes, SupportsFloat, List, Mapping, str, None] + + +class ComponentBase(abc.ABC, ResourceBase): """ Base component. All components must inherit from this class. """ - def __init__(self, name: str): - self.name = name - - @classmethod - def get_resource_name(cls, name: str) -> ResourceName: - """ - Get the ResourceName for this component type with the given name - - Args: - name (str): The name of the Component - """ - for klass in cls.mro(): - class_name = str(klass) - if "viam.components" not in class_name: - continue - if "ComponentBase" in class_name: - continue + API: ClassVar["API"] - subtype = class_name.split("viam.components.")[1].split(".")[0] - - return ResourceName(namespace="rdk", type="component", subtype=subtype, name=name) - - # Getting here should be impossible! - raise TypeError(f"Unable to create a ResourceName for {cls} named {name}." + "This should not be possible. Please file an issue.") + def __init__(self, name: str, *, logger: Optional[Logger] = None): + self.name = name + self.logger = logger if logger is not None else getLogger(f"{self.API}.{name}") @classmethod def from_robot(cls, robot: "RobotClient", name: str) -> Self: - """Get the component named `name` from the provided robot. + """Get the component named ``name`` from the provided robot. Args: robot (RobotClient): The robot @@ -51,18 +41,25 @@ def from_robot(cls, robot: "RobotClient", name: str) -> Self: Self: The component, if it exists on the robot """ component = robot.get_component(cls.get_resource_name(name)) - return cast(cls, component) + return cast(cls, component) # type: ignore - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - """Send/Receive arbitrary commands + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + raise NotImplementedError() - Args: - command (Dict[str, Any]): The command to execute + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + """ + Get all geometries associated with the component, in their current configuration, in the + `frame `__ of the component. + + :: - Raises: - NotImplementedError: Raised if the component does not support arbitrary commands + geometries = await component.get_geometries() + + if geometries: + # Get the center of the first geometry + print(f"Pose of the first geometry's centerpoint: {geometries[0].center}") Returns: - Dict[str, Any]: Result of the executed command + List[Geometry]: The geometries associated with the Component. """ - raise NotImplementedError() + raise MethodNotImplementedError("get_geometries") diff --git a/src/viam/components/encoder/__init__.py b/src/viam/components/encoder/__init__.py new file mode 100644 index 000000000..06173a0ec --- /dev/null +++ b/src/viam/components/encoder/__init__.py @@ -0,0 +1,18 @@ +from viam.resource.registry import Registry, ResourceRegistration + +from .client import EncoderClient +from .encoder import Encoder +from .service import EncoderRPCService + +__all__ = [ + "Encoder", +] + + +Registry.register_api( + ResourceRegistration( + Encoder, + EncoderRPCService, + lambda name, channel: EncoderClient(name, channel), + ) +) diff --git a/src/viam/components/encoder/client.py b/src/viam/components/encoder/client.py new file mode 100644 index 000000000..150f26e35 --- /dev/null +++ b/src/viam/components/encoder/client.py @@ -0,0 +1,83 @@ +from typing import Any, Dict, List, Mapping, Optional, Tuple + +from grpclib.client import Channel + +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry +from viam.proto.component.encoder import ( + EncoderServiceStub, + GetPositionRequest, + GetPositionResponse, + GetPropertiesRequest, + GetPropertiesResponse, + PositionType, + ResetPositionRequest, +) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict + +from .encoder import Encoder + + +class EncoderClient(Encoder, ReconfigurableResourceRPCClientBase): + """ + gRPC client for the Encoder component. + """ + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = EncoderServiceStub(channel) + super().__init__(name) + + async def reset_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto + request = ResetPositionRequest(name=self.name, extra=dict_to_struct(extra)) + await self.client.ResetPosition(request, timeout=timeout, metadata=md) + + async def get_position( + self, + position_type: Optional[PositionType.ValueType] = None, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[float, PositionType.ValueType]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPositionRequest(name=self.name, position_type=position_type, extra=dict_to_struct(extra)) + response: GetPositionResponse = await self.client.GetPosition(request, timeout=timeout, metadata=md) + return response.value, response.position_type + + async def get_properties( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Encoder.Properties: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPropertiesRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetPropertiesResponse = await self.client.GetProperties(request, timeout=timeout, metadata=md) + return Encoder.Properties( + ticks_count_supported=response.ticks_count_supported, angle_degrees_supported=response.angle_degrees_supported + ) + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/encoder/encoder.py b/src/viam/components/encoder/encoder.py new file mode 100644 index 000000000..aa6ea3403 --- /dev/null +++ b/src/viam/components/encoder/encoder.py @@ -0,0 +1,118 @@ +import abc +from dataclasses import dataclass +from typing import Any, Dict, Final, Optional, Tuple + +from viam.proto.component.encoder import PositionType +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT + +from ..component_base import ComponentBase + + +class Encoder(ComponentBase): + @dataclass + class Properties: + ticks_count_supported: bool + angle_degrees_supported: bool + + """ + Encoder represents a physical encoder. + + This acts as an abstract base class for any drivers representing specific + encoder implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.encoder import Encoder + + For more information, see `Encoder component `_. + """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "encoder" + ) + + @abc.abstractmethod + async def reset_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + """ + Set the current position to be the new zero (home) position. + + :: + + my_encoder = Encoder.from_robot(robot=machine, name='my_encoder') + + # Reset the zero position of the encoder. + await my_encoder.reset_position() + + + For more information, see `Encoder component `_. + """ + ... + + @abc.abstractmethod + async def get_position( + self, + position_type: Optional[PositionType.ValueType] = None, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[float, PositionType.ValueType]: + """ + Report the position of the encoder. + The value returned is the current position in terms of it's ``position_type``. + The position will be either in relative units (ticks away from a zero position) for + ``PositionType.POSITION_TYPE_TICKS_COUNT`` or absolute units (degrees along a circle) + for ``PositionType.POSITION_TYPE_ANGLE_DEGREES``. + + :: + + my_encoder = Encoder.from_robot(robot=machine, name='my_encoder') + + # Get the position of the encoder in ticks + position = await my_encoder.get_position(PositionType.POSITION_TYPE_TICKS_COUNT) + print("The encoder position is currently ", position[0], position[1]) + + Args: + position_type (PositionType): The desired output type of the position. + + Returns: + Tuple[float, PositionType]: + A tuple containing two values; the first [0] the position of the encoder which can either be + ticks since last zeroing for a relative encoder or degrees for an absolute encoder, and the second [1] the type of + position the encoder returns (ticks or degrees). + + For more information, see `Encoder component `_. + """ + ... + + @abc.abstractmethod + async def get_properties( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Properties: + """ + Return a dictionary of the types of position reporting this encoder supports. + + :: + + my_encoder = Encoder.from_robot(robot=machine, name='my_encoder') + + # Get whether the encoder returns position in ticks or degrees. + properties = await my_encoder.get_properties() + + Returns: + Encoder.Properties: Map of position types to supported status. + + For more information, see `Encoder component `_. + """ + ... diff --git a/src/viam/components/encoder/service.py b/src/viam/components/encoder/service.py new file mode 100644 index 000000000..9937a692b --- /dev/null +++ b/src/viam/components/encoder/service.py @@ -0,0 +1,72 @@ +from grpclib.server import Stream + +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse +from viam.proto.component.encoder import ( + EncoderServiceBase, + GetPositionRequest, + GetPositionResponse, + GetPropertiesRequest, + GetPropertiesResponse, + ResetPositionRequest, + ResetPositionResponse, +) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict + +from .encoder import Encoder + + +class EncoderRPCService(EncoderServiceBase, ResourceRPCServiceBase[Encoder]): + """ + gRPC Service for an Encoder + """ + + RESOURCE_TYPE = Encoder + + async def ResetPosition(self, stream: Stream[ResetPositionRequest, ResetPositionResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + encoder = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await encoder.reset_position(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + await stream.send_message(ResetPositionResponse()) + + async def GetPosition(self, stream: Stream[GetPositionRequest, GetPositionResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + encoder = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + position, pos_type = await encoder.get_position( + position_type=request.position_type, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata + ) + await stream.send_message(GetPositionResponse(value=position, position_type=pos_type)) + + async def GetProperties(self, stream: Stream[GetPropertiesRequest, GetPropertiesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + encoder = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + properties = await encoder.get_properties(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = GetPropertiesResponse(**properties.__dict__) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + encoder = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await encoder.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + arm = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await arm.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/gantry/__init__.py b/src/viam/components/gantry/__init__.py index ac0fb1ded..a829e33e6 100644 --- a/src/viam/components/gantry/__init__.py +++ b/src/viam/components/gantry/__init__.py @@ -1,22 +1,11 @@ -from viam.proto.component.gantry import Status as GantryStatus -from viam.proto.robot import Status -from viam.registry import ComponentRegistration, Registry -from viam.utils import message_to_struct +from viam.resource.registry import Registry, ResourceRegistration from .client import GantryClient from .gantry import Gantry -from .service import GantryService +from .service import GantryRPCService __all__ = [ "Gantry", ] - -async def create_status(component: Gantry) -> Status: - s = GantryStatus( - positions_mm=await component.get_position(), lengths_mm=await component.get_lengths(), is_moving=await component.is_moving() - ) - return Status(name=Gantry.get_resource_name(component.name), status=message_to_struct(s)) - - -Registry.register(ComponentRegistration(Gantry, "gantry", GantryService, lambda name, channel: GantryClient(name, channel), create_status)) +Registry.register_api(ResourceRegistration(Gantry, GantryRPCService, lambda name, channel: GantryClient(name, channel))) diff --git a/src/viam/components/gantry/client.py b/src/viam/components/gantry/client.py index adf165e8a..525a5ae05 100644 --- a/src/viam/components/gantry/client.py +++ b/src/viam/components/gantry/client.py @@ -1,23 +1,28 @@ -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Mapping, Optional from grpclib.client import Channel -from viam.components.generic.client import do_command -from viam.proto.common import WorldState + +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry from viam.proto.component.gantry import ( GantryServiceStub, GetLengthsRequest, GetLengthsResponse, GetPositionRequest, GetPositionResponse, + HomeRequest, + HomeResponse, + IsMovingRequest, + IsMovingResponse, MoveToPositionRequest, StopRequest, ) -from viam.utils import dict_to_struct +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict from .gantry import Gantry -class GantryClient(Gantry): +class GantryClient(Gantry, ReconfigurableResourceRPCClientBase): """ gRPC client for the Gantry component. """ @@ -27,36 +32,84 @@ def __init__(self, name: str, channel: Channel): self.client = GantryServiceStub(channel) super().__init__(name) - async def get_position(self, extra: Optional[Dict[str, Any]] = None) -> List[float]: - if extra is None: - extra = {} + async def get_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> List[float]: + md = kwargs.get("metadata", self.Metadata()).proto request = GetPositionRequest(name=self.name, extra=dict_to_struct(extra)) - response: GetPositionResponse = await self.client.GetPosition(request) + response: GetPositionResponse = await self.client.GetPosition(request, timeout=timeout, metadata=md) return list(response.positions_mm) async def move_to_position( self, positions: List[float], - world_state: Optional[WorldState] = None, + speeds: List[float], + *, extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, ): - if extra is None: - extra = {} - request = MoveToPositionRequest(name=self.name, positions_mm=positions, world_state=world_state, extra=dict_to_struct(extra)) - await self.client.MoveToPosition(request) - - async def get_lengths(self, extra: Optional[Dict[str, Any]] = None) -> List[float]: - if extra is None: - extra = {} + md = kwargs.get("metadata", self.Metadata()).proto + request = MoveToPositionRequest(name=self.name, positions_mm=positions, speeds_mm_per_sec=speeds, extra=dict_to_struct(extra)) + await self.client.MoveToPosition(request, timeout=timeout, metadata=md) + + async def home( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> bool: + md = kwargs.get("metadata", self.Metadata()).proto + request = HomeRequest(name=self.name, extra=dict_to_struct(extra)) + response: HomeResponse = await self.client.Home(request, timeout=timeout, metadata=md) + return response.homed + + async def get_lengths( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> List[float]: + md = kwargs.get("metadata", self.Metadata()).proto request = GetLengthsRequest(name=self.name, extra=dict_to_struct(extra)) - response: GetLengthsResponse = await self.client.GetLengths(request) + response: GetLengthsResponse = await self.client.GetLengths(request, timeout=timeout, metadata=md) return list(response.lengths_mm) - async def stop(self, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = StopRequest(name=self.name, extra=dict_to_struct(extra)) - await self.client.Stop(request) + await self.client.Stop(request, timeout=timeout, metadata=md) + + async def is_moving(self, *, timeout: Optional[float] = None, **kwargs) -> bool: + md = kwargs.get("metadata", self.Metadata()).proto + request = IsMovingRequest(name=self.name) + response: IsMovingResponse = await self.client.IsMoving(request, timeout=timeout, metadata=md) + return response.is_moving + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/gantry/gantry.py b/src/viam/components/gantry/gantry.py index 37d679607..865b09e91 100644 --- a/src/viam/components/gantry/gantry.py +++ b/src/viam/components/gantry/gantry.py @@ -1,8 +1,7 @@ import abc -from typing import Any, Dict, List, Optional +from typing import Any, Dict, Final, List, Optional -from viam.errors import NotSupportedError -from viam.proto.common import WorldState +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT from ..component_base import ComponentBase @@ -12,57 +11,146 @@ class Gantry(ComponentBase): Gantry represents a physical Gantry and can be used for controlling gantries of N axes. This acts as an abstract base class for any drivers representing specific - gantry implementations. This cannot be used on its own. If the `__init__()` function is - overriden, it must call the `super().__init__()` function. + gantry implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.gantry import Gantry + + For more information, see `Gantry component `_. """ + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "gantry" + ) + @abc.abstractmethod - async def get_position(self, extra: Optional[Dict[str, Any]] = None) -> List[float]: + async def get_position(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[float]: """ - Get the position in millimeters. + Get the positions of the axes of the gantry in millimeters. + + :: + + my_gantry = Gantry.from_robot(robot=machine, name="my_gantry") + + # Get the current positions of the axes of the gantry in millimeters. + positions = await my_gantry.get_position() Returns: - List[float]: The position of the axes. + List[float]: A list of the position of the axes of the gantry in millimeters. + + For more information, see `Gantry component `_. """ ... @abc.abstractmethod async def move_to_position( - self, positions: List[float], world_state: Optional[WorldState] = None, extra: Optional[Dict[str, Any]] = None + self, + positions: List[float], + speeds: List[float], + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, ): """ - Move the gantry to a new position. + Move the axes of the gantry to the desired positions (mm) at the requested speeds (mm/sec). + + :: + + my_gantry = Gantry.from_robot(robot=machine, name="my_gantry") + + # Create a list of positions for the axes of the gantry to move to. Assume in + # this example that the gantry is multi-axis, with 3 axes. + examplePositions = [1, 2, 3] + + exampleSpeeds = [3, 9, 12] + + # Move the axes of the gantry to the positions specified. + await my_gantry.move_to_position( + positions=examplePositions, speeds=exampleSpeeds) Args: - positions (List[float]): List of positions for the axes to move to, - in millimeters. - world_state (Optional[WorldState]): Object describing - obstacles for the gantry to avoid on its way to `positions`. + positions (List[float]): A list of positions for the axes of the gantry to move to, in millimeters. + speeds (List[float]): A list of speeds in millimeters per second for the gantry to move at respective to each axis. + + For more information, see `Gantry component `_. """ ... @abc.abstractmethod - async def get_lengths(self, extra: Optional[Dict[str, Any]] = None) -> List[float]: + async def home(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> bool: + """ + Run the homing sequence of the gantry to re-calibrate the axes with respect to the limit switches. + + :: + + my_gantry = Gantry.from_robot(robot=machine, name="my_gantry") + + await my_gantry.home() + + Returns: + bool: Whether the gantry has run the homing sequence successfully. + + For more information, see `Gantry component `_. + """ + + @abc.abstractmethod + async def get_lengths(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[float]: """ Get the lengths of the axes of the gantry in millimeters. + :: + + my_gantry = Gantry.from_robot(robot=machine, name="my_gantry") + + # Get the lengths of the axes of the gantry in millimeters. + lengths_mm = await my_gantry.get_lengths() + Returns: - List[float]: The lengths of the axes. + List[float]: A list of the lengths of the axes of the gantry in millimeters. + + For more information, see `Gantry component `_. """ ... @abc.abstractmethod - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): """ Stop all motion of the gantry. It is assumed that the gantry stops immediately. + + :: + + my_gantry = Gantry.from_robot(robot=machine, name="my_gantry") + + # Stop all motion of the gantry. It is assumed that the gantry stops + # immediately. + await my_gantry.stop() + + For more information, see `Gantry component `_. """ ... + @abc.abstractmethod async def is_moving(self) -> bool: """ Get if the gantry is currently moving. + :: + + my_gantry = Gantry.from_robot(robot=machine, name="my_gantry") + + # Stop all motion of the gantry. It is assumed that the + # gantry stops immediately. + await my_gantry.stop() + + # Print if the gantry is currently moving. + print(await my_gantry.is_moving()) + Returns: bool: Whether the gantry is moving. + + For more information, see `Gantry component `_. """ - raise NotSupportedError(f"Gantry named {self.name} does not support returning whether it is moving") + ... diff --git a/src/viam/components/gantry/service.py b/src/viam/components/gantry/service.py index c7f95785d..8828f3496 100644 --- a/src/viam/components/gantry/service.py +++ b/src/viam/components/gantry/service.py @@ -1,25 +1,30 @@ from grpclib.server import Stream -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError + +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.gantry import ( GantryServiceBase, GetLengthsRequest, GetLengthsResponse, GetPositionRequest, GetPositionResponse, + HomeRequest, + HomeResponse, + IsMovingRequest, + IsMovingResponse, MoveToPositionRequest, MoveToPositionResponse, StopRequest, StopResponse, ) -from viam.utils import struct_to_dict +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict from .gantry import Gantry -class GantryService(GantryServiceBase, ComponentServiceBase[Gantry]): +class GantryRPCService(GantryServiceBase, ResourceRPCServiceBase[Gantry]): """ - gRPC Service for an Gantry + gRPC Service for a Gantry """ RESOURCE_TYPE = Gantry @@ -28,11 +33,9 @@ async def GetPosition(self, stream: Stream[GetPositionRequest, GetPositionRespon request = await stream.recv_message() assert request is not None name = request.name - try: - gantry = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - position = await gantry.get_position(extra=struct_to_dict(request.extra)) + gantry = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + position = await gantry.get_position(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetPositionResponse(positions_mm=position) await stream.send_message(response) @@ -40,23 +43,35 @@ async def MoveToPosition(self, stream: Stream[MoveToPositionRequest, MoveToPosit request = await stream.recv_message() assert request is not None name = request.name - try: - gantry = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await gantry.move_to_position(list(request.positions_mm), request.world_state, struct_to_dict(request.extra)) + gantry = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await gantry.move_to_position( + list(request.positions_mm), + list(request.speeds_mm_per_sec), + extra=struct_to_dict(request.extra), + timeout=timeout, + metadata=stream.metadata, + ) response = MoveToPositionResponse() await stream.send_message(response) + async def Home(self, stream: Stream[HomeRequest, HomeResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + gantry = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + homed = await gantry.home(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = HomeResponse(homed=homed) + await stream.send_message(response) + async def GetLengths(self, stream: Stream[GetLengthsRequest, GetLengthsResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - gantry = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - lengths = await gantry.get_lengths(extra=struct_to_dict(request.extra)) + gantry = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + lengths = await gantry.get_lengths(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetLengthsResponse(lengths_mm=lengths) await stream.send_message(response) @@ -64,10 +79,35 @@ async def Stop(self, stream: Stream[StopRequest, StopResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - gantry = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await gantry.stop(extra=struct_to_dict(request.extra)) + gantry = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await gantry.stop(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = StopResponse() await stream.send_message(response) + + async def IsMoving(self, stream: Stream[IsMovingRequest, IsMovingResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + gantry = self.get_resource(name) + is_moving = await gantry.is_moving() + response = IsMovingResponse(is_moving=is_moving) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + gantry = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await gantry.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + arm = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await arm.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/generic/__init__.py b/src/viam/components/generic/__init__.py index ce77a53f9..c5799942c 100644 --- a/src/viam/components/generic/__init__.py +++ b/src/viam/components/generic/__init__.py @@ -1,18 +1,18 @@ -from viam.registry import ComponentRegistration, Registry +import viam.gen.component.generic.v1.generic_pb2 # Need this import for Generic service descriptors to resolve +from viam.resource.registry import Registry, ResourceRegistration from .client import GenericClient from .generic import Generic -from .service import GenericService +from .service import GenericRPCService __all__ = [ "Generic", ] -Registry.register( - ComponentRegistration( +Registry.register_api( + ResourceRegistration( Generic, - "generic", - GenericService, + GenericRPCService, lambda name, channel: GenericClient(name, channel), ) ) diff --git a/src/viam/components/generic/client.py b/src/viam/components/generic/client.py index b6eb6b2b5..3b720ea81 100644 --- a/src/viam/components/generic/client.py +++ b/src/viam/components/generic/client.py @@ -1,25 +1,37 @@ -from typing import Any, Dict +from typing import Any, Dict, List, Mapping, Optional + from grpclib import GRPCError, Status from grpclib.client import Channel -from viam.proto.component.generic import GenericServiceStub, DoCommandRequest, DoCommandResponse -from viam.utils import dict_to_struct, struct_to_dict + +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry +from viam.proto.component.generic import GenericServiceStub +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase, ResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict from .generic import Generic -class GenericClient(Generic): +class GenericClient(Generic, ReconfigurableResourceRPCClientBase): """ gRPC client for the Generic component. """ def __init__(self, name: str, channel: Channel): + self.channel = channel self.client = GenericServiceStub(channel) super().__init__(name) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: + async def do_command( + self, + command: Mapping[str, Any], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, Any]: + md = kwargs.get("metadata", self.Metadata()).proto request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) try: - response: DoCommandResponse = await self.client.DoCommand(request) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) except GRPCError as e: if e.status == Status.UNIMPLEMENTED: raise NotImplementedError() @@ -27,9 +39,15 @@ async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: return struct_to_dict(response.result) + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) + -async def do_command(channel: Channel, name: str, command: Dict[str, Any]) -> Dict[str, Any]: - """Convenience method to allow component clients to execute `do_command` functions +async def do_command( + channel: Channel, name: str, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs +) -> Mapping[str, ValueTypes]: + """Convenience method to allow component clients to execute ``do_command`` functions Args: channel (Channel): A gRPC channel @@ -39,5 +57,6 @@ async def do_command(channel: Channel, name: str, command: Dict[str, Any]) -> Di Returns: Dict[str, Any]: The result of the executed command """ + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto client = GenericClient(name, channel) - return await client.do_command(command) + return await client.do_command(command, timeout=timeout, metadata=md) diff --git a/src/viam/components/generic/generic.py b/src/viam/components/generic/generic.py index c60a007c9..72de64e96 100644 --- a/src/viam/components/generic/generic.py +++ b/src/viam/components/generic/generic.py @@ -1,3 +1,7 @@ +from typing import Final + +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT + from ..component_base import ComponentBase @@ -6,54 +10,67 @@ class Generic(ComponentBase): Generic component, which represents any type of component that can executes arbitrary commands This acts as an abstract base class for any drivers representing generic components. - This cannot be used on its own. If the `__init__()` function is overridden, it must call the `super().__init__()` function. - - To create a Generic component (an arbitrary component that can process commands), this `Generic` component should be subclassed - and the `do_command` function implemented. - - Example: - - ```python - class ComplexComponent(Generic): - - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - result = {key: False for key in command.keys()} - for (name, args) in command.items(): - if name == 'on': - self.on(*args) - result[name] = True - if name == 'set_frequency': - self.set_frequency(*args) - result[name] = True - if name == 'get_frequency': - result[name] = self.frequency - if name == 'complex_command': - self.complex_command(*args) - result[name] = True - return result - - def set_frequency(self, frequency: int): - self.frequency = frequency - - def on(self, frequency: int, duration: int): - self.frequency = frequency - self.power = 1 - task = threading.Timer(duration, self.off) - task.start() - - def off(self): - self.power = 0 - - def complex_command(self, arg1, arg2, arg3): - ... - ``` - - To execute commands, simply call the `do_command` function with the appropriate parameters - - ```python - await component.do_command({'on': [300, 10]}) - component.power # 1 - await asyncio.sleep(10) - component.power # 0 - ``` + This cannot be used on its own. If the ``__init__()`` function is overridden, it must call the ``super().__init__()`` function. + + To create a Generic component (an arbitrary component that can process commands), this ``Generic`` component should be subclassed + and the ``do_command`` function implemented. + + :: + + from viam.components.generic import Generic + + Example:: + + class ComplexComponent(Generic): + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs + ) -> Mapping[str, ValueTypes]: + result = {key: False for key in command.keys()} + for (name, args) in command.items(): + if name == 'on': + self.on(*args) + result[name] = True + if name == 'set_frequency': + self.set_frequency(*args) + result[name] = True + if name == 'get_frequency': + result[name] = self.frequency + if name == 'complex_command': + self.complex_command(*args) + result[name] = True + return result + + def set_frequency(self, frequency: int): + self.frequency = frequency + + def on(self, frequency: int, duration: int): + self.frequency = frequency + self.power = 1 + task = threading.Timer(duration, self.off) + task.start() + + def off(self): + self.power = 0 + + def complex_command(self, arg1, arg2, arg3): + ... + + To execute commands, simply call the ``do_command`` function with the appropriate parameters. + :: + + await component.do_command({'on': [300, 10]}) + component.power # 1 + await asyncio.sleep(10) + component.power # 0 + + For more information, see `Gantry component `_. """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "generic" + ) diff --git a/src/viam/components/generic/service.py b/src/viam/components/generic/service.py index 79c6f62dc..e8c60ea59 100644 --- a/src/viam/components/generic/service.py +++ b/src/viam/components/generic/service.py @@ -2,19 +2,15 @@ from grpclib.server import Stream from viam.components.component_base import ComponentBase -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError -from viam.proto.component.generic import ( - DoCommandRequest, - DoCommandResponse, - GenericServiceBase, -) +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse +from viam.proto.component.generic import GenericServiceBase +from viam.resource.rpc_service_base import ResourceRPCServiceBase from viam.utils import dict_to_struct, struct_to_dict # from .generic import Generic -class GenericService(GenericServiceBase, ComponentServiceBase[ComponentBase]): +class GenericRPCService(GenericServiceBase, ResourceRPCServiceBase): """ gRPC Service for a Generic component """ @@ -25,13 +21,20 @@ async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) - request = await stream.recv_message() assert request is not None name = request.name + component = self.get_resource(name) try: - component = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - try: - result = await component.do_command(struct_to_dict(request.command)) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await component.do_command(struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) except NotImplementedError: - raise GRPCError(Status.UNIMPLEMENTED, f"`DO` command is unimplemented for component named: {name}") + raise GRPCError(Status.UNIMPLEMENTED, f"``DO`` command is unimplemented for component named: {name}") response = DoCommandResponse(result=dict_to_struct(result)) await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + component = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await component.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/gripper/__init__.py b/src/viam/components/gripper/__init__.py index fc2737bd8..6da00cfcd 100644 --- a/src/viam/components/gripper/__init__.py +++ b/src/viam/components/gripper/__init__.py @@ -1,23 +1,11 @@ -from viam.components.gantry.gantry import Gantry -from viam.registry import ComponentRegistration, Registry -from viam.proto.common import ActuatorStatus -from viam.proto.robot import Status -from viam.utils import message_to_struct +from viam.resource.registry import Registry, ResourceRegistration from .client import GripperClient from .gripper import Gripper -from .service import GripperService +from .service import GripperRPCService __all__ = [ "Gripper", ] - -async def create_status(component: Gripper) -> Status: - s = ActuatorStatus(is_moving=await component.is_moving()) - return Status(name=Gripper.get_resource_name(component.name), status=message_to_struct(s)) - - -Registry.register( - ComponentRegistration(Gripper, "gripper", GripperService, lambda name, channel: GripperClient(name, channel), create_status) -) +Registry.register_api(ResourceRegistration(Gripper, GripperRPCService, lambda name, channel: GripperClient(name, channel))) diff --git a/src/viam/components/gripper/client.py b/src/viam/components/gripper/client.py index fe7a7e990..945175449 100644 --- a/src/viam/components/gripper/client.py +++ b/src/viam/components/gripper/client.py @@ -1,13 +1,24 @@ -from typing import Any, Dict +from typing import Any, Dict, List, Mapping, Optional from grpclib.client import Channel -from viam.components.generic.client import do_command -from viam.proto.component.gripper import GrabRequest, GrabResponse, GripperServiceStub, OpenRequest, StopRequest + +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry +from viam.proto.component.gripper import ( + GrabRequest, + GrabResponse, + GripperServiceStub, + IsMovingRequest, + IsMovingResponse, + OpenRequest, + StopRequest, +) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict from .gripper import Gripper -class GripperClient(Gripper): +class GripperClient(Gripper, ReconfigurableResourceRPCClientBase): """ gRPC client for the Gripper component """ @@ -17,18 +28,58 @@ def __init__(self, name: str, channel: Channel): self.client = GripperServiceStub(channel) super().__init__(name) - async def open(self): - request = OpenRequest(name=self.name) - await self.client.Open(request) + async def open( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto + request = OpenRequest(name=self.name, extra=dict_to_struct(extra)) + await self.client.Open(request, timeout=timeout, metadata=md) - async def grab(self) -> bool: - request = GrabRequest(name=self.name) - response: GrabResponse = await self.client.Grab(request) + async def grab( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> bool: + md = kwargs.get("metadata", self.Metadata()).proto + request = GrabRequest(name=self.name, extra=dict_to_struct(extra)) + response: GrabResponse = await self.client.Grab(request, timeout=timeout, metadata=md) return response.success - async def stop(self): - request = StopRequest(name=self.name) - await self.client.Stop(request) + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto + request = StopRequest(name=self.name, extra=dict_to_struct(extra)) + await self.client.Stop(request, timeout=timeout, metadata=md) + + async def is_moving(self, *, timeout: Optional[float] = None, **kwargs) -> bool: + md = kwargs.get("metadata", self.Metadata()).proto + request = IsMovingRequest(name=self.name) + response: IsMovingResponse = await self.client.IsMoving(request, timeout=timeout, metadata=md) + return response.is_moving + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/gripper/gripper.py b/src/viam/components/gripper/gripper.py index 2a62462d9..286c2eeef 100644 --- a/src/viam/components/gripper/gripper.py +++ b/src/viam/components/gripper/gripper.py @@ -1,8 +1,8 @@ import abc - -from viam.errors import NotSupportedError +from typing import Any, Dict, Final, Optional from viam.components.component_base import ComponentBase +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT class Gripper(ComponentBase): @@ -10,39 +10,105 @@ class Gripper(ComponentBase): Gripper represents a physical robotic gripper. This acts as an abstract base class for any drivers representing specific - gripper implementations. This cannot be used on its own. If the `__init__()` function is - overriden, it must call the `super().__init__()` function. + gripper implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.gripper import Gripper + + For more information, see `Gripper component `_. """ + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "gripper" + ) + @abc.abstractmethod - async def open(self): + async def open( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ Open the gripper. + + :: + + my_gripper = Gripper.from_robot(robot=machine, name="my_gripper") + + # Open the gripper. + await my_gripper.open() + + For more information, see `Gripper component `_. """ ... @abc.abstractmethod - async def grab(self) -> bool: + async def grab( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> bool: """ Instruct the gripper to grab. + :: + + my_gripper = Gripper.from_robot(robot=machine, name="my_gripper") + + # Grab with the gripper. + grabbed = await my_gripper.grab() + Returns: bool: Indicates if the gripper grabbed something. + + For more information, see `Gripper component `_. """ ... @abc.abstractmethod - async def stop(self): + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ Stop the gripper. It is assumed the gripper stops immediately. + + :: + + my_gripper = Gripper.from_robot(robot=machine, name="my_gripper") + + # Stop the gripper. + await my_gripper.stop() + + For more information, see `Gripper component `_. """ ... + @abc.abstractmethod async def is_moving(self) -> bool: """ Get if the gripper is currently moving. + :: + + my_gripper = Gripper.from_robot(robot=machine, name="my_gripper") + + # Check whether the gripper is currently moving. + moving = await my_gripper.is_moving() + print('Moving:', moving) + Returns: bool: Whether the gripper is moving. + + For more information, see `Gripper component `_. """ - raise NotSupportedError(f"Gripper named {self.name} does not support returning whether it is moving") + ... diff --git a/src/viam/components/gripper/service.py b/src/viam/components/gripper/service.py index 6cc439ec3..bd8b6f1da 100644 --- a/src/viam/components/gripper/service.py +++ b/src/viam/components/gripper/service.py @@ -1,20 +1,24 @@ from grpclib.server import Stream -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError + +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.gripper import ( GrabRequest, GrabResponse, GripperServiceBase, + IsMovingRequest, + IsMovingResponse, OpenRequest, OpenResponse, StopRequest, StopResponse, ) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict from .gripper import Gripper -class GripperService(GripperServiceBase, ComponentServiceBase[Gripper]): +class GripperRPCService(GripperServiceBase, ResourceRPCServiceBase[Gripper]): """ gRPC Service for a Gripper """ @@ -25,11 +29,9 @@ async def Open(self, stream: Stream[OpenRequest, OpenResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - gripper = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await gripper.open() + gripper = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await gripper.open(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = OpenResponse() await stream.send_message(response) @@ -37,20 +39,43 @@ async def Grab(self, stream: Stream[GrabRequest, GrabResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - gripper = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - grabbed = await gripper.grab() + gripper = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + grabbed = await gripper.grab(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GrabResponse(success=grabbed) await stream.send_message(response) async def Stop(self, stream: Stream[StopRequest, StopResponse]) -> None: request = await stream.recv_message() assert request is not None - try: - gripper = self.get_component(request.name) - except ComponentNotFoundError as e: - raise e.grpc_error - await gripper.stop() + gripper = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await gripper.stop(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) await stream.send_message(StopResponse()) + + async def IsMoving(self, stream: Stream[IsMovingRequest, IsMovingResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + gripper = self.get_resource(name) + is_moving = await gripper.is_moving() + response = IsMovingResponse(is_moving=is_moving) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + gripper = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await gripper.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + arm = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await arm.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/input/__init__.py b/src/viam/components/input/__init__.py index 65404e0a9..7994deb22 100644 --- a/src/viam/components/input/__init__.py +++ b/src/viam/components/input/__init__.py @@ -1,11 +1,8 @@ -from viam.proto.component.inputcontroller import Status as InputStatus -from viam.proto.robot import Status -from viam.registry import ComponentRegistration, Registry -from viam.utils import message_to_struct +from viam.resource.registry import Registry, ResourceRegistration from .client import ControllerClient from .input import Control, ControlFunction, Controller, Event, EventType -from .service import InputControllerService +from .service import InputControllerRPCService __all__ = [ "Controller", @@ -15,16 +12,4 @@ "EventType", ] - -async def create_status(component: Controller) -> Status: - return Status( - name=Controller.get_resource_name(component.name), - status=message_to_struct(InputStatus(events=[event.proto for event in (await component.get_events()).values()])), - ) - - -Registry.register( - ComponentRegistration( - Controller, "input_controller", InputControllerService, lambda name, channel: ControllerClient(name, channel), create_status - ) -) +Registry.register_api(ResourceRegistration(Controller, InputControllerRPCService, lambda name, channel: ControllerClient(name, channel))) diff --git a/src/viam/components/input/client.py b/src/viam/components/input/client.py index 882a3fed3..9a2704bf2 100644 --- a/src/viam/components/input/client.py +++ b/src/viam/components/input/client.py @@ -1,14 +1,16 @@ import asyncio -from threading import Lock +from threading import Lock, RLock from time import time -from typing import Any, Dict, List, Optional -from grpclib import GRPCError, Status +from typing import Any, Dict, List, Mapping, Optional +from google.protobuf.struct_pb2 import Struct +from grpclib import GRPCError, Status from grpclib.client import Channel + import viam -from viam.components.generic.client import do_command from viam.errors import NotSupportedError from viam.logging import getLogger +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry from viam.proto.component.inputcontroller import ( GetControlsRequest, GetControlsResponse, @@ -19,36 +21,62 @@ StreamEventsResponse, TriggerEventRequest, ) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase, ResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict from .input import Control, ControlFunction, Controller, Event, EventType LOGGER = getLogger(__name__) -class ControllerClient(Controller): +class ControllerClient(Controller, ReconfigurableResourceRPCClientBase): """gRPC client for an Input Controller""" def __init__(self, name: str, channel: Channel): self.channel = channel self.client = InputControllerServiceStub(channel) self.callbacks: Dict[Control, Dict[EventType, Optional[ControlFunction]]] = {} - self._lock = Lock() + self._lock = RLock() self._stream_lock = Lock() self._is_streaming = False self._is_stream_ready = False + self._callback_extra: Struct = dict_to_struct({}) super().__init__(name) - async def get_controls(self) -> List[Control]: - request = GetControlsRequest(controller=self.name) - response: GetControlsResponse = await self.client.GetControls(request) + async def get_controls( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> List[Control]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetControlsRequest(controller=self.name, extra=dict_to_struct(extra)) + response: GetControlsResponse = await self.client.GetControls(request, timeout=timeout, metadata=md) return [Control(control) for control in response.controls] - async def get_events(self) -> Dict[Control, Event]: - request = GetEventsRequest(controller=self.name) - response: GetEventsResponse = await self.client.GetEvents(request) + async def get_events( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Dict[Control, Event]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetEventsRequest(controller=self.name, extra=dict_to_struct(extra)) + response: GetEventsResponse = await self.client.GetEvents(request, timeout=timeout, metadata=md) return {Control(event.control): Event.from_proto(event) for (event) in response.events} - def register_control_callback(self, control: Control, triggers: List[EventType], function: Optional[ControlFunction]): + def register_control_callback( + self, + control: Control, + triggers: List[EventType], + function: Optional[ControlFunction], + extra: Optional[Dict[str, Any]] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()) + self._callback_extra = dict_to_struct(extra) with self._lock: callbacks = self.callbacks.get(control, {}) for trigger in triggers: @@ -68,18 +96,34 @@ def handle_task_result(task: asyncio.Task): except Exception: LOGGER.exception("Exception raised by task = %r", task) - task = asyncio.create_task(self._stream_events(), name=f"{viam._TASK_PREFIX}-input_stream_events") + task = asyncio.create_task(self._stream_events(md), name=f"{viam._TASK_PREFIX}-input_stream_events") task.add_done_callback(handle_task_result) - async def trigger_event(self, event: Event): - request = TriggerEventRequest(controller=self.name, event=event.proto) + def reset_channel(self, channel: Channel): + super().reset_channel(channel) + with self._lock: + for control, callback in self.callbacks.items(): + for event_type, func in callback.items(): + self.register_control_callback(control, [event_type], func) + + async def trigger_event( + self, + event: Event, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto + request = TriggerEventRequest(controller=self.name, event=event.proto, extra=dict_to_struct(extra)) try: - await self.client.TriggerEvent(request) + await self.client.TriggerEvent(request, timeout=timeout, metadata=md) except GRPCError as e: if e.status == Status.UNIMPLEMENTED and ("does not support triggering events" in e.message if e.message else False): raise NotSupportedError(f"Input controller named {self.name} does not support triggering events") + raise e - async def _stream_events(self): + async def _stream_events(self, metadata: ResourceRPCClientBase.Metadata): with self._stream_lock: if self._is_streaming: return @@ -88,9 +132,10 @@ async def _stream_events(self): if not self.callbacks: return - request = StreamEventsRequest(controller=self.name, events=[]) + md = metadata.proto + request = StreamEventsRequest(controller=self.name, events=[], extra=self._callback_extra) with self._lock: - for (control, callbacks) in self.callbacks.items(): + for control, callbacks in self.callbacks.items(): event = StreamEventsRequest.Events( control=control, events=[et for (et, func) in callbacks.items() if func is not None], @@ -99,7 +144,7 @@ async def _stream_events(self): request.events.append(event) try: - async with self.client.StreamEvents.open() as stream: + async with self.client.StreamEvents.open(metadata=md) as stream: await stream.send_message(request, end=True) self._send_connection_status(True) reply: StreamEventsResponse @@ -132,5 +177,18 @@ def _execute_callback(self, event: Event): if all_callback is not None: all_callback(event) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/input/input.py b/src/viam/components/input/input.py index 27b8fd1fb..963a724d5 100644 --- a/src/viam/components/input/input.py +++ b/src/viam/components/input/input.py @@ -2,13 +2,15 @@ from dataclasses import dataclass from datetime import datetime from enum import Enum -from typing import Callable, Dict, List, Optional +from typing import Any, Callable, Dict, Final, List, Optional from google.protobuf.timestamp_pb2 import Timestamp from typing_extensions import Self + from viam.components.component_base import ComponentBase from viam.errors import NotSupportedError from viam.proto.component.inputcontroller import Event as PBEvent +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT class EventType(str, Enum): @@ -94,6 +96,11 @@ class Control(str, Enum): BUTTON_RECORD = "ButtonRecord" BUTTON_E_STOP = "ButtonEStop" + # Pedals + ABSOLUTE_PEDAL_ACCELERATOR = "AbsolutePedalAccelerator" + ABSOLUTE_PEDAL_BRAKE = "AbsolutePedalBrake" + ABSOLUTE_PEDAL_CLUTCH = "AbsolutePedalClutch" + @dataclass class Event: @@ -128,48 +135,163 @@ class Controller(ComponentBase): Controller is a logical "container" more than an actual device Could be a single gamepad, or a collection of digitalInterrupts and analogReaders, a keyboard, etc. + + :: + + from viam.components.input import Control, Controller, EventType + + For more information, see `Input Controller component `_. """ + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "input_controller" + ) + @abc.abstractmethod - async def get_controls(self) -> List[Control]: + async def get_controls(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Control]: """ Returns a list of Controls provided by the Controller + :: + + # Get the controller from the machine. + my_controller = Controller.from_robot( + robot=machine, "my_controller") + + # Get the list of Controls provided by the controller. + controls = await my_controller.get_controls() + + # Print the list of Controls provided by the controller. + print(f"Controls: {controls}") + Returns: List[Control]: List of controls provided by the Controller + + For more information, see `Input Controller component `_. """ ... @abc.abstractmethod - async def get_events(self) -> Dict[Control, Event]: + async def get_events( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Dict[Control, Event]: """ Returns the most recent Event for each input (which should be the current state) + :: + + # Get the controller from the machine. + my_controller = Controller.from_robot( + robot=machine, "my_controller") + + # Get the most recent Event for each Control. + recent_events = await my_controller.get_events() + + # Print out the most recent Event for each Control. + print(f"Recent Events: {recent_events}") + Returns: Dict[Control, Event]: The most recent event for each input + + For more information, see `Input Controller component `_. """ ... @abc.abstractmethod - def register_control_callback(self, control: Control, triggers: List[EventType], function: Optional[ControlFunction]): + def register_control_callback( + self, + control: Control, + triggers: List[EventType], + function: Optional[ControlFunction], + *, + extra: Optional[Dict[str, Any]] = None, + **kwargs, + ): """ Register a function that will fire on given EventTypes for a given Control + :: + + from viam.components.input import Control, EventType + + # Define a function to handle pressing the Start Menu Button "BUTTON_START" on + # your controller, printing out the start time. + def print_start_time(event): + print(f"Start Menu Button was pressed at this time: {event.time}") + + + # Define a function that handles the controller. + async def handle_controller(controller): + # Get the list of Controls on the controller. + controls = await controller.get_controls() + + # If the "BUTTON_START" Control is found, register the function + # print_start_time to fire when "BUTTON_START" has the event "ButtonPress" + # occur. + if Control.BUTTON_START in controls: + controller.register_control_callback( + Control.BUTTON_START, [EventType.BUTTON_PRESS], print_start_time) + else: + print("Oops! Couldn't find the start button control! Is your " + "controller connected?") + exit() + + while True: + await asyncio.sleep(1.0) + + + async def main(): + # ... < INSERT CONNECTION CODE FROM MACHINE'S CODE SAMPLE TAB > + + # Get your controller from the machine. + my_controller = Controller.from_robot( + robot=machine, "my_controller") + + # Run the handleController function. + await handle_controller(my_controller) + + # ... < INSERT ANY OTHER CODE FOR MAIN FUNCTION > + Args: control (Control): The control to register the function for triggers (List[EventType]): The events that will trigger the function function (ControlFunction): The function to run on specific triggers + + For more information, see `Input Controller component `_. """ ... - async def trigger_event(self, event: Event): + async def trigger_event( + self, + event: Event, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> None: # Explicitly return None for typechecking, as this is technically a NoReturn default implementation """Directly send an Event (such as a button press) from external code + :: + + # Get your controller from the machine. + my_controller = Controller.from_robot( + robot=machine, "my_controller") + + # Define a "Button is Pressed" event for the control BUTTON_START. + button_is_pressed_event = Event( + time(), EventType.BUTTON_PRESS, Control.BUTTON_START, 1.0) + + # Trigger the event on your controller. Set this trigger to timeout if it has + # not completed in 7 seconds. + await my_controller.trigger_event(event=button_is_pressed_event, timeout=7.0) + Args: event (Event): The event to trigger + + For more information, see `Input Controller component `_. """ raise NotSupportedError(f"Input controller named {self.name} does not support triggering events") diff --git a/src/viam/components/input/service.py b/src/viam/components/input/service.py index d56e11fba..01175a183 100644 --- a/src/viam/components/input/service.py +++ b/src/viam/components/input/service.py @@ -1,12 +1,13 @@ import asyncio from multiprocessing import Pipe from typing import Optional -from h2.exceptions import StreamClosedError from grpclib.server import Stream +from h2.exceptions import StreamClosedError + import viam -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError, NotSupportedError +from viam.errors import NotSupportedError +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.inputcontroller import ( GetControlsRequest, GetControlsResponse, @@ -18,14 +19,15 @@ TriggerEventRequest, TriggerEventResponse, ) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict from .input import Control, Controller, Event, EventType - LOGGER = viam.logging.getLogger(__name__) -class InputControllerService(InputControllerServiceBase, ComponentServiceBase[Controller]): +class InputControllerRPCService(InputControllerServiceBase, ResourceRPCServiceBase[Controller]): """ gRPC Service for an input controller """ @@ -36,11 +38,9 @@ async def GetControls(self, stream: Stream[GetControlsRequest, GetControlsRespon request = await stream.recv_message() assert request is not None name = request.controller - try: - controller = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - controls = await controller.get_controls() + controller = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + controls = await controller.get_controls(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetControlsResponse(controls=[c.value for c in controls]) await stream.send_message(response) @@ -48,11 +48,9 @@ async def GetEvents(self, stream: Stream[GetEventsRequest, GetEventsResponse]) - request = await stream.recv_message() assert request is not None name = request.controller - try: - controller = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - events = await controller.get_events() + controller = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + events = await controller.get_events(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) pb_events = [e.proto for e in events.values()] response = GetEventsResponse(events=pb_events) await stream.send_message(response) @@ -61,10 +59,7 @@ async def StreamEvents(self, stream: Stream[StreamEventsRequest, StreamEventsRes request = await stream.recv_message() assert request is not None name = request.controller - try: - controller = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error + controller = self.get_resource(name) loop = asyncio.get_running_loop() # Using Pipes to send event data back to this function so it can be streamed to clients @@ -83,11 +78,21 @@ def ctrlFunc(event: Event): for event in request.events: triggers = [EventType(et) for et in event.events] if len(triggers): - controller.register_control_callback(Control(event.control), triggers, ctrlFunc) + controller.register_control_callback( + Control(event.control), + triggers, + ctrlFunc, + extra=struct_to_dict(request.extra), + ) cancelled_triggers = [EventType(et) for et in event.cancelled_events] if len(cancelled_triggers): - controller.register_control_callback(Control(event.control), cancelled_triggers, None) + controller.register_control_callback( + Control(event.control), + cancelled_triggers, + None, + extra=struct_to_dict(request.extra), + ) # Asynchronously wait for messages to come over the read pipe and run the READ function whenever the pipe is ready. def read(): @@ -111,7 +116,7 @@ async def send_message(): # HACK: Keep the stream open when this function returns. # When the StreamEvents function returns, the Stream is closed. But we don't want the stream to close because we still need # to send events to any clients who have registered callbacks. - # By setting `stream._cancel_done` to `True`, this tricks grpclib into thinking it already closed the stream, so it doesn't + # By setting ``stream._cancel_done`` to ``True``, this tricks grpclib into thinking it already closed the stream, so it doesn't # perform any cleanup (like removing the stream). We eventually do want to actually close this stream, so we undo this hack # every time we send a message. That way, the trailing metadata is sent when either the server closes or the client disconnects. stream._cancel_done = True @@ -128,21 +133,43 @@ def unregister_pipe_callbacks(): for event in request.events: triggers = [EventType(et) for et in event.events] if len(triggers): - controller.register_control_callback(Control(event.control), triggers, None) + controller.register_control_callback( + Control(event.control), + triggers, + None, + extra=struct_to_dict(request.extra), + ) async def TriggerEvent(self, stream: Stream[TriggerEventRequest, TriggerEventResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.controller + timeout = stream.deadline.time_remaining() if stream.deadline else None + controller = self.get_resource(name) try: - controller = self.get_component(name) pb_event = request.event event = Event.from_proto(pb_event) - await controller.trigger_event(event) - except ComponentNotFoundError as e: - raise e.grpc_error + await controller.trigger_event(event, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) except NotSupportedError as e: raise e.grpc_error response = TriggerEventResponse() await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + controller = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await controller.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + arm = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await arm.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/motor/__init__.py b/src/viam/components/motor/__init__.py index 17e7e6a1e..7e4885d67 100644 --- a/src/viam/components/motor/__init__.py +++ b/src/viam/components/motor/__init__.py @@ -1,25 +1,11 @@ -from viam.proto.component.motor import Status as MotorStatus -from viam.proto.robot import Status -from viam.registry import ComponentRegistration, Registry -from viam.utils import message_to_struct +from viam.resource.registry import Registry, ResourceRegistration from .client import MotorClient from .motor import Motor -from .service import MotorService +from .service import MotorRPCService __all__ = [ "Motor", ] - -async def create_status(component: Motor) -> Status: - s = MotorStatus( - is_powered=await component.is_powered(), - position=await component.get_position(), - position_reporting=(await component.get_properties()).position_reporting, - is_moving=await component.is_moving(), - ) - return Status(name=Motor.get_resource_name(component.name), status=message_to_struct(s)) - - -Registry.register(ComponentRegistration(Motor, "motor", MotorService, lambda name, channel: MotorClient(name, channel), create_status)) +Registry.register_api(ResourceRegistration(Motor, MotorRPCService, lambda name, channel: MotorClient(name, channel))) diff --git a/src/viam/components/motor/client.py b/src/viam/components/motor/client.py index 725f797d3..9b3a193a7 100644 --- a/src/viam/components/motor/client.py +++ b/src/viam/components/motor/client.py @@ -1,27 +1,32 @@ -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Mapping, Optional, Tuple from grpclib.client import Channel -from viam.components.generic.client import do_command + +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry from viam.proto.component.motor import ( - GetPropertiesRequest, - GetPropertiesResponse, GetPositionRequest, GetPositionResponse, + GetPropertiesRequest, + GetPropertiesResponse, GoForRequest, GoToRequest, + IsMovingRequest, + IsMovingResponse, IsPoweredRequest, IsPoweredResponse, MotorServiceStub, ResetZeroPositionRequest, SetPowerRequest, + SetRPMRequest, StopRequest, ) -from viam.utils import dict_to_struct +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict from .motor import Motor -class MotorClient(Motor): +class MotorClient(Motor, ReconfigurableResourceRPCClientBase): """ gRPC client for the Motor component. """ @@ -31,56 +36,133 @@ def __init__(self, name: str, channel: Channel): self.client = MotorServiceStub(channel) super().__init__(name) - async def set_power(self, power: float, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def set_power( + self, + power: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = SetPowerRequest(name=self.name, power_pct=power, extra=dict_to_struct(extra)) - await self.client.SetPower(request) + await self.client.SetPower(request, timeout=timeout, metadata=md) - async def go_for(self, rpm: float, revolutions: float, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def go_for( + self, + rpm: float, + revolutions: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = GoForRequest(name=self.name, rpm=rpm, revolutions=revolutions, extra=dict_to_struct(extra)) - await self.client.GoFor(request) + await self.client.GoFor(request, timeout=timeout, metadata=md) - async def go_to(self, rpm: float, position_revolutions: float, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def go_to( + self, + rpm: float, + position_revolutions: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = GoToRequest(name=self.name, rpm=rpm, position_revolutions=position_revolutions, extra=dict_to_struct(extra)) - await self.client.GoTo(request) + await self.client.GoTo(request, timeout=timeout, metadata=md) + + async def set_rpm( + self, + rpm: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto + request = SetRPMRequest(name=self.name, rpm=rpm, extra=dict_to_struct(extra)) + await self.client.SetRPM(request, timeout=timeout, metadata=md) - async def reset_zero_position(self, offset: float, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def reset_zero_position( + self, + offset: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = ResetZeroPositionRequest(name=self.name, offset=offset, extra=dict_to_struct(extra)) - await self.client.ResetZeroPosition(request) + await self.client.ResetZeroPosition(request, timeout=timeout, metadata=md) - async def get_position(self, extra: Optional[Dict[str, Any]] = None) -> float: - if extra is None: - extra = {} + async def get_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> float: + md = kwargs.get("metadata", self.Metadata()).proto request = GetPositionRequest(name=self.name, extra=dict_to_struct(extra)) - response: GetPositionResponse = await self.client.GetPosition(request) + response: GetPositionResponse = await self.client.GetPosition(request, timeout=timeout, metadata=md) return response.position - async def get_properties(self, extra: Optional[Dict[str, Any]] = None) -> Motor.Properties: - if extra is None: - extra = {} + async def get_properties( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Motor.Properties: + md = kwargs.get("metadata", self.Metadata()).proto request = GetPropertiesRequest(name=self.name, extra=dict_to_struct(extra)) - response: GetPropertiesResponse = await self.client.GetProperties(request) + response: GetPropertiesResponse = await self.client.GetProperties(request, timeout=timeout, metadata=md) return Motor.Properties(position_reporting=response.position_reporting) - async def stop(self, extra: Optional[Dict[str, Any]] = None): - if extra is None: - extra = {} + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto request = StopRequest(name=self.name, extra=dict_to_struct(extra)) - await self.client.Stop(request) + await self.client.Stop(request, timeout=timeout, metadata=md) - async def is_powered(self, extra: Optional[Dict[str, Any]] = None) -> bool: - if extra is None: - extra = {} + async def is_powered( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[bool, float]: + md = kwargs.get("metadata", self.Metadata()).proto request = IsPoweredRequest(name=self.name, extra=dict_to_struct(extra)) - response: IsPoweredResponse = await self.client.IsPowered(request) - return response.is_on + response: IsPoweredResponse = await self.client.IsPowered(request, timeout=timeout, metadata=md) + return response.is_on, response.power_pct + + async def is_moving(self, *, timeout: Optional[float] = None, **kwargs) -> bool: + md = kwargs.get("metadata", self.Metadata()).proto + request = IsMovingRequest(name=self.name) + response: IsMovingResponse = await self.client.IsMoving(request, timeout=timeout, metadata=md) + return response.is_moving + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/motor/motor.py b/src/viam/components/motor/motor.py index 6684d22d7..1a350eab1 100644 --- a/src/viam/components/motor/motor.py +++ b/src/viam/components/motor/motor.py @@ -1,121 +1,301 @@ import abc from dataclasses import dataclass -from typing import Any, Dict, Optional +from typing import Any, Dict, Final, Optional, Tuple -from viam.errors import NotSupportedError +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT from ..component_base import ComponentBase class Motor(ComponentBase): + """Motor represents a physical motor. + + This acts as an abstract base class for any drivers representing specific + motor implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.motor import Motor + + For more information, see `Motor component `_. + """ + @dataclass class Properties: position_reporting: bool - """ - Motor represents a physical motor. - - This acts as an abstract base class for any drivers representing specific - motor implementations. This cannot be used on its own. If the `__init__()` function is - overriden, it must call the `super().__init__()` function. - """ + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "motor" + ) @abc.abstractmethod - async def set_power(self, power: float, extra: Optional[Dict[str, Any]] = None): + async def set_power( + self, + power: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ Sets the "percentage" of power the motor should employ between -1 and 1. - When `power` is negative, the rotation will be in the backward direction. + When ``power`` is negative, the rotation will be in the backward direction. + + :: + + my_motor = Motor.from_robot(robot=machine, name="my_motor") + + # Set the power to 40% forwards. + await my_motor.set_power(power=0.4) Args: power (float): Power between -1 and 1 (negative implies backwards). + + For more information, see `Motor component `_. """ ... @abc.abstractmethod - async def go_for(self, rpm: float, revolutions: float, extra: Optional[Dict[str, Any]] = None): + async def go_for( + self, + rpm: float, + revolutions: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ - Spin the motor the specified number of `revolutions` at specified `rpm`. - When `rpm` or `revolutions` is a negative value, the rotation will be in the backward direction. - Note: if both `rpm` and `revolutions` are negative, the motor will spin in the forward direction. + Spin the motor the specified number of ``revolutions`` at specified ``rpm``. + When ``rpm`` or ``revolutions`` is a negative value, the rotation will be in the backward direction. + Note: if both ``rpm`` and ``revolutions`` are negative, the motor will spin in the forward direction. + + :: + + my_motor = Motor.from_robot(robot=machine, name="my_motor") + + # Turn the motor 7.2 revolutions at 60 RPM. + await my_motor.go_for(rpm=60, revolutions=7.2) Args: rpm (float): Speed at which the motor should move in rotations per minute (negative implies backwards). revolutions (float): Number of revolutions the motor should run for (negative implies backwards). + + For more information, see `Motor component `_. """ ... @abc.abstractmethod - async def go_to(self, rpm: float, position_revolutions: float, extra: Optional[Dict[str, Any]] = None): + async def go_to( + self, + rpm: float, + position_revolutions: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ Spin the motor to the specified position (provided in revolutions from home/zero), at the specified speed, in revolutions per minute. - Regardless of the directionality of the `rpm` this function will move + Regardless of the directionality of the ``rpm`` this function will move the motor towards the specified position. + :: + + my_motor = Motor.from_robot(robot=machine, name="my_motor") + + # Turn the motor to 8.3 revolutions from home at 75 RPM. + await my_motor.go_to(rpm=75, revolutions=8.3) + Args: rpm (float): Speed at which the motor should rotate (absolute value). position_revolutions (float): Target position relative to home/zero, in revolutions. + + For more information, see `Motor component `_. + """ + ... + + @abc.abstractmethod + async def set_rpm( + self, + rpm: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + """ + Spin the motor indefinitely at the specified speed, in revolutions per minute. + Positive ``rpm`` will result in forward movement and negative ``rpm`` will result in backwards movement + + :: + + my_motor = Motor.from_robot(robot=machine, name="my_motor") + + # Spin the motor at 75 RPM. + await my_motor.set_rpm(rpm=75) + + Args: + rpm (float): Speed at which the motor should rotate. + + For more information, see `Motor component `_. """ ... @abc.abstractmethod - async def reset_zero_position(self, offset: float, extra: Optional[Dict[str, Any]] = None): + async def reset_zero_position( + self, + offset: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ - Set the current position (modified by `offset`) to be the new zero (home) position. + Set the current position (modified by ``offset``) to be the new zero (home) position. + + :: + + my_motor = Motor.from_robot(robot=machine, name="my_motor") + + # Set the current position as the new home position with no offset. + await my_motor.reset_zero_position(offset=0.0) Args: offset (float): The offset from the current position to new home/zero position. + + For more information, see `Motor component `_. """ ... @abc.abstractmethod - async def get_position(self, extra: Optional[Dict[str, Any]] = None) -> float: + async def get_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> float: """ Report the position of the motor based on its encoder. The value returned is the number of revolutions relative to its zero position. This method will raise an exception if position reporting is not supported by the motor. + :: + + my_motor = Motor.from_robot(robot=machine, name="my_motor") + + # Get the current position of the motor. + position = await my_motor.get_position() + Returns: float: Number of revolutions the motor is away from zero/home. + + For more information, see `Motor component `_. """ ... @abc.abstractmethod - async def get_properties(self, extra: Optional[Dict[str, Any]] = None) -> Properties: + async def get_properties( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Properties: """ Report a dictionary mapping optional properties to whether it is supported by this motor. + :: + + my_motor = Motor.from_robot(robot=machine, name="my_motor") + + # Report a dictionary mapping optional properties to whether it is supported by + # this motor. + properties = await my_motor.get_properties() + + # Print out the properties. + print(f'Properties: {properties}') + Returns: Properties: Map of feature names to supported status. + + For more information, see `Motor component `_. """ ... @abc.abstractmethod - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): """ Stop the motor immediately, without any gradual step down. + + :: + + my_motor = Motor.from_robot(robot=machine, name="my_motor") + + # Stop the motor. + await my_motor.stop() + + For more information, see `Motor component `_. """ ... @abc.abstractmethod - async def is_powered(self, extra: Optional[Dict[str, Any]] = None) -> bool: + async def is_powered( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[bool, float]: """ Returns whether or not the motor is currently running. + :: + + my_motor = Motor.from_robot(robot=machine, name="my_motor") + + # Check whether the motor is currently running. + powered = await my_motor.is_powered() + + print('Powered: ', powered) + Returns: - bool: Indicates whether the motor is currently powered. + Tuple[bool, float]: A tuple containing two values; the first [0] value indicates whether the motor is currently powered, and + the second [1] value indicates the current power percentage of the motor. + + For more information, see `Motor component `_. """ ... + @abc.abstractmethod async def is_moving(self) -> bool: """ Get if the motor is currently moving. + :: + + my_motor = Motor.from_robot(robot=machine, name="my_motor") + + # Check whether the motor is currently moving. + moving = await my_motor.is_moving() + print('Moving: ', moving) + Returns: bool: Whether the motor is moving. + + For more information, see `Motor component `_. """ - raise NotSupportedError(f"Motor named {self.name} does not support returning whether it is moving") + ... diff --git a/src/viam/components/motor/service.py b/src/viam/components/motor/service.py index da2824635..98bb314c5 100644 --- a/src/viam/components/motor/service.py +++ b/src/viam/components/motor/service.py @@ -1,31 +1,36 @@ from grpclib.server import Stream -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError + +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.motor import ( - GetPropertiesRequest, - GetPropertiesResponse, GetPositionRequest, GetPositionResponse, + GetPropertiesRequest, + GetPropertiesResponse, GoForRequest, GoForResponse, GoToRequest, GoToResponse, + IsMovingRequest, + IsMovingResponse, IsPoweredRequest, IsPoweredResponse, - MotorServiceBase, ResetZeroPositionRequest, ResetZeroPositionResponse, SetPowerRequest, SetPowerResponse, + SetRPMRequest, + SetRPMResponse, StopRequest, StopResponse, + UnimplementedMotorServiceBase, ) -from viam.utils import struct_to_dict +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict from .motor import Motor -class MotorService(MotorServiceBase, ComponentServiceBase[Motor]): +class MotorRPCService(UnimplementedMotorServiceBase, ResourceRPCServiceBase[Motor]): """ gRPC Service for a Motor """ @@ -36,66 +41,65 @@ async def SetPower(self, stream: Stream[SetPowerRequest, SetPowerResponse]) -> N request = await stream.recv_message() assert request is not None name = request.name - try: - motor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await motor.set_power(request.power_pct, struct_to_dict(request.extra)) + motor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await motor.set_power(request.power_pct, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) await stream.send_message(SetPowerResponse()) async def GoFor(self, stream: Stream[GoForRequest, GoForResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - motor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await motor.go_for(request.rpm, request.revolutions, struct_to_dict(request.extra)) + motor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await motor.go_for(request.rpm, request.revolutions, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) await stream.send_message(GoForResponse()) async def GoTo(self, stream: Stream[GoToRequest, GoToResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - motor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await motor.go_to(request.rpm, request.position_revolutions, struct_to_dict(request.extra)) + motor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await motor.go_to( + request.rpm, request.position_revolutions, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata + ) await stream.send_message(GoToResponse()) + async def SetRPM(self, stream: Stream[SetRPMRequest, SetRPMResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + motor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await motor.set_rpm(request.rpm, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + await stream.send_message(SetRPMResponse()) + async def ResetZeroPosition(self, stream: Stream[ResetZeroPositionRequest, ResetZeroPositionResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - motor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await motor.reset_zero_position(request.offset, struct_to_dict(request.extra)) + motor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await motor.reset_zero_position(request.offset, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) await stream.send_message(ResetZeroPositionResponse()) async def GetPosition(self, stream: Stream[GetPositionRequest, GetPositionResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - motor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - position = await motor.get_position(struct_to_dict(request.extra)) + motor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + position = await motor.get_position(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) await stream.send_message(GetPositionResponse(position=position)) async def GetProperties(self, stream: Stream[GetPropertiesRequest, GetPropertiesResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - motor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - properties = await motor.get_properties(struct_to_dict(request.extra)) + motor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + properties = await motor.get_properties(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetPropertiesResponse(**properties.__dict__) await stream.send_message(response) @@ -103,11 +107,9 @@ async def Stop(self, stream: Stream[StopRequest, StopResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - motor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await motor.stop(struct_to_dict(request.extra)) + motor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await motor.stop(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = StopResponse() await stream.send_message(response) @@ -115,9 +117,34 @@ async def IsPowered(self, stream: Stream[IsPoweredRequest, IsPoweredResponse]) - request = await stream.recv_message() assert request is not None name = request.name - try: - motor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - is_powered = await motor.is_powered(struct_to_dict(request.extra)) - await stream.send_message(IsPoweredResponse(is_on=is_powered)) + motor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + is_powered, power_pct = await motor.is_powered(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + await stream.send_message(IsPoweredResponse(is_on=is_powered, power_pct=power_pct)) + + async def IsMoving(self, stream: Stream[IsMovingRequest, IsMovingResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + motor = self.get_resource(name) + is_moving = await motor.is_moving() + response = IsMovingResponse(is_moving=is_moving) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + motor = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await motor.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + motor = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await motor.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/movement_sensor/__init__.py b/src/viam/components/movement_sensor/__init__.py index 7f7dee639..042c0d3cd 100644 --- a/src/viam/components/movement_sensor/__init__.py +++ b/src/viam/components/movement_sensor/__init__.py @@ -1,8 +1,9 @@ -from viam.registry import ComponentRegistration, Registry +from viam.proto.common import GeoPoint, Orientation, Vector3 +from viam.resource.registry import Registry, ResourceRegistration from .client import MovementSensorClient -from .movement_sensor import GeoPoint, MovementSensor, Orientation, Vector3 -from .service import MovementSensorService +from .movement_sensor import MovementSensor +from .service import MovementSensorRPCService __all__ = [ "MovementSensor", @@ -11,11 +12,10 @@ "Vector3", ] -Registry.register( - ComponentRegistration( +Registry.register_api( + ResourceRegistration( MovementSensor, - "movement_sensor", - MovementSensorService, + MovementSensorRPCService, lambda name, channel: MovementSensorClient(name, channel), ) ) diff --git a/src/viam/components/movement_sensor/client.py b/src/viam/components/movement_sensor/client.py index fdd3edf5e..c03dfd87b 100644 --- a/src/viam/components/movement_sensor/client.py +++ b/src/viam/components/movement_sensor/client.py @@ -1,17 +1,17 @@ -from typing import Any, Dict, Mapping, Tuple +from typing import Any, Dict, List, Mapping, Optional, Tuple from grpclib.client import Channel -from viam.components.generic.client import do_command from viam.components.movement_sensor.movement_sensor import MovementSensor -from viam.proto.common import GeoPoint, Orientation, Vector3 +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, GetReadingsRequest, GetReadingsResponse from viam.proto.component.movementsensor import ( GetAccuracyRequest, - GetAccuracyResponse, GetAngularVelocityRequest, GetAngularVelocityResponse, GetCompassHeadingRequest, GetCompassHeadingResponse, + GetLinearAccelerationRequest, + GetLinearAccelerationResponse, GetLinearVelocityRequest, GetLinearVelocityResponse, GetOrientationRequest, @@ -22,9 +22,13 @@ GetPropertiesResponse, MovementSensorServiceStub, ) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import SensorReading, ValueTypes, dict_to_struct, get_geometries, sensor_readings_value_to_native, struct_to_dict +from . import GeoPoint, Orientation, Vector3 -class MovementSensorClient(MovementSensor): + +class MovementSensorClient(MovementSensor, ReconfigurableResourceRPCClientBase): """gRPC client for the MovementSensor component.""" def __init__(self, name: str, channel: Channel): @@ -32,43 +36,126 @@ def __init__(self, name: str, channel: Channel): self.client = MovementSensorServiceStub(channel) super().__init__(name) - async def get_position(self) -> Tuple[GeoPoint, float]: - request = GetPositionRequest(name=self.name) - response: GetPositionResponse = await self.client.GetPosition(request) - return response.coordinate, response.altitude_mm + async def get_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[GeoPoint, float]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPositionRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetPositionResponse = await self.client.GetPosition(request, timeout=timeout, metadata=md) + return response.coordinate, response.altitude_m - async def get_linear_velocity(self) -> Vector3: - request = GetLinearVelocityRequest(name=self.name) - response: GetLinearVelocityResponse = await self.client.GetLinearVelocity(request) + async def get_linear_velocity( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Vector3: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetLinearVelocityRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetLinearVelocityResponse = await self.client.GetLinearVelocity(request, timeout=timeout, metadata=md) return response.linear_velocity - async def get_angular_velocity(self) -> Vector3: - request = GetAngularVelocityRequest(name=self.name) - response: GetAngularVelocityResponse = await self.client.GetAngularVelocity(request) + async def get_angular_velocity( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Vector3: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetAngularVelocityRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetAngularVelocityResponse = await self.client.GetAngularVelocity(request, timeout=timeout, metadata=md) return response.angular_velocity - async def get_compass_heading(self) -> float: - request = GetCompassHeadingRequest(name=self.name) - response: GetCompassHeadingResponse = await self.client.GetCompassHeading(request) + async def get_linear_acceleration( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Vector3: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetLinearAccelerationRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetLinearAccelerationResponse = await self.client.GetLinearAcceleration(request, timeout=timeout, metadata=md) + return response.linear_acceleration + + async def get_compass_heading( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> float: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetCompassHeadingRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetCompassHeadingResponse = await self.client.GetCompassHeading(request, timeout=timeout, metadata=md) return response.value - async def get_orientation(self) -> Orientation: - request = GetOrientationRequest(name=self.name) - response: GetOrientationResponse = await self.client.GetOrientation(request) + async def get_orientation( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Orientation: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetOrientationRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetOrientationResponse = await self.client.GetOrientation(request, timeout=timeout, metadata=md) return response.orientation - async def get_properties(self) -> MovementSensor.Properties: - request = GetPropertiesRequest(name=self.name) - response: GetPropertiesResponse = await self.client.GetProperties(request) - return response + async def get_properties( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> MovementSensor.Properties: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPropertiesRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetPropertiesResponse = await self.client.GetProperties(request, timeout=timeout, metadata=md) + return MovementSensor.Properties.from_proto(response) + + async def get_accuracy( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> MovementSensor.Accuracy: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetAccuracyRequest(name=self.name, extra=dict_to_struct(extra)) + return await self.client.GetAccuracy(request, timeout=timeout, metadata=md) + + async def get_readings( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, SensorReading]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetReadingsRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetReadingsResponse = await self.client.GetReadings(request, timeout=timeout, metadata=md) - async def get_accuracy(self) -> Mapping[str, float]: - request = GetAccuracyRequest(name=self.name) - response: GetAccuracyResponse = await self.client.GetAccuracy(request) - return response.accuracy_mm + return sensor_readings_value_to_native(response.readings) - async def get_readings(self) -> Mapping[str, Any]: - return await super().get_readings() + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/movement_sensor/movement_sensor.py b/src/viam/components/movement_sensor/movement_sensor.py index e81b62bc0..214453634 100644 --- a/src/viam/components/movement_sensor/movement_sensor.py +++ b/src/viam/components/movement_sensor/movement_sensor.py @@ -1,104 +1,253 @@ import abc -from typing import Any, Mapping, Tuple +import sys +from dataclasses import dataclass +from typing import Any, Dict, Final, Mapping, Optional, Tuple -from viam.proto.common import GeoPoint, Orientation, Vector3 -from viam.proto.component.movementsensor import GetPropertiesResponse +from typing_extensions import Self -from ..sensor import Sensor +from viam.components.component_base import ComponentBase +from viam.proto.component.movementsensor import GetAccuracyResponse, GetPropertiesResponse +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT +from viam.utils import SensorReading +from . import GeoPoint, Orientation, Vector3 -class MovementSensor(Sensor): +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + + +class MovementSensor(ComponentBase): """MovementSensor reports information about the robot's direction, position and speed. This acts as an abstract base class for any sensors that can provide data regarding the robot's direction, position, and speed. - This cannot be used on its own. If the `__init__()` function is overriden, it must call the `super().__init__()` function. + This cannot be used on its own. If the ``__init__()`` function is overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.movement_sensor import MovementSensor + + For more information, see `Movement Sensor component `_. """ - Properties = GetPropertiesResponse + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "movement_sensor" + ) + + Accuracy: "TypeAlias" = GetAccuracyResponse + + @dataclass + class Properties: + linear_acceleration_supported: bool + angular_velocity_supported: bool + orientation_supported: bool + position_supported: bool + compass_heading_supported: bool + linear_velocity_supported: bool + + @property + def proto(self) -> GetPropertiesResponse: + return GetPropertiesResponse( + linear_acceleration_supported=self.linear_acceleration_supported, + angular_velocity_supported=self.angular_velocity_supported, + orientation_supported=self.orientation_supported, + position_supported=self.position_supported, + compass_heading_supported=self.compass_heading_supported, + linear_velocity_supported=self.linear_velocity_supported, + ) + + @classmethod + def from_proto(cls, proto: GetPropertiesResponse) -> Self: + return cls( + linear_acceleration_supported=proto.linear_acceleration_supported, + angular_velocity_supported=proto.angular_velocity_supported, + orientation_supported=proto.orientation_supported, + position_supported=proto.position_supported, + compass_heading_supported=proto.compass_heading_supported, + linear_velocity_supported=proto.linear_velocity_supported, + ) @abc.abstractmethod - async def get_position(self) -> Tuple[GeoPoint, float]: - """Get the current GeoPoint (latitude, longitude) and altitude (mm) + async def get_position( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Tuple[GeoPoint, float]: + """Get the current GeoPoint (latitude, longitude) and altitude (m) + + :: + + my_movement_sensor = MovementSensor.from_robot( + robot=robot, + name="my_movement_sensor") + + # Get the current position of the movement sensor. + position = await my_movement_sensor.get_position() Returns: - Tuple[GeoPoint, float]: The current lat/long, along with the altitude in mm + Tuple[GeoPoint, float]: The current lat/long, along with the altitude in m + + For more information, see `Movement Sensor component `_. """ ... @abc.abstractmethod - async def get_linear_velocity(self) -> Vector3: - """Get the current linear velocity as a `Vector3` with x, y, and z axes represented in mm/sec + async def get_linear_velocity(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Vector3: + """Get the current linear velocity as a ``Vector3`` with x, y, and z axes represented in m/sec + + :: + + my_movement_sensor = MovementSensor.from_robot( + robot=robot, name="my_movement_sensor") + + # Get the current linear velocity of the movement sensor. + lin_vel = await my_movement_sensor.get_linear_velocity() Returns: - Vector3: The linear velocity in mm/sec + Vector3: The linear velocity in m/sec + + For more information, see `Movement Sensor component `_. """ ... @abc.abstractmethod - async def get_angular_velocity(self) -> Vector3: - """Get the current angular velocity as a `Vector3` with x, y, and z axes represented in radians/sec + async def get_angular_velocity(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Vector3: + """Get the current angular velocity as a ``Vector3`` with x, y, and z axes represented in degrees/sec + + :: + + my_movement_sensor = MovementSensor.from_robot( + robot=robot, name="my_movement_sensor") + + # Get the current angular velocity of the movement sensor. + ang_vel = await my_movement_sensor.get_angular_velocity() + + # Get the y component of angular velocity. + y_ang_vel = ang_vel.y + + Returns: + Vector3: The angular velocity in degrees/sec + + For more information, see `Movement Sensor component `_. + """ + ... + + @abc.abstractmethod + async def get_linear_acceleration( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Vector3: + """Get the current linear acceleration as a ``Vector3`` with x, y, and z axes represented in m/sec^2 + + :: + + my_movement_sensor = MovementSensor.from_robot( + robot=robot, name="my_movement_sensor") + + # Get the current linear acceleration of the movement sensor. + lin_accel = await my_movement_sensor.get_linear_acceleration() + + # Get the x component of linear acceleration. + x_lin_accel = lin_accel.x Returns: - Vector3: The angular velocity in rad/sec + Vector3: The linear acceleration in m/sec^2 + + For more information, see `Movement Sensor component `_. """ ... @abc.abstractmethod - async def get_compass_heading(self) -> float: + async def get_compass_heading(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> float: """Get the current compass heading in degrees + :: + + my_movement_sensor = MovementSensor.from_robot( + robot=robot, name="my_movement_sensor") + + # Get the current compass heading of the movement sensor. + heading = await my_movement_sensor.get_compass_heading() + Returns: float: The compass heading in degrees + + For more information, see `Movement Sensor component `_. """ ... @abc.abstractmethod - async def get_orientation(self) -> Orientation: + async def get_orientation(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Orientation: """Get the current orientation + :: + + my_movement_sensor = MovementSensor.from_robot( + robot=robot, name="my_movement_sensor") + + # Get the current orientation vector of the movement sensor. + orientation = await my_movement_sensor.get_orientation() + Returns: Orientation: The orientation + + For more information, see `Movement Sensor component `_. """ ... @abc.abstractmethod - async def get_properties(self) -> Properties: + async def get_properties(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Properties: """Get the supported properties of this sensor + :: + + my_movement_sensor = MovementSensor.from_robot( + robot=robot, name="my_movement_sensor") + + # Get the supported properties of the movement sensor. + properties = await my_movement_sensor.get_properties() + Returns: - Properties: The properties + MovementSensor.Properties: The properties + + For more information, see `Movement Sensor component `_. """ ... @abc.abstractmethod - async def get_accuracy(self) -> Mapping[str, float]: + async def get_accuracy(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Accuracy: """Get the accuracy of the various sensors + :: + + my_movement_sensor = MovementSensor.from_robot( + robot=robot, name="my_movement_sensor") + + # Get the accuracy of the movement sensor. + accuracy = await my_movement_sensor.get_accuracy() + Returns: - Dict[str, float]: The accuracy in mm + MovementSensor.Accuracy: The accuracies of the movement sensor + + For more information, see `Movement Sensor component `_. """ ... - async def get_readings(self) -> Mapping[str, Any]: + async def get_readings( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Mapping[str, SensorReading]: """Obtain the measurements/data specific to this sensor. + If a sensor is not configured to have a measurement or fails to read a piece of data, it will not appear in the readings dictionary. + + :: + + my_movement_sensor = MovementSensor.from_robot( + robot=robot, name="my_movement_sensor") + + # Get the latest readings from the movement sensor. + readings = await my_movement_sensor.get_readings() Returns: - Mapping[str, Any]: The readings for the MovementSensor: - { - position: GeoPoint, - altitude: float, - linear_velocity: Vector3, - angular_velocity: Vector3, - compass: float, - orientation: Orientation - } + Mapping[str, Any]: The readings for the MovementSensor. Can be of any type. + + For more information, see `Movement Sensor component `_. """ - (pos, alt) = await self.get_position() - return { - "position": pos, - "altitude": alt, - "linear_velocity": await self.get_linear_velocity(), - "angular_velocity": await self.get_angular_velocity(), - "compass": await self.get_compass_heading(), - "orientation": await self.get_orientation(), - } + ... diff --git a/src/viam/components/movement_sensor/service.py b/src/viam/components/movement_sensor/service.py index ec606cda3..003dbae01 100644 --- a/src/viam/components/movement_sensor/service.py +++ b/src/viam/components/movement_sensor/service.py @@ -1,8 +1,14 @@ from grpclib.server import Stream from viam.components.movement_sensor.movement_sensor import MovementSensor -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, + GetGeometriesRequest, + GetGeometriesResponse, + GetReadingsRequest, + GetReadingsResponse, +) from viam.proto.component.movementsensor import ( GetAccuracyRequest, GetAccuracyResponse, @@ -10,6 +16,8 @@ GetAngularVelocityResponse, GetCompassHeadingRequest, GetCompassHeadingResponse, + GetLinearAccelerationRequest, + GetLinearAccelerationResponse, GetLinearVelocityRequest, GetLinearVelocityResponse, GetOrientationRequest, @@ -20,9 +28,11 @@ GetPropertiesResponse, MovementSensorServiceBase, ) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, sensor_readings_native_to_value, struct_to_dict -class MovementSensorService(MovementSensorServiceBase, ComponentServiceBase[MovementSensor]): +class MovementSensorRPCService(MovementSensorServiceBase, ResourceRPCServiceBase[MovementSensor]): """ gRPC Service for a MovementSensor """ @@ -33,11 +43,9 @@ async def GetLinearVelocity(self, stream: Stream[GetLinearVelocityRequest, GetLi request = await stream.recv_message() assert request is not None name = request.name - try: - sensor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - velocity = await sensor.get_linear_velocity() + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + velocity = await sensor.get_linear_velocity(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetLinearVelocityResponse(linear_velocity=velocity) await stream.send_message(response) @@ -45,23 +53,29 @@ async def GetAngularVelocity(self, stream: Stream[GetAngularVelocityRequest, Get request = await stream.recv_message() assert request is not None name = request.name - try: - sensor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - velocity = await sensor.get_angular_velocity() + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + velocity = await sensor.get_angular_velocity(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetAngularVelocityResponse(angular_velocity=velocity) await stream.send_message(response) + async def GetLinearAcceleration(self, stream: Stream[GetLinearAccelerationRequest, GetLinearAccelerationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + acceleration = await sensor.get_linear_acceleration(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = GetLinearAccelerationResponse(linear_acceleration=acceleration) + await stream.send_message(response) + async def GetCompassHeading(self, stream: Stream[GetCompassHeadingRequest, GetCompassHeadingResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - sensor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - heading = await sensor.get_compass_heading() + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + heading = await sensor.get_compass_heading(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetCompassHeadingResponse(value=heading) await stream.send_message(response) @@ -69,11 +83,9 @@ async def GetOrientation(self, stream: Stream[GetOrientationRequest, GetOrientat request = await stream.recv_message() assert request is not None name = request.name - try: - sensor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - orientation = await sensor.get_orientation() + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + orientation = await sensor.get_orientation(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetOrientationResponse(orientation=orientation) await stream.send_message(response) @@ -81,33 +93,54 @@ async def GetPosition(self, stream: Stream[GetPositionRequest, GetPositionRespon request = await stream.recv_message() assert request is not None name = request.name - try: - sensor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - point, alt = await sensor.get_position() - response = GetPositionResponse(coordinate=point, altitude_mm=alt) + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + point, alt = await sensor.get_position(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = GetPositionResponse(coordinate=point, altitude_m=alt) await stream.send_message(response) async def GetProperties(self, stream: Stream[GetPropertiesRequest, GetPropertiesResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - sensor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - response = await sensor.get_properties() - await stream.send_message(response) + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + response = await sensor.get_properties(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + await stream.send_message(response.proto) async def GetAccuracy(self, stream: Stream[GetAccuracyRequest, GetAccuracyResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - sensor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - accuracy = await sensor.get_accuracy() - response = GetAccuracyResponse(accuracy_mm=accuracy) + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + accuracy = await sensor.get_accuracy(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + await stream.send_message(accuracy) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + sensor = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await sensor.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + sensor = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await sensor.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) + + async def GetReadings(self, stream: Stream[GetReadingsRequest, GetReadingsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + readings = await sensor.get_readings(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = GetReadingsResponse(readings=sensor_readings_native_to_value(readings)) await stream.send_message(response) diff --git a/src/viam/components/pose_tracker/__init__.py b/src/viam/components/pose_tracker/__init__.py index 5356e9f15..90af107b4 100644 --- a/src/viam/components/pose_tracker/__init__.py +++ b/src/viam/components/pose_tracker/__init__.py @@ -1,18 +1,17 @@ -from viam.registry import ComponentRegistration, Registry +from viam.resource.registry import Registry, ResourceRegistration from .client import PoseTrackerClient from .pose_tracker import PoseTracker -from .service import PoseTrackerService +from .service import PoseTrackerRPCService __all__ = [ "PoseTracker", ] -Registry.register( - ComponentRegistration( +Registry.register_api( + ResourceRegistration( PoseTracker, - "pose_tracker", - PoseTrackerService, + PoseTrackerRPCService, lambda name, channel: PoseTrackerClient(name, channel), ) ) diff --git a/src/viam/components/pose_tracker/client.py b/src/viam/components/pose_tracker/client.py index db9c92c6a..6f0b3344c 100644 --- a/src/viam/components/pose_tracker/client.py +++ b/src/viam/components/pose_tracker/client.py @@ -1,14 +1,16 @@ -from typing import Any, Dict, List +from typing import Any, Dict, List, Mapping, Optional from grpclib.client import Channel -from viam.components.generic.client import do_command -from viam.proto.common import PoseInFrame + +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, PoseInFrame from viam.proto.component.posetracker import GetPosesRequest, GetPosesResponse, PoseTrackerServiceStub +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict from .pose_tracker import PoseTracker -class PoseTrackerClient(PoseTracker): +class PoseTrackerClient(PoseTracker, ReconfigurableResourceRPCClientBase): """ gRPC client for the PoseTracker component. """ @@ -18,13 +20,31 @@ def __init__(self, name: str, channel: Channel): self.client = PoseTrackerServiceStub(channel) super().__init__(name) - async def get_poses(self, body_names: List[str]) -> Dict[str, PoseInFrame]: - request = GetPosesRequest( - name=self.name, - body_names=body_names, - ) - response: GetPosesResponse = await self.client.GetPoses(request) + async def get_poses( + self, + body_names: List[str], + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Dict[str, PoseInFrame]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPosesRequest(name=self.name, body_names=body_names, extra=dict_to_struct(extra)) + response: GetPosesResponse = await self.client.GetPoses(request, timeout=timeout, metadata=md) return {key: response.body_poses[key] for key in response.body_poses.keys()} - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/pose_tracker/pose_tracker.py b/src/viam/components/pose_tracker/pose_tracker.py index 64bf41d9b..29329d59a 100644 --- a/src/viam/components/pose_tracker/pose_tracker.py +++ b/src/viam/components/pose_tracker/pose_tracker.py @@ -1,7 +1,8 @@ import abc -from typing import Dict, List +from typing import Any, Dict, Final, List, Mapping, Optional from viam.proto.common import PoseInFrame +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT from ..component_base import ComponentBase @@ -11,12 +12,23 @@ class PoseTracker(ComponentBase): PoseTracker represents a physical pose or motion tracking device. This acts as an abstract base class for any drivers representing specific - pose tracker implementations. This cannot be used on its own. If the `__init__()` function is - overriden, it must call the `super().__init__()` function. + pose tracker implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. """ + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "pose_tracker" + ) + @abc.abstractmethod - async def get_poses(self, body_names: List[str]) -> Dict[str, PoseInFrame]: + async def get_poses( + self, + body_names: List[str], + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Dict[str, PoseInFrame]: """ Returns the current pose of each body tracked by the pose tracker. diff --git a/src/viam/components/pose_tracker/service.py b/src/viam/components/pose_tracker/service.py index 5ebbeab72..96fc069f9 100644 --- a/src/viam/components/pose_tracker/service.py +++ b/src/viam/components/pose_tracker/service.py @@ -1,12 +1,14 @@ from grpclib.server import Stream -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError + +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.posetracker import GetPosesRequest, GetPosesResponse, PoseTrackerServiceBase +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict from .pose_tracker import PoseTracker -class PoseTrackerService(PoseTrackerServiceBase, ComponentServiceBase[PoseTracker]): +class PoseTrackerRPCService(PoseTrackerServiceBase, ResourceRPCServiceBase[PoseTracker]): """ gRPC service for a pose tracker """ @@ -17,9 +19,27 @@ async def GetPoses(self, stream: Stream[GetPosesRequest, GetPosesResponse]) -> N request = await stream.recv_message() assert request is not None name = request.name - try: - pose_tracker = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - poses = await pose_tracker.get_poses(list(request.body_names)) + pose_tracker = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + poses = await pose_tracker.get_poses( + list(request.body_names), extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata + ) await stream.send_message(GetPosesResponse(body_poses=poses)) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + pose_tracker = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await pose_tracker.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + pose_tracker = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await pose_tracker.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/power_sensor/__init__.py b/src/viam/components/power_sensor/__init__.py new file mode 100644 index 000000000..e23c782ef --- /dev/null +++ b/src/viam/components/power_sensor/__init__.py @@ -0,0 +1,17 @@ +from viam.resource.registry import Registry, ResourceRegistration + +from .client import PowerSensorClient +from .power_sensor import PowerSensor +from .service import PowerSensorRPCService + +__all__ = [ + "PowerSensor", +] + +Registry.register_api( + ResourceRegistration( + PowerSensor, + PowerSensorRPCService, + lambda name, channel: PowerSensorClient(name, channel), + ) +) diff --git a/src/viam/components/power_sensor/client.py b/src/viam/components/power_sensor/client.py new file mode 100644 index 000000000..0490b19fa --- /dev/null +++ b/src/viam/components/power_sensor/client.py @@ -0,0 +1,86 @@ +from typing import Any, Dict, Mapping, Optional, Tuple + +from grpclib.client import Channel + +from viam.components.power_sensor.power_sensor import PowerSensor +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetReadingsRequest, GetReadingsResponse +from viam.proto.component.powersensor import ( + GetCurrentRequest, + GetCurrentResponse, + GetPowerRequest, + GetPowerResponse, + GetVoltageRequest, + GetVoltageResponse, + PowerSensorServiceStub, +) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import SensorReading, ValueTypes, dict_to_struct, sensor_readings_value_to_native, struct_to_dict + + +class PowerSensorClient(PowerSensor, ReconfigurableResourceRPCClientBase): + """gRPC client for the PowerSensor component.""" + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = PowerSensorServiceStub(channel) + super().__init__(name) + + async def get_voltage( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[float, bool]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetVoltageRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetVoltageResponse = await self.client.GetVoltage(request, timeout=timeout, metadata=md) + return response.volts, response.is_ac + + async def get_current( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[float, bool]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetCurrentRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetCurrentResponse = await self.client.GetCurrent(request, timeout=timeout, metadata=md) + return response.amperes, response.is_ac + + async def get_power( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> float: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPowerRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetPowerResponse = await self.client.GetPower(request, timeout=timeout, metadata=md) + return response.watts + + async def get_readings( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, SensorReading]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetReadingsRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetReadingsResponse = await self.client.GetReadings(request, timeout=timeout, metadata=md) + return sensor_readings_value_to_native(response.readings) + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) diff --git a/src/viam/components/power_sensor/power_sensor.py b/src/viam/components/power_sensor/power_sensor.py new file mode 100644 index 000000000..2fd47023d --- /dev/null +++ b/src/viam/components/power_sensor/power_sensor.py @@ -0,0 +1,104 @@ +import abc +from typing import Any, Dict, Final, Mapping, Optional, Tuple + +from viam.components.component_base import ComponentBase +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT +from viam.utils import SensorReading + + +class PowerSensor(ComponentBase): + """PowerSensor reports information about voltage, current and power. + + This acts as an abstract base class for any sensors that can provide data regarding voltage, current and/or power. + This cannot be used on its own. If the ``__init__()`` function is overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.power_sensor import PowerSensor + + For more information, see `Power Sensor component `_. + """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "power_sensor" + ) + + @abc.abstractmethod + async def get_voltage(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Tuple[float, bool]: + """Return the voltage reading of a specified device and whether it is AC or DC. + + :: + + my_power_sensor = PowerSensor.from_robot(robot=machine, name='my_power_sensor') + + # Get the voltage reading from the power sensor + voltage, is_ac = await my_power_sensor.get_voltage() + print("The voltage is", voltage, "V, Is AC:", is_ac) + + Returns: + Tuple[float, bool]: A float representing the voltage reading in V. A bool indicating whether the voltage is AC (`true`) or DC + (`false`). + + For more information, see `Power Sensor component `_. + """ + ... + + @abc.abstractmethod + async def get_current(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Tuple[float, bool]: + """Return the current of a specified device and whether it is AC or DC. + + :: + + my_power_sensor = PowerSensor.from_robot(robot=machine, name='my_power_sensor') + + # Get the current reading from the power sensor + current, is_ac = await my_power_sensor.get_current() + print("The current is ", current, " A, Is AC: ", is_ac) + + Returns: + Tuple[float, bool]: A tuple which includes a float representing the current reading in amps, and a bool indicating whether the + current is AC (`true`) or DC (`false`). + + For more information, see `Power Sensor component `_. + """ + ... + + @abc.abstractmethod + async def get_power(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> float: + """Return the power reading in watts. + + :: + + my_power_sensor = PowerSensor.from_robot(robot=machine, name='my_power_sensor') + + # Get the power reading from the power sensor + power = await my_power_sensor.get_power() + print("The power is", power, "Watts") + + Returns: + float: The power reading in watts. + + For more information, see `Power Sensor component `_. + """ + ... + + async def get_readings( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Mapping[str, SensorReading]: + """Get the measurements or readings that this power sensor provides. If a sensor is not configured correctly or fails to read a + piece of data, it will not appear in the readings dictionary. + + :: + + my_power_sensor = PowerSensor.from_robot(robot=machine, name='my_power_sensor') + + # Get the readings provided by the sensor. + readings = await my_power_sensor.get_readings() + + Returns: + Mapping[str, Any]: The readings for the PowerSensor. Can be of any type. Includes voltage in volts (float), current in + amperes (float), is_ac (bool), and power in watts (float). + + For more information, see `Power Sensor component `_. + """ + ... diff --git a/src/viam/components/power_sensor/service.py b/src/viam/components/power_sensor/service.py new file mode 100644 index 000000000..1de709fdc --- /dev/null +++ b/src/viam/components/power_sensor/service.py @@ -0,0 +1,72 @@ +from grpclib.server import Stream + +from viam.components.power_sensor.power_sensor import PowerSensor +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetReadingsRequest, GetReadingsResponse +from viam.proto.component.powersensor import ( + GetCurrentRequest, + GetCurrentResponse, + GetPowerRequest, + GetPowerResponse, + GetVoltageRequest, + GetVoltageResponse, + PowerSensorServiceBase, +) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, sensor_readings_native_to_value, struct_to_dict + + +class PowerSensorRPCService(PowerSensorServiceBase, ResourceRPCServiceBase[PowerSensor]): + """ + gRPC Service for a PowerSensor + """ + + RESOURCE_TYPE = PowerSensor + + async def GetReadings(self, stream: Stream[GetReadingsRequest, GetReadingsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + readings = await sensor.get_readings(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = GetReadingsResponse(readings=sensor_readings_native_to_value(readings)) + await stream.send_message(response) + + async def GetVoltage(self, stream: Stream[GetVoltageRequest, GetVoltageResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + voltage, is_ac = await sensor.get_voltage(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = GetVoltageResponse(volts=voltage, is_ac=is_ac) + await stream.send_message(response) + + async def GetCurrent(self, stream: Stream[GetCurrentRequest, GetCurrentResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + current, is_ac = await sensor.get_current(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = GetCurrentResponse(amperes=current, is_ac=is_ac) + await stream.send_message(response) + + async def GetPower(self, stream: Stream[GetPowerRequest, GetPowerResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + power = await sensor.get_power(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = GetPowerResponse(watts=power) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + sensor = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await sensor.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) diff --git a/src/viam/components/resource_manager.py b/src/viam/components/resource_manager.py deleted file mode 100644 index 740798df6..000000000 --- a/src/viam/components/resource_manager.py +++ /dev/null @@ -1,60 +0,0 @@ -from typing import Dict, List, Type, TypeVar - -from .component_base import ComponentBase -from ..errors import ComponentNotFoundError, DuplicateComponentError - - -ResourceType = TypeVar("ResourceType", bound=ComponentBase) - - -class ResourceManager: - """ - Registry containing all components registered to this server. - """ - - components: Dict[str, ComponentBase] - - def __init__(self, components: List[ComponentBase] = []) -> None: - self.components = {} - for component in components: - self.register(component) - - def register(self, component: ComponentBase): - """ - Register a new component with the registry. - Components may not have the same name. - - Raises: - DuplicateComponentError: Error if attempting to register component - with the name of an existing component - - Args: - component (ComponentBase): The component to register - """ - if component.name in self.components: - raise DuplicateComponentError(component.name) - self.components[component.name] = component - - def get_component(self, of_type: Type[ResourceType], name: str) -> ResourceType: - """ - Return a component from the registry. - - Args: - of_type (Type[ResourceType]): The type of the component - name (str): The name of the component - - Raises: - ComponentNotFoundError: Error if component with the given type - and name does not exist in the registry - - Returns: - ResourceType: The component - """ - component = self.components.get(name, None) - if component and isinstance(component, of_type): - return component - - class_name = str(of_type) - if len(class_name.split(".")) > 2: - class_name = class_name.split(".")[2] - raise ComponentNotFoundError(class_name, name) diff --git a/src/viam/components/sensor/__init__.py b/src/viam/components/sensor/__init__.py index be309e3dd..264aa1797 100644 --- a/src/viam/components/sensor/__init__.py +++ b/src/viam/components/sensor/__init__.py @@ -1,18 +1,18 @@ -from viam.registry import ComponentRegistration, Registry +import viam.gen.component.sensor.v1.sensor_pb2 # Need this import for Sensor service descriptors to resolve +from viam.resource.registry import Registry, ResourceRegistration from .client import SensorClient from .sensor import Sensor -from .service import SensorService +from .service import SensorRPCService __all__ = [ "Sensor", ] -Registry.register( - ComponentRegistration( +Registry.register_api( + ResourceRegistration( Sensor, - "sensor", - SensorService, + SensorRPCService, lambda name, channel: SensorClient(name, channel), ) ) diff --git a/src/viam/components/sensor/client.py b/src/viam/components/sensor/client.py index fcc65f93b..b4017a5b8 100644 --- a/src/viam/components/sensor/client.py +++ b/src/viam/components/sensor/client.py @@ -1,19 +1,16 @@ -from typing import Any, Dict, Mapping +from typing import Any, Dict, List, Mapping, Optional from grpclib.client import Channel -from viam.components.generic.client import do_command -from viam.proto.component.sensor import ( - GetReadingsRequest, - GetReadingsResponse, - SensorServiceStub, -) -from viam.utils import sensor_readings_value_to_native +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry, GetReadingsRequest, GetReadingsResponse +from viam.proto.component.sensor import SensorServiceStub +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import SensorReading, ValueTypes, dict_to_struct, get_geometries, sensor_readings_value_to_native, struct_to_dict from .sensor import Sensor -class SensorClient(Sensor): +class SensorClient(Sensor, ReconfigurableResourceRPCClientBase): """ gRPC client for the Sensor component. """ @@ -23,10 +20,30 @@ def __init__(self, name: str, channel: Channel): self.client = SensorServiceStub(channel) super().__init__(name) - async def get_readings(self) -> Mapping[str, Any]: - request = GetReadingsRequest(name=self.name) - response: GetReadingsResponse = await self.client.GetReadings(request) + async def get_readings( + self, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, SensorReading]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetReadingsRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetReadingsResponse = await self.client.GetReadings(request, timeout=timeout, metadata=md) return sensor_readings_value_to_native(response.readings) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/sensor/sensor.py b/src/viam/components/sensor/sensor.py index 3e1015452..674662666 100644 --- a/src/viam/components/sensor/sensor.py +++ b/src/viam/components/sensor/sensor.py @@ -1,5 +1,8 @@ import abc -from typing import Any, Mapping +from typing import Any, Final, Mapping, Optional + +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT +from viam.utils import SensorReading from ..component_base import ComponentBase @@ -9,16 +12,37 @@ class Sensor(ComponentBase): Sensor represents a physical sensing device that can provide measurement readings. This acts as an abstract base class for any drivers representing specific - sensor implementations. This cannot be used on its own. If the `__init__()` function is - overriden, it must call the `super().__init__()` function. + sensor implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.sensor import Sensor + + For more information, see `Sensor component `_. """ + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "sensor" + ) + @abc.abstractmethod - async def get_readings(self) -> Mapping[str, Any]: + async def get_readings( + self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Mapping[str, SensorReading]: """ Obtain the measurements/data specific to this sensor. + :: + + my_sensor = Sensor.from_robot(robot=machine, name='my_sensor') + + # Get the readings provided by the sensor. + readings = await my_sensor.get_readings() + Returns: Mapping[str, Any]: The measurements. Can be of any type. + + For more information, see `Sensor component `_. """ ... diff --git a/src/viam/components/sensor/service.py b/src/viam/components/sensor/service.py index ce34e1532..d669b0ed6 100644 --- a/src/viam/components/sensor/service.py +++ b/src/viam/components/sensor/service.py @@ -1,18 +1,21 @@ from grpclib.server import Stream -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError -from viam.proto.component.sensor import ( +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, + GetGeometriesRequest, + GetGeometriesResponse, GetReadingsRequest, GetReadingsResponse, - SensorServiceBase, ) -from viam.utils import sensor_readings_native_to_value +from viam.proto.component.sensor import SensorServiceBase +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, sensor_readings_native_to_value, struct_to_dict from .sensor import Sensor -class SensorService(SensorServiceBase, ComponentServiceBase[Sensor]): +class SensorRPCService(SensorServiceBase, ResourceRPCServiceBase[Sensor]): """ gRPC Service for a generic Sensor """ @@ -23,10 +26,26 @@ async def GetReadings(self, stream: Stream[GetReadingsRequest, GetReadingsRespon request = await stream.recv_message() assert request is not None name = request.name - try: - sensor = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - readings = await sensor.get_readings() + sensor = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + readings = await sensor.get_readings(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) response = GetReadingsResponse(readings=sensor_readings_native_to_value(readings)) await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + sensor = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await sensor.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + sensor = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await sensor.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/service_base.py b/src/viam/components/service_base.py deleted file mode 100644 index 5c98e12e0..000000000 --- a/src/viam/components/service_base.py +++ /dev/null @@ -1,33 +0,0 @@ -import abc -from typing import Generic, Type - -from viam.components.resource_manager import ResourceManager, ResourceType - - -class ComponentServiceBase(Generic[ResourceType], abc.ABC): - """ - Base component service. - All component services must inherit from this class. - """ - - RESOURCE_TYPE = Type - - def __init__(self, manager: ResourceManager): - self.manager = manager - - def get_component(self, name: str) -> ResourceType: - """ - Return the component with the given name if it exists in the registry. - If the component does not exist in the registry, - this function will raise an error - - Args: - name (str): Name of the component - - Raises: - ComponentNotFoundError - - Returns: - ResourceType: The component - """ - return self.manager.get_component(self.RESOURCE_TYPE, name) diff --git a/src/viam/components/servo/__init__.py b/src/viam/components/servo/__init__.py index 145d8e0c6..09104152a 100644 --- a/src/viam/components/servo/__init__.py +++ b/src/viam/components/servo/__init__.py @@ -1,21 +1,11 @@ -from viam.proto.component.servo import Status as ServoStatus -from viam.proto.robot import Status -from viam.registry import ComponentRegistration, Registry -from viam.utils import message_to_struct +from viam.resource.registry import Registry, ResourceRegistration from .client import ServoClient +from .service import ServoRPCService from .servo import Servo -from .service import ServoService - __all__ = [ "Servo", ] - -async def create_status(component: Servo) -> Status: - s = ServoStatus(position_deg=await component.get_position(), is_moving=await component.is_moving()) - return Status(name=Servo.get_resource_name(component.name), status=message_to_struct(s)) - - -Registry.register(ComponentRegistration(Servo, "servo", ServoService, lambda name, channel: ServoClient(name, channel), create_status)) +Registry.register_api(ResourceRegistration(Servo, ServoRPCService, lambda name, channel: ServoClient(name, channel))) diff --git a/src/viam/components/servo/client.py b/src/viam/components/servo/client.py index 60f110062..67a1bb643 100644 --- a/src/viam/components/servo/client.py +++ b/src/viam/components/servo/client.py @@ -1,13 +1,24 @@ -from typing import Any, Dict +from typing import Any, Dict, List, Mapping, Optional from grpclib.client import Channel -from viam.components.generic.client import do_command -from viam.proto.component.servo import GetPositionRequest, GetPositionResponse, MoveRequest, ServoServiceStub, StopRequest + +from viam.proto.common import DoCommandRequest, DoCommandResponse, Geometry +from viam.proto.component.servo import ( + GetPositionRequest, + GetPositionResponse, + IsMovingRequest, + IsMovingResponse, + MoveRequest, + ServoServiceStub, + StopRequest, +) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, get_geometries, struct_to_dict from .servo import Servo -class ServoClient(Servo): +class ServoClient(Servo, ReconfigurableResourceRPCClientBase): """ gRPC client for the Servo component. """ @@ -17,18 +28,59 @@ def __init__(self, name: str, channel: Channel): self.client = ServoServiceStub(channel) super().__init__(name) - async def get_position(self) -> int: - request = GetPositionRequest(name=self.name) - response: GetPositionResponse = await self.client.GetPosition(request) + async def get_position( + self, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> int: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPositionRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetPositionResponse = await self.client.GetPosition(request, timeout=timeout, metadata=md) return response.position_deg - async def move(self, angle: int): - request = MoveRequest(name=self.name, angle_deg=angle) - await self.client.Move(request) + async def move( + self, + angle: int, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto + request = MoveRequest(name=self.name, angle_deg=angle, extra=dict_to_struct(extra)) + await self.client.Move(request, timeout=timeout, metadata=md) + + async def stop( + self, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto + request = StopRequest(name=self.name, extra=dict_to_struct(extra)) + await self.client.Stop(request, timeout=timeout, metadata=md) + + async def is_moving(self, *, timeout: Optional[float] = None, **kwargs) -> bool: + md = kwargs.get("metadata", self.Metadata()).proto + request = IsMovingRequest(name=self.name) + response: IsMovingResponse = await self.client.IsMoving(request, timeout=timeout, metadata=md) + return response.is_moving - async def stop(self): - request = StopRequest(name=self.name) - await self.client.Stop(request) + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: - return await do_command(self.channel, self.name, command) + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Geometry]: + md = kwargs.get("metadata", self.Metadata()) + return await get_geometries(self.client, self.name, extra, timeout, md) diff --git a/src/viam/components/servo/service.py b/src/viam/components/servo/service.py index 166a79be2..928a353f4 100644 --- a/src/viam/components/servo/service.py +++ b/src/viam/components/servo/service.py @@ -1,20 +1,24 @@ from grpclib.server import Stream -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError + +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.servo import ( GetPositionRequest, GetPositionResponse, + IsMovingRequest, + IsMovingResponse, MoveRequest, MoveResponse, ServoServiceBase, StopRequest, StopResponse, ) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict from .servo import Servo -class ServoService(ServoServiceBase, ComponentServiceBase[Servo]): +class ServoRPCService(ServoServiceBase, ResourceRPCServiceBase[Servo]): """ gRPC Service for a Servo """ @@ -25,22 +29,18 @@ async def Move(self, stream: Stream[MoveRequest, MoveResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - servo = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await servo.move(request.angle_deg) + servo = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await servo.move(request.angle_deg, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) await stream.send_message(MoveResponse()) async def GetPosition(self, stream: Stream[GetPositionRequest, GetPositionResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - servo = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - position = await servo.get_position() + servo = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + position = await servo.get_position(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) resp = GetPositionResponse(position_deg=position) await stream.send_message(resp) @@ -48,9 +48,33 @@ async def Stop(self, stream: Stream[StopRequest, StopResponse]) -> None: request = await stream.recv_message() assert request is not None name = request.name - try: - servo = self.get_component(name) - except ComponentNotFoundError as e: - raise e.grpc_error - await servo.stop() + servo = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await servo.stop(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) await stream.send_message(StopResponse()) + + async def IsMoving(self, stream: Stream[IsMovingRequest, IsMovingResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + servo = self.get_resource(name) + is_moving = await servo.is_moving() + await stream.send_message(IsMovingResponse(is_moving=is_moving)) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + servo = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await servo.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) + + async def GetGeometries(self, stream: Stream[GetGeometriesRequest, GetGeometriesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + servo = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + geometries = await servo.get_geometries(extra=struct_to_dict(request.extra), timeout=timeout) + response = GetGeometriesResponse(geometries=geometries) + await stream.send_message(response) diff --git a/src/viam/components/servo/servo.py b/src/viam/components/servo/servo.py index 4af1f1fa4..c40d4de87 100644 --- a/src/viam/components/servo/servo.py +++ b/src/viam/components/servo/servo.py @@ -1,6 +1,7 @@ import abc +from typing import Any, Final, Mapping, Optional -from viam.errors import NotSupportedError +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT from ..component_base import ComponentBase @@ -10,44 +11,104 @@ class Servo(ComponentBase): Servo represents a physical servo. This acts as an abstract base class for any drivers representing specific - servo implementations. This cannot be used on its own. If the `__init__()` function is - overriden, it must call the `super().__init__()` function. + servo implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.servo import Servo + + For more information, see `Servo component `_. """ - name: str + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "servo" + ) @abc.abstractmethod - async def move(self, angle: int): + async def move(self, angle: int, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs): """ Move the servo to the provided angle. + :: + + my_servo = Servo.from_robot(robot=machine, name="my_servo") + + # Move the servo from its origin to the desired angle of 10 degrees. + await my_servo.move(10) + + # Move the servo from its origin to the desired angle of 90 degrees. + await my_servo.move(90) + Args: angle (int): The desired angle of the servo in degrees. + + For more information, see `Servo component `_. """ ... @abc.abstractmethod - async def get_position(self) -> int: + async def get_position(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> int: """ Get the current angle (degrees) of the servo. + :: + + my_servo = Servo.from_robot(robot=machine, name="my_servo") + + # Move the servo from its origin to the desired angle of 10 degrees. + await my_servo.move(10) + + # Get the current set angle of the servo. + pos1 = await my_servo.get_position() + + # Move the servo from its origin to the desired angle of 20 degrees. + await my_servo.move(20) + + # Get the current set angle of the servo. + pos2 = await my_servo.get_position() + Returns: int: The current angle of the servo in degrees. + + For more information, see `Servo component `_. """ ... @abc.abstractmethod - async def stop(self): + async def stop(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs): """ Stop the servo. It is assumed that the servo stops immediately. + + :: + + my_servo = Servo.from_robot(robot=machine, name="my_servo") + + # Move the servo from its origin to the desired angle of 10 degrees. + await my_servo.move(10) + + # Stop the servo. It is assumed that the servo stops moving immediately. + await my_servo.stop() + + For more information, see `Servo component `_. """ ... + @abc.abstractmethod async def is_moving(self) -> bool: """ Get if the servo is currently moving. + :: + + my_servo = Servo.from_robot(robot=machine, name="my_servo") + + print(await my_servo.is_moving()) + + Returns: bool: Whether the servo is moving. + + For more information, see `Servo component `_. """ - raise NotSupportedError(f"Servo named {self.name} does not support returning whether it is moving") + ... diff --git a/src/viam/components/switch/__init__.py b/src/viam/components/switch/__init__.py new file mode 100644 index 000000000..4cf39345b --- /dev/null +++ b/src/viam/components/switch/__init__.py @@ -0,0 +1,10 @@ +import viam.gen.component.switch.v1.switch_pb2 +from viam.resource.registry import Registry, ResourceRegistration + +from .client import SwitchClient +from .service import SwitchRPCService +from .switch import Switch + +__all__ = ["Switch"] + +Registry.register_api(ResourceRegistration(Switch, SwitchRPCService, lambda name, channel: SwitchClient(name, channel))) diff --git a/src/viam/components/switch/client.py b/src/viam/components/switch/client.py new file mode 100644 index 000000000..b6342b72d --- /dev/null +++ b/src/viam/components/switch/client.py @@ -0,0 +1,83 @@ +from typing import Any, Mapping, Optional + +from grpclib.client import Channel + +from viam.gen.component.switch.v1.switch_pb2 import ( + GetNumberOfPositionsRequest, + GetNumberOfPositionsResponse, + GetPositionRequest, + GetPositionResponse, + SetPositionRequest, +) +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, +) +from viam.proto.component.switch import SwitchServiceStub +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ( + ValueTypes, + dict_to_struct, + struct_to_dict, +) + +from .switch import Switch + + +class SwitchClient(Switch, ReconfigurableResourceRPCClientBase): + """ + gRPC client for Switch component + """ + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = SwitchServiceStub(channel) + super().__init__(name) + + async def get_position( + self, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> int: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPositionRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetPositionResponse = await self.client.GetPosition(request, timeout=timeout, metadata=md) + return response.position + + async def set_position( + self, + position: int, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> None: + md = kwargs.get("metadata", self.Metadata()).proto + request = SetPositionRequest(name=self.name, position=position, extra=dict_to_struct(extra)) + await self.client.SetPosition(request, timeout=timeout, metadata=md) + + async def get_number_of_positions( + self, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> int: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetNumberOfPositionsRequest(name=self.name, extra=dict_to_struct(extra)) + response: GetNumberOfPositionsResponse = await self.client.GetNumberOfPositions(request, timeout=timeout, metadata=md) + return response.number_of_positions + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) diff --git a/src/viam/components/switch/service.py b/src/viam/components/switch/service.py new file mode 100644 index 000000000..82374e442 --- /dev/null +++ b/src/viam/components/switch/service.py @@ -0,0 +1,72 @@ +from grpclib.server import Stream + +from viam.gen.component.switch.v1.switch_pb2 import ( + GetNumberOfPositionsRequest, + GetNumberOfPositionsResponse, + GetPositionRequest, + GetPositionResponse, + SetPositionRequest, + SetPositionResponse, +) +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, +) +from viam.proto.component.switch import SwitchServiceBase +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict + +from .switch import Switch + + +class SwitchRPCService(SwitchServiceBase, ResourceRPCServiceBase[Switch]): + """ + gRPC Service for a generic Switch + """ + + RESOURCE_TYPE = Switch + + async def GetPosition(self, stream: Stream[GetPositionRequest, GetPositionResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + switch = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + position = await switch.get_position(extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + response = GetPositionResponse(position=position) + await stream.send_message(response) + + async def SetPosition(self, stream: Stream[SetPositionRequest, SetPositionResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + switch = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await switch.set_position(position=request.position, extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata) + await stream.send_message(SetPositionResponse()) + + async def GetNumberOfPositions(self, stream: Stream[GetNumberOfPositionsRequest, GetNumberOfPositionsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + switch = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + number_of_positions = await switch.get_number_of_positions( + extra=struct_to_dict(request.extra), timeout=timeout, metadata=stream.metadata + ) + response = GetNumberOfPositionsResponse(number_of_positions=number_of_positions) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + switch = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await switch.do_command( + command=struct_to_dict(request.command), + timeout=timeout, + metadata=stream.metadata, + ) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) diff --git a/src/viam/components/switch/switch.py b/src/viam/components/switch/switch.py new file mode 100644 index 000000000..379ca16ea --- /dev/null +++ b/src/viam/components/switch/switch.py @@ -0,0 +1,95 @@ +import abc +from typing import Any, Final, Mapping, Optional + +from viam.components.component_base import ComponentBase +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT + + +class Switch(ComponentBase): + """ + Switch represents a device with two or more finite states (or positions) than can be set and retrieved. + + This acts as an abstract base class for any drivers representing specific + switch implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + :: + + from viam.components.switch import Switch + + For more information, see `Switch component `_. + """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "switch" + ) + + @abc.abstractmethod + async def get_position(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> int: + """ + Get the current position of the switch + + :: + + my_switch = Switch.from_robot(robot=machine, name="my_switch") + + # Update the switch from its current position to the desired position of 1. + await my_switch.set_position(1) + + # Get the current set position of the switch. + pos1 = await my_switch.get_position() + + # Update the switch from its current position to the desired position. + await my_switch.set_position(0) + + # Get the current set position of the switch. + pos2 = await my_switch.get_position() + + Returns: + int: The current position of the switch within the range of available positions. + + For more information, see `Switch component `_. + """ + ... + + @abc.abstractmethod + async def set_position( + self, position: int, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> None: + """ + Sets the current position of the switch. + + :: + + my_switch = Switch.from_robot(robot=machine, name="my_switch") + + # Update the switch from its current position to the desired position of 1. + await my_switch.set_position(1) + + # Update the switch from its current position to the desired position of 0. + await my_switch.set_position(0) + + Args: + position (int): The position of the switch within the range of available positions. + + For more information, see `Switch component `_. + """ + ... + + @abc.abstractmethod + async def get_number_of_positions(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> int: + """ + Get the number of available positions on the switch. + + :: + + my_switch = Switch.from_robot(robot=machine, name="my_switch") + + print(await my_switch.get_number_of_positions()) + + Returns: + int: The number of available positions. + + For more information, see `Switch component `_. + """ + ... diff --git a/src/viam/components/types.py b/src/viam/components/types.py deleted file mode 100644 index 083badc69..000000000 --- a/src/viam/components/types.py +++ /dev/null @@ -1,62 +0,0 @@ -from enum import Enum -from io import BytesIO -from typing import NamedTuple, Union - -from PIL.Image import Image - - -class RawImage(NamedTuple): - """A raw bytes representation of an image. - - When requesting `CameraMimeType.RAW` or if the camera returns a mime type that is not supported, - the returned type will be this RawImage type. - """ - - data: bytes - """The raw data of the image""" - - mime_type: str - """The mimetype of the image""" - - width: int - """The width of the image""" - - height: int - """The height of the image""" - - def close(self): - """Close the image and release resources. For RawImage, this is a noop.""" - return - - -class CameraMimeType(str, Enum): - RAW = "image/vnd.viam.rgba" - JPEG = "image/jpeg" - PNG = "image/png" - PCD = "pointcloud/pcd" - - def encode_image(self, image: Union[Image, RawImage]) -> bytes: - if isinstance(image, RawImage): - return image.data - if self == CameraMimeType.JPEG: - buf = BytesIO() - image.save(buf, format="JPEG") - return buf.getvalue() - if self == CameraMimeType.PNG: - buf = BytesIO() - image.save(buf, format="PNG") - return buf.getvalue() - else: - raise ValueError(f"Cannot encode image to {self}") - - @classmethod - def is_supported(cls, mime_type: str) -> bool: - """Check if the provided mime_type is supported - - Args: - mime_type (str): The mime_type to check - - Returns: - bool: Whether the mime_type is supported - """ - return mime_type in set(item.value for item in cls) diff --git a/src/viam/errors.py b/src/viam/errors.py index 10b0975e1..b9a6b0e7d 100644 --- a/src/viam/errors.py +++ b/src/viam/errors.py @@ -26,16 +26,16 @@ def __init__(self, address: str, authenticated: bool = False) -> None: super().__init__(self.message) -class DuplicateComponentError(ViamError): +class DuplicateResourceError(ViamError): """ - Exception raised when attempting to register a component - with the same name as an existing component already in + Exception raised when attempting to register a resource + with the same name as an existing resource already in the registry """ def __init__(self, name: str) -> None: self.name = name - self.message = "Cannot add component with " + f'duplicate name "{name}" to the registry' + self.message = f'Cannot add resource with duplicate name "{name}" to the registry' super().__init__(self.message) @@ -53,30 +53,18 @@ def grpc_error(self) -> GRPCError: return GRPCError(self.grpc_code, self.message) -class ComponentNotFoundError(ViamGRPCError): +class ResourceNotFoundError(ViamGRPCError): """ - Exception raised when a component is not found in the registry + Exception raised when a resource is not found in the registry """ - def __init__(self, component: str, name: str) -> None: - self.component = component + def __init__(self, resource: str, name: str) -> None: + self.resource = resource self.name = name - self.message = f'No {component} with name "{name}" ' + "found in the registry" + self.message = f'No {resource} with name "{name}" ' + "found in the registry" self.grpc_code = Status.NOT_FOUND -class ServiceNotImplementedError(ViamGRPCError): - """ - Exception raised when a service is not implemented on the Robot - """ - - def __init__(self, service: str, name: str): - self.service = service - self.name = name - self.message = f"{service} service named {name} is not implemented on the Robot" - self.grpc_code = Status.UNIMPLEMENTED - - class MethodNotImplementedError(ViamGRPCError): """ Exception raised when specific methods are unimplemented. @@ -95,3 +83,23 @@ class NotSupportedError(ViamGRPCError): def __init__(self, message: str): self.message = message self.grpc_code = Status.UNIMPLEMENTED + + +class ValidationError(ViamGRPCError): + """ + Exception raised when there is an error during module validation + """ + + def __init__(self, message: str): + self.message = message + self.grpc_code = Status.INVALID_ARGUMENT + + +class NoCaptureToStoreError(ViamGRPCError): + """ + Exception raised in filter module to signal that data capture should not be stored + """ + + def __init__(self): + self.message = "no capture from filter module" # Do not change the contents of this string + self.grpc_code = Status.FAILED_PRECONDITION diff --git a/src/viam/gen/app/agent/__init__.py b/src/viam/gen/app/agent/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/agent/v1/__init__.py b/src/viam/gen/app/agent/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/agent/v1/agent_grpc.py b/src/viam/gen/app/agent/v1/agent_grpc.py new file mode 100644 index 000000000..21aebb646 --- /dev/null +++ b/src/viam/gen/app/agent/v1/agent_grpc.py @@ -0,0 +1,29 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +import google.protobuf.duration_pb2 +import google.protobuf.struct_pb2 +from .... import app + +class AgentDeviceServiceBase(abc.ABC): + + @abc.abstractmethod + async def DeviceAgentConfig(self, stream: 'grpclib.server.Stream[app.agent.v1.agent_pb2.DeviceAgentConfigRequest, app.agent.v1.agent_pb2.DeviceAgentConfigResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.app.agent.v1.AgentDeviceService/DeviceAgentConfig': grpclib.const.Handler(self.DeviceAgentConfig, grpclib.const.Cardinality.UNARY_UNARY, app.agent.v1.agent_pb2.DeviceAgentConfigRequest, app.agent.v1.agent_pb2.DeviceAgentConfigResponse)} + +class UnimplementedAgentDeviceServiceBase(AgentDeviceServiceBase): + + async def DeviceAgentConfig(self, stream: 'grpclib.server.Stream[app.agent.v1.agent_pb2.DeviceAgentConfigRequest, app.agent.v1.agent_pb2.DeviceAgentConfigResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class AgentDeviceServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.DeviceAgentConfig = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.agent.v1.AgentDeviceService/DeviceAgentConfig', app.agent.v1.agent_pb2.DeviceAgentConfigRequest, app.agent.v1.agent_pb2.DeviceAgentConfigResponse) \ No newline at end of file diff --git a/src/viam/gen/app/agent/v1/agent_pb2.py b/src/viam/gen/app/agent/v1/agent_pb2.py new file mode 100644 index 000000000..dca884bc7 --- /dev/null +++ b/src/viam/gen/app/agent/v1/agent_pb2.py @@ -0,0 +1,47 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/agent/v1/agent.proto') +_sym_db = _symbol_database.Default() +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18app/agent/v1/agent.proto\x12\x11viam.app.agent.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto"\xe4\x02\n\x18DeviceAgentConfigRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x128\n\thost_info\x18\x02 \x01(\x0b2\x1b.viam.app.agent.v1.HostInfoR\x08hostInfo\x12u\n\x12subsystem_versions\x18\x03 \x03(\x0b2B.viam.app.agent.v1.DeviceAgentConfigRequest.SubsystemVersionsEntryB\x02\x18\x01R\x11subsystemVersions\x12A\n\x0cversion_info\x18\x04 \x01(\x0b2\x1e.viam.app.agent.v1.VersionInfoR\x0bversionInfo\x1aD\n\x16SubsystemVersionsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x028\x01"\x8c\x06\n\x19DeviceAgentConfigResponse\x12s\n\x11subsystem_configs\x18\x01 \x03(\x0b2B.viam.app.agent.v1.DeviceAgentConfigResponse.SubsystemConfigsEntryB\x02\x18\x01R\x10subsystemConfigs\x12@\n\x0echeck_interval\x18\x02 \x01(\x0b2\x19.google.protobuf.DurationR\rcheckInterval\x12I\n\x11agent_update_info\x18\x03 \x01(\x0b2\x1d.viam.app.agent.v1.UpdateInfoR\x0fagentUpdateInfo\x12T\n\x17viam_server_update_info\x18\x04 \x01(\x0b2\x1d.viam.app.agent.v1.UpdateInfoR\x14viamServerUpdateInfo\x12D\n\x11advanced_settings\x18\x05 \x01(\x0b2\x17.google.protobuf.StructR\x10advancedSettings\x12L\n\x15network_configuration\x18\x06 \x01(\x0b2\x17.google.protobuf.StructR\x14networkConfiguration\x12H\n\x13additional_networks\x18\x07 \x01(\x0b2\x17.google.protobuf.StructR\x12additionalNetworks\x12J\n\x14system_configuration\x18\x08 \x01(\x0b2\x17.google.protobuf.StructR\x13systemConfiguration\x1am\n\x15SubsystemConfigsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12>\n\x05value\x18\x02 \x01(\x0b2(.viam.app.agent.v1.DeviceSubsystemConfigR\x05value:\x028\x01"\xd3\x01\n\x15DeviceSubsystemConfig\x12>\n\x0bupdate_info\x18\x01 \x01(\x0b2\x1d.viam.app.agent.v1.UpdateInfoR\nupdateInfo\x12\x18\n\x07disable\x18\x02 \x01(\x08R\x07disable\x12#\n\rforce_restart\x18\x03 \x01(\x08R\x0cforceRestart\x127\n\nattributes\x18\x04 \x01(\x0b2\x17.google.protobuf.StructR\nattributes:\x02\x18\x01"\xbf\x01\n\x0bVersionInfo\x12#\n\ragent_running\x18\x01 \x01(\tR\x0cagentRunning\x12\'\n\x0fagent_installed\x18\x02 \x01(\tR\x0eagentInstalled\x12.\n\x13viam_server_running\x18\x03 \x01(\tR\x11viamServerRunning\x122\n\x15viam_server_installed\x18\x04 \x01(\tR\x13viamServerInstalled"R\n\x08HostInfo\x12\x1a\n\x08platform\x18\x01 \x01(\tR\x08platform\x12\x16\n\x06distro\x18\x02 \x01(\tR\x06distro\x12\x12\n\x04tags\x18\x03 \x03(\tR\x04tags"\xa6\x01\n\nUpdateInfo\x12\x1a\n\x08filename\x18\x01 \x01(\tR\x08filename\x12\x10\n\x03url\x18\x02 \x01(\tR\x03url\x12\x18\n\x07version\x18\x03 \x01(\tR\x07version\x12\x16\n\x06sha256\x18\x04 \x01(\x0cR\x06sha256\x128\n\x06format\x18\x05 \x01(\x0e2 .viam.app.agent.v1.PackageFormatR\x06format*\x9f\x01\n\rPackageFormat\x12\x1e\n\x1aPACKAGE_FORMAT_UNSPECIFIED\x10\x00\x12\x16\n\x12PACKAGE_FORMAT_RAW\x10\x01\x12\x15\n\x11PACKAGE_FORMAT_XZ\x10\x02\x12\x1d\n\x19PACKAGE_FORMAT_EXECUTABLE\x10\x03\x12 \n\x1cPACKAGE_FORMAT_XZ_EXECUTABLE\x10\x042\x84\x01\n\x12AgentDeviceService\x12n\n\x11DeviceAgentConfig\x12+.viam.app.agent.v1.DeviceAgentConfigRequest\x1a,.viam.app.agent.v1.DeviceAgentConfigResponseB\x1eZ\x1cgo.viam.com/api/app/agent/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.agent.v1.agent_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x1cgo.viam.com/api/app/agent/v1' + _globals['_DEVICEAGENTCONFIGREQUEST_SUBSYSTEMVERSIONSENTRY']._loaded_options = None + _globals['_DEVICEAGENTCONFIGREQUEST_SUBSYSTEMVERSIONSENTRY']._serialized_options = b'8\x01' + _globals['_DEVICEAGENTCONFIGREQUEST'].fields_by_name['subsystem_versions']._loaded_options = None + _globals['_DEVICEAGENTCONFIGREQUEST'].fields_by_name['subsystem_versions']._serialized_options = b'\x18\x01' + _globals['_DEVICEAGENTCONFIGRESPONSE_SUBSYSTEMCONFIGSENTRY']._loaded_options = None + _globals['_DEVICEAGENTCONFIGRESPONSE_SUBSYSTEMCONFIGSENTRY']._serialized_options = b'8\x01' + _globals['_DEVICEAGENTCONFIGRESPONSE'].fields_by_name['subsystem_configs']._loaded_options = None + _globals['_DEVICEAGENTCONFIGRESPONSE'].fields_by_name['subsystem_configs']._serialized_options = b'\x18\x01' + _globals['_DEVICESUBSYSTEMCONFIG']._loaded_options = None + _globals['_DEVICESUBSYSTEMCONFIG']._serialized_options = b'\x18\x01' + _globals['_PACKAGEFORMAT']._serialized_start = 1913 + _globals['_PACKAGEFORMAT']._serialized_end = 2072 + _globals['_DEVICEAGENTCONFIGREQUEST']._serialized_start = 110 + _globals['_DEVICEAGENTCONFIGREQUEST']._serialized_end = 466 + _globals['_DEVICEAGENTCONFIGREQUEST_SUBSYSTEMVERSIONSENTRY']._serialized_start = 398 + _globals['_DEVICEAGENTCONFIGREQUEST_SUBSYSTEMVERSIONSENTRY']._serialized_end = 466 + _globals['_DEVICEAGENTCONFIGRESPONSE']._serialized_start = 469 + _globals['_DEVICEAGENTCONFIGRESPONSE']._serialized_end = 1249 + _globals['_DEVICEAGENTCONFIGRESPONSE_SUBSYSTEMCONFIGSENTRY']._serialized_start = 1140 + _globals['_DEVICEAGENTCONFIGRESPONSE_SUBSYSTEMCONFIGSENTRY']._serialized_end = 1249 + _globals['_DEVICESUBSYSTEMCONFIG']._serialized_start = 1252 + _globals['_DEVICESUBSYSTEMCONFIG']._serialized_end = 1463 + _globals['_VERSIONINFO']._serialized_start = 1466 + _globals['_VERSIONINFO']._serialized_end = 1657 + _globals['_HOSTINFO']._serialized_start = 1659 + _globals['_HOSTINFO']._serialized_end = 1741 + _globals['_UPDATEINFO']._serialized_start = 1744 + _globals['_UPDATEINFO']._serialized_end = 1910 + _globals['_AGENTDEVICESERVICE']._serialized_start = 2075 + _globals['_AGENTDEVICESERVICE']._serialized_end = 2207 \ No newline at end of file diff --git a/src/viam/gen/app/agent/v1/agent_pb2.pyi b/src/viam/gen/app/agent/v1/agent_pb2.pyi new file mode 100644 index 000000000..8888d0e09 --- /dev/null +++ b/src/viam/gen/app/agent/v1/agent_pb2.pyi @@ -0,0 +1,280 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.duration_pb2 +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import google.protobuf.struct_pb2 +import sys +import typing +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _PackageFormat: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PackageFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PackageFormat.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PACKAGE_FORMAT_UNSPECIFIED: _PackageFormat.ValueType + 'unknown/unset (autodetection may be attempted)' + PACKAGE_FORMAT_RAW: _PackageFormat.ValueType + 'do nothing' + PACKAGE_FORMAT_XZ: _PackageFormat.ValueType + 'decompress .xz file' + PACKAGE_FORMAT_EXECUTABLE: _PackageFormat.ValueType + 'set executable permissions' + PACKAGE_FORMAT_XZ_EXECUTABLE: _PackageFormat.ValueType + 'decompress and set executable' + +class PackageFormat(_PackageFormat, metaclass=_PackageFormatEnumTypeWrapper): + ... +PACKAGE_FORMAT_UNSPECIFIED: PackageFormat.ValueType +'unknown/unset (autodetection may be attempted)' +PACKAGE_FORMAT_RAW: PackageFormat.ValueType +'do nothing' +PACKAGE_FORMAT_XZ: PackageFormat.ValueType +'decompress .xz file' +PACKAGE_FORMAT_EXECUTABLE: PackageFormat.ValueType +'set executable permissions' +PACKAGE_FORMAT_XZ_EXECUTABLE: PackageFormat.ValueType +'decompress and set executable' +global___PackageFormat = PackageFormat + +@typing.final +class DeviceAgentConfigRequest(google.protobuf.message.Message): + """Device side""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class SubsystemVersionsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + + def __init__(self, *, key: builtins.str=..., value: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... + ID_FIELD_NUMBER: builtins.int + HOST_INFO_FIELD_NUMBER: builtins.int + SUBSYSTEM_VERSIONS_FIELD_NUMBER: builtins.int + VERSION_INFO_FIELD_NUMBER: builtins.int + id: builtins.str + 'robot partID' + + @property + def host_info(self) -> global___HostInfo: + """info about the host system""" + + @property + def subsystem_versions(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """current subsystems and versions + DEPRECATED in favor of version_info + """ + + @property + def version_info(self) -> global___VersionInfo: + """Currently installed versions for agent and viam-server""" + + def __init__(self, *, id: builtins.str=..., host_info: global___HostInfo | None=..., subsystem_versions: collections.abc.Mapping[builtins.str, builtins.str] | None=..., version_info: global___VersionInfo | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['host_info', b'host_info', 'version_info', b'version_info']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['host_info', b'host_info', 'id', b'id', 'subsystem_versions', b'subsystem_versions', 'version_info', b'version_info']) -> None: + ... +global___DeviceAgentConfigRequest = DeviceAgentConfigRequest + +@typing.final +class DeviceAgentConfigResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class SubsystemConfigsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + + @property + def value(self) -> global___DeviceSubsystemConfig: + ... + + def __init__(self, *, key: builtins.str=..., value: global___DeviceSubsystemConfig | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['value', b'value']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... + SUBSYSTEM_CONFIGS_FIELD_NUMBER: builtins.int + CHECK_INTERVAL_FIELD_NUMBER: builtins.int + AGENT_UPDATE_INFO_FIELD_NUMBER: builtins.int + VIAM_SERVER_UPDATE_INFO_FIELD_NUMBER: builtins.int + ADVANCED_SETTINGS_FIELD_NUMBER: builtins.int + NETWORK_CONFIGURATION_FIELD_NUMBER: builtins.int + ADDITIONAL_NETWORKS_FIELD_NUMBER: builtins.int + SYSTEM_CONFIGURATION_FIELD_NUMBER: builtins.int + + @property + def subsystem_configs(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___DeviceSubsystemConfig]: + """subsystems to be installed/configured/updated + note: previously installed subsystems will be removed from the system if removed from this list + DEPRECATED in favor of indidivual update_info and settings fields + """ + + @property + def check_interval(self) -> google.protobuf.duration_pb2.Duration: + """how often this request should be repeated""" + + @property + def agent_update_info(self) -> global___UpdateInfo: + """update info for agent and viam-server, parsed/processed in App""" + + @property + def viam_server_update_info(self) -> global___UpdateInfo: + ... + + @property + def advanced_settings(self) -> google.protobuf.struct_pb2.Struct: + """various settings that are passed directly to device Agent""" + + @property + def network_configuration(self) -> google.protobuf.struct_pb2.Struct: + ... + + @property + def additional_networks(self) -> google.protobuf.struct_pb2.Struct: + ... + + @property + def system_configuration(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, subsystem_configs: collections.abc.Mapping[builtins.str, global___DeviceSubsystemConfig] | None=..., check_interval: google.protobuf.duration_pb2.Duration | None=..., agent_update_info: global___UpdateInfo | None=..., viam_server_update_info: global___UpdateInfo | None=..., advanced_settings: google.protobuf.struct_pb2.Struct | None=..., network_configuration: google.protobuf.struct_pb2.Struct | None=..., additional_networks: google.protobuf.struct_pb2.Struct | None=..., system_configuration: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['additional_networks', b'additional_networks', 'advanced_settings', b'advanced_settings', 'agent_update_info', b'agent_update_info', 'check_interval', b'check_interval', 'network_configuration', b'network_configuration', 'system_configuration', b'system_configuration', 'viam_server_update_info', b'viam_server_update_info']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['additional_networks', b'additional_networks', 'advanced_settings', b'advanced_settings', 'agent_update_info', b'agent_update_info', 'check_interval', b'check_interval', 'network_configuration', b'network_configuration', 'subsystem_configs', b'subsystem_configs', 'system_configuration', b'system_configuration', 'viam_server_update_info', b'viam_server_update_info']) -> None: + ... +global___DeviceAgentConfigResponse = DeviceAgentConfigResponse + +@typing.final +class DeviceSubsystemConfig(google.protobuf.message.Message): + """DEPRECATED as of January 2025""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + UPDATE_INFO_FIELD_NUMBER: builtins.int + DISABLE_FIELD_NUMBER: builtins.int + FORCE_RESTART_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + disable: builtins.bool + 'if this subsystem is disabled and should not be started by the agent' + force_restart: builtins.bool + 'force_restart will restart the subsystem, even if no updates are available' + + @property + def update_info(self) -> global___UpdateInfo: + """data needed to download/validate the subsystem""" + + @property + def attributes(self) -> google.protobuf.struct_pb2.Struct: + """arbitrary config sections""" + + def __init__(self, *, update_info: global___UpdateInfo | None=..., disable: builtins.bool=..., force_restart: builtins.bool=..., attributes: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['attributes', b'attributes', 'update_info', b'update_info']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['attributes', b'attributes', 'disable', b'disable', 'force_restart', b'force_restart', 'update_info', b'update_info']) -> None: + ... +global___DeviceSubsystemConfig = DeviceSubsystemConfig + +@typing.final +class VersionInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + AGENT_RUNNING_FIELD_NUMBER: builtins.int + AGENT_INSTALLED_FIELD_NUMBER: builtins.int + VIAM_SERVER_RUNNING_FIELD_NUMBER: builtins.int + VIAM_SERVER_INSTALLED_FIELD_NUMBER: builtins.int + agent_running: builtins.str + 'the version of agent currently running and making the request' + agent_installed: builtins.str + 'the version of agent installed (will run after restart if different)' + viam_server_running: builtins.str + 'the version of viam-server currently running' + viam_server_installed: builtins.str + 'the version of viam-server installed (will run after restart if different)' + + def __init__(self, *, agent_running: builtins.str=..., agent_installed: builtins.str=..., viam_server_running: builtins.str=..., viam_server_installed: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['agent_installed', b'agent_installed', 'agent_running', b'agent_running', 'viam_server_installed', b'viam_server_installed', 'viam_server_running', b'viam_server_running']) -> None: + ... +global___VersionInfo = VersionInfo + +@typing.final +class HostInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PLATFORM_FIELD_NUMBER: builtins.int + DISTRO_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + platform: builtins.str + 'platform is the docker styled combination of kernel and architecture. Ex: linux/amd64, darwin/arm64' + distro: builtins.str + 'ID and VERSION_ID fields from /etc/os-release, colon seperated. Ex: ubuntu:22.04, debian:11' + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """additional tags for specific hardware or software that's present and may affect software selection + ex: "jetson", "rpi4", "systemd", etc. + """ + + def __init__(self, *, platform: builtins.str=..., distro: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['distro', b'distro', 'platform', b'platform', 'tags', b'tags']) -> None: + ... +global___HostInfo = HostInfo + +@typing.final +class UpdateInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FILENAME_FIELD_NUMBER: builtins.int + URL_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + SHA256_FIELD_NUMBER: builtins.int + FORMAT_FIELD_NUMBER: builtins.int + filename: builtins.str + 'unpacked filename as it is expected on disk (regardless of url)' + url: builtins.str + 'url to download from' + version: builtins.str + 'version expected at the url' + sha256: builtins.bytes + 'sha256 sum of file as downloaded' + format: global___PackageFormat.ValueType + 'determines if decompression or executable permissions are needed' + + def __init__(self, *, filename: builtins.str=..., url: builtins.str=..., version: builtins.str=..., sha256: builtins.bytes=..., format: global___PackageFormat.ValueType=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['filename', b'filename', 'format', b'format', 'sha256', b'sha256', 'url', b'url', 'version', b'version']) -> None: + ... +global___UpdateInfo = UpdateInfo \ No newline at end of file diff --git a/src/viam/gen/app/cloudslam/__init__.py b/src/viam/gen/app/cloudslam/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/cloudslam/v1/__init__.py b/src/viam/gen/app/cloudslam/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/cloudslam/v1/cloud_slam_grpc.py b/src/viam/gen/app/cloudslam/v1/cloud_slam_grpc.py new file mode 100644 index 000000000..46c47c470 --- /dev/null +++ b/src/viam/gen/app/cloudslam/v1/cloud_slam_grpc.py @@ -0,0 +1,70 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from .... import common +import google.protobuf.struct_pb2 +import google.protobuf.timestamp_pb2 +from .... import app + +class CloudSLAMServiceBase(abc.ABC): + + @abc.abstractmethod + async def StartMappingSession(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.StartMappingSessionRequest, app.cloudslam.v1.cloud_slam_pb2.StartMappingSessionResponse]') -> None: + pass + + @abc.abstractmethod + async def GetActiveMappingSessionsForRobot(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.GetActiveMappingSessionsForRobotRequest, app.cloudslam.v1.cloud_slam_pb2.GetActiveMappingSessionsForRobotResponse]') -> None: + pass + + @abc.abstractmethod + async def GetMappingSessionPointCloud(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionPointCloudRequest, app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionPointCloudResponse]') -> None: + pass + + @abc.abstractmethod + async def ListMappingSessions(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.ListMappingSessionsRequest, app.cloudslam.v1.cloud_slam_pb2.ListMappingSessionsResponse]') -> None: + pass + + @abc.abstractmethod + async def StopMappingSession(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.StopMappingSessionRequest, app.cloudslam.v1.cloud_slam_pb2.StopMappingSessionResponse]') -> None: + pass + + @abc.abstractmethod + async def GetMappingSessionMetadataByID(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionMetadataByIDRequest, app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionMetadataByIDResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.app.cloudslam.v1.CloudSLAMService/StartMappingSession': grpclib.const.Handler(self.StartMappingSession, grpclib.const.Cardinality.UNARY_UNARY, app.cloudslam.v1.cloud_slam_pb2.StartMappingSessionRequest, app.cloudslam.v1.cloud_slam_pb2.StartMappingSessionResponse), '/viam.app.cloudslam.v1.CloudSLAMService/GetActiveMappingSessionsForRobot': grpclib.const.Handler(self.GetActiveMappingSessionsForRobot, grpclib.const.Cardinality.UNARY_UNARY, app.cloudslam.v1.cloud_slam_pb2.GetActiveMappingSessionsForRobotRequest, app.cloudslam.v1.cloud_slam_pb2.GetActiveMappingSessionsForRobotResponse), '/viam.app.cloudslam.v1.CloudSLAMService/GetMappingSessionPointCloud': grpclib.const.Handler(self.GetMappingSessionPointCloud, grpclib.const.Cardinality.UNARY_UNARY, app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionPointCloudRequest, app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionPointCloudResponse), '/viam.app.cloudslam.v1.CloudSLAMService/ListMappingSessions': grpclib.const.Handler(self.ListMappingSessions, grpclib.const.Cardinality.UNARY_UNARY, app.cloudslam.v1.cloud_slam_pb2.ListMappingSessionsRequest, app.cloudslam.v1.cloud_slam_pb2.ListMappingSessionsResponse), '/viam.app.cloudslam.v1.CloudSLAMService/StopMappingSession': grpclib.const.Handler(self.StopMappingSession, grpclib.const.Cardinality.UNARY_UNARY, app.cloudslam.v1.cloud_slam_pb2.StopMappingSessionRequest, app.cloudslam.v1.cloud_slam_pb2.StopMappingSessionResponse), '/viam.app.cloudslam.v1.CloudSLAMService/GetMappingSessionMetadataByID': grpclib.const.Handler(self.GetMappingSessionMetadataByID, grpclib.const.Cardinality.UNARY_UNARY, app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionMetadataByIDRequest, app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionMetadataByIDResponse)} + +class UnimplementedCloudSLAMServiceBase(CloudSLAMServiceBase): + + async def StartMappingSession(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.StartMappingSessionRequest, app.cloudslam.v1.cloud_slam_pb2.StartMappingSessionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetActiveMappingSessionsForRobot(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.GetActiveMappingSessionsForRobotRequest, app.cloudslam.v1.cloud_slam_pb2.GetActiveMappingSessionsForRobotResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetMappingSessionPointCloud(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionPointCloudRequest, app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionPointCloudResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListMappingSessions(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.ListMappingSessionsRequest, app.cloudslam.v1.cloud_slam_pb2.ListMappingSessionsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def StopMappingSession(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.StopMappingSessionRequest, app.cloudslam.v1.cloud_slam_pb2.StopMappingSessionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetMappingSessionMetadataByID(self, stream: 'grpclib.server.Stream[app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionMetadataByIDRequest, app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionMetadataByIDResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class CloudSLAMServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.StartMappingSession = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.cloudslam.v1.CloudSLAMService/StartMappingSession', app.cloudslam.v1.cloud_slam_pb2.StartMappingSessionRequest, app.cloudslam.v1.cloud_slam_pb2.StartMappingSessionResponse) + self.GetActiveMappingSessionsForRobot = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.cloudslam.v1.CloudSLAMService/GetActiveMappingSessionsForRobot', app.cloudslam.v1.cloud_slam_pb2.GetActiveMappingSessionsForRobotRequest, app.cloudslam.v1.cloud_slam_pb2.GetActiveMappingSessionsForRobotResponse) + self.GetMappingSessionPointCloud = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.cloudslam.v1.CloudSLAMService/GetMappingSessionPointCloud', app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionPointCloudRequest, app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionPointCloudResponse) + self.ListMappingSessions = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.cloudslam.v1.CloudSLAMService/ListMappingSessions', app.cloudslam.v1.cloud_slam_pb2.ListMappingSessionsRequest, app.cloudslam.v1.cloud_slam_pb2.ListMappingSessionsResponse) + self.StopMappingSession = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.cloudslam.v1.CloudSLAMService/StopMappingSession', app.cloudslam.v1.cloud_slam_pb2.StopMappingSessionRequest, app.cloudslam.v1.cloud_slam_pb2.StopMappingSessionResponse) + self.GetMappingSessionMetadataByID = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.cloudslam.v1.CloudSLAMService/GetMappingSessionMetadataByID', app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionMetadataByIDRequest, app.cloudslam.v1.cloud_slam_pb2.GetMappingSessionMetadataByIDResponse) \ No newline at end of file diff --git a/src/viam/gen/app/cloudslam/v1/cloud_slam_pb2.py b/src/viam/gen/app/cloudslam/v1/cloud_slam_pb2.py new file mode 100644 index 000000000..d55ad2750 --- /dev/null +++ b/src/viam/gen/app/cloudslam/v1/cloud_slam_pb2.py @@ -0,0 +1,54 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/cloudslam/v1/cloud_slam.proto') +_sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!app/cloudslam/v1/cloud_slam.proto\x12\x15viam.app.cloudslam.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\xa2\x04\n\x1aStartMappingSessionRequest\x12!\n\x0cslam_version\x18\x01 \x01(\tR\x0bslamVersion\x12.\n\x13viam_server_version\x18\x02 \x01(\tR\x11viamServerVersion\x12\x19\n\x08map_name\x18\x03 \x01(\tR\x07mapName\x12\'\n\x0forganization_id\x18\x04 \x01(\tR\x0eorganizationId\x12\x1f\n\x0blocation_id\x18\x05 \x01(\tR\nlocationId\x12\x19\n\x08robot_id\x18\x06 \x01(\tR\x07robotId\x12Q\n\x10capture_interval\x18\x07 \x01(\x0b2&.viam.app.cloudslam.v1.CaptureIntervalR\x0fcaptureInterval\x12;\n\x07sensors\x18\x08 \x03(\x0b2!.viam.app.cloudslam.v1.SensorInfoR\x07sensors\x128\n\x0bslam_config\x18\n \x01(\x0b2\x17.google.protobuf.StructR\nslamConfig\x120\n\x14existing_map_version\x18\x0b \x01(\tR\x12existingMapVersion\x125\n\x06module\x18\x0c \x01(\x0b2\x1d.viam.app.cloudslam.v1.ModuleR\x06module"S\n\x06Module\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x1b\n\tmodule_id\x18\x03 \x01(\tR\x08moduleId\x12\x18\n\x07version\x18\x04 \x01(\tR\x07version"\x80\x01\n\nSensorInfo\x122\n\x15source_component_name\x18\x01 \x01(\tR\x13sourceComponentName\x12\x12\n\x04type\x18\x02 \x01(\tR\x04type\x12*\n\x11data_frequency_hz\x18\x03 \x01(\tR\x0fdataFrequencyHz"\x83\x01\n\x0fCaptureInterval\x129\n\nstart_time\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\tstartTime\x125\n\x08end_time\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x07endTime"<\n\x1bStartMappingSessionResponse\x12\x1d\n\nsession_id\x18\x01 \x01(\tR\tsessionId"D\n\'GetActiveMappingSessionsForRobotRequest\x12\x19\n\x08robot_id\x18\x01 \x01(\tR\x07robotId"I\n(GetActiveMappingSessionsForRobotResponse\x12\x1d\n\nsession_id\x18\x01 \x01(\tR\tsessionId"C\n"GetMappingSessionPointCloudRequest\x12\x1d\n\nsession_id\x18\x01 \x01(\tR\tsessionId"h\n#GetMappingSessionPointCloudResponse\x12\x17\n\x07map_url\x18\x01 \x01(\tR\x06mapUrl\x12(\n\x04pose\x18\x02 \x01(\x0b2\x14.viam.common.v1.PoseR\x04pose"f\n\x1aListMappingSessionsRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x1f\n\x0blocation_id\x18\x02 \x01(\tR\nlocationId"_\n\x1bListMappingSessionsResponse\x12@\n\x07session\x18\x01 \x03(\x0b2&.viam.app.cloudslam.v1.MappingMetadataR\x07session":\n\x19StopMappingSessionRequest\x12\x1d\n\nsession_id\x18\x01 \x01(\tR\tsessionId"U\n\x1aStopMappingSessionResponse\x12\x1d\n\npackage_id\x18\x01 \x01(\tR\tpackageId\x12\x18\n\x07version\x18\x02 \x01(\tR\x07version"E\n$GetMappingSessionMetadataByIDRequest\x12\x1d\n\nsession_id\x18\x01 \x01(\tR\tsessionId"z\n%GetMappingSessionMetadataByIDResponse\x12Q\n\x10session_metadata\x18\x01 \x01(\x0b2&.viam.app.cloudslam.v1.MappingMetadataR\x0fsessionMetadata"\xb5\x05\n\x0fMappingMetadata\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x1f\n\x0blocation_id\x18\x02 \x01(\tR\nlocationId\x12\x19\n\x08robot_id\x18\x03 \x01(\tR\x07robotId\x12L\n\x14time_start_submitted\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampR\x12timeStartSubmitted\x12V\n\x1atime_cloud_run_job_started\x18\x05 \x01(\x0b2\x1a.google.protobuf.TimestampR\x16timeCloudRunJobStarted\x12H\n\x12time_end_submitted\x18\x06 \x01(\x0b2\x1a.google.protobuf.TimestampR\x10timeEndSubmitted\x12R\n\x18time_cloud_run_job_ended\x18\x07 \x01(\x0b2\x1a.google.protobuf.TimestampR\x14timeCloudRunJobEnded\x12?\n\nend_status\x18\x08 \x01(\x0e2 .viam.app.cloudslam.v1.EndStatusR\tendStatus\x12\'\n\x10cloud_run_job_id\x18\t \x01(\tR\rcloudRunJobId\x12.\n\x13viam_server_version\x18\n \x01(\tR\x11viamServerVersion\x12\x19\n\x08map_name\x18\x0b \x01(\tR\x07mapName\x12!\n\x0cslam_version\x18\x0c \x01(\tR\x0bslamVersion\x12\x16\n\x06config\x18\r \x01(\tR\x06config\x12\x1b\n\terror_msg\x18\x0e \x01(\tR\x08errorMsg*l\n\tEndStatus\x12\x1a\n\x16END_STATUS_UNSPECIFIED\x10\x00\x12\x16\n\x12END_STATUS_SUCCESS\x10\x01\x12\x16\n\x12END_STATUS_TIMEOUT\x10\x02\x12\x13\n\x0fEND_STATUS_FAIL\x10\x032\xe3\x06\n\x10CloudSLAMService\x12|\n\x13StartMappingSession\x121.viam.app.cloudslam.v1.StartMappingSessionRequest\x1a2.viam.app.cloudslam.v1.StartMappingSessionResponse\x12\xa3\x01\n GetActiveMappingSessionsForRobot\x12>.viam.app.cloudslam.v1.GetActiveMappingSessionsForRobotRequest\x1a?.viam.app.cloudslam.v1.GetActiveMappingSessionsForRobotResponse\x12\x94\x01\n\x1bGetMappingSessionPointCloud\x129.viam.app.cloudslam.v1.GetMappingSessionPointCloudRequest\x1a:.viam.app.cloudslam.v1.GetMappingSessionPointCloudResponse\x12|\n\x13ListMappingSessions\x121.viam.app.cloudslam.v1.ListMappingSessionsRequest\x1a2.viam.app.cloudslam.v1.ListMappingSessionsResponse\x12y\n\x12StopMappingSession\x120.viam.app.cloudslam.v1.StopMappingSessionRequest\x1a1.viam.app.cloudslam.v1.StopMappingSessionResponse\x12\x9a\x01\n\x1dGetMappingSessionMetadataByID\x12;.viam.app.cloudslam.v1.GetMappingSessionMetadataByIDRequest\x1a<.viam.app.cloudslam.v1.GetMappingSessionMetadataByIDResponseB"Z go.viam.com/api/app/cloudslam/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.cloudslam.v1.cloud_slam_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z go.viam.com/api/app/cloudslam/v1' + _globals['_ENDSTATUS']._serialized_start = 2667 + _globals['_ENDSTATUS']._serialized_end = 2775 + _globals['_STARTMAPPINGSESSIONREQUEST']._serialized_start = 148 + _globals['_STARTMAPPINGSESSIONREQUEST']._serialized_end = 694 + _globals['_MODULE']._serialized_start = 696 + _globals['_MODULE']._serialized_end = 779 + _globals['_SENSORINFO']._serialized_start = 782 + _globals['_SENSORINFO']._serialized_end = 910 + _globals['_CAPTUREINTERVAL']._serialized_start = 913 + _globals['_CAPTUREINTERVAL']._serialized_end = 1044 + _globals['_STARTMAPPINGSESSIONRESPONSE']._serialized_start = 1046 + _globals['_STARTMAPPINGSESSIONRESPONSE']._serialized_end = 1106 + _globals['_GETACTIVEMAPPINGSESSIONSFORROBOTREQUEST']._serialized_start = 1108 + _globals['_GETACTIVEMAPPINGSESSIONSFORROBOTREQUEST']._serialized_end = 1176 + _globals['_GETACTIVEMAPPINGSESSIONSFORROBOTRESPONSE']._serialized_start = 1178 + _globals['_GETACTIVEMAPPINGSESSIONSFORROBOTRESPONSE']._serialized_end = 1251 + _globals['_GETMAPPINGSESSIONPOINTCLOUDREQUEST']._serialized_start = 1253 + _globals['_GETMAPPINGSESSIONPOINTCLOUDREQUEST']._serialized_end = 1320 + _globals['_GETMAPPINGSESSIONPOINTCLOUDRESPONSE']._serialized_start = 1322 + _globals['_GETMAPPINGSESSIONPOINTCLOUDRESPONSE']._serialized_end = 1426 + _globals['_LISTMAPPINGSESSIONSREQUEST']._serialized_start = 1428 + _globals['_LISTMAPPINGSESSIONSREQUEST']._serialized_end = 1530 + _globals['_LISTMAPPINGSESSIONSRESPONSE']._serialized_start = 1532 + _globals['_LISTMAPPINGSESSIONSRESPONSE']._serialized_end = 1627 + _globals['_STOPMAPPINGSESSIONREQUEST']._serialized_start = 1629 + _globals['_STOPMAPPINGSESSIONREQUEST']._serialized_end = 1687 + _globals['_STOPMAPPINGSESSIONRESPONSE']._serialized_start = 1689 + _globals['_STOPMAPPINGSESSIONRESPONSE']._serialized_end = 1774 + _globals['_GETMAPPINGSESSIONMETADATABYIDREQUEST']._serialized_start = 1776 + _globals['_GETMAPPINGSESSIONMETADATABYIDREQUEST']._serialized_end = 1845 + _globals['_GETMAPPINGSESSIONMETADATABYIDRESPONSE']._serialized_start = 1847 + _globals['_GETMAPPINGSESSIONMETADATABYIDRESPONSE']._serialized_end = 1969 + _globals['_MAPPINGMETADATA']._serialized_start = 1972 + _globals['_MAPPINGMETADATA']._serialized_end = 2665 + _globals['_CLOUDSLAMSERVICE']._serialized_start = 2778 + _globals['_CLOUDSLAMSERVICE']._serialized_end = 3645 \ No newline at end of file diff --git a/src/viam/gen/app/cloudslam/v1/cloud_slam_pb2.pyi b/src/viam/gen/app/cloudslam/v1/cloud_slam_pb2.pyi new file mode 100644 index 000000000..8f1939117 --- /dev/null +++ b/src/viam/gen/app/cloudslam/v1/cloud_slam_pb2.pyi @@ -0,0 +1,384 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +from .... import common +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import google.protobuf.struct_pb2 +import google.protobuf.timestamp_pb2 +import sys +import typing +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _EndStatus: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EndStatusEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EndStatus.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + END_STATUS_UNSPECIFIED: _EndStatus.ValueType + END_STATUS_SUCCESS: _EndStatus.ValueType + END_STATUS_TIMEOUT: _EndStatus.ValueType + END_STATUS_FAIL: _EndStatus.ValueType + +class EndStatus(_EndStatus, metaclass=_EndStatusEnumTypeWrapper): + ... +END_STATUS_UNSPECIFIED: EndStatus.ValueType +END_STATUS_SUCCESS: EndStatus.ValueType +END_STATUS_TIMEOUT: EndStatus.ValueType +END_STATUS_FAIL: EndStatus.ValueType +global___EndStatus = EndStatus + +@typing.final +class StartMappingSessionRequest(google.protobuf.message.Message): + """StartMappingSession""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SLAM_VERSION_FIELD_NUMBER: builtins.int + VIAM_SERVER_VERSION_FIELD_NUMBER: builtins.int + MAP_NAME_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + LOCATION_ID_FIELD_NUMBER: builtins.int + ROBOT_ID_FIELD_NUMBER: builtins.int + CAPTURE_INTERVAL_FIELD_NUMBER: builtins.int + SENSORS_FIELD_NUMBER: builtins.int + SLAM_CONFIG_FIELD_NUMBER: builtins.int + EXISTING_MAP_VERSION_FIELD_NUMBER: builtins.int + MODULE_FIELD_NUMBER: builtins.int + slam_version: builtins.str + 'Version to use for slam, defaults stable' + viam_server_version: builtins.str + 'Version to use for viam, defaults stable' + map_name: builtins.str + organization_id: builtins.str + location_id: builtins.str + robot_id: builtins.str + existing_map_version: builtins.str + + @property + def capture_interval(self) -> global___CaptureInterval: + ... + + @property + def sensors(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SensorInfo]: + ... + + @property + def slam_config(self) -> google.protobuf.struct_pb2.Struct: + ... + + @property + def module(self) -> global___Module: + ... + + def __init__(self, *, slam_version: builtins.str=..., viam_server_version: builtins.str=..., map_name: builtins.str=..., organization_id: builtins.str=..., location_id: builtins.str=..., robot_id: builtins.str=..., capture_interval: global___CaptureInterval | None=..., sensors: collections.abc.Iterable[global___SensorInfo] | None=..., slam_config: google.protobuf.struct_pb2.Struct | None=..., existing_map_version: builtins.str=..., module: global___Module | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['capture_interval', b'capture_interval', 'module', b'module', 'slam_config', b'slam_config']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['capture_interval', b'capture_interval', 'existing_map_version', b'existing_map_version', 'location_id', b'location_id', 'map_name', b'map_name', 'module', b'module', 'organization_id', b'organization_id', 'robot_id', b'robot_id', 'sensors', b'sensors', 'slam_config', b'slam_config', 'slam_version', b'slam_version', 'viam_server_version', b'viam_server_version']) -> None: + ... +global___StartMappingSessionRequest = StartMappingSessionRequest + +@typing.final +class Module(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + MODULE_ID_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + name: builtins.str + module_id: builtins.str + version: builtins.str + + def __init__(self, *, name: builtins.str=..., module_id: builtins.str=..., version: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['module_id', b'module_id', 'name', b'name', 'version', b'version']) -> None: + ... +global___Module = Module + +@typing.final +class SensorInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SOURCE_COMPONENT_NAME_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + DATA_FREQUENCY_HZ_FIELD_NUMBER: builtins.int + source_component_name: builtins.str + type: builtins.str + 'type is the RDK component type' + data_frequency_hz: builtins.str + + def __init__(self, *, source_component_name: builtins.str=..., type: builtins.str=..., data_frequency_hz: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data_frequency_hz', b'data_frequency_hz', 'source_component_name', b'source_component_name', 'type', b'type']) -> None: + ... +global___SensorInfo = SensorInfo + +@typing.final +class CaptureInterval(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + START_TIME_FIELD_NUMBER: builtins.int + END_TIME_FIELD_NUMBER: builtins.int + + @property + def start_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def end_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """if no end_time specified cloud slam will be run using live sensors""" + + def __init__(self, *, start_time: google.protobuf.timestamp_pb2.Timestamp | None=..., end_time: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['end_time', b'end_time', 'start_time', b'start_time']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['end_time', b'end_time', 'start_time', b'start_time']) -> None: + ... +global___CaptureInterval = CaptureInterval + +@typing.final +class StartMappingSessionResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SESSION_ID_FIELD_NUMBER: builtins.int + session_id: builtins.str + + def __init__(self, *, session_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['session_id', b'session_id']) -> None: + ... +global___StartMappingSessionResponse = StartMappingSessionResponse + +@typing.final +class GetActiveMappingSessionsForRobotRequest(google.protobuf.message.Message): + """GetActiveMappingSessionsForRobot""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ROBOT_ID_FIELD_NUMBER: builtins.int + robot_id: builtins.str + 'assumes only one active mapping session on a robot' + + def __init__(self, *, robot_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['robot_id', b'robot_id']) -> None: + ... +global___GetActiveMappingSessionsForRobotRequest = GetActiveMappingSessionsForRobotRequest + +@typing.final +class GetActiveMappingSessionsForRobotResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SESSION_ID_FIELD_NUMBER: builtins.int + session_id: builtins.str + + def __init__(self, *, session_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['session_id', b'session_id']) -> None: + ... +global___GetActiveMappingSessionsForRobotResponse = GetActiveMappingSessionsForRobotResponse + +@typing.final +class GetMappingSessionPointCloudRequest(google.protobuf.message.Message): + """GetMappingSessionPointCloud""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SESSION_ID_FIELD_NUMBER: builtins.int + session_id: builtins.str + + def __init__(self, *, session_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['session_id', b'session_id']) -> None: + ... +global___GetMappingSessionPointCloudRequest = GetMappingSessionPointCloudRequest + +@typing.final +class GetMappingSessionPointCloudResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MAP_URL_FIELD_NUMBER: builtins.int + POSE_FIELD_NUMBER: builtins.int + map_url: builtins.str + 'url to the pointcloud map' + + @property + def pose(self) -> common.v1.common_pb2.Pose: + """Current position within the SLAM Map""" + + def __init__(self, *, map_url: builtins.str=..., pose: common.v1.common_pb2.Pose | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['pose', b'pose']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['map_url', b'map_url', 'pose', b'pose']) -> None: + ... +global___GetMappingSessionPointCloudResponse = GetMappingSessionPointCloudResponse + +@typing.final +class ListMappingSessionsRequest(google.protobuf.message.Message): + """ListMappingSessions""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + LOCATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + location_id: builtins.str + + def __init__(self, *, organization_id: builtins.str=..., location_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id', 'organization_id', b'organization_id']) -> None: + ... +global___ListMappingSessionsRequest = ListMappingSessionsRequest + +@typing.final +class ListMappingSessionsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SESSION_FIELD_NUMBER: builtins.int + + @property + def session(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MappingMetadata]: + ... + + def __init__(self, *, session: collections.abc.Iterable[global___MappingMetadata] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['session', b'session']) -> None: + ... +global___ListMappingSessionsResponse = ListMappingSessionsResponse + +@typing.final +class StopMappingSessionRequest(google.protobuf.message.Message): + """StopMappingSession""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SESSION_ID_FIELD_NUMBER: builtins.int + session_id: builtins.str + + def __init__(self, *, session_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['session_id', b'session_id']) -> None: + ... +global___StopMappingSessionRequest = StopMappingSessionRequest + +@typing.final +class StopMappingSessionResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PACKAGE_ID_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + package_id: builtins.str + version: builtins.str + + def __init__(self, *, package_id: builtins.str=..., version: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['package_id', b'package_id', 'version', b'version']) -> None: + ... +global___StopMappingSessionResponse = StopMappingSessionResponse + +@typing.final +class GetMappingSessionMetadataByIDRequest(google.protobuf.message.Message): + """GetMappingSessionMetadataByID""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SESSION_ID_FIELD_NUMBER: builtins.int + session_id: builtins.str + + def __init__(self, *, session_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['session_id', b'session_id']) -> None: + ... +global___GetMappingSessionMetadataByIDRequest = GetMappingSessionMetadataByIDRequest + +@typing.final +class GetMappingSessionMetadataByIDResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SESSION_METADATA_FIELD_NUMBER: builtins.int + + @property + def session_metadata(self) -> global___MappingMetadata: + ... + + def __init__(self, *, session_metadata: global___MappingMetadata | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['session_metadata', b'session_metadata']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['session_metadata', b'session_metadata']) -> None: + ... +global___GetMappingSessionMetadataByIDResponse = GetMappingSessionMetadataByIDResponse + +@typing.final +class MappingMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + LOCATION_ID_FIELD_NUMBER: builtins.int + ROBOT_ID_FIELD_NUMBER: builtins.int + TIME_START_SUBMITTED_FIELD_NUMBER: builtins.int + TIME_CLOUD_RUN_JOB_STARTED_FIELD_NUMBER: builtins.int + TIME_END_SUBMITTED_FIELD_NUMBER: builtins.int + TIME_CLOUD_RUN_JOB_ENDED_FIELD_NUMBER: builtins.int + END_STATUS_FIELD_NUMBER: builtins.int + CLOUD_RUN_JOB_ID_FIELD_NUMBER: builtins.int + VIAM_SERVER_VERSION_FIELD_NUMBER: builtins.int + MAP_NAME_FIELD_NUMBER: builtins.int + SLAM_VERSION_FIELD_NUMBER: builtins.int + CONFIG_FIELD_NUMBER: builtins.int + ERROR_MSG_FIELD_NUMBER: builtins.int + org_id: builtins.str + 'org associated with the slam session' + location_id: builtins.str + 'location associated with the slam session' + robot_id: builtins.str + 'robot associated with slam session' + end_status: global___EndStatus.ValueType + 'enums that represent “success”, “failed”, etc' + cloud_run_job_id: builtins.str + 'initially unset' + viam_server_version: builtins.str + 'version tag from request, defaults to stable' + map_name: builtins.str + 'name of the map package' + slam_version: builtins.str + 'version tag from request, defaults to stable' + config: builtins.str + 'a robot config for a slam session' + error_msg: builtins.str + 'additional details on the end status if needed, such as errors' + + @property + def time_start_submitted(self) -> google.protobuf.timestamp_pb2.Timestamp: + """time this document was created""" + + @property + def time_cloud_run_job_started(self) -> google.protobuf.timestamp_pb2.Timestamp: + """time the cloud run job started""" + + @property + def time_end_submitted(self) -> google.protobuf.timestamp_pb2.Timestamp: + """time StopSlamSession was called""" + + @property + def time_cloud_run_job_ended(self) -> google.protobuf.timestamp_pb2.Timestamp: + """time the cloud run job ended""" + + def __init__(self, *, org_id: builtins.str=..., location_id: builtins.str=..., robot_id: builtins.str=..., time_start_submitted: google.protobuf.timestamp_pb2.Timestamp | None=..., time_cloud_run_job_started: google.protobuf.timestamp_pb2.Timestamp | None=..., time_end_submitted: google.protobuf.timestamp_pb2.Timestamp | None=..., time_cloud_run_job_ended: google.protobuf.timestamp_pb2.Timestamp | None=..., end_status: global___EndStatus.ValueType=..., cloud_run_job_id: builtins.str=..., viam_server_version: builtins.str=..., map_name: builtins.str=..., slam_version: builtins.str=..., config: builtins.str=..., error_msg: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['time_cloud_run_job_ended', b'time_cloud_run_job_ended', 'time_cloud_run_job_started', b'time_cloud_run_job_started', 'time_end_submitted', b'time_end_submitted', 'time_start_submitted', b'time_start_submitted']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['cloud_run_job_id', b'cloud_run_job_id', 'config', b'config', 'end_status', b'end_status', 'error_msg', b'error_msg', 'location_id', b'location_id', 'map_name', b'map_name', 'org_id', b'org_id', 'robot_id', b'robot_id', 'slam_version', b'slam_version', 'time_cloud_run_job_ended', b'time_cloud_run_job_ended', 'time_cloud_run_job_started', b'time_cloud_run_job_started', 'time_end_submitted', b'time_end_submitted', 'time_start_submitted', b'time_start_submitted', 'viam_server_version', b'viam_server_version']) -> None: + ... +global___MappingMetadata = MappingMetadata \ No newline at end of file diff --git a/src/viam/gen/app/data/v1/data_grpc.py b/src/viam/gen/app/data/v1/data_grpc.py index 273a809b4..fdb2cdc3c 100644 --- a/src/viam/gen/app/data/v1/data_grpc.py +++ b/src/viam/gen/app/data/v1/data_grpc.py @@ -2,21 +2,205 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +import google.protobuf.any_pb2 +import google.protobuf.struct_pb2 import google.protobuf.timestamp_pb2 from .... import app class DataServiceBase(abc.ABC): @abc.abstractmethod - async def Query(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.QueryRequest, app.data.v1.data_pb2.QueryResponse]') -> None: + async def TabularDataByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.TabularDataByFilterRequest, app.data.v1.data_pb2.TabularDataByFilterResponse]') -> None: + pass + + @abc.abstractmethod + async def TabularDataBySQL(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.TabularDataBySQLRequest, app.data.v1.data_pb2.TabularDataBySQLResponse]') -> None: + pass + + @abc.abstractmethod + async def TabularDataByMQL(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.TabularDataByMQLRequest, app.data.v1.data_pb2.TabularDataByMQLResponse]') -> None: + pass + + @abc.abstractmethod + async def ExportTabularData(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.ExportTabularDataRequest, app.data.v1.data_pb2.ExportTabularDataResponse]') -> None: + pass + + @abc.abstractmethod + async def GetLatestTabularData(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.GetLatestTabularDataRequest, app.data.v1.data_pb2.GetLatestTabularDataResponse]') -> None: + pass + + @abc.abstractmethod + async def BinaryDataByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.BinaryDataByFilterRequest, app.data.v1.data_pb2.BinaryDataByFilterResponse]') -> None: + pass + + @abc.abstractmethod + async def BinaryDataByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.BinaryDataByIDsRequest, app.data.v1.data_pb2.BinaryDataByIDsResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteTabularData(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.DeleteTabularDataRequest, app.data.v1.data_pb2.DeleteTabularDataResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteBinaryDataByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.DeleteBinaryDataByFilterRequest, app.data.v1.data_pb2.DeleteBinaryDataByFilterResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteBinaryDataByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.DeleteBinaryDataByIDsRequest, app.data.v1.data_pb2.DeleteBinaryDataByIDsResponse]') -> None: + pass + + @abc.abstractmethod + async def AddTagsToBinaryDataByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.AddTagsToBinaryDataByIDsRequest, app.data.v1.data_pb2.AddTagsToBinaryDataByIDsResponse]') -> None: + pass + + @abc.abstractmethod + async def AddTagsToBinaryDataByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.AddTagsToBinaryDataByFilterRequest, app.data.v1.data_pb2.AddTagsToBinaryDataByFilterResponse]') -> None: + pass + + @abc.abstractmethod + async def RemoveTagsFromBinaryDataByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.RemoveTagsFromBinaryDataByIDsRequest, app.data.v1.data_pb2.RemoveTagsFromBinaryDataByIDsResponse]') -> None: + pass + + @abc.abstractmethod + async def RemoveTagsFromBinaryDataByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.RemoveTagsFromBinaryDataByFilterRequest, app.data.v1.data_pb2.RemoveTagsFromBinaryDataByFilterResponse]') -> None: + pass + + @abc.abstractmethod + async def TagsByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.TagsByFilterRequest, app.data.v1.data_pb2.TagsByFilterResponse]') -> None: + pass + + @abc.abstractmethod + async def AddBoundingBoxToImageByID(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.AddBoundingBoxToImageByIDRequest, app.data.v1.data_pb2.AddBoundingBoxToImageByIDResponse]') -> None: + pass + + @abc.abstractmethod + async def RemoveBoundingBoxFromImageByID(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.RemoveBoundingBoxFromImageByIDRequest, app.data.v1.data_pb2.RemoveBoundingBoxFromImageByIDResponse]') -> None: + pass + + @abc.abstractmethod + async def BoundingBoxLabelsByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.BoundingBoxLabelsByFilterRequest, app.data.v1.data_pb2.BoundingBoxLabelsByFilterResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateBoundingBox(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.UpdateBoundingBoxRequest, app.data.v1.data_pb2.UpdateBoundingBoxResponse]') -> None: + pass + + @abc.abstractmethod + async def GetDatabaseConnection(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.GetDatabaseConnectionRequest, app.data.v1.data_pb2.GetDatabaseConnectionResponse]') -> None: + pass + + @abc.abstractmethod + async def ConfigureDatabaseUser(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.ConfigureDatabaseUserRequest, app.data.v1.data_pb2.ConfigureDatabaseUserResponse]') -> None: + pass + + @abc.abstractmethod + async def AddBinaryDataToDatasetByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.AddBinaryDataToDatasetByIDsRequest, app.data.v1.data_pb2.AddBinaryDataToDatasetByIDsResponse]') -> None: + pass + + @abc.abstractmethod + async def RemoveBinaryDataFromDatasetByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.RemoveBinaryDataFromDatasetByIDsRequest, app.data.v1.data_pb2.RemoveBinaryDataFromDatasetByIDsResponse]') -> None: pass def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.app.data.v1.DataService/Query': grpclib.const.Handler(self.Query, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.QueryRequest, app.data.v1.data_pb2.QueryResponse)} + return {'/viam.app.data.v1.DataService/TabularDataByFilter': grpclib.const.Handler(self.TabularDataByFilter, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.TabularDataByFilterRequest, app.data.v1.data_pb2.TabularDataByFilterResponse), '/viam.app.data.v1.DataService/TabularDataBySQL': grpclib.const.Handler(self.TabularDataBySQL, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.TabularDataBySQLRequest, app.data.v1.data_pb2.TabularDataBySQLResponse), '/viam.app.data.v1.DataService/TabularDataByMQL': grpclib.const.Handler(self.TabularDataByMQL, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.TabularDataByMQLRequest, app.data.v1.data_pb2.TabularDataByMQLResponse), '/viam.app.data.v1.DataService/ExportTabularData': grpclib.const.Handler(self.ExportTabularData, grpclib.const.Cardinality.UNARY_STREAM, app.data.v1.data_pb2.ExportTabularDataRequest, app.data.v1.data_pb2.ExportTabularDataResponse), '/viam.app.data.v1.DataService/GetLatestTabularData': grpclib.const.Handler(self.GetLatestTabularData, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.GetLatestTabularDataRequest, app.data.v1.data_pb2.GetLatestTabularDataResponse), '/viam.app.data.v1.DataService/BinaryDataByFilter': grpclib.const.Handler(self.BinaryDataByFilter, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.BinaryDataByFilterRequest, app.data.v1.data_pb2.BinaryDataByFilterResponse), '/viam.app.data.v1.DataService/BinaryDataByIDs': grpclib.const.Handler(self.BinaryDataByIDs, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.BinaryDataByIDsRequest, app.data.v1.data_pb2.BinaryDataByIDsResponse), '/viam.app.data.v1.DataService/DeleteTabularData': grpclib.const.Handler(self.DeleteTabularData, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.DeleteTabularDataRequest, app.data.v1.data_pb2.DeleteTabularDataResponse), '/viam.app.data.v1.DataService/DeleteBinaryDataByFilter': grpclib.const.Handler(self.DeleteBinaryDataByFilter, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.DeleteBinaryDataByFilterRequest, app.data.v1.data_pb2.DeleteBinaryDataByFilterResponse), '/viam.app.data.v1.DataService/DeleteBinaryDataByIDs': grpclib.const.Handler(self.DeleteBinaryDataByIDs, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.DeleteBinaryDataByIDsRequest, app.data.v1.data_pb2.DeleteBinaryDataByIDsResponse), '/viam.app.data.v1.DataService/AddTagsToBinaryDataByIDs': grpclib.const.Handler(self.AddTagsToBinaryDataByIDs, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.AddTagsToBinaryDataByIDsRequest, app.data.v1.data_pb2.AddTagsToBinaryDataByIDsResponse), '/viam.app.data.v1.DataService/AddTagsToBinaryDataByFilter': grpclib.const.Handler(self.AddTagsToBinaryDataByFilter, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.AddTagsToBinaryDataByFilterRequest, app.data.v1.data_pb2.AddTagsToBinaryDataByFilterResponse), '/viam.app.data.v1.DataService/RemoveTagsFromBinaryDataByIDs': grpclib.const.Handler(self.RemoveTagsFromBinaryDataByIDs, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.RemoveTagsFromBinaryDataByIDsRequest, app.data.v1.data_pb2.RemoveTagsFromBinaryDataByIDsResponse), '/viam.app.data.v1.DataService/RemoveTagsFromBinaryDataByFilter': grpclib.const.Handler(self.RemoveTagsFromBinaryDataByFilter, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.RemoveTagsFromBinaryDataByFilterRequest, app.data.v1.data_pb2.RemoveTagsFromBinaryDataByFilterResponse), '/viam.app.data.v1.DataService/TagsByFilter': grpclib.const.Handler(self.TagsByFilter, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.TagsByFilterRequest, app.data.v1.data_pb2.TagsByFilterResponse), '/viam.app.data.v1.DataService/AddBoundingBoxToImageByID': grpclib.const.Handler(self.AddBoundingBoxToImageByID, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.AddBoundingBoxToImageByIDRequest, app.data.v1.data_pb2.AddBoundingBoxToImageByIDResponse), '/viam.app.data.v1.DataService/RemoveBoundingBoxFromImageByID': grpclib.const.Handler(self.RemoveBoundingBoxFromImageByID, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.RemoveBoundingBoxFromImageByIDRequest, app.data.v1.data_pb2.RemoveBoundingBoxFromImageByIDResponse), '/viam.app.data.v1.DataService/BoundingBoxLabelsByFilter': grpclib.const.Handler(self.BoundingBoxLabelsByFilter, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.BoundingBoxLabelsByFilterRequest, app.data.v1.data_pb2.BoundingBoxLabelsByFilterResponse), '/viam.app.data.v1.DataService/UpdateBoundingBox': grpclib.const.Handler(self.UpdateBoundingBox, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.UpdateBoundingBoxRequest, app.data.v1.data_pb2.UpdateBoundingBoxResponse), '/viam.app.data.v1.DataService/GetDatabaseConnection': grpclib.const.Handler(self.GetDatabaseConnection, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.GetDatabaseConnectionRequest, app.data.v1.data_pb2.GetDatabaseConnectionResponse), '/viam.app.data.v1.DataService/ConfigureDatabaseUser': grpclib.const.Handler(self.ConfigureDatabaseUser, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.ConfigureDatabaseUserRequest, app.data.v1.data_pb2.ConfigureDatabaseUserResponse), '/viam.app.data.v1.DataService/AddBinaryDataToDatasetByIDs': grpclib.const.Handler(self.AddBinaryDataToDatasetByIDs, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.AddBinaryDataToDatasetByIDsRequest, app.data.v1.data_pb2.AddBinaryDataToDatasetByIDsResponse), '/viam.app.data.v1.DataService/RemoveBinaryDataFromDatasetByIDs': grpclib.const.Handler(self.RemoveBinaryDataFromDatasetByIDs, grpclib.const.Cardinality.UNARY_UNARY, app.data.v1.data_pb2.RemoveBinaryDataFromDatasetByIDsRequest, app.data.v1.data_pb2.RemoveBinaryDataFromDatasetByIDsResponse)} + +class UnimplementedDataServiceBase(DataServiceBase): + + async def TabularDataByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.TabularDataByFilterRequest, app.data.v1.data_pb2.TabularDataByFilterResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def TabularDataBySQL(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.TabularDataBySQLRequest, app.data.v1.data_pb2.TabularDataBySQLResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def TabularDataByMQL(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.TabularDataByMQLRequest, app.data.v1.data_pb2.TabularDataByMQLResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ExportTabularData(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.ExportTabularDataRequest, app.data.v1.data_pb2.ExportTabularDataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetLatestTabularData(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.GetLatestTabularDataRequest, app.data.v1.data_pb2.GetLatestTabularDataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def BinaryDataByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.BinaryDataByFilterRequest, app.data.v1.data_pb2.BinaryDataByFilterResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def BinaryDataByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.BinaryDataByIDsRequest, app.data.v1.data_pb2.BinaryDataByIDsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteTabularData(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.DeleteTabularDataRequest, app.data.v1.data_pb2.DeleteTabularDataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteBinaryDataByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.DeleteBinaryDataByFilterRequest, app.data.v1.data_pb2.DeleteBinaryDataByFilterResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteBinaryDataByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.DeleteBinaryDataByIDsRequest, app.data.v1.data_pb2.DeleteBinaryDataByIDsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def AddTagsToBinaryDataByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.AddTagsToBinaryDataByIDsRequest, app.data.v1.data_pb2.AddTagsToBinaryDataByIDsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def AddTagsToBinaryDataByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.AddTagsToBinaryDataByFilterRequest, app.data.v1.data_pb2.AddTagsToBinaryDataByFilterResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RemoveTagsFromBinaryDataByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.RemoveTagsFromBinaryDataByIDsRequest, app.data.v1.data_pb2.RemoveTagsFromBinaryDataByIDsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RemoveTagsFromBinaryDataByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.RemoveTagsFromBinaryDataByFilterRequest, app.data.v1.data_pb2.RemoveTagsFromBinaryDataByFilterResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def TagsByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.TagsByFilterRequest, app.data.v1.data_pb2.TagsByFilterResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def AddBoundingBoxToImageByID(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.AddBoundingBoxToImageByIDRequest, app.data.v1.data_pb2.AddBoundingBoxToImageByIDResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RemoveBoundingBoxFromImageByID(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.RemoveBoundingBoxFromImageByIDRequest, app.data.v1.data_pb2.RemoveBoundingBoxFromImageByIDResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def BoundingBoxLabelsByFilter(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.BoundingBoxLabelsByFilterRequest, app.data.v1.data_pb2.BoundingBoxLabelsByFilterResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateBoundingBox(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.UpdateBoundingBoxRequest, app.data.v1.data_pb2.UpdateBoundingBoxResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetDatabaseConnection(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.GetDatabaseConnectionRequest, app.data.v1.data_pb2.GetDatabaseConnectionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ConfigureDatabaseUser(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.ConfigureDatabaseUserRequest, app.data.v1.data_pb2.ConfigureDatabaseUserResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def AddBinaryDataToDatasetByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.AddBinaryDataToDatasetByIDsRequest, app.data.v1.data_pb2.AddBinaryDataToDatasetByIDsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RemoveBinaryDataFromDatasetByIDs(self, stream: 'grpclib.server.Stream[app.data.v1.data_pb2.RemoveBinaryDataFromDatasetByIDsRequest, app.data.v1.data_pb2.RemoveBinaryDataFromDatasetByIDsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class DataServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: - self.Query = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/Query', app.data.v1.data_pb2.QueryRequest, app.data.v1.data_pb2.QueryResponse) \ No newline at end of file + self.TabularDataByFilter = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/TabularDataByFilter', app.data.v1.data_pb2.TabularDataByFilterRequest, app.data.v1.data_pb2.TabularDataByFilterResponse) + self.TabularDataBySQL = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/TabularDataBySQL', app.data.v1.data_pb2.TabularDataBySQLRequest, app.data.v1.data_pb2.TabularDataBySQLResponse) + self.TabularDataByMQL = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/TabularDataByMQL', app.data.v1.data_pb2.TabularDataByMQLRequest, app.data.v1.data_pb2.TabularDataByMQLResponse) + self.ExportTabularData = grpclib.client.UnaryStreamMethod(channel, '/viam.app.data.v1.DataService/ExportTabularData', app.data.v1.data_pb2.ExportTabularDataRequest, app.data.v1.data_pb2.ExportTabularDataResponse) + self.GetLatestTabularData = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/GetLatestTabularData', app.data.v1.data_pb2.GetLatestTabularDataRequest, app.data.v1.data_pb2.GetLatestTabularDataResponse) + self.BinaryDataByFilter = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/BinaryDataByFilter', app.data.v1.data_pb2.BinaryDataByFilterRequest, app.data.v1.data_pb2.BinaryDataByFilterResponse) + self.BinaryDataByIDs = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/BinaryDataByIDs', app.data.v1.data_pb2.BinaryDataByIDsRequest, app.data.v1.data_pb2.BinaryDataByIDsResponse) + self.DeleteTabularData = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/DeleteTabularData', app.data.v1.data_pb2.DeleteTabularDataRequest, app.data.v1.data_pb2.DeleteTabularDataResponse) + self.DeleteBinaryDataByFilter = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/DeleteBinaryDataByFilter', app.data.v1.data_pb2.DeleteBinaryDataByFilterRequest, app.data.v1.data_pb2.DeleteBinaryDataByFilterResponse) + self.DeleteBinaryDataByIDs = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/DeleteBinaryDataByIDs', app.data.v1.data_pb2.DeleteBinaryDataByIDsRequest, app.data.v1.data_pb2.DeleteBinaryDataByIDsResponse) + self.AddTagsToBinaryDataByIDs = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/AddTagsToBinaryDataByIDs', app.data.v1.data_pb2.AddTagsToBinaryDataByIDsRequest, app.data.v1.data_pb2.AddTagsToBinaryDataByIDsResponse) + self.AddTagsToBinaryDataByFilter = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/AddTagsToBinaryDataByFilter', app.data.v1.data_pb2.AddTagsToBinaryDataByFilterRequest, app.data.v1.data_pb2.AddTagsToBinaryDataByFilterResponse) + self.RemoveTagsFromBinaryDataByIDs = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/RemoveTagsFromBinaryDataByIDs', app.data.v1.data_pb2.RemoveTagsFromBinaryDataByIDsRequest, app.data.v1.data_pb2.RemoveTagsFromBinaryDataByIDsResponse) + self.RemoveTagsFromBinaryDataByFilter = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/RemoveTagsFromBinaryDataByFilter', app.data.v1.data_pb2.RemoveTagsFromBinaryDataByFilterRequest, app.data.v1.data_pb2.RemoveTagsFromBinaryDataByFilterResponse) + self.TagsByFilter = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/TagsByFilter', app.data.v1.data_pb2.TagsByFilterRequest, app.data.v1.data_pb2.TagsByFilterResponse) + self.AddBoundingBoxToImageByID = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/AddBoundingBoxToImageByID', app.data.v1.data_pb2.AddBoundingBoxToImageByIDRequest, app.data.v1.data_pb2.AddBoundingBoxToImageByIDResponse) + self.RemoveBoundingBoxFromImageByID = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/RemoveBoundingBoxFromImageByID', app.data.v1.data_pb2.RemoveBoundingBoxFromImageByIDRequest, app.data.v1.data_pb2.RemoveBoundingBoxFromImageByIDResponse) + self.BoundingBoxLabelsByFilter = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/BoundingBoxLabelsByFilter', app.data.v1.data_pb2.BoundingBoxLabelsByFilterRequest, app.data.v1.data_pb2.BoundingBoxLabelsByFilterResponse) + self.UpdateBoundingBox = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/UpdateBoundingBox', app.data.v1.data_pb2.UpdateBoundingBoxRequest, app.data.v1.data_pb2.UpdateBoundingBoxResponse) + self.GetDatabaseConnection = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/GetDatabaseConnection', app.data.v1.data_pb2.GetDatabaseConnectionRequest, app.data.v1.data_pb2.GetDatabaseConnectionResponse) + self.ConfigureDatabaseUser = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/ConfigureDatabaseUser', app.data.v1.data_pb2.ConfigureDatabaseUserRequest, app.data.v1.data_pb2.ConfigureDatabaseUserResponse) + self.AddBinaryDataToDatasetByIDs = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/AddBinaryDataToDatasetByIDs', app.data.v1.data_pb2.AddBinaryDataToDatasetByIDsRequest, app.data.v1.data_pb2.AddBinaryDataToDatasetByIDsResponse) + self.RemoveBinaryDataFromDatasetByIDs = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.data.v1.DataService/RemoveBinaryDataFromDatasetByIDs', app.data.v1.data_pb2.RemoveBinaryDataFromDatasetByIDsRequest, app.data.v1.data_pb2.RemoveBinaryDataFromDatasetByIDsResponse) \ No newline at end of file diff --git a/src/viam/gen/app/data/v1/data_pb2.py b/src/viam/gen/app/data/v1/data_pb2.py index fa2e43624..ee1e09c4a 100644 --- a/src/viam/gen/app/data/v1/data_pb2.py +++ b/src/viam/gen/app/data/v1/data_pb2.py @@ -1,31 +1,178 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/data/v1/data.proto') _sym_db = _symbol_database.Default() +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16app/data/v1/data.proto\x12\x10viam.app.data.v1\x1a\x1fgoogle/protobuf/timestamp.proto"\xbc\x01\n\x0cQueryRequest\x123\n\x07filters\x18\x01 \x01(\x0b2\x19.viam.app.data.v1.FiltersR\x07filters\x12.\n\x04type\x18\x02 \x01(\x0e2\x1a.viam.app.data.v1.DataTypeR\x04type\x12\x12\n\x04skip\x18\x03 \x01(\x05R\x04skip\x12\x14\n\x05limit\x18\x04 \x01(\x05R\x05limit\x12\x1d\n\ncount_only\x18\x05 \x01(\x08R\tcountOnly"\x93\x03\n\x07Filters\x12%\n\x0ecomponent_name\x18\x01 \x01(\tR\rcomponentName\x12%\n\x0ecomponent_type\x18\x02 \x01(\tR\rcomponentType\x12\'\n\x0fcomponent_model\x18\x03 \x01(\tR\x0ecomponentModel\x12\x16\n\x06method\x18\x04 \x01(\tR\x06method\x12\x12\n\x04tags\x18\x05 \x03(\tR\x04tags\x12\x1d\n\nrobot_name\x18\x06 \x01(\tR\trobotName\x12\x19\n\x08robot_id\x18\x07 \x01(\tR\x07robotId\x12\x1b\n\tpart_name\x18\x08 \x01(\tR\x08partName\x12\x17\n\x07part_id\x18\t \x01(\tR\x06partId\x12\x1f\n\x0blocation_id\x18\n \x01(\tR\nlocationId\x12\x15\n\x06org_id\x18\x0b \x01(\tR\x05orgId\x12=\n\x08interval\x18\x0c \x01(\x0b2!.viam.app.data.v1.CaptureIntervalR\x08interval"q\n\x0fCaptureInterval\x120\n\x05start\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\x05start\x12,\n\x03end\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x03end"\xa1\x02\n\rQueryResponse\x12;\n\x07tabular\x18\x01 \x03(\x0b2!.viam.app.data.v1.TabularMetadataR\x07tabular\x128\n\x06binary\x18\x02 \x03(\x0b2 .viam.app.data.v1.BinaryMetadataR\x06binary\x122\n\x04file\x18\x03 \x03(\x0b2\x1e.viam.app.data.v1.FileMetadataR\x04file\x12#\n\rtabular_count\x18\x04 \x01(\x05R\x0ctabularCount\x12!\n\x0cbinary_count\x18\x05 \x01(\x05R\x0bbinaryCount\x12\x1d\n\nfile_count\x18\x06 \x01(\x05R\tfileCount"\xbe\x03\n\x0fTabularMetadata\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n\nrobot_name\x18\x02 \x01(\tR\trobotName\x12\x19\n\x08robot_id\x18\x03 \x01(\tR\x07robotId\x12\x1b\n\tpart_name\x18\x04 \x01(\tR\x08partName\x12\x17\n\x07part_id\x18\x05 \x01(\tR\x06partId\x12%\n\x0ecomponent_name\x18\x06 \x01(\tR\rcomponentName\x12%\n\x0ecomponent_type\x18\x07 \x01(\tR\rcomponentType\x12\'\n\x0fcomponent_model\x18\x08 \x01(\tR\x0ecomponentModel\x12\x16\n\x06method\x18\t \x01(\tR\x06method\x12\x12\n\x04tags\x18\n \x03(\tR\x04tags\x12=\n\x08interval\x18\x0b \x01(\x0b2!.viam.app.data.v1.CaptureIntervalR\x08interval\x12&\n\x0ffile_size_bytes\x18\x0c \x01(\x05R\rfileSizeBytes\x12!\n\x0cnum_readings\x18\r \x01(\x05R\x0bnumReadings"\xb3\x03\n\x0eBinaryMetadata\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n\nrobot_name\x18\x02 \x01(\tR\trobotName\x12\x19\n\x08robot_id\x18\x03 \x01(\tR\x07robotId\x12\x1b\n\tpart_name\x18\x04 \x01(\tR\x08partName\x12\x17\n\x07part_id\x18\x05 \x01(\tR\x06partId\x12%\n\x0ecomponent_name\x18\x06 \x01(\tR\rcomponentName\x12%\n\x0ecomponent_type\x18\x07 \x01(\tR\rcomponentType\x12\'\n\x0fcomponent_model\x18\x08 \x01(\tR\x0ecomponentModel\x12\x16\n\x06method\x18\t \x01(\tR\x06method\x12\x12\n\x04tags\x18\n \x03(\tR\x04tags\x12=\n\x08interval\x18\x0b \x01(\x0b2!.viam.app.data.v1.CaptureIntervalR\x08interval\x12&\n\x0ffile_size_bytes\x18\x0c \x01(\x05R\rfileSizeBytes\x12\x17\n\x07file_id\x18\r \x01(\tR\x06fileId"\x88\x02\n\x0cFileMetadata\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1d\n\nrobot_name\x18\x02 \x01(\tR\trobotName\x12\x19\n\x08robot_id\x18\x03 \x01(\tR\x07robotId\x12\x1b\n\tpart_name\x18\x04 \x01(\tR\x08partName\x12\x17\n\x07part_id\x18\x05 \x01(\tR\x06partId\x127\n\tsync_time\x18\x06 \x01(\x0b2\x1a.google.protobuf.TimestampR\x08syncTime\x12&\n\x0ffile_size_bytes\x18\x07 \x01(\x05R\rfileSizeBytes\x12\x17\n\x07file_id\x18\x08 \x01(\tR\x06fileId*t\n\x08DataType\x12\x19\n\x15DATA_TYPE_UNSPECIFIED\x10\x00\x12\x1b\n\x17DATA_TYPE_BINARY_SENSOR\x10\x01\x12\x1c\n\x18DATA_TYPE_TABULAR_SENSOR\x10\x02\x12\x12\n\x0eDATA_TYPE_FILE\x10\x032W\n\x0bDataService\x12H\n\x05Query\x12\x1e.viam.app.data.v1.QueryRequest\x1a\x1f.viam.app.data.v1.QueryResponseB\x1dZ\x1bgo.viam.com/api/app/data/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.data.v1.data_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z\x1bgo.viam.com/api/app/data/v1' - _DATATYPE._serialized_start = 2235 - _DATATYPE._serialized_end = 2351 - _QUERYREQUEST._serialized_start = 78 - _QUERYREQUEST._serialized_end = 266 - _FILTERS._serialized_start = 269 - _FILTERS._serialized_end = 672 - _CAPTUREINTERVAL._serialized_start = 674 - _CAPTUREINTERVAL._serialized_end = 787 - _QUERYRESPONSE._serialized_start = 790 - _QUERYRESPONSE._serialized_end = 1079 - _TABULARMETADATA._serialized_start = 1082 - _TABULARMETADATA._serialized_end = 1528 - _BINARYMETADATA._serialized_start = 1531 - _BINARYMETADATA._serialized_end = 1966 - _FILEMETADATA._serialized_start = 1969 - _FILEMETADATA._serialized_end = 2233 - _DATASERVICE._serialized_start = 2353 - _DATASERVICE._serialized_end = 2440 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16app/data/v1/data.proto\x12\x10viam.app.data.v1\x1a\x19google/protobuf/any.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\xa1\x01\n\x0bDataRequest\x120\n\x06filter\x18\x01 \x01(\x0b2\x18.viam.app.data.v1.FilterR\x06filter\x12\x14\n\x05limit\x18\x02 \x01(\x04R\x05limit\x12\x12\n\x04last\x18\x03 \x01(\tR\x04last\x126\n\nsort_order\x18\x04 \x01(\x0e2\x17.viam.app.data.v1.OrderR\tsortOrder"\xaa\x04\n\x06Filter\x12%\n\x0ecomponent_name\x18\x01 \x01(\tR\rcomponentName\x12%\n\x0ecomponent_type\x18\x02 \x01(\tR\rcomponentType\x12\x16\n\x06method\x18\x04 \x01(\tR\x06method\x12\x1d\n\nrobot_name\x18\x06 \x01(\tR\trobotName\x12\x19\n\x08robot_id\x18\x07 \x01(\tR\x07robotId\x12\x1b\n\tpart_name\x18\x08 \x01(\tR\x08partName\x12\x17\n\x07part_id\x18\t \x01(\tR\x06partId\x12!\n\x0clocation_ids\x18\n \x03(\tR\x0blocationIds\x12)\n\x10organization_ids\x18\x0b \x03(\tR\x0forganizationIds\x12\x1b\n\tmime_type\x18\x0c \x03(\tR\x08mimeType\x12=\n\x08interval\x18\r \x01(\x0b2!.viam.app.data.v1.CaptureIntervalR\x08interval\x12=\n\x0btags_filter\x18\x0e \x01(\x0b2\x1c.viam.app.data.v1.TagsFilterR\ntagsFilter\x12\x1f\n\x0bbbox_labels\x18\x0f \x03(\tR\nbboxLabels\x12\x1d\n\ndataset_id\x18\x10 \x01(\tR\tdatasetIdJ\x04\x08\x03\x10\x04J\x04\x08\x05\x10\x06R\x0fcomponent_modelR\x04tags"V\n\nTagsFilter\x124\n\x04type\x18\x01 \x01(\x0e2 .viam.app.data.v1.TagsFilterTypeR\x04type\x12\x12\n\x04tags\x18\x02 \x03(\tR\x04tags"\xc3\x04\n\x0fCaptureMetadata\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x1f\n\x0blocation_id\x18\x02 \x01(\tR\nlocationId\x12\x1d\n\nrobot_name\x18\x03 \x01(\tR\trobotName\x12\x19\n\x08robot_id\x18\x04 \x01(\tR\x07robotId\x12\x1b\n\tpart_name\x18\x05 \x01(\tR\x08partName\x12\x17\n\x07part_id\x18\x06 \x01(\tR\x06partId\x12%\n\x0ecomponent_type\x18\x07 \x01(\tR\rcomponentType\x12%\n\x0ecomponent_name\x18\t \x01(\tR\rcomponentName\x12\x1f\n\x0bmethod_name\x18\n \x01(\tR\nmethodName\x12d\n\x11method_parameters\x18\x0b \x03(\x0b27.viam.app.data.v1.CaptureMetadata.MethodParametersEntryR\x10methodParameters\x12\x12\n\x04tags\x18\x0c \x03(\tR\x04tags\x12\x1b\n\tmime_type\x18\r \x01(\tR\x08mimeType\x1aY\n\x15MethodParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12*\n\x05value\x18\x02 \x01(\x0b2\x14.google.protobuf.AnyR\x05value:\x028\x01J\x04\x08\x08\x10\tR\x0fcomponent_model"q\n\x0fCaptureInterval\x120\n\x05start\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\x05start\x12,\n\x03end\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x03end"\xb5\x01\n\x1aTabularDataByFilterRequest\x12@\n\x0cdata_request\x18\x01 \x01(\x0b2\x1d.viam.app.data.v1.DataRequestR\x0bdataRequest\x12\x1d\n\ncount_only\x18\x02 \x01(\x08R\tcountOnly\x122\n\x15include_internal_data\x18\x03 \x01(\x08R\x13includeInternalData:\x02\x18\x01"\xe7\x01\n\x1bTabularDataByFilterResponse\x12=\n\x08metadata\x18\x01 \x03(\x0b2!.viam.app.data.v1.CaptureMetadataR\x08metadata\x121\n\x04data\x18\x02 \x03(\x0b2\x1d.viam.app.data.v1.TabularDataR\x04data\x12\x14\n\x05count\x18\x03 \x01(\x04R\x05count\x12\x12\n\x04last\x18\x04 \x01(\tR\x04last\x12(\n\x10total_size_bytes\x18\x05 \x01(\x04R\x0etotalSizeBytes:\x02\x18\x01"\xe9\x01\n\x0bTabularData\x12+\n\x04data\x18\x01 \x01(\x0b2\x17.google.protobuf.StructR\x04data\x12%\n\x0emetadata_index\x18\x02 \x01(\rR\rmetadataIndex\x12A\n\x0etime_requested\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\rtimeRequested\x12?\n\rtime_received\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0ctimeReceived:\x02\x18\x01"_\n\x17TabularDataBySQLRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x1b\n\tsql_query\x18\x02 \x01(\tR\x08sqlQuery"A\n\x18TabularDataBySQLResponse\x12\x19\n\x08raw_data\x18\x02 \x03(\x0cR\x07rawDataJ\x04\x08\x01\x10\x02R\x04data"\x86\x01\n\x11TabularDataSource\x12;\n\x04type\x18\x01 \x01(\x0e2\'.viam.app.data.v1.TabularDataSourceTypeR\x04type\x12$\n\x0bpipeline_id\x18\x02 \x01(\tH\x00R\npipelineId\x88\x01\x01B\x0e\n\x0c_pipeline_id"\xa7\x02\n\x17TabularDataByMQLRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x1d\n\nmql_binary\x18\x03 \x03(\x0cR\tmqlBinary\x12+\n\x0fuse_recent_data\x18\x04 \x01(\x08H\x00R\ruseRecentData\x88\x01\x01\x12I\n\x0bdata_source\x18\x06 \x01(\x0b2#.viam.app.data.v1.TabularDataSourceH\x01R\ndataSource\x88\x01\x01B\x12\n\x10_use_recent_dataB\x0e\n\x0c_data_sourceJ\x04\x08\x02\x10\x03J\x04\x08\x05\x10\x06R\tmql_queryR\x11use_data_pipeline"A\n\x18TabularDataByMQLResponse\x12\x19\n\x08raw_data\x18\x02 \x03(\x0cR\x07rawDataJ\x04\x08\x01\x10\x02R\x04data"\xe3\x01\n\x18ExportTabularDataRequest\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId\x12#\n\rresource_name\x18\x02 \x01(\tR\x0cresourceName\x12)\n\x10resource_subtype\x18\x03 \x01(\tR\x0fresourceSubtype\x12\x1f\n\x0bmethod_name\x18\x04 \x01(\tR\nmethodName\x12=\n\x08interval\x18\x05 \x01(\x0b2!.viam.app.data.v1.CaptureIntervalR\x08interval"\x94\x04\n\x19ExportTabularDataResponse\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId\x12#\n\rresource_name\x18\x02 \x01(\tR\x0cresourceName\x12)\n\x10resource_subtype\x18\x03 \x01(\tR\x0fresourceSubtype\x12\x1f\n\x0bmethod_name\x18\x04 \x01(\tR\nmethodName\x12?\n\rtime_captured\x18\x05 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0ctimeCaptured\x12\'\n\x0forganization_id\x18\x06 \x01(\tR\x0eorganizationId\x12\x1f\n\x0blocation_id\x18\x07 \x01(\tR\nlocationId\x12\x1d\n\nrobot_name\x18\x08 \x01(\tR\trobotName\x12\x19\n\x08robot_id\x18\t \x01(\tR\x07robotId\x12\x1b\n\tpart_name\x18\n \x01(\tR\x08partName\x12D\n\x11method_parameters\x18\x0b \x01(\x0b2\x17.google.protobuf.StructR\x10methodParameters\x12\x12\n\x04tags\x18\x0c \x03(\tR\x04tags\x121\n\x07payload\x18\r \x01(\x0b2\x17.google.protobuf.StructR\x07payload"\xa7\x01\n\x1bGetLatestTabularDataRequest\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId\x12#\n\rresource_name\x18\x02 \x01(\tR\x0cresourceName\x12\x1f\n\x0bmethod_name\x18\x03 \x01(\tR\nmethodName\x12)\n\x10resource_subtype\x18\x04 \x01(\tR\x0fresourceSubtype"\xcf\x01\n\x1cGetLatestTabularDataResponse\x12?\n\rtime_captured\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0ctimeCaptured\x12;\n\x0btime_synced\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\ntimeSynced\x121\n\x07payload\x18\x03 \x01(\x0b2\x17.google.protobuf.StructR\x07payload"b\n\nBinaryData\x12\x16\n\x06binary\x18\x01 \x01(\x0cR\x06binary\x12<\n\x08metadata\x18\x02 \x01(\x0b2 .viam.app.data.v1.BinaryMetadataR\x08metadata"\xd7\x01\n\x19BinaryDataByFilterRequest\x12@\n\x0cdata_request\x18\x01 \x01(\x0b2\x1d.viam.app.data.v1.DataRequestR\x0bdataRequest\x12%\n\x0einclude_binary\x18\x02 \x01(\x08R\rincludeBinary\x12\x1d\n\ncount_only\x18\x03 \x01(\x08R\tcountOnly\x122\n\x15include_internal_data\x18\x04 \x01(\x08R\x13includeInternalData"\xa2\x01\n\x1aBinaryDataByFilterResponse\x120\n\x04data\x18\x01 \x03(\x0b2\x1c.viam.app.data.v1.BinaryDataR\x04data\x12\x14\n\x05count\x18\x02 \x01(\x04R\x05count\x12\x12\n\x04last\x18\x03 \x01(\tR\x04last\x12(\n\x10total_size_bytes\x18\x04 \x01(\x04R\x0etotalSizeBytes"q\n\x08BinaryID\x12\x17\n\x07file_id\x18\x01 \x01(\tR\x06fileId\x12\'\n\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId\x12\x1f\n\x0blocation_id\x18\x03 \x01(\tR\nlocationId:\x02\x18\x01"\xb6\x01\n\x16BinaryDataByIDsRequest\x12%\n\x0einclude_binary\x18\x02 \x01(\x08R\rincludeBinary\x12=\n\nbinary_ids\x18\x03 \x03(\x0b2\x1a.viam.app.data.v1.BinaryIDB\x02\x18\x01R\tbinaryIds\x12&\n\x0fbinary_data_ids\x18\x04 \x03(\tR\rbinaryDataIdsJ\x04\x08\x01\x10\x02R\x08file_ids"a\n\x17BinaryDataByIDsResponse\x120\n\x04data\x18\x01 \x03(\x0b2\x1c.viam.app.data.v1.BinaryDataR\x04data\x12\x14\n\x05count\x18\x02 \x01(\x04R\x05count"\x8f\x02\n\x0bBoundingBox\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x14\n\x05label\x18\x02 \x01(\tR\x05label\x12(\n\x10x_min_normalized\x18\x03 \x01(\x01R\x0exMinNormalized\x12(\n\x10y_min_normalized\x18\x04 \x01(\x01R\x0eyMinNormalized\x12(\n\x10x_max_normalized\x18\x05 \x01(\x01R\x0exMaxNormalized\x12(\n\x10y_max_normalized\x18\x06 \x01(\x01R\x0eyMaxNormalized\x12#\n\nconfidence\x18\x07 \x01(\x01H\x00R\nconfidence\x88\x01\x01B\r\n\x0b_confidence"Z\n\x0eClassification\x12\x14\n\x05label\x18\x01 \x01(\tR\x05label\x12#\n\nconfidence\x18\x02 \x01(\x01H\x00R\nconfidence\x88\x01\x01B\r\n\x0b_confidence"\x90\x01\n\x0bAnnotations\x125\n\x06bboxes\x18\x01 \x03(\x0b2\x1d.viam.app.data.v1.BoundingBoxR\x06bboxes\x12J\n\x0fclassifications\x18\x02 \x03(\x0b2 .viam.app.data.v1.ClassificationR\x0fclassifications"\xc8\x03\n\x0eBinaryMetadata\x12\x12\n\x02id\x18\x01 \x01(\tB\x02\x18\x01R\x02id\x12L\n\x10capture_metadata\x18\x02 \x01(\x0b2!.viam.app.data.v1.CaptureMetadataR\x0fcaptureMetadata\x12A\n\x0etime_requested\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\rtimeRequested\x12?\n\rtime_received\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0ctimeReceived\x12\x1b\n\tfile_name\x18\x05 \x01(\tR\x08fileName\x12\x19\n\x08file_ext\x18\x06 \x01(\tR\x07fileExt\x12\x10\n\x03uri\x18\x07 \x01(\tR\x03uri\x12?\n\x0bannotations\x18\x08 \x01(\x0b2\x1d.viam.app.data.v1.AnnotationsR\x0bannotations\x12\x1f\n\x0bdataset_ids\x18\t \x03(\tR\ndatasetIds\x12$\n\x0ebinary_data_id\x18\n \x01(\tR\x0cbinaryDataId"x\n\x18DeleteTabularDataRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x123\n\x16delete_older_than_days\x18\x02 \x01(\rR\x13deleteOlderThanDays"@\n\x19DeleteTabularDataResponse\x12#\n\rdeleted_count\x18\x01 \x01(\x04R\x0cdeletedCount"\x87\x01\n\x1fDeleteBinaryDataByFilterRequest\x120\n\x06filter\x18\x01 \x01(\x0b2\x18.viam.app.data.v1.FilterR\x06filter\x122\n\x15include_internal_data\x18\x02 \x01(\x08R\x13includeInternalData"U\n DeleteBinaryDataByFilterResponse\x12#\n\rdeleted_count\x18\x01 \x01(\x04R\x0cdeletedCountJ\x04\x08\x02\x10\x03R\x06result"\x95\x01\n\x1cDeleteBinaryDataByIDsRequest\x12=\n\nbinary_ids\x18\x02 \x03(\x0b2\x1a.viam.app.data.v1.BinaryIDB\x02\x18\x01R\tbinaryIds\x12&\n\x0fbinary_data_ids\x18\x03 \x03(\tR\rbinaryDataIdsJ\x04\x08\x01\x10\x02R\x08file_ids"R\n\x1dDeleteBinaryDataByIDsResponse\x12#\n\rdeleted_count\x18\x01 \x01(\x04R\x0cdeletedCountJ\x04\x08\x02\x10\x03R\x06result"\xac\x01\n\x1fAddTagsToBinaryDataByIDsRequest\x12=\n\nbinary_ids\x18\x03 \x03(\x0b2\x1a.viam.app.data.v1.BinaryIDB\x02\x18\x01R\tbinaryIds\x12&\n\x0fbinary_data_ids\x18\x04 \x03(\tR\rbinaryDataIds\x12\x12\n\x04tags\x18\x02 \x03(\tR\x04tagsJ\x04\x08\x01\x10\x02R\x08file_ids""\n AddTagsToBinaryDataByIDsResponse"j\n"AddTagsToBinaryDataByFilterRequest\x120\n\x06filter\x18\x01 \x01(\x0b2\x18.viam.app.data.v1.FilterR\x06filter\x12\x12\n\x04tags\x18\x02 \x03(\tR\x04tags"%\n#AddTagsToBinaryDataByFilterResponse"\xb1\x01\n$RemoveTagsFromBinaryDataByIDsRequest\x12=\n\nbinary_ids\x18\x03 \x03(\x0b2\x1a.viam.app.data.v1.BinaryIDB\x02\x18\x01R\tbinaryIds\x12&\n\x0fbinary_data_ids\x18\x04 \x03(\tR\rbinaryDataIds\x12\x12\n\x04tags\x18\x02 \x03(\tR\x04tagsJ\x04\x08\x01\x10\x02R\x08file_ids"L\n%RemoveTagsFromBinaryDataByIDsResponse\x12#\n\rdeleted_count\x18\x01 \x01(\x04R\x0cdeletedCount"o\n\'RemoveTagsFromBinaryDataByFilterRequest\x120\n\x06filter\x18\x01 \x01(\x0b2\x18.viam.app.data.v1.FilterR\x06filter\x12\x12\n\x04tags\x18\x02 \x03(\tR\x04tags"O\n(RemoveTagsFromBinaryDataByFilterResponse\x12#\n\rdeleted_count\x18\x01 \x01(\x04R\x0cdeletedCount"G\n\x13TagsByFilterRequest\x120\n\x06filter\x18\x01 \x01(\x0b2\x18.viam.app.data.v1.FilterR\x06filter"*\n\x14TagsByFilterResponse\x12\x12\n\x04tags\x18\x01 \x03(\tR\x04tags"\xd2\x02\n AddBoundingBoxToImageByIDRequest\x12;\n\tbinary_id\x18\x07 \x01(\x0b2\x1a.viam.app.data.v1.BinaryIDB\x02\x18\x01R\x08binaryId\x12$\n\x0ebinary_data_id\x18\x08 \x01(\tR\x0cbinaryDataId\x12\x14\n\x05label\x18\x02 \x01(\tR\x05label\x12(\n\x10x_min_normalized\x18\x03 \x01(\x01R\x0exMinNormalized\x12(\n\x10y_min_normalized\x18\x04 \x01(\x01R\x0eyMinNormalized\x12(\n\x10x_max_normalized\x18\x05 \x01(\x01R\x0exMaxNormalized\x12(\n\x10y_max_normalized\x18\x06 \x01(\x01R\x0eyMaxNormalizedJ\x04\x08\x01\x10\x02R\x07file_id"<\n!AddBoundingBoxToImageByIDResponse\x12\x17\n\x07bbox_id\x18\x01 \x01(\tR\x06bboxId"\xb2\x01\n%RemoveBoundingBoxFromImageByIDRequest\x12;\n\tbinary_id\x18\x03 \x01(\x0b2\x1a.viam.app.data.v1.BinaryIDB\x02\x18\x01R\x08binaryId\x12$\n\x0ebinary_data_id\x18\x04 \x01(\tR\x0cbinaryDataId\x12\x17\n\x07bbox_id\x18\x02 \x01(\tR\x06bboxIdJ\x04\x08\x01\x10\x02R\x07file_id"(\n&RemoveBoundingBoxFromImageByIDResponse"\xcb\x03\n\x18UpdateBoundingBoxRequest\x12;\n\tbinary_id\x18\x01 \x01(\x0b2\x1a.viam.app.data.v1.BinaryIDB\x02\x18\x01R\x08binaryId\x12$\n\x0ebinary_data_id\x18\x08 \x01(\tR\x0cbinaryDataId\x12\x17\n\x07bbox_id\x18\x02 \x01(\tR\x06bboxId\x12\x19\n\x05label\x18\x03 \x01(\tH\x00R\x05label\x88\x01\x01\x12-\n\x10x_min_normalized\x18\x04 \x01(\x01H\x01R\x0exMinNormalized\x88\x01\x01\x12-\n\x10y_min_normalized\x18\x05 \x01(\x01H\x02R\x0eyMinNormalized\x88\x01\x01\x12-\n\x10x_max_normalized\x18\x06 \x01(\x01H\x03R\x0exMaxNormalized\x88\x01\x01\x12-\n\x10y_max_normalized\x18\x07 \x01(\x01H\x04R\x0eyMaxNormalized\x88\x01\x01B\x08\n\x06_labelB\x13\n\x11_x_min_normalizedB\x13\n\x11_y_min_normalizedB\x13\n\x11_x_max_normalizedB\x13\n\x11_y_max_normalized"\x1b\n\x19UpdateBoundingBoxResponse"T\n BoundingBoxLabelsByFilterRequest\x120\n\x06filter\x18\x01 \x01(\x0b2\x18.viam.app.data.v1.FilterR\x06filter";\n!BoundingBoxLabelsByFilterResponse\x12\x16\n\x06labels\x18\x01 \x03(\tR\x06labels"c\n\x1cConfigureDatabaseUserRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x1a\n\x08password\x18\x02 \x01(\tR\x08password"\x1f\n\x1dConfigureDatabaseUserResponse"G\n\x1cGetDatabaseConnectionRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId"\x88\x01\n\x1dGetDatabaseConnectionResponse\x12\x1a\n\x08hostname\x18\x01 \x01(\tR\x08hostname\x12\x1f\n\x0bmongodb_uri\x18\x02 \x01(\tR\nmongodbUri\x12*\n\x11has_database_user\x18\x03 \x01(\x08R\x0fhasDatabaseUser"\xaa\x01\n"AddBinaryDataToDatasetByIDsRequest\x12=\n\nbinary_ids\x18\x01 \x03(\x0b2\x1a.viam.app.data.v1.BinaryIDB\x02\x18\x01R\tbinaryIds\x12&\n\x0fbinary_data_ids\x18\x03 \x03(\tR\rbinaryDataIds\x12\x1d\n\ndataset_id\x18\x02 \x01(\tR\tdatasetId"%\n#AddBinaryDataToDatasetByIDsResponse"\xaf\x01\n\'RemoveBinaryDataFromDatasetByIDsRequest\x12=\n\nbinary_ids\x18\x01 \x03(\x0b2\x1a.viam.app.data.v1.BinaryIDB\x02\x18\x01R\tbinaryIds\x12&\n\x0fbinary_data_ids\x18\x03 \x03(\tR\rbinaryDataIds\x12\x1d\n\ndataset_id\x18\x02 \x01(\tR\tdatasetId"*\n(RemoveBinaryDataFromDatasetByIDsResponse*I\n\x05Order\x12\x15\n\x11ORDER_UNSPECIFIED\x10\x00\x12\x14\n\x10ORDER_DESCENDING\x10\x01\x12\x13\n\x0fORDER_ASCENDING\x10\x02*\x90\x01\n\x0eTagsFilterType\x12 \n\x1cTAGS_FILTER_TYPE_UNSPECIFIED\x10\x00\x12 \n\x1cTAGS_FILTER_TYPE_MATCH_BY_OR\x10\x01\x12\x1b\n\x17TAGS_FILTER_TYPE_TAGGED\x10\x02\x12\x1d\n\x19TAGS_FILTER_TYPE_UNTAGGED\x10\x03*\xbe\x01\n\x15TabularDataSourceType\x12(\n$TABULAR_DATA_SOURCE_TYPE_UNSPECIFIED\x10\x00\x12%\n!TABULAR_DATA_SOURCE_TYPE_STANDARD\x10\x01\x12(\n$TABULAR_DATA_SOURCE_TYPE_HOT_STORAGE\x10\x02\x12*\n&TABULAR_DATA_SOURCE_TYPE_PIPELINE_SINK\x10\x032\xd6\x16\n\x0bDataService\x12w\n\x13TabularDataByFilter\x12,.viam.app.data.v1.TabularDataByFilterRequest\x1a-.viam.app.data.v1.TabularDataByFilterResponse"\x03\x88\x02\x01\x12i\n\x10TabularDataBySQL\x12).viam.app.data.v1.TabularDataBySQLRequest\x1a*.viam.app.data.v1.TabularDataBySQLResponse\x12i\n\x10TabularDataByMQL\x12).viam.app.data.v1.TabularDataByMQLRequest\x1a*.viam.app.data.v1.TabularDataByMQLResponse\x12n\n\x11ExportTabularData\x12*.viam.app.data.v1.ExportTabularDataRequest\x1a+.viam.app.data.v1.ExportTabularDataResponse0\x01\x12u\n\x14GetLatestTabularData\x12-.viam.app.data.v1.GetLatestTabularDataRequest\x1a..viam.app.data.v1.GetLatestTabularDataResponse\x12o\n\x12BinaryDataByFilter\x12+.viam.app.data.v1.BinaryDataByFilterRequest\x1a,.viam.app.data.v1.BinaryDataByFilterResponse\x12f\n\x0fBinaryDataByIDs\x12(.viam.app.data.v1.BinaryDataByIDsRequest\x1a).viam.app.data.v1.BinaryDataByIDsResponse\x12l\n\x11DeleteTabularData\x12*.viam.app.data.v1.DeleteTabularDataRequest\x1a+.viam.app.data.v1.DeleteTabularDataResponse\x12\x81\x01\n\x18DeleteBinaryDataByFilter\x121.viam.app.data.v1.DeleteBinaryDataByFilterRequest\x1a2.viam.app.data.v1.DeleteBinaryDataByFilterResponse\x12x\n\x15DeleteBinaryDataByIDs\x12..viam.app.data.v1.DeleteBinaryDataByIDsRequest\x1a/.viam.app.data.v1.DeleteBinaryDataByIDsResponse\x12\x81\x01\n\x18AddTagsToBinaryDataByIDs\x121.viam.app.data.v1.AddTagsToBinaryDataByIDsRequest\x1a2.viam.app.data.v1.AddTagsToBinaryDataByIDsResponse\x12\x8a\x01\n\x1bAddTagsToBinaryDataByFilter\x124.viam.app.data.v1.AddTagsToBinaryDataByFilterRequest\x1a5.viam.app.data.v1.AddTagsToBinaryDataByFilterResponse\x12\x90\x01\n\x1dRemoveTagsFromBinaryDataByIDs\x126.viam.app.data.v1.RemoveTagsFromBinaryDataByIDsRequest\x1a7.viam.app.data.v1.RemoveTagsFromBinaryDataByIDsResponse\x12\x99\x01\n RemoveTagsFromBinaryDataByFilter\x129.viam.app.data.v1.RemoveTagsFromBinaryDataByFilterRequest\x1a:.viam.app.data.v1.RemoveTagsFromBinaryDataByFilterResponse\x12]\n\x0cTagsByFilter\x12%.viam.app.data.v1.TagsByFilterRequest\x1a&.viam.app.data.v1.TagsByFilterResponse\x12\x84\x01\n\x19AddBoundingBoxToImageByID\x122.viam.app.data.v1.AddBoundingBoxToImageByIDRequest\x1a3.viam.app.data.v1.AddBoundingBoxToImageByIDResponse\x12\x93\x01\n\x1eRemoveBoundingBoxFromImageByID\x127.viam.app.data.v1.RemoveBoundingBoxFromImageByIDRequest\x1a8.viam.app.data.v1.RemoveBoundingBoxFromImageByIDResponse\x12\x84\x01\n\x19BoundingBoxLabelsByFilter\x122.viam.app.data.v1.BoundingBoxLabelsByFilterRequest\x1a3.viam.app.data.v1.BoundingBoxLabelsByFilterResponse\x12l\n\x11UpdateBoundingBox\x12*.viam.app.data.v1.UpdateBoundingBoxRequest\x1a+.viam.app.data.v1.UpdateBoundingBoxResponse\x12x\n\x15GetDatabaseConnection\x12..viam.app.data.v1.GetDatabaseConnectionRequest\x1a/.viam.app.data.v1.GetDatabaseConnectionResponse\x12x\n\x15ConfigureDatabaseUser\x12..viam.app.data.v1.ConfigureDatabaseUserRequest\x1a/.viam.app.data.v1.ConfigureDatabaseUserResponse\x12\x8a\x01\n\x1bAddBinaryDataToDatasetByIDs\x124.viam.app.data.v1.AddBinaryDataToDatasetByIDsRequest\x1a5.viam.app.data.v1.AddBinaryDataToDatasetByIDsResponse\x12\x99\x01\n RemoveBinaryDataFromDatasetByIDs\x129.viam.app.data.v1.RemoveBinaryDataFromDatasetByIDsRequest\x1a:.viam.app.data.v1.RemoveBinaryDataFromDatasetByIDsResponseB\x1dZ\x1bgo.viam.com/api/app/data/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.data.v1.data_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x1bgo.viam.com/api/app/data/v1' + _globals['_CAPTUREMETADATA_METHODPARAMETERSENTRY']._loaded_options = None + _globals['_CAPTUREMETADATA_METHODPARAMETERSENTRY']._serialized_options = b'8\x01' + _globals['_TABULARDATABYFILTERREQUEST']._loaded_options = None + _globals['_TABULARDATABYFILTERREQUEST']._serialized_options = b'\x18\x01' + _globals['_TABULARDATABYFILTERRESPONSE']._loaded_options = None + _globals['_TABULARDATABYFILTERRESPONSE']._serialized_options = b'\x18\x01' + _globals['_TABULARDATA']._loaded_options = None + _globals['_TABULARDATA']._serialized_options = b'\x18\x01' + _globals['_BINARYID']._loaded_options = None + _globals['_BINARYID']._serialized_options = b'\x18\x01' + _globals['_BINARYDATABYIDSREQUEST'].fields_by_name['binary_ids']._loaded_options = None + _globals['_BINARYDATABYIDSREQUEST'].fields_by_name['binary_ids']._serialized_options = b'\x18\x01' + _globals['_BINARYMETADATA'].fields_by_name['id']._loaded_options = None + _globals['_BINARYMETADATA'].fields_by_name['id']._serialized_options = b'\x18\x01' + _globals['_DELETEBINARYDATABYIDSREQUEST'].fields_by_name['binary_ids']._loaded_options = None + _globals['_DELETEBINARYDATABYIDSREQUEST'].fields_by_name['binary_ids']._serialized_options = b'\x18\x01' + _globals['_ADDTAGSTOBINARYDATABYIDSREQUEST'].fields_by_name['binary_ids']._loaded_options = None + _globals['_ADDTAGSTOBINARYDATABYIDSREQUEST'].fields_by_name['binary_ids']._serialized_options = b'\x18\x01' + _globals['_REMOVETAGSFROMBINARYDATABYIDSREQUEST'].fields_by_name['binary_ids']._loaded_options = None + _globals['_REMOVETAGSFROMBINARYDATABYIDSREQUEST'].fields_by_name['binary_ids']._serialized_options = b'\x18\x01' + _globals['_ADDBOUNDINGBOXTOIMAGEBYIDREQUEST'].fields_by_name['binary_id']._loaded_options = None + _globals['_ADDBOUNDINGBOXTOIMAGEBYIDREQUEST'].fields_by_name['binary_id']._serialized_options = b'\x18\x01' + _globals['_REMOVEBOUNDINGBOXFROMIMAGEBYIDREQUEST'].fields_by_name['binary_id']._loaded_options = None + _globals['_REMOVEBOUNDINGBOXFROMIMAGEBYIDREQUEST'].fields_by_name['binary_id']._serialized_options = b'\x18\x01' + _globals['_UPDATEBOUNDINGBOXREQUEST'].fields_by_name['binary_id']._loaded_options = None + _globals['_UPDATEBOUNDINGBOXREQUEST'].fields_by_name['binary_id']._serialized_options = b'\x18\x01' + _globals['_ADDBINARYDATATODATASETBYIDSREQUEST'].fields_by_name['binary_ids']._loaded_options = None + _globals['_ADDBINARYDATATODATASETBYIDSREQUEST'].fields_by_name['binary_ids']._serialized_options = b'\x18\x01' + _globals['_REMOVEBINARYDATAFROMDATASETBYIDSREQUEST'].fields_by_name['binary_ids']._loaded_options = None + _globals['_REMOVEBINARYDATAFROMDATASETBYIDSREQUEST'].fields_by_name['binary_ids']._serialized_options = b'\x18\x01' + _globals['_DATASERVICE'].methods_by_name['TabularDataByFilter']._loaded_options = None + _globals['_DATASERVICE'].methods_by_name['TabularDataByFilter']._serialized_options = b'\x88\x02\x01' + _globals['_ORDER']._serialized_start = 9579 + _globals['_ORDER']._serialized_end = 9652 + _globals['_TAGSFILTERTYPE']._serialized_start = 9655 + _globals['_TAGSFILTERTYPE']._serialized_end = 9799 + _globals['_TABULARDATASOURCETYPE']._serialized_start = 9802 + _globals['_TABULARDATASOURCETYPE']._serialized_end = 9992 + _globals['_DATAREQUEST']._serialized_start = 135 + _globals['_DATAREQUEST']._serialized_end = 296 + _globals['_FILTER']._serialized_start = 299 + _globals['_FILTER']._serialized_end = 853 + _globals['_TAGSFILTER']._serialized_start = 855 + _globals['_TAGSFILTER']._serialized_end = 941 + _globals['_CAPTUREMETADATA']._serialized_start = 944 + _globals['_CAPTUREMETADATA']._serialized_end = 1523 + _globals['_CAPTUREMETADATA_METHODPARAMETERSENTRY']._serialized_start = 1411 + _globals['_CAPTUREMETADATA_METHODPARAMETERSENTRY']._serialized_end = 1500 + _globals['_CAPTUREINTERVAL']._serialized_start = 1525 + _globals['_CAPTUREINTERVAL']._serialized_end = 1638 + _globals['_TABULARDATABYFILTERREQUEST']._serialized_start = 1641 + _globals['_TABULARDATABYFILTERREQUEST']._serialized_end = 1822 + _globals['_TABULARDATABYFILTERRESPONSE']._serialized_start = 1825 + _globals['_TABULARDATABYFILTERRESPONSE']._serialized_end = 2056 + _globals['_TABULARDATA']._serialized_start = 2059 + _globals['_TABULARDATA']._serialized_end = 2292 + _globals['_TABULARDATABYSQLREQUEST']._serialized_start = 2294 + _globals['_TABULARDATABYSQLREQUEST']._serialized_end = 2389 + _globals['_TABULARDATABYSQLRESPONSE']._serialized_start = 2391 + _globals['_TABULARDATABYSQLRESPONSE']._serialized_end = 2456 + _globals['_TABULARDATASOURCE']._serialized_start = 2459 + _globals['_TABULARDATASOURCE']._serialized_end = 2593 + _globals['_TABULARDATABYMQLREQUEST']._serialized_start = 2596 + _globals['_TABULARDATABYMQLREQUEST']._serialized_end = 2891 + _globals['_TABULARDATABYMQLRESPONSE']._serialized_start = 2893 + _globals['_TABULARDATABYMQLRESPONSE']._serialized_end = 2958 + _globals['_EXPORTTABULARDATAREQUEST']._serialized_start = 2961 + _globals['_EXPORTTABULARDATAREQUEST']._serialized_end = 3188 + _globals['_EXPORTTABULARDATARESPONSE']._serialized_start = 3191 + _globals['_EXPORTTABULARDATARESPONSE']._serialized_end = 3723 + _globals['_GETLATESTTABULARDATAREQUEST']._serialized_start = 3726 + _globals['_GETLATESTTABULARDATAREQUEST']._serialized_end = 3893 + _globals['_GETLATESTTABULARDATARESPONSE']._serialized_start = 3896 + _globals['_GETLATESTTABULARDATARESPONSE']._serialized_end = 4103 + _globals['_BINARYDATA']._serialized_start = 4105 + _globals['_BINARYDATA']._serialized_end = 4203 + _globals['_BINARYDATABYFILTERREQUEST']._serialized_start = 4206 + _globals['_BINARYDATABYFILTERREQUEST']._serialized_end = 4421 + _globals['_BINARYDATABYFILTERRESPONSE']._serialized_start = 4424 + _globals['_BINARYDATABYFILTERRESPONSE']._serialized_end = 4586 + _globals['_BINARYID']._serialized_start = 4588 + _globals['_BINARYID']._serialized_end = 4701 + _globals['_BINARYDATABYIDSREQUEST']._serialized_start = 4704 + _globals['_BINARYDATABYIDSREQUEST']._serialized_end = 4886 + _globals['_BINARYDATABYIDSRESPONSE']._serialized_start = 4888 + _globals['_BINARYDATABYIDSRESPONSE']._serialized_end = 4985 + _globals['_BOUNDINGBOX']._serialized_start = 4988 + _globals['_BOUNDINGBOX']._serialized_end = 5259 + _globals['_CLASSIFICATION']._serialized_start = 5261 + _globals['_CLASSIFICATION']._serialized_end = 5351 + _globals['_ANNOTATIONS']._serialized_start = 5354 + _globals['_ANNOTATIONS']._serialized_end = 5498 + _globals['_BINARYMETADATA']._serialized_start = 5501 + _globals['_BINARYMETADATA']._serialized_end = 5957 + _globals['_DELETETABULARDATAREQUEST']._serialized_start = 5959 + _globals['_DELETETABULARDATAREQUEST']._serialized_end = 6079 + _globals['_DELETETABULARDATARESPONSE']._serialized_start = 6081 + _globals['_DELETETABULARDATARESPONSE']._serialized_end = 6145 + _globals['_DELETEBINARYDATABYFILTERREQUEST']._serialized_start = 6148 + _globals['_DELETEBINARYDATABYFILTERREQUEST']._serialized_end = 6283 + _globals['_DELETEBINARYDATABYFILTERRESPONSE']._serialized_start = 6285 + _globals['_DELETEBINARYDATABYFILTERRESPONSE']._serialized_end = 6370 + _globals['_DELETEBINARYDATABYIDSREQUEST']._serialized_start = 6373 + _globals['_DELETEBINARYDATABYIDSREQUEST']._serialized_end = 6522 + _globals['_DELETEBINARYDATABYIDSRESPONSE']._serialized_start = 6524 + _globals['_DELETEBINARYDATABYIDSRESPONSE']._serialized_end = 6606 + _globals['_ADDTAGSTOBINARYDATABYIDSREQUEST']._serialized_start = 6609 + _globals['_ADDTAGSTOBINARYDATABYIDSREQUEST']._serialized_end = 6781 + _globals['_ADDTAGSTOBINARYDATABYIDSRESPONSE']._serialized_start = 6783 + _globals['_ADDTAGSTOBINARYDATABYIDSRESPONSE']._serialized_end = 6817 + _globals['_ADDTAGSTOBINARYDATABYFILTERREQUEST']._serialized_start = 6819 + _globals['_ADDTAGSTOBINARYDATABYFILTERREQUEST']._serialized_end = 6925 + _globals['_ADDTAGSTOBINARYDATABYFILTERRESPONSE']._serialized_start = 6927 + _globals['_ADDTAGSTOBINARYDATABYFILTERRESPONSE']._serialized_end = 6964 + _globals['_REMOVETAGSFROMBINARYDATABYIDSREQUEST']._serialized_start = 6967 + _globals['_REMOVETAGSFROMBINARYDATABYIDSREQUEST']._serialized_end = 7144 + _globals['_REMOVETAGSFROMBINARYDATABYIDSRESPONSE']._serialized_start = 7146 + _globals['_REMOVETAGSFROMBINARYDATABYIDSRESPONSE']._serialized_end = 7222 + _globals['_REMOVETAGSFROMBINARYDATABYFILTERREQUEST']._serialized_start = 7224 + _globals['_REMOVETAGSFROMBINARYDATABYFILTERREQUEST']._serialized_end = 7335 + _globals['_REMOVETAGSFROMBINARYDATABYFILTERRESPONSE']._serialized_start = 7337 + _globals['_REMOVETAGSFROMBINARYDATABYFILTERRESPONSE']._serialized_end = 7416 + _globals['_TAGSBYFILTERREQUEST']._serialized_start = 7418 + _globals['_TAGSBYFILTERREQUEST']._serialized_end = 7489 + _globals['_TAGSBYFILTERRESPONSE']._serialized_start = 7491 + _globals['_TAGSBYFILTERRESPONSE']._serialized_end = 7533 + _globals['_ADDBOUNDINGBOXTOIMAGEBYIDREQUEST']._serialized_start = 7536 + _globals['_ADDBOUNDINGBOXTOIMAGEBYIDREQUEST']._serialized_end = 7874 + _globals['_ADDBOUNDINGBOXTOIMAGEBYIDRESPONSE']._serialized_start = 7876 + _globals['_ADDBOUNDINGBOXTOIMAGEBYIDRESPONSE']._serialized_end = 7936 + _globals['_REMOVEBOUNDINGBOXFROMIMAGEBYIDREQUEST']._serialized_start = 7939 + _globals['_REMOVEBOUNDINGBOXFROMIMAGEBYIDREQUEST']._serialized_end = 8117 + _globals['_REMOVEBOUNDINGBOXFROMIMAGEBYIDRESPONSE']._serialized_start = 8119 + _globals['_REMOVEBOUNDINGBOXFROMIMAGEBYIDRESPONSE']._serialized_end = 8159 + _globals['_UPDATEBOUNDINGBOXREQUEST']._serialized_start = 8162 + _globals['_UPDATEBOUNDINGBOXREQUEST']._serialized_end = 8621 + _globals['_UPDATEBOUNDINGBOXRESPONSE']._serialized_start = 8623 + _globals['_UPDATEBOUNDINGBOXRESPONSE']._serialized_end = 8650 + _globals['_BOUNDINGBOXLABELSBYFILTERREQUEST']._serialized_start = 8652 + _globals['_BOUNDINGBOXLABELSBYFILTERREQUEST']._serialized_end = 8736 + _globals['_BOUNDINGBOXLABELSBYFILTERRESPONSE']._serialized_start = 8738 + _globals['_BOUNDINGBOXLABELSBYFILTERRESPONSE']._serialized_end = 8797 + _globals['_CONFIGUREDATABASEUSERREQUEST']._serialized_start = 8799 + _globals['_CONFIGUREDATABASEUSERREQUEST']._serialized_end = 8898 + _globals['_CONFIGUREDATABASEUSERRESPONSE']._serialized_start = 8900 + _globals['_CONFIGUREDATABASEUSERRESPONSE']._serialized_end = 8931 + _globals['_GETDATABASECONNECTIONREQUEST']._serialized_start = 8933 + _globals['_GETDATABASECONNECTIONREQUEST']._serialized_end = 9004 + _globals['_GETDATABASECONNECTIONRESPONSE']._serialized_start = 9007 + _globals['_GETDATABASECONNECTIONRESPONSE']._serialized_end = 9143 + _globals['_ADDBINARYDATATODATASETBYIDSREQUEST']._serialized_start = 9146 + _globals['_ADDBINARYDATATODATASETBYIDSREQUEST']._serialized_end = 9316 + _globals['_ADDBINARYDATATODATASETBYIDSRESPONSE']._serialized_start = 9318 + _globals['_ADDBINARYDATATODATASETBYIDSRESPONSE']._serialized_end = 9355 + _globals['_REMOVEBINARYDATAFROMDATASETBYIDSREQUEST']._serialized_start = 9358 + _globals['_REMOVEBINARYDATAFROMDATASETBYIDSREQUEST']._serialized_end = 9533 + _globals['_REMOVEBINARYDATAFROMDATASETBYIDSRESPONSE']._serialized_start = 9535 + _globals['_REMOVEBINARYDATAFROMDATASETBYIDSRESPONSE']._serialized_end = 9577 + _globals['_DATASERVICE']._serialized_start = 9995 + _globals['_DATASERVICE']._serialized_end = 12897 \ No newline at end of file diff --git a/src/viam/gen/app/data/v1/data_pb2.pyi b/src/viam/gen/app/data/v1/data_pb2.pyi index 4a8d0bc39..d86165264 100644 --- a/src/viam/gen/app/data/v1/data_pb2.pyi +++ b/src/viam/gen/app/data/v1/data_pb2.pyi @@ -4,10 +4,12 @@ isort:skip_file """ import builtins import collections.abc +import google.protobuf.any_pb2 import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message +import google.protobuf.struct_pb2 import google.protobuf.timestamp_pb2 import sys import typing @@ -17,99 +19,255 @@ else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor -class _DataType: +class _Order: ValueType = typing.NewType('ValueType', builtins.int) V: typing_extensions.TypeAlias = ValueType -class _DataTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DataType.ValueType], builtins.type): +class _OrderEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Order.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - DATA_TYPE_UNSPECIFIED: _DataType.ValueType - DATA_TYPE_BINARY_SENSOR: _DataType.ValueType - DATA_TYPE_TABULAR_SENSOR: _DataType.ValueType - DATA_TYPE_FILE: _DataType.ValueType - -class DataType(_DataType, metaclass=_DataTypeEnumTypeWrapper): - ... -DATA_TYPE_UNSPECIFIED: DataType.ValueType -DATA_TYPE_BINARY_SENSOR: DataType.ValueType -DATA_TYPE_TABULAR_SENSOR: DataType.ValueType -DATA_TYPE_FILE: DataType.ValueType -global___DataType = DataType - -class QueryRequest(google.protobuf.message.Message): - """QueryRequest specifies filters to get metadata from a particular component, method, robot, location, etc...""" - DESCRIPTOR: google.protobuf.descriptor.Descriptor - FILTERS_FIELD_NUMBER: builtins.int - TYPE_FIELD_NUMBER: builtins.int - SKIP_FIELD_NUMBER: builtins.int + ORDER_UNSPECIFIED: _Order.ValueType + ORDER_DESCENDING: _Order.ValueType + ORDER_ASCENDING: _Order.ValueType + +class Order(_Order, metaclass=_OrderEnumTypeWrapper): + """Order specifies the order in which data is returned.""" +ORDER_UNSPECIFIED: Order.ValueType +ORDER_DESCENDING: Order.ValueType +ORDER_ASCENDING: Order.ValueType +global___Order = Order + +class _TagsFilterType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _TagsFilterTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_TagsFilterType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + TAGS_FILTER_TYPE_UNSPECIFIED: _TagsFilterType.ValueType + TAGS_FILTER_TYPE_MATCH_BY_OR: _TagsFilterType.ValueType + 'TAGS_FILTER_TYPE_MATCH_BY_OR specifies documents matched (using logical OR) on the tags field in the TagsFilter.' + TAGS_FILTER_TYPE_TAGGED: _TagsFilterType.ValueType + 'TAGS_FILTER_TYPE_TAGGED specifies that all tagged documents should be returned.' + TAGS_FILTER_TYPE_UNTAGGED: _TagsFilterType.ValueType + 'TAGS_FILTER_TYPE_UNTAGGED specifes that all untagged documents should be returned.' + +class TagsFilterType(_TagsFilterType, metaclass=_TagsFilterTypeEnumTypeWrapper): + """TagsFilterType specifies how data can be filtered based on tags.""" +TAGS_FILTER_TYPE_UNSPECIFIED: TagsFilterType.ValueType +TAGS_FILTER_TYPE_MATCH_BY_OR: TagsFilterType.ValueType +'TAGS_FILTER_TYPE_MATCH_BY_OR specifies documents matched (using logical OR) on the tags field in the TagsFilter.' +TAGS_FILTER_TYPE_TAGGED: TagsFilterType.ValueType +'TAGS_FILTER_TYPE_TAGGED specifies that all tagged documents should be returned.' +TAGS_FILTER_TYPE_UNTAGGED: TagsFilterType.ValueType +'TAGS_FILTER_TYPE_UNTAGGED specifes that all untagged documents should be returned.' +global___TagsFilterType = TagsFilterType + +class _TabularDataSourceType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _TabularDataSourceTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_TabularDataSourceType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + TABULAR_DATA_SOURCE_TYPE_UNSPECIFIED: _TabularDataSourceType.ValueType + TABULAR_DATA_SOURCE_TYPE_STANDARD: _TabularDataSourceType.ValueType + 'TABULAR_DATA_SOURCE_TYPE_STANDARD indicates reading from standard storage. This is\n the default option and available for all data synced to Viam.\n ' + TABULAR_DATA_SOURCE_TYPE_HOT_STORAGE: _TabularDataSourceType.ValueType + 'TABULAR_DATA_SOURCE_TYPE_HOT_STORAGE indicates reading from hot storage. This is a\n premium feature requiring opting in specific data sources.\n See docs at https://docs.viam.com/data-ai/capture-data/advanced/advanced-data-capture-sync/#capture-to-the-hot-data-store\n ' + TABULAR_DATA_SOURCE_TYPE_PIPELINE_SINK: _TabularDataSourceType.ValueType + 'TABULAR_DATA_SOURCE_TYPE_PIPELINE_SINK indicates reading the output data of\n a data pipeline. When using this, a pipeline ID needs to be specified.\n ' + +class TabularDataSourceType(_TabularDataSourceType, metaclass=_TabularDataSourceTypeEnumTypeWrapper): + """TabularDataSourceType specifies the data source type for TabularDataByMQL queries.""" +TABULAR_DATA_SOURCE_TYPE_UNSPECIFIED: TabularDataSourceType.ValueType +TABULAR_DATA_SOURCE_TYPE_STANDARD: TabularDataSourceType.ValueType +'TABULAR_DATA_SOURCE_TYPE_STANDARD indicates reading from standard storage. This is\nthe default option and available for all data synced to Viam.\n' +TABULAR_DATA_SOURCE_TYPE_HOT_STORAGE: TabularDataSourceType.ValueType +'TABULAR_DATA_SOURCE_TYPE_HOT_STORAGE indicates reading from hot storage. This is a\npremium feature requiring opting in specific data sources.\nSee docs at https://docs.viam.com/data-ai/capture-data/advanced/advanced-data-capture-sync/#capture-to-the-hot-data-store\n' +TABULAR_DATA_SOURCE_TYPE_PIPELINE_SINK: TabularDataSourceType.ValueType +'TABULAR_DATA_SOURCE_TYPE_PIPELINE_SINK indicates reading the output data of\na data pipeline. When using this, a pipeline ID needs to be specified.\n' +global___TabularDataSourceType = TabularDataSourceType + +@typing.final +class DataRequest(google.protobuf.message.Message): + """DataRequest encapsulates the filter for the data, a limit on the maximum results returned, + a last string associated with the last returned document, and the sorting order by time. + last is returned in the responses TabularDataByFilterResponse and BinaryDataByFilterResponse + from the API calls TabularDataByFilter and BinaryDataByFilter, respectively. + We can then use the last string from the previous API calls in the subsequent request + to get the next set of ordered documents. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FILTER_FIELD_NUMBER: builtins.int LIMIT_FIELD_NUMBER: builtins.int - COUNT_ONLY_FIELD_NUMBER: builtins.int + LAST_FIELD_NUMBER: builtins.int + SORT_ORDER_FIELD_NUMBER: builtins.int + limit: builtins.int + last: builtins.str + sort_order: global___Order.ValueType @property - def filters(self) -> global___Filters: + def filter(self) -> global___Filter: ... - type: global___DataType.ValueType - skip: builtins.int - 'Skips over the number of specified results.' - limit: builtins.int - 'Limits the number of results.' - count_only: builtins.bool - 'Only includes the number of results rather than getting actual data.' - def __init__(self, *, filters: global___Filters | None=..., type: global___DataType.ValueType=..., skip: builtins.int=..., limit: builtins.int=..., count_only: builtins.bool=...) -> None: + def __init__(self, *, filter: global___Filter | None=..., limit: builtins.int=..., last: builtins.str=..., sort_order: global___Order.ValueType=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['filters', b'filters']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['filter', b'filter']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['count_only', b'count_only', 'filters', b'filters', 'limit', b'limit', 'skip', b'skip', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['filter', b'filter', 'last', b'last', 'limit', b'limit', 'sort_order', b'sort_order']) -> None: ... -global___QueryRequest = QueryRequest +global___DataRequest = DataRequest -class Filters(google.protobuf.message.Message): +@typing.final +class Filter(google.protobuf.message.Message): + """Filter defines the fields over which we can filter data using a logic AND. + For example, if component_type and robot_id are specified, only data from that `robot_id` of + type `component_type` is returned. However, we logical OR over the specified tags and bounding + box labels, such that if component_type, robot_id, tagA, tagB are specified, + we return data from that `robot_id` of type `component_type` with `tagA` or `tagB`. + """ DESCRIPTOR: google.protobuf.descriptor.Descriptor COMPONENT_NAME_FIELD_NUMBER: builtins.int COMPONENT_TYPE_FIELD_NUMBER: builtins.int - COMPONENT_MODEL_FIELD_NUMBER: builtins.int METHOD_FIELD_NUMBER: builtins.int - TAGS_FIELD_NUMBER: builtins.int ROBOT_NAME_FIELD_NUMBER: builtins.int ROBOT_ID_FIELD_NUMBER: builtins.int PART_NAME_FIELD_NUMBER: builtins.int PART_ID_FIELD_NUMBER: builtins.int - LOCATION_ID_FIELD_NUMBER: builtins.int - ORG_ID_FIELD_NUMBER: builtins.int + LOCATION_IDS_FIELD_NUMBER: builtins.int + ORGANIZATION_IDS_FIELD_NUMBER: builtins.int + MIME_TYPE_FIELD_NUMBER: builtins.int INTERVAL_FIELD_NUMBER: builtins.int + TAGS_FILTER_FIELD_NUMBER: builtins.int + BBOX_LABELS_FIELD_NUMBER: builtins.int + DATASET_ID_FIELD_NUMBER: builtins.int component_name: builtins.str component_type: builtins.str - component_model: builtins.str method: builtins.str + robot_name: builtins.str + robot_id: builtins.str + part_name: builtins.str + part_id: builtins.str + dataset_id: builtins.str + + @property + def location_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def organization_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def mime_type(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def interval(self) -> global___CaptureInterval: + ... + + @property + def tags_filter(self) -> global___TagsFilter: + ... + + @property + def bbox_labels(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """bbox_labels are used to match documents with the specified bounding box labels (using logical OR).""" + + def __init__(self, *, component_name: builtins.str=..., component_type: builtins.str=..., method: builtins.str=..., robot_name: builtins.str=..., robot_id: builtins.str=..., part_name: builtins.str=..., part_id: builtins.str=..., location_ids: collections.abc.Iterable[builtins.str] | None=..., organization_ids: collections.abc.Iterable[builtins.str] | None=..., mime_type: collections.abc.Iterable[builtins.str] | None=..., interval: global___CaptureInterval | None=..., tags_filter: global___TagsFilter | None=..., bbox_labels: collections.abc.Iterable[builtins.str] | None=..., dataset_id: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['interval', b'interval', 'tags_filter', b'tags_filter']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['bbox_labels', b'bbox_labels', 'component_name', b'component_name', 'component_type', b'component_type', 'dataset_id', b'dataset_id', 'interval', b'interval', 'location_ids', b'location_ids', 'method', b'method', 'mime_type', b'mime_type', 'organization_ids', b'organization_ids', 'part_id', b'part_id', 'part_name', b'part_name', 'robot_id', b'robot_id', 'robot_name', b'robot_name', 'tags_filter', b'tags_filter']) -> None: + ... +global___Filter = Filter + +@typing.final +class TagsFilter(google.protobuf.message.Message): + """TagsFilter defines the type of filtering and, if applicable, over which tags to perform a logical OR.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TYPE_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + type: global___TagsFilterType.ValueType @property def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Tags are used to match documents if `type` is UNSPECIFIED or MATCH_BY_OR.""" + + def __init__(self, *, type: global___TagsFilterType.ValueType=..., tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['tags', b'tags', 'type', b'type']) -> None: ... +global___TagsFilter = TagsFilter + +@typing.final +class CaptureMetadata(google.protobuf.message.Message): + """CaptureMetadata contains information on the settings used for the data capture.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class MethodParametersEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + + @property + def value(self) -> google.protobuf.any_pb2.Any: + ... + + def __init__(self, *, key: builtins.str=..., value: google.protobuf.any_pb2.Any | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['value', b'value']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + LOCATION_ID_FIELD_NUMBER: builtins.int + ROBOT_NAME_FIELD_NUMBER: builtins.int + ROBOT_ID_FIELD_NUMBER: builtins.int + PART_NAME_FIELD_NUMBER: builtins.int + PART_ID_FIELD_NUMBER: builtins.int + COMPONENT_TYPE_FIELD_NUMBER: builtins.int + COMPONENT_NAME_FIELD_NUMBER: builtins.int + METHOD_NAME_FIELD_NUMBER: builtins.int + METHOD_PARAMETERS_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + MIME_TYPE_FIELD_NUMBER: builtins.int + organization_id: builtins.str + location_id: builtins.str robot_name: builtins.str robot_id: builtins.str part_name: builtins.str part_id: builtins.str - location_id: builtins.str - org_id: builtins.str + component_type: builtins.str + component_name: builtins.str + method_name: builtins.str + mime_type: builtins.str @property - def interval(self) -> global___CaptureInterval: + def method_parameters(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, google.protobuf.any_pb2.Any]: ... - def __init__(self, *, component_name: builtins.str=..., component_type: builtins.str=..., component_model: builtins.str=..., method: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=..., robot_name: builtins.str=..., robot_id: builtins.str=..., part_name: builtins.str=..., part_id: builtins.str=..., location_id: builtins.str=..., org_id: builtins.str=..., interval: global___CaptureInterval | None=...) -> None: + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... - def HasField(self, field_name: typing_extensions.Literal['interval', b'interval']) -> builtins.bool: + def __init__(self, *, organization_id: builtins.str=..., location_id: builtins.str=..., robot_name: builtins.str=..., robot_id: builtins.str=..., part_name: builtins.str=..., part_id: builtins.str=..., component_type: builtins.str=..., component_name: builtins.str=..., method_name: builtins.str=..., method_parameters: collections.abc.Mapping[builtins.str, google.protobuf.any_pb2.Any] | None=..., tags: collections.abc.Iterable[builtins.str] | None=..., mime_type: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['component_model', b'component_model', 'component_name', b'component_name', 'component_type', b'component_type', 'interval', b'interval', 'location_id', b'location_id', 'method', b'method', 'org_id', b'org_id', 'part_id', b'part_id', 'part_name', b'part_name', 'robot_id', b'robot_id', 'robot_name', b'robot_name', 'tags', b'tags']) -> None: + def ClearField(self, field_name: typing.Literal['component_name', b'component_name', 'component_type', b'component_type', 'location_id', b'location_id', 'method_name', b'method_name', 'method_parameters', b'method_parameters', 'mime_type', b'mime_type', 'organization_id', b'organization_id', 'part_id', b'part_id', 'part_name', b'part_name', 'robot_id', b'robot_id', 'robot_name', b'robot_name', 'tags', b'tags']) -> None: ... -global___Filters = Filters +global___CaptureMetadata = CaptureMetadata +@typing.final class CaptureInterval(google.protobuf.message.Message): + """CaptureInterval describes the start and end time of the capture in this file.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor START_FIELD_NUMBER: builtins.int END_FIELD_NUMBER: builtins.int @@ -125,165 +283,1203 @@ class CaptureInterval(google.protobuf.message.Message): def __init__(self, *, start: google.protobuf.timestamp_pb2.Timestamp | None=..., end: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['end', b'end', 'start', b'start']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['end', b'end', 'start', b'start']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['end', b'end', 'start', b'start']) -> None: + def ClearField(self, field_name: typing.Literal['end', b'end', 'start', b'start']) -> None: ... global___CaptureInterval = CaptureInterval -class QueryResponse(google.protobuf.message.Message): +@typing.final +class TabularDataByFilterRequest(google.protobuf.message.Message): + """TabularDataByFilterRequest requests tabular data based on filter values.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor - TABULAR_FIELD_NUMBER: builtins.int - BINARY_FIELD_NUMBER: builtins.int - FILE_FIELD_NUMBER: builtins.int - TABULAR_COUNT_FIELD_NUMBER: builtins.int - BINARY_COUNT_FIELD_NUMBER: builtins.int - FILE_COUNT_FIELD_NUMBER: builtins.int + DATA_REQUEST_FIELD_NUMBER: builtins.int + COUNT_ONLY_FIELD_NUMBER: builtins.int + INCLUDE_INTERNAL_DATA_FIELD_NUMBER: builtins.int + count_only: builtins.bool + include_internal_data: builtins.bool @property - def tabular(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TabularMetadata]: + def data_request(self) -> global___DataRequest: + ... + + def __init__(self, *, data_request: global___DataRequest | None=..., count_only: builtins.bool=..., include_internal_data: builtins.bool=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['data_request', b'data_request']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['count_only', b'count_only', 'data_request', b'data_request', 'include_internal_data', b'include_internal_data']) -> None: ... +global___TabularDataByFilterRequest = TabularDataByFilterRequest + +@typing.final +class TabularDataByFilterResponse(google.protobuf.message.Message): + """TabularDataByFilterResponse provides the data and metadata of tabular data.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + METADATA_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + COUNT_FIELD_NUMBER: builtins.int + LAST_FIELD_NUMBER: builtins.int + TOTAL_SIZE_BYTES_FIELD_NUMBER: builtins.int + count: builtins.int + last: builtins.str + total_size_bytes: builtins.int @property - def binary(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BinaryMetadata]: + def metadata(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___CaptureMetadata]: ... @property - def file(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___FileMetadata]: + def data(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TabularData]: ... - tabular_count: builtins.int - binary_count: builtins.int - file_count: builtins.int - def __init__(self, *, tabular: collections.abc.Iterable[global___TabularMetadata] | None=..., binary: collections.abc.Iterable[global___BinaryMetadata] | None=..., file: collections.abc.Iterable[global___FileMetadata] | None=..., tabular_count: builtins.int=..., binary_count: builtins.int=..., file_count: builtins.int=...) -> None: + def __init__(self, *, metadata: collections.abc.Iterable[global___CaptureMetadata] | None=..., data: collections.abc.Iterable[global___TabularData] | None=..., count: builtins.int=..., last: builtins.str=..., total_size_bytes: builtins.int=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['binary', b'binary', 'binary_count', b'binary_count', 'file', b'file', 'file_count', b'file_count', 'tabular', b'tabular', 'tabular_count', b'tabular_count']) -> None: + def ClearField(self, field_name: typing.Literal['count', b'count', 'data', b'data', 'last', b'last', 'metadata', b'metadata', 'total_size_bytes', b'total_size_bytes']) -> None: ... -global___QueryResponse = QueryResponse +global___TabularDataByFilterResponse = TabularDataByFilterResponse -class TabularMetadata(google.protobuf.message.Message): +@typing.final +class TabularData(google.protobuf.message.Message): + """TabularData contains data and metadata associated with tabular data.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - ROBOT_NAME_FIELD_NUMBER: builtins.int - ROBOT_ID_FIELD_NUMBER: builtins.int - PART_NAME_FIELD_NUMBER: builtins.int - PART_ID_FIELD_NUMBER: builtins.int - COMPONENT_NAME_FIELD_NUMBER: builtins.int - COMPONENT_TYPE_FIELD_NUMBER: builtins.int - COMPONENT_MODEL_FIELD_NUMBER: builtins.int - METHOD_FIELD_NUMBER: builtins.int - TAGS_FIELD_NUMBER: builtins.int - INTERVAL_FIELD_NUMBER: builtins.int - FILE_SIZE_BYTES_FIELD_NUMBER: builtins.int - NUM_READINGS_FIELD_NUMBER: builtins.int - id: builtins.str - 'Result id.' - robot_name: builtins.str - robot_id: builtins.str - part_name: builtins.str - part_id: builtins.str - component_name: builtins.str - component_type: builtins.str - component_model: builtins.str - method: builtins.str + DATA_FIELD_NUMBER: builtins.int + METADATA_INDEX_FIELD_NUMBER: builtins.int + TIME_REQUESTED_FIELD_NUMBER: builtins.int + TIME_RECEIVED_FIELD_NUMBER: builtins.int + metadata_index: builtins.int @property - def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + def data(self) -> google.protobuf.struct_pb2.Struct: + ... + + @property + def time_requested(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def time_received(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, data: google.protobuf.struct_pb2.Struct | None=..., metadata_index: builtins.int=..., time_requested: google.protobuf.timestamp_pb2.Timestamp | None=..., time_received: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['data', b'data', 'time_received', b'time_received', 'time_requested', b'time_requested']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data', 'metadata_index', b'metadata_index', 'time_received', b'time_received', 'time_requested', b'time_requested']) -> None: + ... +global___TabularData = TabularData + +@typing.final +class TabularDataBySQLRequest(google.protobuf.message.Message): + """TabularDataBySQLRequest requests tabular data using a SQL query.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + SQL_QUERY_FIELD_NUMBER: builtins.int + organization_id: builtins.str + sql_query: builtins.str + 'sql_query accepts any valid SQL SELECT statement. Tabular data is held in a database\n called "sensorData" and a table called readings, so queries should select from "readings"\n or "sensorData.readings".\n ' + + def __init__(self, *, organization_id: builtins.str=..., sql_query: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id', 'sql_query', b'sql_query']) -> None: + ... +global___TabularDataBySQLRequest = TabularDataBySQLRequest + +@typing.final +class TabularDataBySQLResponse(google.protobuf.message.Message): + """TabularDataBySQLResponse provides unified tabular data and metadata, queried with SQL.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RAW_DATA_FIELD_NUMBER: builtins.int + + @property + def raw_data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]: + ... + + def __init__(self, *, raw_data: collections.abc.Iterable[builtins.bytes] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['raw_data', b'raw_data']) -> None: + ... +global___TabularDataBySQLResponse = TabularDataBySQLResponse + +@typing.final +class TabularDataSource(google.protobuf.message.Message): + """TabularDataSource specifies the data source for user queries to execute on.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TYPE_FIELD_NUMBER: builtins.int + PIPELINE_ID_FIELD_NUMBER: builtins.int + type: global___TabularDataSourceType.ValueType + pipeline_id: builtins.str + 'pipeline_id is the ID of the pipeline to query. Required when using\n TABULAR_DATA_SOURCE_TYPE_PIPELINE_SINK.\n ' + + def __init__(self, *, type: global___TabularDataSourceType.ValueType=..., pipeline_id: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_pipeline_id', b'_pipeline_id', 'pipeline_id', b'pipeline_id']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_pipeline_id', b'_pipeline_id', 'pipeline_id', b'pipeline_id', 'type', b'type']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_pipeline_id', b'_pipeline_id']) -> typing.Literal['pipeline_id'] | None: + ... +global___TabularDataSource = TabularDataSource + +@typing.final +class TabularDataByMQLRequest(google.protobuf.message.Message): + """TabularDataByMQLRequest requests tabular data using an MQL query.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + MQL_BINARY_FIELD_NUMBER: builtins.int + USE_RECENT_DATA_FIELD_NUMBER: builtins.int + DATA_SOURCE_FIELD_NUMBER: builtins.int + organization_id: builtins.str + use_recent_data: builtins.bool + 'Deprecated, please use TABULAR_DATA_SOURCE_TYPE_HOT_STORAGE instead.' + + @property + def mql_binary(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]: + """mql_binary accepts a MongoDB aggregation pipeline as a list of BSON documents, where each + document is one stage in the pipeline. The pipeline is run on the "sensorData.readings" + namespace, which holds the Viam organization's tabular data. + """ + + @property + def data_source(self) -> global___TabularDataSource: + """data_source is an optional field that can be used to specify the data source for the query. + If not specified, the query will run on "standard" storage. + """ + + def __init__(self, *, organization_id: builtins.str=..., mql_binary: collections.abc.Iterable[builtins.bytes] | None=..., use_recent_data: builtins.bool | None=..., data_source: global___TabularDataSource | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_data_source', b'_data_source', '_use_recent_data', b'_use_recent_data', 'data_source', b'data_source', 'use_recent_data', b'use_recent_data']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_data_source', b'_data_source', '_use_recent_data', b'_use_recent_data', 'data_source', b'data_source', 'mql_binary', b'mql_binary', 'organization_id', b'organization_id', 'use_recent_data', b'use_recent_data']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_data_source', b'_data_source']) -> typing.Literal['data_source'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_use_recent_data', b'_use_recent_data']) -> typing.Literal['use_recent_data'] | None: + ... +global___TabularDataByMQLRequest = TabularDataByMQLRequest + +@typing.final +class TabularDataByMQLResponse(google.protobuf.message.Message): + """TabularDataByMQLResponse provides unified tabular data and metadata, queried with MQL.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RAW_DATA_FIELD_NUMBER: builtins.int + + @property + def raw_data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]: ... + def __init__(self, *, raw_data: collections.abc.Iterable[builtins.bytes] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['raw_data', b'raw_data']) -> None: + ... +global___TabularDataByMQLResponse = TabularDataByMQLResponse + +@typing.final +class ExportTabularDataRequest(google.protobuf.message.Message): + """ExportTabularDataRequest requests tabular data from the specified data source.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_ID_FIELD_NUMBER: builtins.int + RESOURCE_NAME_FIELD_NUMBER: builtins.int + RESOURCE_SUBTYPE_FIELD_NUMBER: builtins.int + METHOD_NAME_FIELD_NUMBER: builtins.int + INTERVAL_FIELD_NUMBER: builtins.int + part_id: builtins.str + resource_name: builtins.str + resource_subtype: builtins.str + method_name: builtins.str + @property def interval(self) -> global___CaptureInterval: ... - file_size_bytes: builtins.int - num_readings: builtins.int - def __init__(self, *, id: builtins.str=..., robot_name: builtins.str=..., robot_id: builtins.str=..., part_name: builtins.str=..., part_id: builtins.str=..., component_name: builtins.str=..., component_type: builtins.str=..., component_model: builtins.str=..., method: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=..., interval: global___CaptureInterval | None=..., file_size_bytes: builtins.int=..., num_readings: builtins.int=...) -> None: + def __init__(self, *, part_id: builtins.str=..., resource_name: builtins.str=..., resource_subtype: builtins.str=..., method_name: builtins.str=..., interval: global___CaptureInterval | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['interval', b'interval']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['interval', b'interval']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['component_model', b'component_model', 'component_name', b'component_name', 'component_type', b'component_type', 'file_size_bytes', b'file_size_bytes', 'id', b'id', 'interval', b'interval', 'method', b'method', 'num_readings', b'num_readings', 'part_id', b'part_id', 'part_name', b'part_name', 'robot_id', b'robot_id', 'robot_name', b'robot_name', 'tags', b'tags']) -> None: + def ClearField(self, field_name: typing.Literal['interval', b'interval', 'method_name', b'method_name', 'part_id', b'part_id', 'resource_name', b'resource_name', 'resource_subtype', b'resource_subtype']) -> None: ... -global___TabularMetadata = TabularMetadata +global___ExportTabularDataRequest = ExportTabularDataRequest -class BinaryMetadata(google.protobuf.message.Message): +@typing.final +class ExportTabularDataResponse(google.protobuf.message.Message): + """ExportTabularDataResponse provides unified tabular data and metadata for a single data point from the specified data source.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int + PART_ID_FIELD_NUMBER: builtins.int + RESOURCE_NAME_FIELD_NUMBER: builtins.int + RESOURCE_SUBTYPE_FIELD_NUMBER: builtins.int + METHOD_NAME_FIELD_NUMBER: builtins.int + TIME_CAPTURED_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + LOCATION_ID_FIELD_NUMBER: builtins.int ROBOT_NAME_FIELD_NUMBER: builtins.int ROBOT_ID_FIELD_NUMBER: builtins.int PART_NAME_FIELD_NUMBER: builtins.int - PART_ID_FIELD_NUMBER: builtins.int - COMPONENT_NAME_FIELD_NUMBER: builtins.int - COMPONENT_TYPE_FIELD_NUMBER: builtins.int - COMPONENT_MODEL_FIELD_NUMBER: builtins.int - METHOD_FIELD_NUMBER: builtins.int + METHOD_PARAMETERS_FIELD_NUMBER: builtins.int TAGS_FIELD_NUMBER: builtins.int - INTERVAL_FIELD_NUMBER: builtins.int - FILE_SIZE_BYTES_FIELD_NUMBER: builtins.int - FILE_ID_FIELD_NUMBER: builtins.int - id: builtins.str - 'Result id.' + PAYLOAD_FIELD_NUMBER: builtins.int + part_id: builtins.str + resource_name: builtins.str + resource_subtype: builtins.str + method_name: builtins.str + organization_id: builtins.str + location_id: builtins.str robot_name: builtins.str robot_id: builtins.str part_name: builtins.str - part_id: builtins.str - component_name: builtins.str - component_type: builtins.str - component_model: builtins.str - method: builtins.str + + @property + def time_captured(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def method_parameters(self) -> google.protobuf.struct_pb2.Struct: + ... @property def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... @property - def interval(self) -> global___CaptureInterval: + def payload(self) -> google.protobuf.struct_pb2.Struct: ... - file_size_bytes: builtins.int - file_id: builtins.str - def __init__(self, *, id: builtins.str=..., robot_name: builtins.str=..., robot_id: builtins.str=..., part_name: builtins.str=..., part_id: builtins.str=..., component_name: builtins.str=..., component_type: builtins.str=..., component_model: builtins.str=..., method: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=..., interval: global___CaptureInterval | None=..., file_size_bytes: builtins.int=..., file_id: builtins.str=...) -> None: + def __init__(self, *, part_id: builtins.str=..., resource_name: builtins.str=..., resource_subtype: builtins.str=..., method_name: builtins.str=..., time_captured: google.protobuf.timestamp_pb2.Timestamp | None=..., organization_id: builtins.str=..., location_id: builtins.str=..., robot_name: builtins.str=..., robot_id: builtins.str=..., part_name: builtins.str=..., method_parameters: google.protobuf.struct_pb2.Struct | None=..., tags: collections.abc.Iterable[builtins.str] | None=..., payload: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['interval', b'interval']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['method_parameters', b'method_parameters', 'payload', b'payload', 'time_captured', b'time_captured']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['component_model', b'component_model', 'component_name', b'component_name', 'component_type', b'component_type', 'file_id', b'file_id', 'file_size_bytes', b'file_size_bytes', 'id', b'id', 'interval', b'interval', 'method', b'method', 'part_id', b'part_id', 'part_name', b'part_name', 'robot_id', b'robot_id', 'robot_name', b'robot_name', 'tags', b'tags']) -> None: + def ClearField(self, field_name: typing.Literal['location_id', b'location_id', 'method_name', b'method_name', 'method_parameters', b'method_parameters', 'organization_id', b'organization_id', 'part_id', b'part_id', 'part_name', b'part_name', 'payload', b'payload', 'resource_name', b'resource_name', 'resource_subtype', b'resource_subtype', 'robot_id', b'robot_id', 'robot_name', b'robot_name', 'tags', b'tags', 'time_captured', b'time_captured']) -> None: ... -global___BinaryMetadata = BinaryMetadata +global___ExportTabularDataResponse = ExportTabularDataResponse -class FileMetadata(google.protobuf.message.Message): +@typing.final +class GetLatestTabularDataRequest(google.protobuf.message.Message): + """GetLatestTabularDataRequest requests the most recent tabular data captured from the specified data source.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - ROBOT_NAME_FIELD_NUMBER: builtins.int - ROBOT_ID_FIELD_NUMBER: builtins.int - PART_NAME_FIELD_NUMBER: builtins.int PART_ID_FIELD_NUMBER: builtins.int - SYNC_TIME_FIELD_NUMBER: builtins.int - FILE_SIZE_BYTES_FIELD_NUMBER: builtins.int - FILE_ID_FIELD_NUMBER: builtins.int - id: builtins.str - 'Result id.' - robot_name: builtins.str - robot_id: builtins.str - part_name: builtins.str + RESOURCE_NAME_FIELD_NUMBER: builtins.int + METHOD_NAME_FIELD_NUMBER: builtins.int + RESOURCE_SUBTYPE_FIELD_NUMBER: builtins.int part_id: builtins.str + resource_name: builtins.str + method_name: builtins.str + resource_subtype: builtins.str + + def __init__(self, *, part_id: builtins.str=..., resource_name: builtins.str=..., method_name: builtins.str=..., resource_subtype: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['method_name', b'method_name', 'part_id', b'part_id', 'resource_name', b'resource_name', 'resource_subtype', b'resource_subtype']) -> None: + ... +global___GetLatestTabularDataRequest = GetLatestTabularDataRequest + +@typing.final +class GetLatestTabularDataResponse(google.protobuf.message.Message): + """GetLatestTabularDataResponse provides the data, time synced, and time captured of the most recent tabular data captured + from the requested data source, as long as it was synced within the last year. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TIME_CAPTURED_FIELD_NUMBER: builtins.int + TIME_SYNCED_FIELD_NUMBER: builtins.int + PAYLOAD_FIELD_NUMBER: builtins.int @property - def sync_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + def time_captured(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def time_synced(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def payload(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, time_captured: google.protobuf.timestamp_pb2.Timestamp | None=..., time_synced: google.protobuf.timestamp_pb2.Timestamp | None=..., payload: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['payload', b'payload', 'time_captured', b'time_captured', 'time_synced', b'time_synced']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['payload', b'payload', 'time_captured', b'time_captured', 'time_synced', b'time_synced']) -> None: + ... +global___GetLatestTabularDataResponse = GetLatestTabularDataResponse + +@typing.final +class BinaryData(google.protobuf.message.Message): + """BinaryData contains data and metadata associated with binary data.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BINARY_FIELD_NUMBER: builtins.int + METADATA_FIELD_NUMBER: builtins.int + binary: builtins.bytes + + @property + def metadata(self) -> global___BinaryMetadata: + ... + + def __init__(self, *, binary: builtins.bytes=..., metadata: global___BinaryMetadata | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['metadata', b'metadata']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['binary', b'binary', 'metadata', b'metadata']) -> None: + ... +global___BinaryData = BinaryData + +@typing.final +class BinaryDataByFilterRequest(google.protobuf.message.Message): + """BinaryDataByFilterRequest requests the data and metadata of binary (image + file) data when a filter is provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_REQUEST_FIELD_NUMBER: builtins.int + INCLUDE_BINARY_FIELD_NUMBER: builtins.int + COUNT_ONLY_FIELD_NUMBER: builtins.int + INCLUDE_INTERNAL_DATA_FIELD_NUMBER: builtins.int + include_binary: builtins.bool + count_only: builtins.bool + include_internal_data: builtins.bool + + @property + def data_request(self) -> global___DataRequest: + ... + + def __init__(self, *, data_request: global___DataRequest | None=..., include_binary: builtins.bool=..., count_only: builtins.bool=..., include_internal_data: builtins.bool=...) -> None: ... - file_size_bytes: builtins.int - file_id: builtins.str - def __init__(self, *, id: builtins.str=..., robot_name: builtins.str=..., robot_id: builtins.str=..., part_name: builtins.str=..., part_id: builtins.str=..., sync_time: google.protobuf.timestamp_pb2.Timestamp | None=..., file_size_bytes: builtins.int=..., file_id: builtins.str=...) -> None: + def HasField(self, field_name: typing.Literal['data_request', b'data_request']) -> builtins.bool: ... - def HasField(self, field_name: typing_extensions.Literal['sync_time', b'sync_time']) -> builtins.bool: + def ClearField(self, field_name: typing.Literal['count_only', b'count_only', 'data_request', b'data_request', 'include_binary', b'include_binary', 'include_internal_data', b'include_internal_data']) -> None: ... +global___BinaryDataByFilterRequest = BinaryDataByFilterRequest + +@typing.final +class BinaryDataByFilterResponse(google.protobuf.message.Message): + """BinaryDataByFilterResponse provides the data and metadata of binary (image + file) data when a filter is provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + COUNT_FIELD_NUMBER: builtins.int + LAST_FIELD_NUMBER: builtins.int + TOTAL_SIZE_BYTES_FIELD_NUMBER: builtins.int + count: builtins.int + last: builtins.str + total_size_bytes: builtins.int + + @property + def data(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BinaryData]: + ... + + def __init__(self, *, data: collections.abc.Iterable[global___BinaryData] | None=..., count: builtins.int=..., last: builtins.str=..., total_size_bytes: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['count', b'count', 'data', b'data', 'last', b'last', 'total_size_bytes', b'total_size_bytes']) -> None: + ... +global___BinaryDataByFilterResponse = BinaryDataByFilterResponse + +@typing.final +class BinaryID(google.protobuf.message.Message): + """BinaryID is the unique identifier for a file that one can request to be retrieved or modified.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FILE_ID_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + LOCATION_ID_FIELD_NUMBER: builtins.int + file_id: builtins.str + organization_id: builtins.str + location_id: builtins.str + + def __init__(self, *, file_id: builtins.str=..., organization_id: builtins.str=..., location_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['file_id', b'file_id', 'location_id', b'location_id', 'organization_id', b'organization_id']) -> None: + ... +global___BinaryID = BinaryID + +@typing.final +class BinaryDataByIDsRequest(google.protobuf.message.Message): + """BinaryDataByFilterRequest requests the data and metadata of binary (image + file) data by binary ids.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + INCLUDE_BINARY_FIELD_NUMBER: builtins.int + BINARY_IDS_FIELD_NUMBER: builtins.int + BINARY_DATA_IDS_FIELD_NUMBER: builtins.int + include_binary: builtins.bool + + @property + def binary_ids(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BinaryID]: + ... + + @property + def binary_data_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, include_binary: builtins.bool=..., binary_ids: collections.abc.Iterable[global___BinaryID] | None=..., binary_data_ids: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['binary_data_ids', b'binary_data_ids', 'binary_ids', b'binary_ids', 'include_binary', b'include_binary']) -> None: + ... +global___BinaryDataByIDsRequest = BinaryDataByIDsRequest + +@typing.final +class BinaryDataByIDsResponse(google.protobuf.message.Message): + """BinaryDataByIDsResponse provides the data and metadata of binary (image + file) data when a filter is provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + COUNT_FIELD_NUMBER: builtins.int + count: builtins.int + + @property + def data(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BinaryData]: + ... + + def __init__(self, *, data: collections.abc.Iterable[global___BinaryData] | None=..., count: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['count', b'count', 'data', b'data']) -> None: + ... +global___BinaryDataByIDsResponse = BinaryDataByIDsResponse + +@typing.final +class BoundingBox(google.protobuf.message.Message): + """BoundingBox represents a labeled bounding box on an image. + x and y values are normalized ratios between 0 and 1. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + LABEL_FIELD_NUMBER: builtins.int + X_MIN_NORMALIZED_FIELD_NUMBER: builtins.int + Y_MIN_NORMALIZED_FIELD_NUMBER: builtins.int + X_MAX_NORMALIZED_FIELD_NUMBER: builtins.int + Y_MAX_NORMALIZED_FIELD_NUMBER: builtins.int + CONFIDENCE_FIELD_NUMBER: builtins.int + id: builtins.str + label: builtins.str + x_min_normalized: builtins.float + y_min_normalized: builtins.float + x_max_normalized: builtins.float + y_max_normalized: builtins.float + confidence: builtins.float + 'confidence is an optional range from 0 - 1' + + def __init__(self, *, id: builtins.str=..., label: builtins.str=..., x_min_normalized: builtins.float=..., y_min_normalized: builtins.float=..., x_max_normalized: builtins.float=..., y_max_normalized: builtins.float=..., confidence: builtins.float | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_confidence', b'_confidence', 'confidence', b'confidence']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_confidence', b'_confidence', 'confidence', b'confidence', 'id', b'id', 'label', b'label', 'x_max_normalized', b'x_max_normalized', 'x_min_normalized', b'x_min_normalized', 'y_max_normalized', b'y_max_normalized', 'y_min_normalized', b'y_min_normalized']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_confidence', b'_confidence']) -> typing.Literal['confidence'] | None: + ... +global___BoundingBox = BoundingBox + +@typing.final +class Classification(google.protobuf.message.Message): + """Classification represents a confidence score with a label.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LABEL_FIELD_NUMBER: builtins.int + CONFIDENCE_FIELD_NUMBER: builtins.int + label: builtins.str + confidence: builtins.float + 'confidence is an optional range from 0 - 1' + + def __init__(self, *, label: builtins.str=..., confidence: builtins.float | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_confidence', b'_confidence', 'confidence', b'confidence']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_confidence', b'_confidence', 'confidence', b'confidence', 'label', b'label']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_confidence', b'_confidence']) -> typing.Literal['confidence'] | None: + ... +global___Classification = Classification + +@typing.final +class Annotations(google.protobuf.message.Message): + """Annotations are data annotations used for machine learning.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BBOXES_FIELD_NUMBER: builtins.int + CLASSIFICATIONS_FIELD_NUMBER: builtins.int + + @property + def bboxes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BoundingBox]: + ... + + @property + def classifications(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Classification]: + ... + + def __init__(self, *, bboxes: collections.abc.Iterable[global___BoundingBox] | None=..., classifications: collections.abc.Iterable[global___Classification] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['bboxes', b'bboxes', 'classifications', b'classifications']) -> None: + ... +global___Annotations = Annotations + +@typing.final +class BinaryMetadata(google.protobuf.message.Message): + """BinaryMetadata is the metadata associated with binary data.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + CAPTURE_METADATA_FIELD_NUMBER: builtins.int + TIME_REQUESTED_FIELD_NUMBER: builtins.int + TIME_RECEIVED_FIELD_NUMBER: builtins.int + FILE_NAME_FIELD_NUMBER: builtins.int + FILE_EXT_FIELD_NUMBER: builtins.int + URI_FIELD_NUMBER: builtins.int + ANNOTATIONS_FIELD_NUMBER: builtins.int + DATASET_IDS_FIELD_NUMBER: builtins.int + BINARY_DATA_ID_FIELD_NUMBER: builtins.int + id: builtins.str + file_name: builtins.str + file_ext: builtins.str + uri: builtins.str + binary_data_id: builtins.str + + @property + def capture_metadata(self) -> global___CaptureMetadata: + ... + + @property + def time_requested(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def time_received(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def annotations(self) -> global___Annotations: + ... + + @property + def dataset_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, id: builtins.str=..., capture_metadata: global___CaptureMetadata | None=..., time_requested: google.protobuf.timestamp_pb2.Timestamp | None=..., time_received: google.protobuf.timestamp_pb2.Timestamp | None=..., file_name: builtins.str=..., file_ext: builtins.str=..., uri: builtins.str=..., annotations: global___Annotations | None=..., dataset_ids: collections.abc.Iterable[builtins.str] | None=..., binary_data_id: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['annotations', b'annotations', 'capture_metadata', b'capture_metadata', 'time_received', b'time_received', 'time_requested', b'time_requested']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['annotations', b'annotations', 'binary_data_id', b'binary_data_id', 'capture_metadata', b'capture_metadata', 'dataset_ids', b'dataset_ids', 'file_ext', b'file_ext', 'file_name', b'file_name', 'id', b'id', 'time_received', b'time_received', 'time_requested', b'time_requested', 'uri', b'uri']) -> None: + ... +global___BinaryMetadata = BinaryMetadata + +@typing.final +class DeleteTabularDataRequest(google.protobuf.message.Message): + """DeleteTabularDataRequest deletes the data from the organization that is older than `delete_older_than_days` + in UTC time. For example, if delete_older_than_days=1 and the request is made at 1AM EST on March 11 + (March 11 5AM UTC), this deletes all data captured through March 10 11:59:59PM UTC. + If the request is at 10PM EST on March 11 (March 12 2AM UTC), this deletes all data captured + through March 11 11:59:59PM UTC. + If delete_older_than_days is 0, all existing data is deleted. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + DELETE_OLDER_THAN_DAYS_FIELD_NUMBER: builtins.int + organization_id: builtins.str + delete_older_than_days: builtins.int + + def __init__(self, *, organization_id: builtins.str=..., delete_older_than_days: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['delete_older_than_days', b'delete_older_than_days', 'organization_id', b'organization_id']) -> None: + ... +global___DeleteTabularDataRequest = DeleteTabularDataRequest + +@typing.final +class DeleteTabularDataResponse(google.protobuf.message.Message): + """DeleteBinaryDataResponse returns the number of tabular datapoints deleted.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DELETED_COUNT_FIELD_NUMBER: builtins.int + deleted_count: builtins.int + + def __init__(self, *, deleted_count: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['deleted_count', b'deleted_count']) -> None: + ... +global___DeleteTabularDataResponse = DeleteTabularDataResponse + +@typing.final +class DeleteBinaryDataByFilterRequest(google.protobuf.message.Message): + """DeleteBinaryDataByFilterRequest deletes the data and metadata of binary data when a filter is provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FILTER_FIELD_NUMBER: builtins.int + INCLUDE_INTERNAL_DATA_FIELD_NUMBER: builtins.int + include_internal_data: builtins.bool + + @property + def filter(self) -> global___Filter: + ... + + def __init__(self, *, filter: global___Filter | None=..., include_internal_data: builtins.bool=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['filter', b'filter']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['filter', b'filter', 'include_internal_data', b'include_internal_data']) -> None: + ... +global___DeleteBinaryDataByFilterRequest = DeleteBinaryDataByFilterRequest + +@typing.final +class DeleteBinaryDataByFilterResponse(google.protobuf.message.Message): + """DeleteBinaryDataByFilterResponse returns the number of binary files deleted when a filter is provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DELETED_COUNT_FIELD_NUMBER: builtins.int + deleted_count: builtins.int + + def __init__(self, *, deleted_count: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['deleted_count', b'deleted_count']) -> None: + ... +global___DeleteBinaryDataByFilterResponse = DeleteBinaryDataByFilterResponse + +@typing.final +class DeleteBinaryDataByIDsRequest(google.protobuf.message.Message): + """DeleteBinaryDataByIDsRequest deletes the data and metadata of binary data when binary ids are provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BINARY_IDS_FIELD_NUMBER: builtins.int + BINARY_DATA_IDS_FIELD_NUMBER: builtins.int + + @property + def binary_ids(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BinaryID]: + ... + + @property + def binary_data_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, binary_ids: collections.abc.Iterable[global___BinaryID] | None=..., binary_data_ids: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['binary_data_ids', b'binary_data_ids', 'binary_ids', b'binary_ids']) -> None: + ... +global___DeleteBinaryDataByIDsRequest = DeleteBinaryDataByIDsRequest + +@typing.final +class DeleteBinaryDataByIDsResponse(google.protobuf.message.Message): + """DeleteBinaryDataByIDsResponse returns the number of binary files deleted when binary ids are provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DELETED_COUNT_FIELD_NUMBER: builtins.int + deleted_count: builtins.int + + def __init__(self, *, deleted_count: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['deleted_count', b'deleted_count']) -> None: + ... +global___DeleteBinaryDataByIDsResponse = DeleteBinaryDataByIDsResponse + +@typing.final +class AddTagsToBinaryDataByIDsRequest(google.protobuf.message.Message): + """AddTagsToBinaryDataByIDsRequest requests adding all specified tags to each of the files when binary ids are provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BINARY_IDS_FIELD_NUMBER: builtins.int + BINARY_DATA_IDS_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + + @property + def binary_ids(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BinaryID]: + ... + + @property + def binary_data_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, binary_ids: collections.abc.Iterable[global___BinaryID] | None=..., binary_data_ids: collections.abc.Iterable[builtins.str] | None=..., tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['binary_data_ids', b'binary_data_ids', 'binary_ids', b'binary_ids', 'tags', b'tags']) -> None: + ... +global___AddTagsToBinaryDataByIDsRequest = AddTagsToBinaryDataByIDsRequest + +@typing.final +class AddTagsToBinaryDataByIDsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___AddTagsToBinaryDataByIDsResponse = AddTagsToBinaryDataByIDsResponse + +@typing.final +class AddTagsToBinaryDataByFilterRequest(google.protobuf.message.Message): + """AddTagsToBinaryDataByFilterRequest requests adding all specified tags to each of the files when a filter is provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FILTER_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + + @property + def filter(self) -> global___Filter: + ... + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, filter: global___Filter | None=..., tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['filter', b'filter']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['filter', b'filter', 'tags', b'tags']) -> None: + ... +global___AddTagsToBinaryDataByFilterRequest = AddTagsToBinaryDataByFilterRequest + +@typing.final +class AddTagsToBinaryDataByFilterResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___AddTagsToBinaryDataByFilterResponse = AddTagsToBinaryDataByFilterResponse + +@typing.final +class RemoveTagsFromBinaryDataByIDsRequest(google.protobuf.message.Message): + """RemoveTagsFromBinaryDataByIDsRequest requests removing the given tags value from each file when binary ids are provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BINARY_IDS_FIELD_NUMBER: builtins.int + BINARY_DATA_IDS_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + + @property + def binary_ids(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BinaryID]: + ... + + @property + def binary_data_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, binary_ids: collections.abc.Iterable[global___BinaryID] | None=..., binary_data_ids: collections.abc.Iterable[builtins.str] | None=..., tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['binary_data_ids', b'binary_data_ids', 'binary_ids', b'binary_ids', 'tags', b'tags']) -> None: + ... +global___RemoveTagsFromBinaryDataByIDsRequest = RemoveTagsFromBinaryDataByIDsRequest + +@typing.final +class RemoveTagsFromBinaryDataByIDsResponse(google.protobuf.message.Message): + """RemoveTagsFromBinaryDataByIDsResponse returns the number of binary files which had tags removed""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DELETED_COUNT_FIELD_NUMBER: builtins.int + deleted_count: builtins.int + + def __init__(self, *, deleted_count: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['deleted_count', b'deleted_count']) -> None: + ... +global___RemoveTagsFromBinaryDataByIDsResponse = RemoveTagsFromBinaryDataByIDsResponse + +@typing.final +class RemoveTagsFromBinaryDataByFilterRequest(google.protobuf.message.Message): + """RemoveTagsFromBinaryDataByFilterRequest requests removing the given tags value from each file when a filter is provided.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FILTER_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + + @property + def filter(self) -> global___Filter: + ... + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, filter: global___Filter | None=..., tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['filter', b'filter']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['filter', b'filter', 'tags', b'tags']) -> None: + ... +global___RemoveTagsFromBinaryDataByFilterRequest = RemoveTagsFromBinaryDataByFilterRequest + +@typing.final +class RemoveTagsFromBinaryDataByFilterResponse(google.protobuf.message.Message): + """RemoveTagsFromBinaryDataByFilterResponse returns the number of binary files which had tags removed.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DELETED_COUNT_FIELD_NUMBER: builtins.int + deleted_count: builtins.int + + def __init__(self, *, deleted_count: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['deleted_count', b'deleted_count']) -> None: + ... +global___RemoveTagsFromBinaryDataByFilterResponse = RemoveTagsFromBinaryDataByFilterResponse + +@typing.final +class TagsByFilterRequest(google.protobuf.message.Message): + """TagsByFilterRequest requests the unique tags from data based on given filter.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FILTER_FIELD_NUMBER: builtins.int + + @property + def filter(self) -> global___Filter: + ... + + def __init__(self, *, filter: global___Filter | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['filter', b'filter']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['filter', b'filter']) -> None: + ... +global___TagsByFilterRequest = TagsByFilterRequest + +@typing.final +class TagsByFilterResponse(google.protobuf.message.Message): + """TagsByFilterResponse returns the unique tags from data based on given filter.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TAGS_FIELD_NUMBER: builtins.int + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['tags', b'tags']) -> None: + ... +global___TagsByFilterResponse = TagsByFilterResponse + +@typing.final +class AddBoundingBoxToImageByIDRequest(google.protobuf.message.Message): + """AddBoundingBoxToImageByIDRequest specifies the binary ID to which a bounding box + with the associated label and position in normalized coordinates will be added. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BINARY_ID_FIELD_NUMBER: builtins.int + BINARY_DATA_ID_FIELD_NUMBER: builtins.int + LABEL_FIELD_NUMBER: builtins.int + X_MIN_NORMALIZED_FIELD_NUMBER: builtins.int + Y_MIN_NORMALIZED_FIELD_NUMBER: builtins.int + X_MAX_NORMALIZED_FIELD_NUMBER: builtins.int + Y_MAX_NORMALIZED_FIELD_NUMBER: builtins.int + binary_data_id: builtins.str + label: builtins.str + x_min_normalized: builtins.float + y_min_normalized: builtins.float + x_max_normalized: builtins.float + y_max_normalized: builtins.float + + @property + def binary_id(self) -> global___BinaryID: + ... + + def __init__(self, *, binary_id: global___BinaryID | None=..., binary_data_id: builtins.str=..., label: builtins.str=..., x_min_normalized: builtins.float=..., y_min_normalized: builtins.float=..., x_max_normalized: builtins.float=..., y_max_normalized: builtins.float=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['binary_id', b'binary_id']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['binary_data_id', b'binary_data_id', 'binary_id', b'binary_id', 'label', b'label', 'x_max_normalized', b'x_max_normalized', 'x_min_normalized', b'x_min_normalized', 'y_max_normalized', b'y_max_normalized', 'y_min_normalized', b'y_min_normalized']) -> None: + ... +global___AddBoundingBoxToImageByIDRequest = AddBoundingBoxToImageByIDRequest + +@typing.final +class AddBoundingBoxToImageByIDResponse(google.protobuf.message.Message): + """AddBoundingBoxToImageByIDResponse returns the bounding box ID of the successfully added bounding box.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BBOX_ID_FIELD_NUMBER: builtins.int + bbox_id: builtins.str + + def __init__(self, *, bbox_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['bbox_id', b'bbox_id']) -> None: + ... +global___AddBoundingBoxToImageByIDResponse = AddBoundingBoxToImageByIDResponse + +@typing.final +class RemoveBoundingBoxFromImageByIDRequest(google.protobuf.message.Message): + """RemoveBoundingBoxFromImageByIDRequest removes the bounding box with specified bounding box ID for the file represented by the binary ID.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BINARY_ID_FIELD_NUMBER: builtins.int + BINARY_DATA_ID_FIELD_NUMBER: builtins.int + BBOX_ID_FIELD_NUMBER: builtins.int + binary_data_id: builtins.str + bbox_id: builtins.str + + @property + def binary_id(self) -> global___BinaryID: + ... + + def __init__(self, *, binary_id: global___BinaryID | None=..., binary_data_id: builtins.str=..., bbox_id: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['binary_id', b'binary_id']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['bbox_id', b'bbox_id', 'binary_data_id', b'binary_data_id', 'binary_id', b'binary_id']) -> None: + ... +global___RemoveBoundingBoxFromImageByIDRequest = RemoveBoundingBoxFromImageByIDRequest + +@typing.final +class RemoveBoundingBoxFromImageByIDResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___RemoveBoundingBoxFromImageByIDResponse = RemoveBoundingBoxFromImageByIDResponse + +@typing.final +class UpdateBoundingBoxRequest(google.protobuf.message.Message): + """UpdateBoundingBoxRequest updates the bounding box with specified bounding box ID for the file represented by the binary ID.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BINARY_ID_FIELD_NUMBER: builtins.int + BINARY_DATA_ID_FIELD_NUMBER: builtins.int + BBOX_ID_FIELD_NUMBER: builtins.int + LABEL_FIELD_NUMBER: builtins.int + X_MIN_NORMALIZED_FIELD_NUMBER: builtins.int + Y_MIN_NORMALIZED_FIELD_NUMBER: builtins.int + X_MAX_NORMALIZED_FIELD_NUMBER: builtins.int + Y_MAX_NORMALIZED_FIELD_NUMBER: builtins.int + binary_data_id: builtins.str + bbox_id: builtins.str + label: builtins.str + x_min_normalized: builtins.float + y_min_normalized: builtins.float + x_max_normalized: builtins.float + y_max_normalized: builtins.float + + @property + def binary_id(self) -> global___BinaryID: + ... + + def __init__(self, *, binary_id: global___BinaryID | None=..., binary_data_id: builtins.str=..., bbox_id: builtins.str=..., label: builtins.str | None=..., x_min_normalized: builtins.float | None=..., y_min_normalized: builtins.float | None=..., x_max_normalized: builtins.float | None=..., y_max_normalized: builtins.float | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_label', b'_label', '_x_max_normalized', b'_x_max_normalized', '_x_min_normalized', b'_x_min_normalized', '_y_max_normalized', b'_y_max_normalized', '_y_min_normalized', b'_y_min_normalized', 'binary_id', b'binary_id', 'label', b'label', 'x_max_normalized', b'x_max_normalized', 'x_min_normalized', b'x_min_normalized', 'y_max_normalized', b'y_max_normalized', 'y_min_normalized', b'y_min_normalized']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_label', b'_label', '_x_max_normalized', b'_x_max_normalized', '_x_min_normalized', b'_x_min_normalized', '_y_max_normalized', b'_y_max_normalized', '_y_min_normalized', b'_y_min_normalized', 'bbox_id', b'bbox_id', 'binary_data_id', b'binary_data_id', 'binary_id', b'binary_id', 'label', b'label', 'x_max_normalized', b'x_max_normalized', 'x_min_normalized', b'x_min_normalized', 'y_max_normalized', b'y_max_normalized', 'y_min_normalized', b'y_min_normalized']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_label', b'_label']) -> typing.Literal['label'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_x_max_normalized', b'_x_max_normalized']) -> typing.Literal['x_max_normalized'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_x_min_normalized', b'_x_min_normalized']) -> typing.Literal['x_min_normalized'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_y_max_normalized', b'_y_max_normalized']) -> typing.Literal['y_max_normalized'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_y_min_normalized', b'_y_min_normalized']) -> typing.Literal['y_min_normalized'] | None: + ... +global___UpdateBoundingBoxRequest = UpdateBoundingBoxRequest + +@typing.final +class UpdateBoundingBoxResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___UpdateBoundingBoxResponse = UpdateBoundingBoxResponse + +@typing.final +class BoundingBoxLabelsByFilterRequest(google.protobuf.message.Message): + """BoundingBoxLabelsByFilterRequest requests all the labels of the bounding boxes from files from a given filter.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FILTER_FIELD_NUMBER: builtins.int + + @property + def filter(self) -> global___Filter: + ... + + def __init__(self, *, filter: global___Filter | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['filter', b'filter']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['filter', b'filter']) -> None: + ... +global___BoundingBoxLabelsByFilterRequest = BoundingBoxLabelsByFilterRequest + +@typing.final +class BoundingBoxLabelsByFilterResponse(google.protobuf.message.Message): + """BoundingBoxLabelsByFilterRequest returns all the labels of the bounding boxes from files from a given filter.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LABELS_FIELD_NUMBER: builtins.int + + @property + def labels(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, labels: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['labels', b'labels']) -> None: + ... +global___BoundingBoxLabelsByFilterResponse = BoundingBoxLabelsByFilterResponse + +@typing.final +class ConfigureDatabaseUserRequest(google.protobuf.message.Message): + """ConfigureDatabaseUserRequest accepts a Viam organization ID and a password for the database user + being configured. Viam uses gRPC over TLS, so the entire request will be encrypted while in + flight, including the password. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + PASSWORD_FIELD_NUMBER: builtins.int + organization_id: builtins.str + password: builtins.str + + def __init__(self, *, organization_id: builtins.str=..., password: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id', 'password', b'password']) -> None: + ... +global___ConfigureDatabaseUserRequest = ConfigureDatabaseUserRequest + +@typing.final +class ConfigureDatabaseUserResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___ConfigureDatabaseUserResponse = ConfigureDatabaseUserResponse + +@typing.final +class GetDatabaseConnectionRequest(google.protobuf.message.Message): + """GetDatabaseConnectionRequest requests the database connection hostname.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + + def __init__(self, *, organization_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id']) -> None: + ... +global___GetDatabaseConnectionRequest = GetDatabaseConnectionRequest + +@typing.final +class GetDatabaseConnectionResponse(google.protobuf.message.Message): + """GetDatabaseConnectionResponse returns the database connection hostname endpoint. It also returns + a URI that can be used to connect to the database instance through MongoDB clients, as well as + information on whether the Viam organization has a database user configured. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + HOSTNAME_FIELD_NUMBER: builtins.int + MONGODB_URI_FIELD_NUMBER: builtins.int + HAS_DATABASE_USER_FIELD_NUMBER: builtins.int + hostname: builtins.str + mongodb_uri: builtins.str + has_database_user: builtins.bool + + def __init__(self, *, hostname: builtins.str=..., mongodb_uri: builtins.str=..., has_database_user: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['has_database_user', b'has_database_user', 'hostname', b'hostname', 'mongodb_uri', b'mongodb_uri']) -> None: + ... +global___GetDatabaseConnectionResponse = GetDatabaseConnectionResponse + +@typing.final +class AddBinaryDataToDatasetByIDsRequest(google.protobuf.message.Message): + """AddBinaryDataToDatasetByIDsRequest adds the binary data with the given binary IDs to a dataset with dataset_id.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BINARY_IDS_FIELD_NUMBER: builtins.int + BINARY_DATA_IDS_FIELD_NUMBER: builtins.int + DATASET_ID_FIELD_NUMBER: builtins.int + dataset_id: builtins.str + + @property + def binary_ids(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BinaryID]: + ... + + @property + def binary_data_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, binary_ids: collections.abc.Iterable[global___BinaryID] | None=..., binary_data_ids: collections.abc.Iterable[builtins.str] | None=..., dataset_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['binary_data_ids', b'binary_data_ids', 'binary_ids', b'binary_ids', 'dataset_id', b'dataset_id']) -> None: + ... +global___AddBinaryDataToDatasetByIDsRequest = AddBinaryDataToDatasetByIDsRequest + +@typing.final +class AddBinaryDataToDatasetByIDsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___AddBinaryDataToDatasetByIDsResponse = AddBinaryDataToDatasetByIDsResponse + +@typing.final +class RemoveBinaryDataFromDatasetByIDsRequest(google.protobuf.message.Message): + """RemoveBinaryDataFromDatasetByIDsRequest removes the specified binary IDs from a dataset with dataset_id.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BINARY_IDS_FIELD_NUMBER: builtins.int + BINARY_DATA_IDS_FIELD_NUMBER: builtins.int + DATASET_ID_FIELD_NUMBER: builtins.int + dataset_id: builtins.str + + @property + def binary_ids(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___BinaryID]: + ... + + @property + def binary_data_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, binary_ids: collections.abc.Iterable[global___BinaryID] | None=..., binary_data_ids: collections.abc.Iterable[builtins.str] | None=..., dataset_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['binary_data_ids', b'binary_data_ids', 'binary_ids', b'binary_ids', 'dataset_id', b'dataset_id']) -> None: + ... +global___RemoveBinaryDataFromDatasetByIDsRequest = RemoveBinaryDataFromDatasetByIDsRequest + +@typing.final +class RemoveBinaryDataFromDatasetByIDsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor - def ClearField(self, field_name: typing_extensions.Literal['file_id', b'file_id', 'file_size_bytes', b'file_size_bytes', 'id', b'id', 'part_id', b'part_id', 'part_name', b'part_name', 'robot_id', b'robot_id', 'robot_name', b'robot_name', 'sync_time', b'sync_time']) -> None: + def __init__(self) -> None: ... -global___FileMetadata = FileMetadata \ No newline at end of file +global___RemoveBinaryDataFromDatasetByIDsResponse = RemoveBinaryDataFromDatasetByIDsResponse \ No newline at end of file diff --git a/src/viam/gen/app/datapipelines/__init__.py b/src/viam/gen/app/datapipelines/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/datapipelines/v1/__init__.py b/src/viam/gen/app/datapipelines/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/datapipelines/v1/data_pipelines_grpc.py b/src/viam/gen/app/datapipelines/v1/data_pipelines_grpc.py new file mode 100644 index 000000000..57b837bfd --- /dev/null +++ b/src/viam/gen/app/datapipelines/v1/data_pipelines_grpc.py @@ -0,0 +1,84 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +import google.protobuf.timestamp_pb2 +from .... import app + +class DataPipelinesServiceBase(abc.ABC): + + @abc.abstractmethod + async def GetDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.GetDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.GetDataPipelineResponse]') -> None: + pass + + @abc.abstractmethod + async def ListDataPipelines(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.ListDataPipelinesRequest, app.datapipelines.v1.data_pipelines_pb2.ListDataPipelinesResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.CreateDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.CreateDataPipelineResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.UpdateDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.UpdateDataPipelineResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.DeleteDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.DeleteDataPipelineResponse]') -> None: + pass + + @abc.abstractmethod + async def EnableDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.EnableDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.EnableDataPipelineResponse]') -> None: + pass + + @abc.abstractmethod + async def DisableDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.DisableDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.DisableDataPipelineResponse]') -> None: + pass + + @abc.abstractmethod + async def ListDataPipelineRuns(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.ListDataPipelineRunsRequest, app.datapipelines.v1.data_pipelines_pb2.ListDataPipelineRunsResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.app.datapipelines.v1.DataPipelinesService/GetDataPipeline': grpclib.const.Handler(self.GetDataPipeline, grpclib.const.Cardinality.UNARY_UNARY, app.datapipelines.v1.data_pipelines_pb2.GetDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.GetDataPipelineResponse), '/viam.app.datapipelines.v1.DataPipelinesService/ListDataPipelines': grpclib.const.Handler(self.ListDataPipelines, grpclib.const.Cardinality.UNARY_UNARY, app.datapipelines.v1.data_pipelines_pb2.ListDataPipelinesRequest, app.datapipelines.v1.data_pipelines_pb2.ListDataPipelinesResponse), '/viam.app.datapipelines.v1.DataPipelinesService/CreateDataPipeline': grpclib.const.Handler(self.CreateDataPipeline, grpclib.const.Cardinality.UNARY_UNARY, app.datapipelines.v1.data_pipelines_pb2.CreateDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.CreateDataPipelineResponse), '/viam.app.datapipelines.v1.DataPipelinesService/UpdateDataPipeline': grpclib.const.Handler(self.UpdateDataPipeline, grpclib.const.Cardinality.UNARY_UNARY, app.datapipelines.v1.data_pipelines_pb2.UpdateDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.UpdateDataPipelineResponse), '/viam.app.datapipelines.v1.DataPipelinesService/DeleteDataPipeline': grpclib.const.Handler(self.DeleteDataPipeline, grpclib.const.Cardinality.UNARY_UNARY, app.datapipelines.v1.data_pipelines_pb2.DeleteDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.DeleteDataPipelineResponse), '/viam.app.datapipelines.v1.DataPipelinesService/EnableDataPipeline': grpclib.const.Handler(self.EnableDataPipeline, grpclib.const.Cardinality.UNARY_UNARY, app.datapipelines.v1.data_pipelines_pb2.EnableDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.EnableDataPipelineResponse), '/viam.app.datapipelines.v1.DataPipelinesService/DisableDataPipeline': grpclib.const.Handler(self.DisableDataPipeline, grpclib.const.Cardinality.UNARY_UNARY, app.datapipelines.v1.data_pipelines_pb2.DisableDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.DisableDataPipelineResponse), '/viam.app.datapipelines.v1.DataPipelinesService/ListDataPipelineRuns': grpclib.const.Handler(self.ListDataPipelineRuns, grpclib.const.Cardinality.UNARY_UNARY, app.datapipelines.v1.data_pipelines_pb2.ListDataPipelineRunsRequest, app.datapipelines.v1.data_pipelines_pb2.ListDataPipelineRunsResponse)} + +class UnimplementedDataPipelinesServiceBase(DataPipelinesServiceBase): + + async def GetDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.GetDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.GetDataPipelineResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListDataPipelines(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.ListDataPipelinesRequest, app.datapipelines.v1.data_pipelines_pb2.ListDataPipelinesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.CreateDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.CreateDataPipelineResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.UpdateDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.UpdateDataPipelineResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.DeleteDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.DeleteDataPipelineResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def EnableDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.EnableDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.EnableDataPipelineResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DisableDataPipeline(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.DisableDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.DisableDataPipelineResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListDataPipelineRuns(self, stream: 'grpclib.server.Stream[app.datapipelines.v1.data_pipelines_pb2.ListDataPipelineRunsRequest, app.datapipelines.v1.data_pipelines_pb2.ListDataPipelineRunsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class DataPipelinesServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.GetDataPipeline = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.datapipelines.v1.DataPipelinesService/GetDataPipeline', app.datapipelines.v1.data_pipelines_pb2.GetDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.GetDataPipelineResponse) + self.ListDataPipelines = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.datapipelines.v1.DataPipelinesService/ListDataPipelines', app.datapipelines.v1.data_pipelines_pb2.ListDataPipelinesRequest, app.datapipelines.v1.data_pipelines_pb2.ListDataPipelinesResponse) + self.CreateDataPipeline = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.datapipelines.v1.DataPipelinesService/CreateDataPipeline', app.datapipelines.v1.data_pipelines_pb2.CreateDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.CreateDataPipelineResponse) + self.UpdateDataPipeline = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.datapipelines.v1.DataPipelinesService/UpdateDataPipeline', app.datapipelines.v1.data_pipelines_pb2.UpdateDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.UpdateDataPipelineResponse) + self.DeleteDataPipeline = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.datapipelines.v1.DataPipelinesService/DeleteDataPipeline', app.datapipelines.v1.data_pipelines_pb2.DeleteDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.DeleteDataPipelineResponse) + self.EnableDataPipeline = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.datapipelines.v1.DataPipelinesService/EnableDataPipeline', app.datapipelines.v1.data_pipelines_pb2.EnableDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.EnableDataPipelineResponse) + self.DisableDataPipeline = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.datapipelines.v1.DataPipelinesService/DisableDataPipeline', app.datapipelines.v1.data_pipelines_pb2.DisableDataPipelineRequest, app.datapipelines.v1.data_pipelines_pb2.DisableDataPipelineResponse) + self.ListDataPipelineRuns = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.datapipelines.v1.DataPipelinesService/ListDataPipelineRuns', app.datapipelines.v1.data_pipelines_pb2.ListDataPipelineRunsRequest, app.datapipelines.v1.data_pipelines_pb2.ListDataPipelineRunsResponse) \ No newline at end of file diff --git a/src/viam/gen/app/datapipelines/v1/data_pipelines_pb2.py b/src/viam/gen/app/datapipelines/v1/data_pipelines_pb2.py new file mode 100644 index 000000000..cab8bf796 --- /dev/null +++ b/src/viam/gen/app/datapipelines/v1/data_pipelines_pb2.py @@ -0,0 +1,56 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/datapipelines/v1/data_pipelines.proto') +_sym_db = _symbol_database.Default() +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)app/datapipelines/v1/data_pipelines.proto\x12\x19viam.app.datapipelines.v1\x1a\x1fgoogle/protobuf/timestamp.proto"\xa6\x02\n\x0cDataPipeline\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\'\n\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId\x12\x12\n\x04name\x18\x03 \x01(\tR\x04name\x12\x1d\n\nmql_binary\x18\x04 \x03(\x0cR\tmqlBinary\x12\x1a\n\x08schedule\x18\x05 \x01(\tR\x08schedule\x12\x18\n\x07enabled\x18\x06 \x01(\x08R\x07enabled\x129\n\ncreated_on\x18\x07 \x01(\x0b2\x1a.google.protobuf.TimestampR\tcreatedOn\x129\n\nupdated_at\x18\x08 \x01(\x0b2\x1a.google.protobuf.TimestampR\tupdatedAt"(\n\x16GetDataPipelineRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"g\n\x17GetDataPipelineResponse\x12L\n\rdata_pipeline\x18\x01 \x01(\x0b2\'.viam.app.datapipelines.v1.DataPipelineR\x0cdataPipeline"C\n\x18ListDataPipelinesRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId"k\n\x19ListDataPipelinesResponse\x12N\n\x0edata_pipelines\x18\x01 \x03(\x0b2\'.viam.app.datapipelines.v1.DataPipelineR\rdataPipelines"\x93\x01\n\x19CreateDataPipelineRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x1d\n\nmql_binary\x18\x03 \x03(\x0cR\tmqlBinary\x12\x1a\n\x08schedule\x18\x04 \x01(\tR\x08schedule",\n\x1aCreateDataPipelineResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"z\n\x19UpdateDataPipelineRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x1d\n\nmql_binary\x18\x03 \x03(\x0cR\tmqlBinary\x12\x1a\n\x08schedule\x18\x04 \x01(\tR\x08schedule"\x1c\n\x1aUpdateDataPipelineResponse"+\n\x19DeleteDataPipelineRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x1c\n\x1aDeleteDataPipelineResponse"+\n\x19EnableDataPipelineRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x1c\n\x1aEnableDataPipelineResponse",\n\x1aDisableDataPipelineRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x1d\n\x1bDisableDataPipelineResponse"i\n\x1bListDataPipelineRunsRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1b\n\tpage_size\x18\x02 \x01(\rR\x08pageSize\x12\x1d\n\npage_token\x18\x03 \x01(\tR\tpageToken"\xa7\x01\n\x1cListDataPipelineRunsResponse\x12\x1f\n\x0bpipeline_id\x18\x01 \x01(\tR\npipelineId\x12>\n\x04runs\x18\x02 \x03(\x0b2*.viam.app.datapipelines.v1.DataPipelineRunR\x04runs\x12&\n\x0fnext_page_token\x18\x03 \x01(\tR\rnextPageToken"\xe1\x02\n\x0fDataPipelineRun\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x129\n\nstart_time\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\tstartTime\x125\n\x08end_time\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\x07endTime\x12B\n\x0fdata_start_time\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampR\rdataStartTime\x12>\n\rdata_end_time\x18\x05 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0bdataEndTime\x12H\n\x06status\x18\x06 \x01(\x0e20.viam.app.datapipelines.v1.DataPipelineRunStatusR\x06status*\xdc\x01\n\x15DataPipelineRunStatus\x12(\n$DATA_PIPELINE_RUN_STATUS_UNSPECIFIED\x10\x00\x12&\n"DATA_PIPELINE_RUN_STATUS_SCHEDULED\x10\x01\x12$\n DATA_PIPELINE_RUN_STATUS_STARTED\x10\x02\x12&\n"DATA_PIPELINE_RUN_STATUS_COMPLETED\x10\x03\x12#\n\x1fDATA_PIPELINE_RUN_STATUS_FAILED\x10\x042\xb1\x08\n\x14DataPipelinesService\x12x\n\x0fGetDataPipeline\x121.viam.app.datapipelines.v1.GetDataPipelineRequest\x1a2.viam.app.datapipelines.v1.GetDataPipelineResponse\x12~\n\x11ListDataPipelines\x123.viam.app.datapipelines.v1.ListDataPipelinesRequest\x1a4.viam.app.datapipelines.v1.ListDataPipelinesResponse\x12\x81\x01\n\x12CreateDataPipeline\x124.viam.app.datapipelines.v1.CreateDataPipelineRequest\x1a5.viam.app.datapipelines.v1.CreateDataPipelineResponse\x12\x81\x01\n\x12UpdateDataPipeline\x124.viam.app.datapipelines.v1.UpdateDataPipelineRequest\x1a5.viam.app.datapipelines.v1.UpdateDataPipelineResponse\x12\x81\x01\n\x12DeleteDataPipeline\x124.viam.app.datapipelines.v1.DeleteDataPipelineRequest\x1a5.viam.app.datapipelines.v1.DeleteDataPipelineResponse\x12\x81\x01\n\x12EnableDataPipeline\x124.viam.app.datapipelines.v1.EnableDataPipelineRequest\x1a5.viam.app.datapipelines.v1.EnableDataPipelineResponse\x12\x84\x01\n\x13DisableDataPipeline\x125.viam.app.datapipelines.v1.DisableDataPipelineRequest\x1a6.viam.app.datapipelines.v1.DisableDataPipelineResponse\x12\x87\x01\n\x14ListDataPipelineRuns\x126.viam.app.datapipelines.v1.ListDataPipelineRunsRequest\x1a7.viam.app.datapipelines.v1.ListDataPipelineRunsResponseB&Z$go.viam.com/api/app/datapipelines/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.datapipelines.v1.data_pipelines_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z$go.viam.com/api/app/datapipelines/v1' + _globals['_DATAPIPELINERUNSTATUS']._serialized_start = 1938 + _globals['_DATAPIPELINERUNSTATUS']._serialized_end = 2158 + _globals['_DATAPIPELINE']._serialized_start = 106 + _globals['_DATAPIPELINE']._serialized_end = 400 + _globals['_GETDATAPIPELINEREQUEST']._serialized_start = 402 + _globals['_GETDATAPIPELINEREQUEST']._serialized_end = 442 + _globals['_GETDATAPIPELINERESPONSE']._serialized_start = 444 + _globals['_GETDATAPIPELINERESPONSE']._serialized_end = 547 + _globals['_LISTDATAPIPELINESREQUEST']._serialized_start = 549 + _globals['_LISTDATAPIPELINESREQUEST']._serialized_end = 616 + _globals['_LISTDATAPIPELINESRESPONSE']._serialized_start = 618 + _globals['_LISTDATAPIPELINESRESPONSE']._serialized_end = 725 + _globals['_CREATEDATAPIPELINEREQUEST']._serialized_start = 728 + _globals['_CREATEDATAPIPELINEREQUEST']._serialized_end = 875 + _globals['_CREATEDATAPIPELINERESPONSE']._serialized_start = 877 + _globals['_CREATEDATAPIPELINERESPONSE']._serialized_end = 921 + _globals['_UPDATEDATAPIPELINEREQUEST']._serialized_start = 923 + _globals['_UPDATEDATAPIPELINEREQUEST']._serialized_end = 1045 + _globals['_UPDATEDATAPIPELINERESPONSE']._serialized_start = 1047 + _globals['_UPDATEDATAPIPELINERESPONSE']._serialized_end = 1075 + _globals['_DELETEDATAPIPELINEREQUEST']._serialized_start = 1077 + _globals['_DELETEDATAPIPELINEREQUEST']._serialized_end = 1120 + _globals['_DELETEDATAPIPELINERESPONSE']._serialized_start = 1122 + _globals['_DELETEDATAPIPELINERESPONSE']._serialized_end = 1150 + _globals['_ENABLEDATAPIPELINEREQUEST']._serialized_start = 1152 + _globals['_ENABLEDATAPIPELINEREQUEST']._serialized_end = 1195 + _globals['_ENABLEDATAPIPELINERESPONSE']._serialized_start = 1197 + _globals['_ENABLEDATAPIPELINERESPONSE']._serialized_end = 1225 + _globals['_DISABLEDATAPIPELINEREQUEST']._serialized_start = 1227 + _globals['_DISABLEDATAPIPELINEREQUEST']._serialized_end = 1271 + _globals['_DISABLEDATAPIPELINERESPONSE']._serialized_start = 1273 + _globals['_DISABLEDATAPIPELINERESPONSE']._serialized_end = 1302 + _globals['_LISTDATAPIPELINERUNSREQUEST']._serialized_start = 1304 + _globals['_LISTDATAPIPELINERUNSREQUEST']._serialized_end = 1409 + _globals['_LISTDATAPIPELINERUNSRESPONSE']._serialized_start = 1412 + _globals['_LISTDATAPIPELINERUNSRESPONSE']._serialized_end = 1579 + _globals['_DATAPIPELINERUN']._serialized_start = 1582 + _globals['_DATAPIPELINERUN']._serialized_end = 1935 + _globals['_DATAPIPELINESSERVICE']._serialized_start = 2161 + _globals['_DATAPIPELINESSERVICE']._serialized_end = 3234 \ No newline at end of file diff --git a/src/viam/gen/app/datapipelines/v1/data_pipelines_pb2.pyi b/src/viam/gen/app/datapipelines/v1/data_pipelines_pb2.pyi new file mode 100644 index 000000000..eb7ef3bae --- /dev/null +++ b/src/viam/gen/app/datapipelines/v1/data_pipelines_pb2.pyi @@ -0,0 +1,370 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import google.protobuf.timestamp_pb2 +import sys +import typing +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _DataPipelineRunStatus: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _DataPipelineRunStatusEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DataPipelineRunStatus.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DATA_PIPELINE_RUN_STATUS_UNSPECIFIED: _DataPipelineRunStatus.ValueType + DATA_PIPELINE_RUN_STATUS_SCHEDULED: _DataPipelineRunStatus.ValueType + DATA_PIPELINE_RUN_STATUS_STARTED: _DataPipelineRunStatus.ValueType + DATA_PIPELINE_RUN_STATUS_COMPLETED: _DataPipelineRunStatus.ValueType + DATA_PIPELINE_RUN_STATUS_FAILED: _DataPipelineRunStatus.ValueType + +class DataPipelineRunStatus(_DataPipelineRunStatus, metaclass=_DataPipelineRunStatusEnumTypeWrapper): + ... +DATA_PIPELINE_RUN_STATUS_UNSPECIFIED: DataPipelineRunStatus.ValueType +DATA_PIPELINE_RUN_STATUS_SCHEDULED: DataPipelineRunStatus.ValueType +DATA_PIPELINE_RUN_STATUS_STARTED: DataPipelineRunStatus.ValueType +DATA_PIPELINE_RUN_STATUS_COMPLETED: DataPipelineRunStatus.ValueType +DATA_PIPELINE_RUN_STATUS_FAILED: DataPipelineRunStatus.ValueType +global___DataPipelineRunStatus = DataPipelineRunStatus + +@typing.final +class DataPipeline(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + MQL_BINARY_FIELD_NUMBER: builtins.int + SCHEDULE_FIELD_NUMBER: builtins.int + ENABLED_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + UPDATED_AT_FIELD_NUMBER: builtins.int + id: builtins.str + organization_id: builtins.str + 'The associated Viam organization ID.' + name: builtins.str + 'A unique identifier at the org level.' + schedule: builtins.str + 'A cron expression representing the expected execution schedule in UTC (note this also\n defines the input time window; an hourly schedule would process 1 hour of data at a time).\n ' + enabled: builtins.bool + 'Whether or not the pipeline is enabled.' + + @property + def mql_binary(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]: + """A MongoDB aggregation pipeline as a list of BSON documents, where + each document is one stage in the pipeline. + """ + + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The time the pipeline was created.""" + + @property + def updated_at(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The time the pipeline was last updated.""" + + def __init__(self, *, id: builtins.str=..., organization_id: builtins.str=..., name: builtins.str=..., mql_binary: collections.abc.Iterable[builtins.bytes] | None=..., schedule: builtins.str=..., enabled: builtins.bool=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=..., updated_at: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['created_on', b'created_on', 'updated_at', b'updated_at']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['created_on', b'created_on', 'enabled', b'enabled', 'id', b'id', 'mql_binary', b'mql_binary', 'name', b'name', 'organization_id', b'organization_id', 'schedule', b'schedule', 'updated_at', b'updated_at']) -> None: + ... +global___DataPipeline = DataPipeline + +@typing.final +class GetDataPipelineRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + 'The ID of the data pipeline to retrieve.' + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___GetDataPipelineRequest = GetDataPipelineRequest + +@typing.final +class GetDataPipelineResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_PIPELINE_FIELD_NUMBER: builtins.int + + @property + def data_pipeline(self) -> global___DataPipeline: + ... + + def __init__(self, *, data_pipeline: global___DataPipeline | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['data_pipeline', b'data_pipeline']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['data_pipeline', b'data_pipeline']) -> None: + ... +global___GetDataPipelineResponse = GetDataPipelineResponse + +@typing.final +class ListDataPipelinesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + 'The associated Viam organization ID.' + + def __init__(self, *, organization_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id']) -> None: + ... +global___ListDataPipelinesRequest = ListDataPipelinesRequest + +@typing.final +class ListDataPipelinesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_PIPELINES_FIELD_NUMBER: builtins.int + + @property + def data_pipelines(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___DataPipeline]: + ... + + def __init__(self, *, data_pipelines: collections.abc.Iterable[global___DataPipeline] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data_pipelines', b'data_pipelines']) -> None: + ... +global___ListDataPipelinesResponse = ListDataPipelinesResponse + +@typing.final +class CreateDataPipelineRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + MQL_BINARY_FIELD_NUMBER: builtins.int + SCHEDULE_FIELD_NUMBER: builtins.int + organization_id: builtins.str + 'The associated Viam organization ID.' + name: builtins.str + 'A unique identifier at the org level.' + schedule: builtins.str + 'A cron expression representing the expected execution schedule in UTC (note this also\n defines the input time window; an hourly schedule would process 1 hour of data at a time).\n ' + + @property + def mql_binary(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]: + """A MongoDB aggregation pipeline as a list of BSON documents, where + each document is one stage in the pipeline. + """ + + def __init__(self, *, organization_id: builtins.str=..., name: builtins.str=..., mql_binary: collections.abc.Iterable[builtins.bytes] | None=..., schedule: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['mql_binary', b'mql_binary', 'name', b'name', 'organization_id', b'organization_id', 'schedule', b'schedule']) -> None: + ... +global___CreateDataPipelineRequest = CreateDataPipelineRequest + +@typing.final +class CreateDataPipelineResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + 'The ID of the newly created data pipeline.' + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___CreateDataPipelineResponse = CreateDataPipelineResponse + +@typing.final +class UpdateDataPipelineRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + MQL_BINARY_FIELD_NUMBER: builtins.int + SCHEDULE_FIELD_NUMBER: builtins.int + id: builtins.str + 'The ID of the data pipeline to update.' + name: builtins.str + 'A unique identifier at the org level.' + schedule: builtins.str + 'A cron expression representing the expected execution schedule in UTC (note this also\n defines the input time window; an hourly schedule would process 1 hour of data at a time).\n ' + + @property + def mql_binary(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.bytes]: + """A MongoDB aggregation pipeline as a list of BSON documents, where + each document is one stage in the pipeline. + """ + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., mql_binary: collections.abc.Iterable[builtins.bytes] | None=..., schedule: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'mql_binary', b'mql_binary', 'name', b'name', 'schedule', b'schedule']) -> None: + ... +global___UpdateDataPipelineRequest = UpdateDataPipelineRequest + +@typing.final +class UpdateDataPipelineResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___UpdateDataPipelineResponse = UpdateDataPipelineResponse + +@typing.final +class DeleteDataPipelineRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + 'The ID of the data pipeline to delete.' + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___DeleteDataPipelineRequest = DeleteDataPipelineRequest + +@typing.final +class DeleteDataPipelineResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteDataPipelineResponse = DeleteDataPipelineResponse + +@typing.final +class EnableDataPipelineRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + 'The ID of the data pipeline to enable.' + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___EnableDataPipelineRequest = EnableDataPipelineRequest + +@typing.final +class EnableDataPipelineResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___EnableDataPipelineResponse = EnableDataPipelineResponse + +@typing.final +class DisableDataPipelineRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + 'The ID of the data pipeline to disable.' + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___DisableDataPipelineRequest = DisableDataPipelineRequest + +@typing.final +class DisableDataPipelineResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DisableDataPipelineResponse = DisableDataPipelineResponse + +@typing.final +class ListDataPipelineRunsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + PAGE_SIZE_FIELD_NUMBER: builtins.int + PAGE_TOKEN_FIELD_NUMBER: builtins.int + id: builtins.str + 'The ID of the data pipeline to list runs for.' + page_size: builtins.int + 'pagination fields' + page_token: builtins.str + + def __init__(self, *, id: builtins.str=..., page_size: builtins.int=..., page_token: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'page_size', b'page_size', 'page_token', b'page_token']) -> None: + ... +global___ListDataPipelineRunsRequest = ListDataPipelineRunsRequest + +@typing.final +class ListDataPipelineRunsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PIPELINE_ID_FIELD_NUMBER: builtins.int + RUNS_FIELD_NUMBER: builtins.int + NEXT_PAGE_TOKEN_FIELD_NUMBER: builtins.int + pipeline_id: builtins.str + 'The ID of the data pipeline the runs are for.' + next_page_token: builtins.str + 'A token to retrieve the next page of results.' + + @property + def runs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___DataPipelineRun]: + """The runs that were run.""" + + def __init__(self, *, pipeline_id: builtins.str=..., runs: collections.abc.Iterable[global___DataPipelineRun] | None=..., next_page_token: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['next_page_token', b'next_page_token', 'pipeline_id', b'pipeline_id', 'runs', b'runs']) -> None: + ... +global___ListDataPipelineRunsResponse = ListDataPipelineRunsResponse + +@typing.final +class DataPipelineRun(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + START_TIME_FIELD_NUMBER: builtins.int + END_TIME_FIELD_NUMBER: builtins.int + DATA_START_TIME_FIELD_NUMBER: builtins.int + DATA_END_TIME_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + id: builtins.str + 'The ID of the run.' + status: global___DataPipelineRunStatus.ValueType + 'The status of the run.' + + @property + def start_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The time the run started.""" + + @property + def end_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The time the run ended.""" + + @property + def data_start_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The start time of the data that was processed in the run.""" + + @property + def data_end_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The end time of the data that was processed in the run.""" + + def __init__(self, *, id: builtins.str=..., start_time: google.protobuf.timestamp_pb2.Timestamp | None=..., end_time: google.protobuf.timestamp_pb2.Timestamp | None=..., data_start_time: google.protobuf.timestamp_pb2.Timestamp | None=..., data_end_time: google.protobuf.timestamp_pb2.Timestamp | None=..., status: global___DataPipelineRunStatus.ValueType=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['data_end_time', b'data_end_time', 'data_start_time', b'data_start_time', 'end_time', b'end_time', 'start_time', b'start_time']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['data_end_time', b'data_end_time', 'data_start_time', b'data_start_time', 'end_time', b'end_time', 'id', b'id', 'start_time', b'start_time', 'status', b'status']) -> None: + ... +global___DataPipelineRun = DataPipelineRun \ No newline at end of file diff --git a/src/viam/gen/app/dataset/__init__.py b/src/viam/gen/app/dataset/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/dataset/v1/__init__.py b/src/viam/gen/app/dataset/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/dataset/v1/dataset_grpc.py b/src/viam/gen/app/dataset/v1/dataset_grpc.py new file mode 100644 index 000000000..400ec1511 --- /dev/null +++ b/src/viam/gen/app/dataset/v1/dataset_grpc.py @@ -0,0 +1,60 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +import google.protobuf.timestamp_pb2 +from .... import app + +class DatasetServiceBase(abc.ABC): + + @abc.abstractmethod + async def CreateDataset(self, stream: 'grpclib.server.Stream[app.dataset.v1.dataset_pb2.CreateDatasetRequest, app.dataset.v1.dataset_pb2.CreateDatasetResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteDataset(self, stream: 'grpclib.server.Stream[app.dataset.v1.dataset_pb2.DeleteDatasetRequest, app.dataset.v1.dataset_pb2.DeleteDatasetResponse]') -> None: + pass + + @abc.abstractmethod + async def RenameDataset(self, stream: 'grpclib.server.Stream[app.dataset.v1.dataset_pb2.RenameDatasetRequest, app.dataset.v1.dataset_pb2.RenameDatasetResponse]') -> None: + pass + + @abc.abstractmethod + async def ListDatasetsByOrganizationID(self, stream: 'grpclib.server.Stream[app.dataset.v1.dataset_pb2.ListDatasetsByOrganizationIDRequest, app.dataset.v1.dataset_pb2.ListDatasetsByOrganizationIDResponse]') -> None: + pass + + @abc.abstractmethod + async def ListDatasetsByIDs(self, stream: 'grpclib.server.Stream[app.dataset.v1.dataset_pb2.ListDatasetsByIDsRequest, app.dataset.v1.dataset_pb2.ListDatasetsByIDsResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.app.dataset.v1.DatasetService/CreateDataset': grpclib.const.Handler(self.CreateDataset, grpclib.const.Cardinality.UNARY_UNARY, app.dataset.v1.dataset_pb2.CreateDatasetRequest, app.dataset.v1.dataset_pb2.CreateDatasetResponse), '/viam.app.dataset.v1.DatasetService/DeleteDataset': grpclib.const.Handler(self.DeleteDataset, grpclib.const.Cardinality.UNARY_UNARY, app.dataset.v1.dataset_pb2.DeleteDatasetRequest, app.dataset.v1.dataset_pb2.DeleteDatasetResponse), '/viam.app.dataset.v1.DatasetService/RenameDataset': grpclib.const.Handler(self.RenameDataset, grpclib.const.Cardinality.UNARY_UNARY, app.dataset.v1.dataset_pb2.RenameDatasetRequest, app.dataset.v1.dataset_pb2.RenameDatasetResponse), '/viam.app.dataset.v1.DatasetService/ListDatasetsByOrganizationID': grpclib.const.Handler(self.ListDatasetsByOrganizationID, grpclib.const.Cardinality.UNARY_UNARY, app.dataset.v1.dataset_pb2.ListDatasetsByOrganizationIDRequest, app.dataset.v1.dataset_pb2.ListDatasetsByOrganizationIDResponse), '/viam.app.dataset.v1.DatasetService/ListDatasetsByIDs': grpclib.const.Handler(self.ListDatasetsByIDs, grpclib.const.Cardinality.UNARY_UNARY, app.dataset.v1.dataset_pb2.ListDatasetsByIDsRequest, app.dataset.v1.dataset_pb2.ListDatasetsByIDsResponse)} + +class UnimplementedDatasetServiceBase(DatasetServiceBase): + + async def CreateDataset(self, stream: 'grpclib.server.Stream[app.dataset.v1.dataset_pb2.CreateDatasetRequest, app.dataset.v1.dataset_pb2.CreateDatasetResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteDataset(self, stream: 'grpclib.server.Stream[app.dataset.v1.dataset_pb2.DeleteDatasetRequest, app.dataset.v1.dataset_pb2.DeleteDatasetResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RenameDataset(self, stream: 'grpclib.server.Stream[app.dataset.v1.dataset_pb2.RenameDatasetRequest, app.dataset.v1.dataset_pb2.RenameDatasetResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListDatasetsByOrganizationID(self, stream: 'grpclib.server.Stream[app.dataset.v1.dataset_pb2.ListDatasetsByOrganizationIDRequest, app.dataset.v1.dataset_pb2.ListDatasetsByOrganizationIDResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListDatasetsByIDs(self, stream: 'grpclib.server.Stream[app.dataset.v1.dataset_pb2.ListDatasetsByIDsRequest, app.dataset.v1.dataset_pb2.ListDatasetsByIDsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class DatasetServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.CreateDataset = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.dataset.v1.DatasetService/CreateDataset', app.dataset.v1.dataset_pb2.CreateDatasetRequest, app.dataset.v1.dataset_pb2.CreateDatasetResponse) + self.DeleteDataset = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.dataset.v1.DatasetService/DeleteDataset', app.dataset.v1.dataset_pb2.DeleteDatasetRequest, app.dataset.v1.dataset_pb2.DeleteDatasetResponse) + self.RenameDataset = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.dataset.v1.DatasetService/RenameDataset', app.dataset.v1.dataset_pb2.RenameDatasetRequest, app.dataset.v1.dataset_pb2.RenameDatasetResponse) + self.ListDatasetsByOrganizationID = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.dataset.v1.DatasetService/ListDatasetsByOrganizationID', app.dataset.v1.dataset_pb2.ListDatasetsByOrganizationIDRequest, app.dataset.v1.dataset_pb2.ListDatasetsByOrganizationIDResponse) + self.ListDatasetsByIDs = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.dataset.v1.DatasetService/ListDatasetsByIDs', app.dataset.v1.dataset_pb2.ListDatasetsByIDsRequest, app.dataset.v1.dataset_pb2.ListDatasetsByIDsResponse) \ No newline at end of file diff --git a/src/viam/gen/app/dataset/v1/dataset_pb2.py b/src/viam/gen/app/dataset/v1/dataset_pb2.py new file mode 100644 index 000000000..57ddf727e --- /dev/null +++ b/src/viam/gen/app/dataset/v1/dataset_pb2.py @@ -0,0 +1,40 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/dataset/v1/dataset.proto') +_sym_db = _symbol_database.Default() +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1capp/dataset/v1/dataset.proto\x12\x13viam.app.dataset.v1\x1a\x1fgoogle/protobuf/timestamp.proto"\x95\x01\n\x07Dataset\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\'\n\x0forganization_id\x18\x03 \x01(\tR\x0eorganizationId\x12=\n\x0ctime_created\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0btimeCreated"S\n\x14CreateDatasetRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\'\n\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId"\'\n\x15CreateDatasetResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"&\n\x14DeleteDatasetRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x17\n\x15DeleteDatasetResponse":\n\x14RenameDatasetRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name"\x17\n\x15RenameDatasetResponse"N\n#ListDatasetsByOrganizationIDRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId"`\n$ListDatasetsByOrganizationIDResponse\x128\n\x08datasets\x18\x01 \x03(\x0b2\x1c.viam.app.dataset.v1.DatasetR\x08datasets",\n\x18ListDatasetsByIDsRequest\x12\x10\n\x03ids\x18\x01 \x03(\tR\x03ids"U\n\x19ListDatasetsByIDsResponse\x128\n\x08datasets\x18\x01 \x03(\x0b2\x1c.viam.app.dataset.v1.DatasetR\x08datasets2\xd2\x04\n\x0eDatasetService\x12f\n\rCreateDataset\x12).viam.app.dataset.v1.CreateDatasetRequest\x1a*.viam.app.dataset.v1.CreateDatasetResponse\x12f\n\rDeleteDataset\x12).viam.app.dataset.v1.DeleteDatasetRequest\x1a*.viam.app.dataset.v1.DeleteDatasetResponse\x12f\n\rRenameDataset\x12).viam.app.dataset.v1.RenameDatasetRequest\x1a*.viam.app.dataset.v1.RenameDatasetResponse\x12\x93\x01\n\x1cListDatasetsByOrganizationID\x128.viam.app.dataset.v1.ListDatasetsByOrganizationIDRequest\x1a9.viam.app.dataset.v1.ListDatasetsByOrganizationIDResponse\x12r\n\x11ListDatasetsByIDs\x12-.viam.app.dataset.v1.ListDatasetsByIDsRequest\x1a..viam.app.dataset.v1.ListDatasetsByIDsResponseB Z\x1ego.viam.com/api/app/dataset/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.dataset.v1.dataset_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x1ego.viam.com/api/app/dataset/v1' + _globals['_DATASET']._serialized_start = 87 + _globals['_DATASET']._serialized_end = 236 + _globals['_CREATEDATASETREQUEST']._serialized_start = 238 + _globals['_CREATEDATASETREQUEST']._serialized_end = 321 + _globals['_CREATEDATASETRESPONSE']._serialized_start = 323 + _globals['_CREATEDATASETRESPONSE']._serialized_end = 362 + _globals['_DELETEDATASETREQUEST']._serialized_start = 364 + _globals['_DELETEDATASETREQUEST']._serialized_end = 402 + _globals['_DELETEDATASETRESPONSE']._serialized_start = 404 + _globals['_DELETEDATASETRESPONSE']._serialized_end = 427 + _globals['_RENAMEDATASETREQUEST']._serialized_start = 429 + _globals['_RENAMEDATASETREQUEST']._serialized_end = 487 + _globals['_RENAMEDATASETRESPONSE']._serialized_start = 489 + _globals['_RENAMEDATASETRESPONSE']._serialized_end = 512 + _globals['_LISTDATASETSBYORGANIZATIONIDREQUEST']._serialized_start = 514 + _globals['_LISTDATASETSBYORGANIZATIONIDREQUEST']._serialized_end = 592 + _globals['_LISTDATASETSBYORGANIZATIONIDRESPONSE']._serialized_start = 594 + _globals['_LISTDATASETSBYORGANIZATIONIDRESPONSE']._serialized_end = 690 + _globals['_LISTDATASETSBYIDSREQUEST']._serialized_start = 692 + _globals['_LISTDATASETSBYIDSREQUEST']._serialized_end = 736 + _globals['_LISTDATASETSBYIDSRESPONSE']._serialized_start = 738 + _globals['_LISTDATASETSBYIDSRESPONSE']._serialized_end = 823 + _globals['_DATASETSERVICE']._serialized_start = 826 + _globals['_DATASETSERVICE']._serialized_end = 1420 \ No newline at end of file diff --git a/src/viam/gen/app/dataset/v1/dataset_pb2.pyi b/src/viam/gen/app/dataset/v1/dataset_pb2.pyi new file mode 100644 index 000000000..ed325a639 --- /dev/null +++ b/src/viam/gen/app/dataset/v1/dataset_pb2.pyi @@ -0,0 +1,179 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import google.protobuf.timestamp_pb2 +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class Dataset(google.protobuf.message.Message): + """Dataset stores the metadata of a dataset.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + TIME_CREATED_FIELD_NUMBER: builtins.int + id: builtins.str + name: builtins.str + organization_id: builtins.str + + @property + def time_created(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., organization_id: builtins.str=..., time_created: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['time_created', b'time_created']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'name', b'name', 'organization_id', b'organization_id', 'time_created', b'time_created']) -> None: + ... +global___Dataset = Dataset + +@typing.final +class CreateDatasetRequest(google.protobuf.message.Message): + """CreateDatasetRequest defines the name and organization ID of a dataset.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + name: builtins.str + organization_id: builtins.str + + def __init__(self, *, name: builtins.str=..., organization_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name', 'organization_id', b'organization_id']) -> None: + ... +global___CreateDatasetRequest = CreateDatasetRequest + +@typing.final +class CreateDatasetResponse(google.protobuf.message.Message): + """CreateDatasetResponse returns the dataset ID of the created dataset.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___CreateDatasetResponse = CreateDatasetResponse + +@typing.final +class DeleteDatasetRequest(google.protobuf.message.Message): + """DeleteDatasetRequest deletes the dataset specified by the dataset ID.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___DeleteDatasetRequest = DeleteDatasetRequest + +@typing.final +class DeleteDatasetResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteDatasetResponse = DeleteDatasetResponse + +@typing.final +class RenameDatasetRequest(google.protobuf.message.Message): + """RenameDatasetRequest applies the new name to the dataset specified by the dataset ID.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + id: builtins.str + name: builtins.str + + def __init__(self, *, id: builtins.str=..., name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'name', b'name']) -> None: + ... +global___RenameDatasetRequest = RenameDatasetRequest + +@typing.final +class RenameDatasetResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___RenameDatasetResponse = RenameDatasetResponse + +@typing.final +class ListDatasetsByOrganizationIDRequest(google.protobuf.message.Message): + """ListDatasetsByOrganizationIDRequest requests all of the datasets for an organization.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + + def __init__(self, *, organization_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id']) -> None: + ... +global___ListDatasetsByOrganizationIDRequest = ListDatasetsByOrganizationIDRequest + +@typing.final +class ListDatasetsByOrganizationIDResponse(google.protobuf.message.Message): + """ListDatasetsByOrganizationIDResponse returns all the dataset metadata for the organization.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATASETS_FIELD_NUMBER: builtins.int + + @property + def datasets(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Dataset]: + ... + + def __init__(self, *, datasets: collections.abc.Iterable[global___Dataset] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['datasets', b'datasets']) -> None: + ... +global___ListDatasetsByOrganizationIDResponse = ListDatasetsByOrganizationIDResponse + +@typing.final +class ListDatasetsByIDsRequest(google.protobuf.message.Message): + """ListDatasetsByIDsRequest requests all of the datasets by their dataset IDs.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + IDS_FIELD_NUMBER: builtins.int + + @property + def ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, ids: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['ids', b'ids']) -> None: + ... +global___ListDatasetsByIDsRequest = ListDatasetsByIDsRequest + +@typing.final +class ListDatasetsByIDsResponse(google.protobuf.message.Message): + """ListDatasetsByIDsResponse returns all the dataset metadata for the associated dataset IDs.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATASETS_FIELD_NUMBER: builtins.int + + @property + def datasets(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Dataset]: + ... + + def __init__(self, *, datasets: collections.abc.Iterable[global___Dataset] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['datasets', b'datasets']) -> None: + ... +global___ListDatasetsByIDsResponse = ListDatasetsByIDsResponse \ No newline at end of file diff --git a/src/viam/gen/app/datasync/v1/data_sync_grpc.py b/src/viam/gen/app/datasync/v1/data_sync_grpc.py index 814c9dea4..2fbf2ed00 100644 --- a/src/viam/gen/app/datasync/v1/data_sync_grpc.py +++ b/src/viam/gen/app/datasync/v1/data_sync_grpc.py @@ -2,24 +2,46 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import app +import google.api.annotations_pb2 import google.protobuf.any_pb2 import google.protobuf.struct_pb2 import google.protobuf.timestamp_pb2 -from .... import tagger -from .... import app class DataSyncServiceBase(abc.ABC): @abc.abstractmethod - async def Upload(self, stream: 'grpclib.server.Stream[app.datasync.v1.data_sync_pb2.UploadRequest, app.datasync.v1.data_sync_pb2.UploadResponse]') -> None: + async def DataCaptureUpload(self, stream: 'grpclib.server.Stream[app.datasync.v1.data_sync_pb2.DataCaptureUploadRequest, app.datasync.v1.data_sync_pb2.DataCaptureUploadResponse]') -> None: + pass + + @abc.abstractmethod + async def FileUpload(self, stream: 'grpclib.server.Stream[app.datasync.v1.data_sync_pb2.FileUploadRequest, app.datasync.v1.data_sync_pb2.FileUploadResponse]') -> None: + pass + + @abc.abstractmethod + async def StreamingDataCaptureUpload(self, stream: 'grpclib.server.Stream[app.datasync.v1.data_sync_pb2.StreamingDataCaptureUploadRequest, app.datasync.v1.data_sync_pb2.StreamingDataCaptureUploadResponse]') -> None: pass def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.app.datasync.v1.DataSyncService/Upload': grpclib.const.Handler(self.Upload, grpclib.const.Cardinality.STREAM_STREAM, app.datasync.v1.data_sync_pb2.UploadRequest, app.datasync.v1.data_sync_pb2.UploadResponse)} + return {'/viam.app.datasync.v1.DataSyncService/DataCaptureUpload': grpclib.const.Handler(self.DataCaptureUpload, grpclib.const.Cardinality.UNARY_UNARY, app.datasync.v1.data_sync_pb2.DataCaptureUploadRequest, app.datasync.v1.data_sync_pb2.DataCaptureUploadResponse), '/viam.app.datasync.v1.DataSyncService/FileUpload': grpclib.const.Handler(self.FileUpload, grpclib.const.Cardinality.STREAM_UNARY, app.datasync.v1.data_sync_pb2.FileUploadRequest, app.datasync.v1.data_sync_pb2.FileUploadResponse), '/viam.app.datasync.v1.DataSyncService/StreamingDataCaptureUpload': grpclib.const.Handler(self.StreamingDataCaptureUpload, grpclib.const.Cardinality.STREAM_UNARY, app.datasync.v1.data_sync_pb2.StreamingDataCaptureUploadRequest, app.datasync.v1.data_sync_pb2.StreamingDataCaptureUploadResponse)} + +class UnimplementedDataSyncServiceBase(DataSyncServiceBase): + + async def DataCaptureUpload(self, stream: 'grpclib.server.Stream[app.datasync.v1.data_sync_pb2.DataCaptureUploadRequest, app.datasync.v1.data_sync_pb2.DataCaptureUploadResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def FileUpload(self, stream: 'grpclib.server.Stream[app.datasync.v1.data_sync_pb2.FileUploadRequest, app.datasync.v1.data_sync_pb2.FileUploadResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def StreamingDataCaptureUpload(self, stream: 'grpclib.server.Stream[app.datasync.v1.data_sync_pb2.StreamingDataCaptureUploadRequest, app.datasync.v1.data_sync_pb2.StreamingDataCaptureUploadResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class DataSyncServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: - self.Upload = grpclib.client.StreamStreamMethod(channel, '/viam.app.datasync.v1.DataSyncService/Upload', app.datasync.v1.data_sync_pb2.UploadRequest, app.datasync.v1.data_sync_pb2.UploadResponse) \ No newline at end of file + self.DataCaptureUpload = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.datasync.v1.DataSyncService/DataCaptureUpload', app.datasync.v1.data_sync_pb2.DataCaptureUploadRequest, app.datasync.v1.data_sync_pb2.DataCaptureUploadResponse) + self.FileUpload = grpclib.client.StreamUnaryMethod(channel, '/viam.app.datasync.v1.DataSyncService/FileUpload', app.datasync.v1.data_sync_pb2.FileUploadRequest, app.datasync.v1.data_sync_pb2.FileUploadResponse) + self.StreamingDataCaptureUpload = grpclib.client.StreamUnaryMethod(channel, '/viam.app.datasync.v1.DataSyncService/StreamingDataCaptureUpload', app.datasync.v1.data_sync_pb2.StreamingDataCaptureUploadRequest, app.datasync.v1.data_sync_pb2.StreamingDataCaptureUploadResponse) \ No newline at end of file diff --git a/src/viam/gen/app/datasync/v1/data_sync_pb2.py b/src/viam/gen/app/datasync/v1/data_sync_pb2.py index 73100d2eb..52c072635 100644 --- a/src/viam/gen/app/datasync/v1/data_sync_pb2.py +++ b/src/viam/gen/app/datasync/v1/data_sync_pb2.py @@ -1,144 +1,70 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/datasync/v1/data_sync.proto') _sym_db = _symbol_database.Default() +from ....app.data.v1 import data_pb2 as app_dot_data_dot_v1_dot_data__pb2 +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -from ....tagger.v1 import tagger_pb2 as tagger_dot_v1_dot_tagger__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fapp/datasync/v1/data_sync.proto\x12\x14viam.app.datasync.v1\x1a\x19google/protobuf/any.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x16tagger/v1/tagger.proto"\x94\x01\n\x0eSensorMetadata\x12A\n\x0etime_requested\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\rtimeRequested\x12?\n\rtime_received\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0ctimeReceived"\xa3\x01\n\nSensorData\x12@\n\x08metadata\x18\x01 \x01(\x0b2$.viam.app.datasync.v1.SensorMetadataR\x08metadata\x121\n\x06struct\x18\x02 \x01(\x0b2\x17.google.protobuf.StructH\x00R\x06struct\x12\x18\n\x06binary\x18\x03 \x01(\x0cH\x00R\x06binaryB\x06\n\x04data"\x1e\n\x08FileData\x12\x12\n\x04data\x18\x01 \x01(\x0cR\x04data"\xb0\x04\n\x0eUploadMetadata\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId\x12%\n\x0ecomponent_type\x18\x02 \x01(\tR\rcomponentType\x12%\n\x0ecomponent_name\x18\x03 \x01(\tR\rcomponentName\x12\'\n\x0fcomponent_model\x18\x04 \x01(\tR\x0ecomponentModel\x12\x1f\n\x0bmethod_name\x18\x05 \x01(\tR\nmethodName\x122\n\x04type\x18\x06 \x01(\x0e2\x1e.viam.app.datasync.v1.DataTypeR\x04type\x12\x1b\n\tfile_name\x18\x07 \x01(\tR\x08fileName\x12g\n\x11method_parameters\x18\x08 \x03(\x0b2:.viam.app.datasync.v1.UploadMetadata.MethodParametersEntryR\x10methodParameters\x12%\n\x0efile_extension\x18\t \x01(\tR\rfileExtension\x12\x12\n\x04tags\x18\n \x03(\tR\x04tags\x12\x1d\n\nsession_id\x18\x0b \x01(\tR\tsessionId\x1aY\n\x15MethodParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12*\n\x05value\x18\x02 \x01(\x0b2\x14.google.protobuf.AnyR\x05value:\x028\x01"\xf8\x01\n\rUploadRequest\x12B\n\x08metadata\x18\x01 \x01(\x0b2$.viam.app.datasync.v1.UploadMetadataH\x00R\x08metadata\x12K\n\x0fsensor_contents\x18\x02 \x01(\x0b2 .viam.app.datasync.v1.SensorDataH\x00R\x0esensorContents\x12E\n\rfile_contents\x18\x03 \x01(\x0b2\x1e.viam.app.datasync.v1.FileDataH\x00R\x0cfileContentsB\x0f\n\rupload_packet";\n\x0eUploadResponse\x12)\n\x10requests_written\x18\x01 \x01(\x05R\x0frequestsWritten"q\n\x0fCaptureInterval\x120\n\x05start\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\x05start\x12,\n\x03end\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x03end"\x84\x04\n\x13DataCaptureMetadata\x12%\n\x0ecomponent_type\x18\x01 \x01(\tR\rcomponentType\x12%\n\x0ecomponent_name\x18\x02 \x01(\tR\rcomponentName\x12\'\n\x0fcomponent_model\x18\x03 \x01(\tR\x0ecomponentModel\x12\x1f\n\x0bmethod_name\x18\x04 \x01(\tR\nmethodName\x122\n\x04type\x18\x05 \x01(\x0e2\x1e.viam.app.datasync.v1.DataTypeR\x04type\x12l\n\x11method_parameters\x18\x06 \x03(\x0b2?.viam.app.datasync.v1.DataCaptureMetadata.MethodParametersEntryR\x10methodParameters\x12%\n\x0efile_extension\x18\x07 \x01(\tR\rfileExtension\x12\x12\n\x04tags\x18\x08 \x03(\tR\x04tags\x12\x1d\n\nsession_id\x18\t \x01(\tR\tsessionId\x1aY\n\x15MethodParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12*\n\x05value\x18\x02 \x01(\x0b2\x14.google.protobuf.AnyR\x05value:\x028\x01"\xd5\x0b\n\x0eTabularCapture\x12g\n\x08interval\x18\x01 \x01(\x0b2%.viam.app.datasync.v1.CaptureIntervalB$\x9a\x84\x9e\x03\x1fbson:"interval" json:"interval"R\x08interval\x127\n\x06org_id\x18\x02 \x01(\tB \x9a\x84\x9e\x03\x1bbson:"org_id" json:"org_id"R\x05orgId\x12?\n\x08robot_id\x18\x03 \x01(\tB$\x9a\x84\x9e\x03\x1fbson:"robot_id" json:"robot_id"R\x07robotId\x12;\n\x07part_id\x18\x04 \x01(\tB"\x9a\x84\x9e\x03\x1dbson:"part_id" json:"part_id"R\x06partId\x12K\n\x0blocation_id\x18\x05 \x01(\tB*\x9a\x84\x9e\x03%bson:"location_id" json:"location_id"R\nlocationId\x12W\n\x0ecomponent_name\x18\x06 \x01(\tB0\x9a\x84\x9e\x03+bson:"component_name" json:"component_name"R\rcomponentName\x12W\n\x0ecomponent_type\x18\x07 \x01(\tB0\x9a\x84\x9e\x03+bson:"component_type" json:"component_type"R\rcomponentType\x12[\n\x0fcomponent_model\x18\x08 \x01(\tB2\x9a\x84\x9e\x03-bson:"component_model" json:"component_model"R\x0ecomponentModel\x12K\n\x0bmethod_name\x18\t \x01(\tB*\x9a\x84\x9e\x03%bson:"method_name" json:"method_name"R\nmethodName\x12C\n\tblob_path\x18\n \x01(\tB&\x9a\x84\x9e\x03!bson:"blob_path" json:"blob_path"R\x08blobPath\x12O\n\x0ccolumn_names\x18\x0b \x03(\tB,\x9a\x84\x9e\x03\'bson:"column_names" json:"column_names"R\x0bcolumnNames\x12\x9f\x01\n\x11method_parameters\x18\x0c \x03(\x0b2:.viam.app.datasync.v1.TabularCapture.MethodParametersEntryB6\x9a\x84\x9e\x031bson:"method_parameters" json:"method_parameters"R\x10methodParameters\x12;\n\x07file_id\x18\r \x01(\tB"\x9a\x84\x9e\x03\x1dbson:"file_id" json:"file_id"R\x06fileId\x120\n\x04tags\x18\x0e \x03(\tB\x1c\x9a\x84\x9e\x03\x17bson:"tags" json:"tags"R\x04tags\x12S\n\rmessage_count\x18\x0f \x01(\x05B.\x9a\x84\x9e\x03)bson:"message_count" json:"message_count"R\x0cmessageCount\x12Z\n\x0ffile_size_bytes\x18\x10 \x01(\x03B2\x9a\x84\x9e\x03-bson:"file_size_bytes" json:"file_size_bytes"R\rfileSizeBytes\x12G\n\nsession_id\x18\x11 \x01(\tB(\x9a\x84\x9e\x03#bson:"session_id" json:"session_id"R\tsessionId\x1aY\n\x15MethodParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12*\n\x05value\x18\x02 \x01(\x0b2\x14.google.protobuf.AnyR\x05value:\x028\x01"\xad\n\n\rBinaryCapture\x12g\n\x08interval\x18\x01 \x01(\x0b2%.viam.app.datasync.v1.CaptureIntervalB$\x9a\x84\x9e\x03\x1fbson:"interval" json:"interval"R\x08interval\x127\n\x06org_id\x18\x02 \x01(\tB \x9a\x84\x9e\x03\x1bbson:"org_id" json:"org_id"R\x05orgId\x12?\n\x08robot_id\x18\x03 \x01(\tB$\x9a\x84\x9e\x03\x1fbson:"robot_id" json:"robot_id"R\x07robotId\x12;\n\x07part_id\x18\x04 \x01(\tB"\x9a\x84\x9e\x03\x1dbson:"part_id" json:"part_id"R\x06partId\x12K\n\x0blocation_id\x18\x05 \x01(\tB*\x9a\x84\x9e\x03%bson:"location_id" json:"location_id"R\nlocationId\x12W\n\x0ecomponent_name\x18\x06 \x01(\tB0\x9a\x84\x9e\x03+bson:"component_name" json:"component_name"R\rcomponentName\x12W\n\x0ecomponent_type\x18\x07 \x01(\tB0\x9a\x84\x9e\x03+bson:"component_type" json:"component_type"R\rcomponentType\x12[\n\x0fcomponent_model\x18\x08 \x01(\tB2\x9a\x84\x9e\x03-bson:"component_model" json:"component_model"R\x0ecomponentModel\x12K\n\x0bmethod_name\x18\t \x01(\tB*\x9a\x84\x9e\x03%bson:"method_name" json:"method_name"R\nmethodName\x12C\n\tblob_path\x18\n \x01(\tB&\x9a\x84\x9e\x03!bson:"blob_path" json:"blob_path"R\x08blobPath\x12\x9e\x01\n\x11method_parameters\x18\x0b \x03(\x0b29.viam.app.datasync.v1.BinaryCapture.MethodParametersEntryB6\x9a\x84\x9e\x031bson:"method_parameters" json:"method_parameters"R\x10methodParameters\x12;\n\x07file_id\x18\x0c \x01(\tB"\x9a\x84\x9e\x03\x1dbson:"file_id" json:"file_id"R\x06fileId\x120\n\x04tags\x18\r \x03(\tB\x1c\x9a\x84\x9e\x03\x17bson:"tags" json:"tags"R\x04tags\x12Z\n\x0ffile_size_bytes\x18\x0e \x01(\x03B2\x9a\x84\x9e\x03-bson:"file_size_bytes" json:"file_size_bytes"R\rfileSizeBytes\x12G\n\nsession_id\x18\x0f \x01(\tB(\x9a\x84\x9e\x03#bson:"session_id" json:"session_id"R\tsessionId\x1aY\n\x15MethodParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12*\n\x05value\x18\x02 \x01(\x0b2\x14.google.protobuf.AnyR\x05value:\x028\x01"\xc4\x06\n\x08UserFile\x12_\n\tsync_time\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampB&\x9a\x84\x9e\x03!bson:"sync_time" json:"sync_time"R\x08syncTime\x127\n\x06org_id\x18\x02 \x01(\tB \x9a\x84\x9e\x03\x1bbson:"org_id" json:"org_id"R\x05orgId\x12?\n\x08robot_id\x18\x03 \x01(\tB$\x9a\x84\x9e\x03\x1fbson:"robot_id" json:"robot_id"R\x07robotId\x12;\n\x07part_id\x18\x04 \x01(\tB"\x9a\x84\x9e\x03\x1dbson:"part_id" json:"part_id"R\x06partId\x12K\n\x0blocation_id\x18\x05 \x01(\tB*\x9a\x84\x9e\x03%bson:"location_id" json:"location_id"R\nlocationId\x12C\n\tblob_path\x18\x06 \x01(\tB&\x9a\x84\x9e\x03!bson:"blob_path" json:"blob_path"R\x08blobPath\x12\x99\x01\n\x11method_parameters\x18\x07 \x03(\x0b24.viam.app.datasync.v1.UserFile.MethodParametersEntryB6\x9a\x84\x9e\x031bson:"method_parameters" json:"method_parameters"R\x10methodParameters\x12;\n\x07file_id\x18\x08 \x01(\tB"\x9a\x84\x9e\x03\x1dbson:"file_id" json:"file_id"R\x06fileId\x12Z\n\x0ffile_size_bytes\x18\t \x01(\x03B2\x9a\x84\x9e\x03-bson:"file_size_bytes" json:"file_size_bytes"R\rfileSizeBytes\x1aY\n\x15MethodParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12*\n\x05value\x18\x02 \x01(\x0b2\x14.google.protobuf.AnyR\x05value:\x028\x01*t\n\x08DataType\x12\x19\n\x15DATA_TYPE_UNSPECIFIED\x10\x00\x12\x1b\n\x17DATA_TYPE_BINARY_SENSOR\x10\x01\x12\x1c\n\x18DATA_TYPE_TABULAR_SENSOR\x10\x02\x12\x12\n\x0eDATA_TYPE_FILE\x10\x032j\n\x0fDataSyncService\x12W\n\x06Upload\x12#.viam.app.datasync.v1.UploadRequest\x1a$.viam.app.datasync.v1.UploadResponse(\x010\x01B!Z\x1fgo.viam.com/api/app/datasync/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.datasync.v1.data_sync_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z\x1fgo.viam.com/api/app/datasync/v1' - _UPLOADMETADATA_METHODPARAMETERSENTRY._options = None - _UPLOADMETADATA_METHODPARAMETERSENTRY._serialized_options = b'8\x01' - _DATACAPTUREMETADATA_METHODPARAMETERSENTRY._options = None - _DATACAPTUREMETADATA_METHODPARAMETERSENTRY._serialized_options = b'8\x01' - _TABULARCAPTURE_METHODPARAMETERSENTRY._options = None - _TABULARCAPTURE_METHODPARAMETERSENTRY._serialized_options = b'8\x01' - _TABULARCAPTURE.fields_by_name['interval']._options = None - _TABULARCAPTURE.fields_by_name['interval']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"interval" json:"interval"' - _TABULARCAPTURE.fields_by_name['org_id']._options = None - _TABULARCAPTURE.fields_by_name['org_id']._serialized_options = b'\x9a\x84\x9e\x03\x1bbson:"org_id" json:"org_id"' - _TABULARCAPTURE.fields_by_name['robot_id']._options = None - _TABULARCAPTURE.fields_by_name['robot_id']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"robot_id" json:"robot_id"' - _TABULARCAPTURE.fields_by_name['part_id']._options = None - _TABULARCAPTURE.fields_by_name['part_id']._serialized_options = b'\x9a\x84\x9e\x03\x1dbson:"part_id" json:"part_id"' - _TABULARCAPTURE.fields_by_name['location_id']._options = None - _TABULARCAPTURE.fields_by_name['location_id']._serialized_options = b'\x9a\x84\x9e\x03%bson:"location_id" json:"location_id"' - _TABULARCAPTURE.fields_by_name['component_name']._options = None - _TABULARCAPTURE.fields_by_name['component_name']._serialized_options = b'\x9a\x84\x9e\x03+bson:"component_name" json:"component_name"' - _TABULARCAPTURE.fields_by_name['component_type']._options = None - _TABULARCAPTURE.fields_by_name['component_type']._serialized_options = b'\x9a\x84\x9e\x03+bson:"component_type" json:"component_type"' - _TABULARCAPTURE.fields_by_name['component_model']._options = None - _TABULARCAPTURE.fields_by_name['component_model']._serialized_options = b'\x9a\x84\x9e\x03-bson:"component_model" json:"component_model"' - _TABULARCAPTURE.fields_by_name['method_name']._options = None - _TABULARCAPTURE.fields_by_name['method_name']._serialized_options = b'\x9a\x84\x9e\x03%bson:"method_name" json:"method_name"' - _TABULARCAPTURE.fields_by_name['blob_path']._options = None - _TABULARCAPTURE.fields_by_name['blob_path']._serialized_options = b'\x9a\x84\x9e\x03!bson:"blob_path" json:"blob_path"' - _TABULARCAPTURE.fields_by_name['column_names']._options = None - _TABULARCAPTURE.fields_by_name['column_names']._serialized_options = b'\x9a\x84\x9e\x03\'bson:"column_names" json:"column_names"' - _TABULARCAPTURE.fields_by_name['method_parameters']._options = None - _TABULARCAPTURE.fields_by_name['method_parameters']._serialized_options = b'\x9a\x84\x9e\x031bson:"method_parameters" json:"method_parameters"' - _TABULARCAPTURE.fields_by_name['file_id']._options = None - _TABULARCAPTURE.fields_by_name['file_id']._serialized_options = b'\x9a\x84\x9e\x03\x1dbson:"file_id" json:"file_id"' - _TABULARCAPTURE.fields_by_name['tags']._options = None - _TABULARCAPTURE.fields_by_name['tags']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"tags" json:"tags"' - _TABULARCAPTURE.fields_by_name['message_count']._options = None - _TABULARCAPTURE.fields_by_name['message_count']._serialized_options = b'\x9a\x84\x9e\x03)bson:"message_count" json:"message_count"' - _TABULARCAPTURE.fields_by_name['file_size_bytes']._options = None - _TABULARCAPTURE.fields_by_name['file_size_bytes']._serialized_options = b'\x9a\x84\x9e\x03-bson:"file_size_bytes" json:"file_size_bytes"' - _TABULARCAPTURE.fields_by_name['session_id']._options = None - _TABULARCAPTURE.fields_by_name['session_id']._serialized_options = b'\x9a\x84\x9e\x03#bson:"session_id" json:"session_id"' - _BINARYCAPTURE_METHODPARAMETERSENTRY._options = None - _BINARYCAPTURE_METHODPARAMETERSENTRY._serialized_options = b'8\x01' - _BINARYCAPTURE.fields_by_name['interval']._options = None - _BINARYCAPTURE.fields_by_name['interval']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"interval" json:"interval"' - _BINARYCAPTURE.fields_by_name['org_id']._options = None - _BINARYCAPTURE.fields_by_name['org_id']._serialized_options = b'\x9a\x84\x9e\x03\x1bbson:"org_id" json:"org_id"' - _BINARYCAPTURE.fields_by_name['robot_id']._options = None - _BINARYCAPTURE.fields_by_name['robot_id']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"robot_id" json:"robot_id"' - _BINARYCAPTURE.fields_by_name['part_id']._options = None - _BINARYCAPTURE.fields_by_name['part_id']._serialized_options = b'\x9a\x84\x9e\x03\x1dbson:"part_id" json:"part_id"' - _BINARYCAPTURE.fields_by_name['location_id']._options = None - _BINARYCAPTURE.fields_by_name['location_id']._serialized_options = b'\x9a\x84\x9e\x03%bson:"location_id" json:"location_id"' - _BINARYCAPTURE.fields_by_name['component_name']._options = None - _BINARYCAPTURE.fields_by_name['component_name']._serialized_options = b'\x9a\x84\x9e\x03+bson:"component_name" json:"component_name"' - _BINARYCAPTURE.fields_by_name['component_type']._options = None - _BINARYCAPTURE.fields_by_name['component_type']._serialized_options = b'\x9a\x84\x9e\x03+bson:"component_type" json:"component_type"' - _BINARYCAPTURE.fields_by_name['component_model']._options = None - _BINARYCAPTURE.fields_by_name['component_model']._serialized_options = b'\x9a\x84\x9e\x03-bson:"component_model" json:"component_model"' - _BINARYCAPTURE.fields_by_name['method_name']._options = None - _BINARYCAPTURE.fields_by_name['method_name']._serialized_options = b'\x9a\x84\x9e\x03%bson:"method_name" json:"method_name"' - _BINARYCAPTURE.fields_by_name['blob_path']._options = None - _BINARYCAPTURE.fields_by_name['blob_path']._serialized_options = b'\x9a\x84\x9e\x03!bson:"blob_path" json:"blob_path"' - _BINARYCAPTURE.fields_by_name['method_parameters']._options = None - _BINARYCAPTURE.fields_by_name['method_parameters']._serialized_options = b'\x9a\x84\x9e\x031bson:"method_parameters" json:"method_parameters"' - _BINARYCAPTURE.fields_by_name['file_id']._options = None - _BINARYCAPTURE.fields_by_name['file_id']._serialized_options = b'\x9a\x84\x9e\x03\x1dbson:"file_id" json:"file_id"' - _BINARYCAPTURE.fields_by_name['tags']._options = None - _BINARYCAPTURE.fields_by_name['tags']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"tags" json:"tags"' - _BINARYCAPTURE.fields_by_name['file_size_bytes']._options = None - _BINARYCAPTURE.fields_by_name['file_size_bytes']._serialized_options = b'\x9a\x84\x9e\x03-bson:"file_size_bytes" json:"file_size_bytes"' - _BINARYCAPTURE.fields_by_name['session_id']._options = None - _BINARYCAPTURE.fields_by_name['session_id']._serialized_options = b'\x9a\x84\x9e\x03#bson:"session_id" json:"session_id"' - _USERFILE_METHODPARAMETERSENTRY._options = None - _USERFILE_METHODPARAMETERSENTRY._serialized_options = b'8\x01' - _USERFILE.fields_by_name['sync_time']._options = None - _USERFILE.fields_by_name['sync_time']._serialized_options = b'\x9a\x84\x9e\x03!bson:"sync_time" json:"sync_time"' - _USERFILE.fields_by_name['org_id']._options = None - _USERFILE.fields_by_name['org_id']._serialized_options = b'\x9a\x84\x9e\x03\x1bbson:"org_id" json:"org_id"' - _USERFILE.fields_by_name['robot_id']._options = None - _USERFILE.fields_by_name['robot_id']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"robot_id" json:"robot_id"' - _USERFILE.fields_by_name['part_id']._options = None - _USERFILE.fields_by_name['part_id']._serialized_options = b'\x9a\x84\x9e\x03\x1dbson:"part_id" json:"part_id"' - _USERFILE.fields_by_name['location_id']._options = None - _USERFILE.fields_by_name['location_id']._serialized_options = b'\x9a\x84\x9e\x03%bson:"location_id" json:"location_id"' - _USERFILE.fields_by_name['blob_path']._options = None - _USERFILE.fields_by_name['blob_path']._serialized_options = b'\x9a\x84\x9e\x03!bson:"blob_path" json:"blob_path"' - _USERFILE.fields_by_name['method_parameters']._options = None - _USERFILE.fields_by_name['method_parameters']._serialized_options = b'\x9a\x84\x9e\x031bson:"method_parameters" json:"method_parameters"' - _USERFILE.fields_by_name['file_id']._options = None - _USERFILE.fields_by_name['file_id']._serialized_options = b'\x9a\x84\x9e\x03\x1dbson:"file_id" json:"file_id"' - _USERFILE.fields_by_name['file_size_bytes']._options = None - _USERFILE.fields_by_name['file_size_bytes']._serialized_options = b'\x9a\x84\x9e\x03-bson:"file_size_bytes" json:"file_size_bytes"' - _DATATYPE._serialized_start = 5692 - _DATATYPE._serialized_end = 5808 - _SENSORMETADATA._serialized_start = 172 - _SENSORMETADATA._serialized_end = 320 - _SENSORDATA._serialized_start = 323 - _SENSORDATA._serialized_end = 486 - _FILEDATA._serialized_start = 488 - _FILEDATA._serialized_end = 518 - _UPLOADMETADATA._serialized_start = 521 - _UPLOADMETADATA._serialized_end = 1081 - _UPLOADMETADATA_METHODPARAMETERSENTRY._serialized_start = 992 - _UPLOADMETADATA_METHODPARAMETERSENTRY._serialized_end = 1081 - _UPLOADREQUEST._serialized_start = 1084 - _UPLOADREQUEST._serialized_end = 1332 - _UPLOADRESPONSE._serialized_start = 1334 - _UPLOADRESPONSE._serialized_end = 1393 - _CAPTUREINTERVAL._serialized_start = 1395 - _CAPTUREINTERVAL._serialized_end = 1508 - _DATACAPTUREMETADATA._serialized_start = 1511 - _DATACAPTUREMETADATA._serialized_end = 2027 - _DATACAPTUREMETADATA_METHODPARAMETERSENTRY._serialized_start = 992 - _DATACAPTUREMETADATA_METHODPARAMETERSENTRY._serialized_end = 1081 - _TABULARCAPTURE._serialized_start = 2030 - _TABULARCAPTURE._serialized_end = 3523 - _TABULARCAPTURE_METHODPARAMETERSENTRY._serialized_start = 992 - _TABULARCAPTURE_METHODPARAMETERSENTRY._serialized_end = 1081 - _BINARYCAPTURE._serialized_start = 3526 - _BINARYCAPTURE._serialized_end = 4851 - _BINARYCAPTURE_METHODPARAMETERSENTRY._serialized_start = 992 - _BINARYCAPTURE_METHODPARAMETERSENTRY._serialized_end = 1081 - _USERFILE._serialized_start = 4854 - _USERFILE._serialized_end = 5690 - _USERFILE_METHODPARAMETERSENTRY._serialized_start = 992 - _USERFILE_METHODPARAMETERSENTRY._serialized_end = 1081 - _DATASYNCSERVICE._serialized_start = 5810 - _DATASYNCSERVICE._serialized_end = 5916 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1fapp/datasync/v1/data_sync.proto\x12\x14viam.app.datasync.v1\x1a\x16app/data/v1/data.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x19google/protobuf/any.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\xa7\x01\n\x18DataCaptureUploadRequest\x12@\n\x08metadata\x18\x01 \x01(\x0b2$.viam.app.datasync.v1.UploadMetadataR\x08metadata\x12I\n\x0fsensor_contents\x18\x02 \x03(\x0b2 .viam.app.datasync.v1.SensorDataR\x0esensorContents"Z\n\x19DataCaptureUploadResponse\x12\x17\n\x07file_id\x18\x01 \x01(\tR\x06fileId\x12$\n\x0ebinary_data_id\x18\x02 \x01(\tR\x0cbinaryDataId"\xaf\x01\n\x11FileUploadRequest\x12B\n\x08metadata\x18\x01 \x01(\x0b2$.viam.app.datasync.v1.UploadMetadataH\x00R\x08metadata\x12E\n\rfile_contents\x18\x02 \x01(\x0b2\x1e.viam.app.datasync.v1.FileDataH\x00R\x0cfileContentsB\x0f\n\rupload_packet"W\n\x12FileUploadResponse\x12\x1b\n\x07file_id\x18\x01 \x01(\tB\x02\x18\x01R\x06fileId\x12$\n\x0ebinary_data_id\x18\x02 \x01(\tR\x0cbinaryDataId"\x99\x01\n!StreamingDataCaptureUploadRequest\x12M\n\x08metadata\x18\x01 \x01(\x0b2/.viam.app.datasync.v1.DataCaptureUploadMetadataH\x00R\x08metadata\x12\x14\n\x04data\x18\x02 \x01(\x0cH\x00R\x04dataB\x0f\n\rupload_packet"g\n"StreamingDataCaptureUploadResponse\x12\x1b\n\x07file_id\x18\x01 \x01(\tB\x02\x18\x01R\x06fileId\x12$\n\x0ebinary_data_id\x18\x02 \x01(\tR\x0cbinaryDataId"\x92\x02\n\x0eSensorMetadata\x12A\n\x0etime_requested\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\rtimeRequested\x12?\n\rtime_received\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0ctimeReceived\x12;\n\tmime_type\x18\x03 \x01(\x0e2\x1e.viam.app.datasync.v1.MimeTypeR\x08mimeType\x12?\n\x0bannotations\x18\x04 \x01(\x0b2\x1d.viam.app.data.v1.AnnotationsR\x0bannotations"\xa3\x01\n\nSensorData\x12@\n\x08metadata\x18\x01 \x01(\x0b2$.viam.app.datasync.v1.SensorMetadataR\x08metadata\x121\n\x06struct\x18\x02 \x01(\x0b2\x17.google.protobuf.StructH\x00R\x06struct\x12\x18\n\x06binary\x18\x03 \x01(\x0cH\x00R\x06binaryB\x06\n\x04data"\x1e\n\x08FileData\x12\x12\n\x04data\x18\x01 \x01(\x0cR\x04data"\x91\x04\n\x0eUploadMetadata\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId\x12%\n\x0ecomponent_type\x18\x02 \x01(\tR\rcomponentType\x12%\n\x0ecomponent_name\x18\x03 \x01(\tR\rcomponentName\x12\x1f\n\x0bmethod_name\x18\x05 \x01(\tR\nmethodName\x122\n\x04type\x18\x06 \x01(\x0e2\x1e.viam.app.datasync.v1.DataTypeR\x04type\x12\x1b\n\tfile_name\x18\x07 \x01(\tR\x08fileName\x12g\n\x11method_parameters\x18\x08 \x03(\x0b2:.viam.app.datasync.v1.UploadMetadata.MethodParametersEntryR\x10methodParameters\x12%\n\x0efile_extension\x18\t \x01(\tR\rfileExtension\x12\x12\n\x04tags\x18\n \x03(\tR\x04tags\x1aY\n\x15MethodParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12*\n\x05value\x18\x02 \x01(\x0b2\x14.google.protobuf.AnyR\x05value:\x028\x01J\x04\x08\x04\x10\x05J\x04\x08\x0b\x10\x0cR\x0fcomponent_modelR\nsession_id"q\n\x0fCaptureInterval\x120\n\x05start\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\x05start\x12,\n\x03end\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x03end"\xe5\x03\n\x13DataCaptureMetadata\x12%\n\x0ecomponent_type\x18\x01 \x01(\tR\rcomponentType\x12%\n\x0ecomponent_name\x18\x02 \x01(\tR\rcomponentName\x12\x1f\n\x0bmethod_name\x18\x04 \x01(\tR\nmethodName\x122\n\x04type\x18\x05 \x01(\x0e2\x1e.viam.app.datasync.v1.DataTypeR\x04type\x12l\n\x11method_parameters\x18\x06 \x03(\x0b2?.viam.app.datasync.v1.DataCaptureMetadata.MethodParametersEntryR\x10methodParameters\x12%\n\x0efile_extension\x18\x07 \x01(\tR\rfileExtension\x12\x12\n\x04tags\x18\x08 \x03(\tR\x04tags\x1aY\n\x15MethodParametersEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12*\n\x05value\x18\x02 \x01(\x0b2\x14.google.protobuf.AnyR\x05value:\x028\x01J\x04\x08\x03\x10\x04J\x04\x08\t\x10\nR\x0fcomponent_modelR\nsession_id"\xb9\x01\n\x19DataCaptureUploadMetadata\x12M\n\x0fupload_metadata\x18\x01 \x01(\x0b2$.viam.app.datasync.v1.UploadMetadataR\x0euploadMetadata\x12M\n\x0fsensor_metadata\x18\x02 \x01(\x0b2$.viam.app.datasync.v1.SensorMetadataR\x0esensorMetadata*w\n\x08MimeType\x12\x19\n\x15MIME_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14MIME_TYPE_IMAGE_JPEG\x10\x01\x12\x17\n\x13MIME_TYPE_IMAGE_PNG\x10\x02\x12\x1d\n\x19MIME_TYPE_APPLICATION_PCD\x10\x03*t\n\x08DataType\x12\x19\n\x15DATA_TYPE_UNSPECIFIED\x10\x00\x12\x1b\n\x17DATA_TYPE_BINARY_SENSOR\x10\x01\x12\x1c\n\x18DATA_TYPE_TABULAR_SENSOR\x10\x02\x12\x12\n\x0eDATA_TYPE_FILE\x10\x032\x80\x04\n\x0fDataSyncService\x12\x9e\x01\n\x11DataCaptureUpload\x12..viam.app.datasync.v1.DataCaptureUploadRequest\x1a/.viam.app.datasync.v1.DataCaptureUploadResponse"(\x82\xd3\xe4\x93\x02"" /datasync/v1/data_capture_upload\x12\x83\x01\n\nFileUpload\x12\'.viam.app.datasync.v1.FileUploadRequest\x1a(.viam.app.datasync.v1.FileUploadResponse" \x82\xd3\xe4\x93\x02\x1a"\x18/datasync/v1/file_upload(\x01\x12\xc5\x01\n\x1aStreamingDataCaptureUpload\x127.viam.app.datasync.v1.StreamingDataCaptureUploadRequest\x1a8.viam.app.datasync.v1.StreamingDataCaptureUploadResponse"2\x82\xd3\xe4\x93\x02,"*/datasync/v1/streaming_data_capture_upload(\x01B!Z\x1fgo.viam.com/api/app/datasync/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.datasync.v1.data_sync_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x1fgo.viam.com/api/app/datasync/v1' + _globals['_FILEUPLOADRESPONSE'].fields_by_name['file_id']._loaded_options = None + _globals['_FILEUPLOADRESPONSE'].fields_by_name['file_id']._serialized_options = b'\x18\x01' + _globals['_STREAMINGDATACAPTUREUPLOADRESPONSE'].fields_by_name['file_id']._loaded_options = None + _globals['_STREAMINGDATACAPTUREUPLOADRESPONSE'].fields_by_name['file_id']._serialized_options = b'\x18\x01' + _globals['_UPLOADMETADATA_METHODPARAMETERSENTRY']._loaded_options = None + _globals['_UPLOADMETADATA_METHODPARAMETERSENTRY']._serialized_options = b'8\x01' + _globals['_DATACAPTUREMETADATA_METHODPARAMETERSENTRY']._loaded_options = None + _globals['_DATACAPTUREMETADATA_METHODPARAMETERSENTRY']._serialized_options = b'8\x01' + _globals['_DATASYNCSERVICE'].methods_by_name['DataCaptureUpload']._loaded_options = None + _globals['_DATASYNCSERVICE'].methods_by_name['DataCaptureUpload']._serialized_options = b'\x82\xd3\xe4\x93\x02"" /datasync/v1/data_capture_upload' + _globals['_DATASYNCSERVICE'].methods_by_name['FileUpload']._loaded_options = None + _globals['_DATASYNCSERVICE'].methods_by_name['FileUpload']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1a"\x18/datasync/v1/file_upload' + _globals['_DATASYNCSERVICE'].methods_by_name['StreamingDataCaptureUpload']._loaded_options = None + _globals['_DATASYNCSERVICE'].methods_by_name['StreamingDataCaptureUpload']._serialized_options = b'\x82\xd3\xe4\x93\x02,"*/datasync/v1/streaming_data_capture_upload' + _globals['_MIMETYPE']._serialized_start = 2789 + _globals['_MIMETYPE']._serialized_end = 2908 + _globals['_DATATYPE']._serialized_start = 2910 + _globals['_DATATYPE']._serialized_end = 3026 + _globals['_DATACAPTUREUPLOADREQUEST']._serialized_start = 202 + _globals['_DATACAPTUREUPLOADREQUEST']._serialized_end = 369 + _globals['_DATACAPTUREUPLOADRESPONSE']._serialized_start = 371 + _globals['_DATACAPTUREUPLOADRESPONSE']._serialized_end = 461 + _globals['_FILEUPLOADREQUEST']._serialized_start = 464 + _globals['_FILEUPLOADREQUEST']._serialized_end = 639 + _globals['_FILEUPLOADRESPONSE']._serialized_start = 641 + _globals['_FILEUPLOADRESPONSE']._serialized_end = 728 + _globals['_STREAMINGDATACAPTUREUPLOADREQUEST']._serialized_start = 731 + _globals['_STREAMINGDATACAPTUREUPLOADREQUEST']._serialized_end = 884 + _globals['_STREAMINGDATACAPTUREUPLOADRESPONSE']._serialized_start = 886 + _globals['_STREAMINGDATACAPTUREUPLOADRESPONSE']._serialized_end = 989 + _globals['_SENSORMETADATA']._serialized_start = 992 + _globals['_SENSORMETADATA']._serialized_end = 1266 + _globals['_SENSORDATA']._serialized_start = 1269 + _globals['_SENSORDATA']._serialized_end = 1432 + _globals['_FILEDATA']._serialized_start = 1434 + _globals['_FILEDATA']._serialized_end = 1464 + _globals['_UPLOADMETADATA']._serialized_start = 1467 + _globals['_UPLOADMETADATA']._serialized_end = 1996 + _globals['_UPLOADMETADATA_METHODPARAMETERSENTRY']._serialized_start = 1866 + _globals['_UPLOADMETADATA_METHODPARAMETERSENTRY']._serialized_end = 1955 + _globals['_CAPTUREINTERVAL']._serialized_start = 1998 + _globals['_CAPTUREINTERVAL']._serialized_end = 2111 + _globals['_DATACAPTUREMETADATA']._serialized_start = 2114 + _globals['_DATACAPTUREMETADATA']._serialized_end = 2599 + _globals['_DATACAPTUREMETADATA_METHODPARAMETERSENTRY']._serialized_start = 1866 + _globals['_DATACAPTUREMETADATA_METHODPARAMETERSENTRY']._serialized_end = 1955 + _globals['_DATACAPTUREUPLOADMETADATA']._serialized_start = 2602 + _globals['_DATACAPTUREUPLOADMETADATA']._serialized_end = 2787 + _globals['_DATASYNCSERVICE']._serialized_start = 3029 + _globals['_DATASYNCSERVICE']._serialized_end = 3541 \ No newline at end of file diff --git a/src/viam/gen/app/datasync/v1/data_sync_pb2.pyi b/src/viam/gen/app/datasync/v1/data_sync_pb2.pyi index 78ca90b77..0adab7705 100644 --- a/src/viam/gen/app/datasync/v1/data_sync_pb2.pyi +++ b/src/viam/gen/app/datasync/v1/data_sync_pb2.pyi @@ -2,6 +2,7 @@ @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ +from .... import app import builtins import collections.abc import google.protobuf.any_pb2 @@ -19,6 +20,25 @@ else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _MimeType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _MimeTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MimeType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + MIME_TYPE_UNSPECIFIED: _MimeType.ValueType + MIME_TYPE_IMAGE_JPEG: _MimeType.ValueType + MIME_TYPE_IMAGE_PNG: _MimeType.ValueType + MIME_TYPE_APPLICATION_PCD: _MimeType.ValueType + +class MimeType(_MimeType, metaclass=_MimeTypeEnumTypeWrapper): + ... +MIME_TYPE_UNSPECIFIED: MimeType.ValueType +MIME_TYPE_IMAGE_JPEG: MimeType.ValueType +MIME_TYPE_IMAGE_PNG: MimeType.ValueType +MIME_TYPE_APPLICATION_PCD: MimeType.ValueType +global___MimeType = MimeType + class _DataType: ValueType = typing.NewType('ValueType', builtins.int) V: typing_extensions.TypeAlias = ValueType @@ -31,202 +51,227 @@ class _DataTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumT DATA_TYPE_FILE: _DataType.ValueType class DataType(_DataType, metaclass=_DataTypeEnumTypeWrapper): - ... + """DataType specifies the type of data uploaded.""" DATA_TYPE_UNSPECIFIED: DataType.ValueType DATA_TYPE_BINARY_SENSOR: DataType.ValueType DATA_TYPE_TABULAR_SENSOR: DataType.ValueType DATA_TYPE_FILE: DataType.ValueType global___DataType = DataType -class SensorMetadata(google.protobuf.message.Message): +@typing.final +class DataCaptureUploadRequest(google.protobuf.message.Message): + """DataCaptureUploadRequest requests to upload the contents and metadata for tabular data.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor - TIME_REQUESTED_FIELD_NUMBER: builtins.int - TIME_RECEIVED_FIELD_NUMBER: builtins.int + METADATA_FIELD_NUMBER: builtins.int + SENSOR_CONTENTS_FIELD_NUMBER: builtins.int @property - def time_requested(self) -> google.protobuf.timestamp_pb2.Timestamp: + def metadata(self) -> global___UploadMetadata: ... @property - def time_received(self) -> google.protobuf.timestamp_pb2.Timestamp: + def sensor_contents(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SensorData]: ... - def __init__(self, *, time_requested: google.protobuf.timestamp_pb2.Timestamp | None=..., time_received: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + def __init__(self, *, metadata: global___UploadMetadata | None=..., sensor_contents: collections.abc.Iterable[global___SensorData] | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['time_received', b'time_received', 'time_requested', b'time_requested']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['metadata', b'metadata']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['time_received', b'time_received', 'time_requested', b'time_requested']) -> None: + def ClearField(self, field_name: typing.Literal['metadata', b'metadata', 'sensor_contents', b'sensor_contents']) -> None: ... -global___SensorMetadata = SensorMetadata +global___DataCaptureUploadRequest = DataCaptureUploadRequest -class SensorData(google.protobuf.message.Message): +@typing.final +class DataCaptureUploadResponse(google.protobuf.message.Message): + """DataCaptureUploadResponse returns the file id of the uploaded contents and metadata for tabular data.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FILE_ID_FIELD_NUMBER: builtins.int + BINARY_DATA_ID_FIELD_NUMBER: builtins.int + file_id: builtins.str + binary_data_id: builtins.str + + def __init__(self, *, file_id: builtins.str=..., binary_data_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['binary_data_id', b'binary_data_id', 'file_id', b'file_id']) -> None: + ... +global___DataCaptureUploadResponse = DataCaptureUploadResponse + +@typing.final +class FileUploadRequest(google.protobuf.message.Message): + """FileUploadRequest requests to upload the contents and metadata for binary (image + file) data. + The first packet must be the UploadMetadata associated with the binary data. + """ DESCRIPTOR: google.protobuf.descriptor.Descriptor METADATA_FIELD_NUMBER: builtins.int - STRUCT_FIELD_NUMBER: builtins.int - BINARY_FIELD_NUMBER: builtins.int + FILE_CONTENTS_FIELD_NUMBER: builtins.int @property - def metadata(self) -> global___SensorMetadata: + def metadata(self) -> global___UploadMetadata: ... @property - def struct(self) -> google.protobuf.struct_pb2.Struct: + def file_contents(self) -> global___FileData: ... - binary: builtins.bytes - def __init__(self, *, metadata: global___SensorMetadata | None=..., struct: google.protobuf.struct_pb2.Struct | None=..., binary: builtins.bytes=...) -> None: + def __init__(self, *, metadata: global___UploadMetadata | None=..., file_contents: global___FileData | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['binary', b'binary', 'data', b'data', 'metadata', b'metadata', 'struct', b'struct']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['file_contents', b'file_contents', 'metadata', b'metadata', 'upload_packet', b'upload_packet']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['binary', b'binary', 'data', b'data', 'metadata', b'metadata', 'struct', b'struct']) -> None: + def ClearField(self, field_name: typing.Literal['file_contents', b'file_contents', 'metadata', b'metadata', 'upload_packet', b'upload_packet']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['data', b'data']) -> typing_extensions.Literal['struct', 'binary'] | None: + def WhichOneof(self, oneof_group: typing.Literal['upload_packet', b'upload_packet']) -> typing.Literal['metadata', 'file_contents'] | None: ... -global___SensorData = SensorData +global___FileUploadRequest = FileUploadRequest -class FileData(google.protobuf.message.Message): +@typing.final +class FileUploadResponse(google.protobuf.message.Message): + """FileUploadResponse returns the file id of the uploaded contents and metadata for binary (image + file) data.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor - DATA_FIELD_NUMBER: builtins.int - data: builtins.bytes + FILE_ID_FIELD_NUMBER: builtins.int + BINARY_DATA_ID_FIELD_NUMBER: builtins.int + file_id: builtins.str + binary_data_id: builtins.str - def __init__(self, *, data: builtins.bytes=...) -> None: + def __init__(self, *, file_id: builtins.str=..., binary_data_id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['data', b'data']) -> None: + def ClearField(self, field_name: typing.Literal['binary_data_id', b'binary_data_id', 'file_id', b'file_id']) -> None: ... -global___FileData = FileData +global___FileUploadResponse = FileUploadResponse -class UploadMetadata(google.protobuf.message.Message): +@typing.final +class StreamingDataCaptureUploadRequest(google.protobuf.message.Message): + """StreamingDataCaptureUploadRequest requests to upload the contents and metadata for streaming binary (image + file) data. + The first packet must be the DataCaptureUploadMetadata associated with the data. + """ DESCRIPTOR: google.protobuf.descriptor.Descriptor + METADATA_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + data: builtins.bytes - class MethodParametersEntry(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - KEY_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - key: builtins.str - - @property - def value(self) -> google.protobuf.any_pb2.Any: - ... - - def __init__(self, *, key: builtins.str=..., value: google.protobuf.any_pb2.Any | None=...) -> None: - ... + @property + def metadata(self) -> global___DataCaptureUploadMetadata: + ... - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: - ... + def __init__(self, *, metadata: global___DataCaptureUploadMetadata | None=..., data: builtins.bytes=...) -> None: + ... - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: - ... - PART_ID_FIELD_NUMBER: builtins.int - COMPONENT_TYPE_FIELD_NUMBER: builtins.int - COMPONENT_NAME_FIELD_NUMBER: builtins.int - COMPONENT_MODEL_FIELD_NUMBER: builtins.int - METHOD_NAME_FIELD_NUMBER: builtins.int - TYPE_FIELD_NUMBER: builtins.int - FILE_NAME_FIELD_NUMBER: builtins.int - METHOD_PARAMETERS_FIELD_NUMBER: builtins.int - FILE_EXTENSION_FIELD_NUMBER: builtins.int - TAGS_FIELD_NUMBER: builtins.int - SESSION_ID_FIELD_NUMBER: builtins.int - part_id: builtins.str - component_type: builtins.str - component_name: builtins.str - component_model: builtins.str - method_name: builtins.str - type: global___DataType.ValueType - file_name: builtins.str + def HasField(self, field_name: typing.Literal['data', b'data', 'metadata', b'metadata', 'upload_packet', b'upload_packet']) -> builtins.bool: + ... - @property - def method_parameters(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, google.protobuf.any_pb2.Any]: + def ClearField(self, field_name: typing.Literal['data', b'data', 'metadata', b'metadata', 'upload_packet', b'upload_packet']) -> None: ... - file_extension: builtins.str - @property - def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + def WhichOneof(self, oneof_group: typing.Literal['upload_packet', b'upload_packet']) -> typing.Literal['metadata', 'data'] | None: ... - session_id: builtins.str +global___StreamingDataCaptureUploadRequest = StreamingDataCaptureUploadRequest - def __init__(self, *, part_id: builtins.str=..., component_type: builtins.str=..., component_name: builtins.str=..., component_model: builtins.str=..., method_name: builtins.str=..., type: global___DataType.ValueType=..., file_name: builtins.str=..., method_parameters: collections.abc.Mapping[builtins.str, google.protobuf.any_pb2.Any] | None=..., file_extension: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=..., session_id: builtins.str=...) -> None: +@typing.final +class StreamingDataCaptureUploadResponse(google.protobuf.message.Message): + """StreamingDataCaptureUploadResponse returns the file id of the uploaded contents and metadata for streaming binary (image + file) data.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FILE_ID_FIELD_NUMBER: builtins.int + BINARY_DATA_ID_FIELD_NUMBER: builtins.int + file_id: builtins.str + binary_data_id: builtins.str + + def __init__(self, *, file_id: builtins.str=..., binary_data_id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['component_model', b'component_model', 'component_name', b'component_name', 'component_type', b'component_type', 'file_extension', b'file_extension', 'file_name', b'file_name', 'method_name', b'method_name', 'method_parameters', b'method_parameters', 'part_id', b'part_id', 'session_id', b'session_id', 'tags', b'tags', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['binary_data_id', b'binary_data_id', 'file_id', b'file_id']) -> None: ... -global___UploadMetadata = UploadMetadata +global___StreamingDataCaptureUploadResponse = StreamingDataCaptureUploadResponse -class UploadRequest(google.protobuf.message.Message): +@typing.final +class SensorMetadata(google.protobuf.message.Message): + """SensorMetadata contains the time the sensor data was requested and was + received. + """ DESCRIPTOR: google.protobuf.descriptor.Descriptor - METADATA_FIELD_NUMBER: builtins.int - SENSOR_CONTENTS_FIELD_NUMBER: builtins.int - FILE_CONTENTS_FIELD_NUMBER: builtins.int + TIME_REQUESTED_FIELD_NUMBER: builtins.int + TIME_RECEIVED_FIELD_NUMBER: builtins.int + MIME_TYPE_FIELD_NUMBER: builtins.int + ANNOTATIONS_FIELD_NUMBER: builtins.int + mime_type: global___MimeType.ValueType @property - def metadata(self) -> global___UploadMetadata: + def time_requested(self) -> google.protobuf.timestamp_pb2.Timestamp: ... @property - def sensor_contents(self) -> global___SensorData: + def time_received(self) -> google.protobuf.timestamp_pb2.Timestamp: ... @property - def file_contents(self) -> global___FileData: + def annotations(self) -> app.data.v1.data_pb2.Annotations: ... - def __init__(self, *, metadata: global___UploadMetadata | None=..., sensor_contents: global___SensorData | None=..., file_contents: global___FileData | None=...) -> None: + def __init__(self, *, time_requested: google.protobuf.timestamp_pb2.Timestamp | None=..., time_received: google.protobuf.timestamp_pb2.Timestamp | None=..., mime_type: global___MimeType.ValueType=..., annotations: app.data.v1.data_pb2.Annotations | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['file_contents', b'file_contents', 'metadata', b'metadata', 'sensor_contents', b'sensor_contents', 'upload_packet', b'upload_packet']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['annotations', b'annotations', 'time_received', b'time_received', 'time_requested', b'time_requested']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['file_contents', b'file_contents', 'metadata', b'metadata', 'sensor_contents', b'sensor_contents', 'upload_packet', b'upload_packet']) -> None: + def ClearField(self, field_name: typing.Literal['annotations', b'annotations', 'mime_type', b'mime_type', 'time_received', b'time_received', 'time_requested', b'time_requested']) -> None: ... +global___SensorMetadata = SensorMetadata - def WhichOneof(self, oneof_group: typing_extensions.Literal['upload_packet', b'upload_packet']) -> typing_extensions.Literal['metadata', 'sensor_contents', 'file_contents'] | None: - ... -global___UploadRequest = UploadRequest - -class UploadResponse(google.protobuf.message.Message): +@typing.final +class SensorData(google.protobuf.message.Message): + """SensorData contains the contents and metadata for tabular data.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor - REQUESTS_WRITTEN_FIELD_NUMBER: builtins.int - requests_written: builtins.int + METADATA_FIELD_NUMBER: builtins.int + STRUCT_FIELD_NUMBER: builtins.int + BINARY_FIELD_NUMBER: builtins.int + binary: builtins.bytes - def __init__(self, *, requests_written: builtins.int=...) -> None: + @property + def metadata(self) -> global___SensorMetadata: ... - def ClearField(self, field_name: typing_extensions.Literal['requests_written', b'requests_written']) -> None: + @property + def struct(self) -> google.protobuf.struct_pb2.Struct: ... -global___UploadResponse = UploadResponse -class CaptureInterval(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - START_FIELD_NUMBER: builtins.int - END_FIELD_NUMBER: builtins.int + def __init__(self, *, metadata: global___SensorMetadata | None=..., struct: google.protobuf.struct_pb2.Struct | None=..., binary: builtins.bytes=...) -> None: + ... - @property - def start(self) -> google.protobuf.timestamp_pb2.Timestamp: + def HasField(self, field_name: typing.Literal['binary', b'binary', 'data', b'data', 'metadata', b'metadata', 'struct', b'struct']) -> builtins.bool: ... - @property - def end(self) -> google.protobuf.timestamp_pb2.Timestamp: + def ClearField(self, field_name: typing.Literal['binary', b'binary', 'data', b'data', 'metadata', b'metadata', 'struct', b'struct']) -> None: ... - def __init__(self, *, start: google.protobuf.timestamp_pb2.Timestamp | None=..., end: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + def WhichOneof(self, oneof_group: typing.Literal['data', b'data']) -> typing.Literal['struct', 'binary'] | None: ... +global___SensorData = SensorData + +@typing.final +class FileData(google.protobuf.message.Message): + """FileData contains the contents of binary (image + file) data.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + data: builtins.bytes - def HasField(self, field_name: typing_extensions.Literal['end', b'end', 'start', b'start']) -> builtins.bool: + def __init__(self, *, data: builtins.bytes=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['end', b'end', 'start', b'start']) -> None: + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: ... -global___CaptureInterval = CaptureInterval +global___FileData = FileData -class DataCaptureMetadata(google.protobuf.message.Message): +@typing.final +class UploadMetadata(google.protobuf.message.Message): + """UploadMetadata contains the metadata for binary (image + file) data.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor + @typing.final class MethodParametersEntry(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor KEY_FIELD_NUMBER: builtins.int @@ -240,124 +285,74 @@ class DataCaptureMetadata(google.protobuf.message.Message): def __init__(self, *, key: builtins.str=..., value: google.protobuf.any_pb2.Any | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['value', b'value']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: ... + PART_ID_FIELD_NUMBER: builtins.int COMPONENT_TYPE_FIELD_NUMBER: builtins.int COMPONENT_NAME_FIELD_NUMBER: builtins.int - COMPONENT_MODEL_FIELD_NUMBER: builtins.int METHOD_NAME_FIELD_NUMBER: builtins.int TYPE_FIELD_NUMBER: builtins.int + FILE_NAME_FIELD_NUMBER: builtins.int METHOD_PARAMETERS_FIELD_NUMBER: builtins.int FILE_EXTENSION_FIELD_NUMBER: builtins.int TAGS_FIELD_NUMBER: builtins.int - SESSION_ID_FIELD_NUMBER: builtins.int + part_id: builtins.str component_type: builtins.str component_name: builtins.str - component_model: builtins.str method_name: builtins.str type: global___DataType.ValueType + file_name: builtins.str + file_extension: builtins.str @property def method_parameters(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, google.protobuf.any_pb2.Any]: ... - file_extension: builtins.str @property def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... - session_id: builtins.str - def __init__(self, *, component_type: builtins.str=..., component_name: builtins.str=..., component_model: builtins.str=..., method_name: builtins.str=..., type: global___DataType.ValueType=..., method_parameters: collections.abc.Mapping[builtins.str, google.protobuf.any_pb2.Any] | None=..., file_extension: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=..., session_id: builtins.str=...) -> None: + def __init__(self, *, part_id: builtins.str=..., component_type: builtins.str=..., component_name: builtins.str=..., method_name: builtins.str=..., type: global___DataType.ValueType=..., file_name: builtins.str=..., method_parameters: collections.abc.Mapping[builtins.str, google.protobuf.any_pb2.Any] | None=..., file_extension: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['component_model', b'component_model', 'component_name', b'component_name', 'component_type', b'component_type', 'file_extension', b'file_extension', 'method_name', b'method_name', 'method_parameters', b'method_parameters', 'session_id', b'session_id', 'tags', b'tags', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['component_name', b'component_name', 'component_type', b'component_type', 'file_extension', b'file_extension', 'file_name', b'file_name', 'method_name', b'method_name', 'method_parameters', b'method_parameters', 'part_id', b'part_id', 'tags', b'tags', 'type', b'type']) -> None: ... -global___DataCaptureMetadata = DataCaptureMetadata +global___UploadMetadata = UploadMetadata -class TabularCapture(google.protobuf.message.Message): +@typing.final +class CaptureInterval(google.protobuf.message.Message): + """CaptureInterval specifies the start and end times of the data capture.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor - - class MethodParametersEntry(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - KEY_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - key: builtins.str - - @property - def value(self) -> google.protobuf.any_pb2.Any: - ... - - def __init__(self, *, key: builtins.str=..., value: google.protobuf.any_pb2.Any | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: - ... - INTERVAL_FIELD_NUMBER: builtins.int - ORG_ID_FIELD_NUMBER: builtins.int - ROBOT_ID_FIELD_NUMBER: builtins.int - PART_ID_FIELD_NUMBER: builtins.int - LOCATION_ID_FIELD_NUMBER: builtins.int - COMPONENT_NAME_FIELD_NUMBER: builtins.int - COMPONENT_TYPE_FIELD_NUMBER: builtins.int - COMPONENT_MODEL_FIELD_NUMBER: builtins.int - METHOD_NAME_FIELD_NUMBER: builtins.int - BLOB_PATH_FIELD_NUMBER: builtins.int - COLUMN_NAMES_FIELD_NUMBER: builtins.int - METHOD_PARAMETERS_FIELD_NUMBER: builtins.int - FILE_ID_FIELD_NUMBER: builtins.int - TAGS_FIELD_NUMBER: builtins.int - MESSAGE_COUNT_FIELD_NUMBER: builtins.int - FILE_SIZE_BYTES_FIELD_NUMBER: builtins.int - SESSION_ID_FIELD_NUMBER: builtins.int - - @property - def interval(self) -> global___CaptureInterval: - ... - org_id: builtins.str - robot_id: builtins.str - part_id: builtins.str - location_id: builtins.str - component_name: builtins.str - component_type: builtins.str - component_model: builtins.str - method_name: builtins.str - blob_path: builtins.str - - @property - def column_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: - ... + START_FIELD_NUMBER: builtins.int + END_FIELD_NUMBER: builtins.int @property - def method_parameters(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, google.protobuf.any_pb2.Any]: + def start(self) -> google.protobuf.timestamp_pb2.Timestamp: ... - file_id: builtins.str @property - def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + def end(self) -> google.protobuf.timestamp_pb2.Timestamp: ... - message_count: builtins.int - file_size_bytes: builtins.int - session_id: builtins.str - def __init__(self, *, interval: global___CaptureInterval | None=..., org_id: builtins.str=..., robot_id: builtins.str=..., part_id: builtins.str=..., location_id: builtins.str=..., component_name: builtins.str=..., component_type: builtins.str=..., component_model: builtins.str=..., method_name: builtins.str=..., blob_path: builtins.str=..., column_names: collections.abc.Iterable[builtins.str] | None=..., method_parameters: collections.abc.Mapping[builtins.str, google.protobuf.any_pb2.Any] | None=..., file_id: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=..., message_count: builtins.int=..., file_size_bytes: builtins.int=..., session_id: builtins.str=...) -> None: + def __init__(self, *, start: google.protobuf.timestamp_pb2.Timestamp | None=..., end: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['interval', b'interval']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['end', b'end', 'start', b'start']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['blob_path', b'blob_path', 'column_names', b'column_names', 'component_model', b'component_model', 'component_name', b'component_name', 'component_type', b'component_type', 'file_id', b'file_id', 'file_size_bytes', b'file_size_bytes', 'interval', b'interval', 'location_id', b'location_id', 'message_count', b'message_count', 'method_name', b'method_name', 'method_parameters', b'method_parameters', 'org_id', b'org_id', 'part_id', b'part_id', 'robot_id', b'robot_id', 'session_id', b'session_id', 'tags', b'tags']) -> None: + def ClearField(self, field_name: typing.Literal['end', b'end', 'start', b'start']) -> None: ... -global___TabularCapture = TabularCapture +global___CaptureInterval = CaptureInterval -class BinaryCapture(google.protobuf.message.Message): +@typing.final +class DataCaptureMetadata(google.protobuf.message.Message): + """DataCaptureMetadata contains the metadata for data captured by collectors.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor + @typing.final class MethodParametersEntry(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor KEY_FIELD_NUMBER: builtins.int @@ -371,113 +366,60 @@ class BinaryCapture(google.protobuf.message.Message): def __init__(self, *, key: builtins.str=..., value: google.protobuf.any_pb2.Any | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['value', b'value']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: ... - INTERVAL_FIELD_NUMBER: builtins.int - ORG_ID_FIELD_NUMBER: builtins.int - ROBOT_ID_FIELD_NUMBER: builtins.int - PART_ID_FIELD_NUMBER: builtins.int - LOCATION_ID_FIELD_NUMBER: builtins.int - COMPONENT_NAME_FIELD_NUMBER: builtins.int COMPONENT_TYPE_FIELD_NUMBER: builtins.int - COMPONENT_MODEL_FIELD_NUMBER: builtins.int + COMPONENT_NAME_FIELD_NUMBER: builtins.int METHOD_NAME_FIELD_NUMBER: builtins.int - BLOB_PATH_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int METHOD_PARAMETERS_FIELD_NUMBER: builtins.int - FILE_ID_FIELD_NUMBER: builtins.int + FILE_EXTENSION_FIELD_NUMBER: builtins.int TAGS_FIELD_NUMBER: builtins.int - FILE_SIZE_BYTES_FIELD_NUMBER: builtins.int - SESSION_ID_FIELD_NUMBER: builtins.int - - @property - def interval(self) -> global___CaptureInterval: - ... - org_id: builtins.str - robot_id: builtins.str - part_id: builtins.str - location_id: builtins.str - component_name: builtins.str component_type: builtins.str - component_model: builtins.str + component_name: builtins.str method_name: builtins.str - blob_path: builtins.str + type: global___DataType.ValueType + file_extension: builtins.str @property def method_parameters(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, google.protobuf.any_pb2.Any]: ... - file_id: builtins.str @property def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... - file_size_bytes: builtins.int - session_id: builtins.str - - def __init__(self, *, interval: global___CaptureInterval | None=..., org_id: builtins.str=..., robot_id: builtins.str=..., part_id: builtins.str=..., location_id: builtins.str=..., component_name: builtins.str=..., component_type: builtins.str=..., component_model: builtins.str=..., method_name: builtins.str=..., blob_path: builtins.str=..., method_parameters: collections.abc.Mapping[builtins.str, google.protobuf.any_pb2.Any] | None=..., file_id: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=..., file_size_bytes: builtins.int=..., session_id: builtins.str=...) -> None: - ... - def HasField(self, field_name: typing_extensions.Literal['interval', b'interval']) -> builtins.bool: + def __init__(self, *, component_type: builtins.str=..., component_name: builtins.str=..., method_name: builtins.str=..., type: global___DataType.ValueType=..., method_parameters: collections.abc.Mapping[builtins.str, google.protobuf.any_pb2.Any] | None=..., file_extension: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['blob_path', b'blob_path', 'component_model', b'component_model', 'component_name', b'component_name', 'component_type', b'component_type', 'file_id', b'file_id', 'file_size_bytes', b'file_size_bytes', 'interval', b'interval', 'location_id', b'location_id', 'method_name', b'method_name', 'method_parameters', b'method_parameters', 'org_id', b'org_id', 'part_id', b'part_id', 'robot_id', b'robot_id', 'session_id', b'session_id', 'tags', b'tags']) -> None: + def ClearField(self, field_name: typing.Literal['component_name', b'component_name', 'component_type', b'component_type', 'file_extension', b'file_extension', 'method_name', b'method_name', 'method_parameters', b'method_parameters', 'tags', b'tags', 'type', b'type']) -> None: ... -global___BinaryCapture = BinaryCapture +global___DataCaptureMetadata = DataCaptureMetadata -class UserFile(google.protobuf.message.Message): +@typing.final +class DataCaptureUploadMetadata(google.protobuf.message.Message): + """DataCaptureUploadMetadata contains the metadata for streaming binary (image + file) data.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor - - class MethodParametersEntry(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - KEY_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - key: builtins.str - - @property - def value(self) -> google.protobuf.any_pb2.Any: - ... - - def __init__(self, *, key: builtins.str=..., value: google.protobuf.any_pb2.Any | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: - ... - SYNC_TIME_FIELD_NUMBER: builtins.int - ORG_ID_FIELD_NUMBER: builtins.int - ROBOT_ID_FIELD_NUMBER: builtins.int - PART_ID_FIELD_NUMBER: builtins.int - LOCATION_ID_FIELD_NUMBER: builtins.int - BLOB_PATH_FIELD_NUMBER: builtins.int - METHOD_PARAMETERS_FIELD_NUMBER: builtins.int - FILE_ID_FIELD_NUMBER: builtins.int - FILE_SIZE_BYTES_FIELD_NUMBER: builtins.int + UPLOAD_METADATA_FIELD_NUMBER: builtins.int + SENSOR_METADATA_FIELD_NUMBER: builtins.int @property - def sync_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + def upload_metadata(self) -> global___UploadMetadata: ... - org_id: builtins.str - robot_id: builtins.str - part_id: builtins.str - location_id: builtins.str - blob_path: builtins.str @property - def method_parameters(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, google.protobuf.any_pb2.Any]: + def sensor_metadata(self) -> global___SensorMetadata: ... - file_id: builtins.str - file_size_bytes: builtins.int - def __init__(self, *, sync_time: google.protobuf.timestamp_pb2.Timestamp | None=..., org_id: builtins.str=..., robot_id: builtins.str=..., part_id: builtins.str=..., location_id: builtins.str=..., blob_path: builtins.str=..., method_parameters: collections.abc.Mapping[builtins.str, google.protobuf.any_pb2.Any] | None=..., file_id: builtins.str=..., file_size_bytes: builtins.int=...) -> None: + def __init__(self, *, upload_metadata: global___UploadMetadata | None=..., sensor_metadata: global___SensorMetadata | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['sync_time', b'sync_time']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['sensor_metadata', b'sensor_metadata', 'upload_metadata', b'upload_metadata']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['blob_path', b'blob_path', 'file_id', b'file_id', 'file_size_bytes', b'file_size_bytes', 'location_id', b'location_id', 'method_parameters', b'method_parameters', 'org_id', b'org_id', 'part_id', b'part_id', 'robot_id', b'robot_id', 'sync_time', b'sync_time']) -> None: + def ClearField(self, field_name: typing.Literal['sensor_metadata', b'sensor_metadata', 'upload_metadata', b'upload_metadata']) -> None: ... -global___UserFile = UserFile \ No newline at end of file +global___DataCaptureUploadMetadata = DataCaptureUploadMetadata \ No newline at end of file diff --git a/src/viam/gen/app/mlinference/__init__.py b/src/viam/gen/app/mlinference/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/mlinference/v1/__init__.py b/src/viam/gen/app/mlinference/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/mlinference/v1/ml_inference_grpc.py b/src/viam/gen/app/mlinference/v1/ml_inference_grpc.py new file mode 100644 index 000000000..957ed23e1 --- /dev/null +++ b/src/viam/gen/app/mlinference/v1/ml_inference_grpc.py @@ -0,0 +1,28 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from .... import app +from .... import service + +class MLInferenceServiceBase(abc.ABC): + + @abc.abstractmethod + async def GetInference(self, stream: 'grpclib.server.Stream[app.mlinference.v1.ml_inference_pb2.GetInferenceRequest, app.mlinference.v1.ml_inference_pb2.GetInferenceResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.app.mlinference.v1.MLInferenceService/GetInference': grpclib.const.Handler(self.GetInference, grpclib.const.Cardinality.UNARY_UNARY, app.mlinference.v1.ml_inference_pb2.GetInferenceRequest, app.mlinference.v1.ml_inference_pb2.GetInferenceResponse)} + +class UnimplementedMLInferenceServiceBase(MLInferenceServiceBase): + + async def GetInference(self, stream: 'grpclib.server.Stream[app.mlinference.v1.ml_inference_pb2.GetInferenceRequest, app.mlinference.v1.ml_inference_pb2.GetInferenceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class MLInferenceServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.GetInference = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.mlinference.v1.MLInferenceService/GetInference', app.mlinference.v1.ml_inference_pb2.GetInferenceRequest, app.mlinference.v1.ml_inference_pb2.GetInferenceResponse) \ No newline at end of file diff --git a/src/viam/gen/app/mlinference/v1/ml_inference_pb2.py b/src/viam/gen/app/mlinference/v1/ml_inference_pb2.py new file mode 100644 index 000000000..e1f8780c9 --- /dev/null +++ b/src/viam/gen/app/mlinference/v1/ml_inference_pb2.py @@ -0,0 +1,23 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/mlinference/v1/ml_inference.proto') +_sym_db = _symbol_database.Default() +from ....app.data.v1 import data_pb2 as app_dot_data_dot_v1_dot_data__pb2 +from ....service.mlmodel.v1 import mlmodel_pb2 as service_dot_mlmodel_dot_v1_dot_mlmodel__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%app/mlinference/v1/ml_inference.proto\x12\x17viam.app.mlinference.v1\x1a\x16app/data/v1/data.proto\x1a service/mlmodel/v1/mlmodel.proto"\xfb\x01\n\x13GetInferenceRequest\x12(\n\x10registry_item_id\x18\x01 \x01(\tR\x0eregistryItemId\x122\n\x15registry_item_version\x18\x02 \x01(\tR\x13registryItemVersion\x127\n\tbinary_id\x18\x03 \x01(\x0b2\x1a.viam.app.data.v1.BinaryIDR\x08binaryId\x12$\n\x0ebinary_data_id\x18\x05 \x01(\tR\x0cbinaryDataId\x12\'\n\x0forganization_id\x18\x04 \x01(\tR\x0eorganizationId"\xa4\x01\n\x14GetInferenceResponse\x12K\n\x0eoutput_tensors\x18\x01 \x01(\x0b2$.viam.service.mlmodel.v1.FlatTensorsR\routputTensors\x12?\n\x0bannotations\x18\x02 \x01(\x0b2\x1d.viam.app.data.v1.AnnotationsR\x0bannotations2\x81\x01\n\x12MLInferenceService\x12k\n\x0cGetInference\x12,.viam.app.mlinference.v1.GetInferenceRequest\x1a-.viam.app.mlinference.v1.GetInferenceResponseB$Z"go.viam.com/api/app/mlinference/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.mlinference.v1.ml_inference_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z"go.viam.com/api/app/mlinference/v1' + _globals['_GETINFERENCEREQUEST']._serialized_start = 125 + _globals['_GETINFERENCEREQUEST']._serialized_end = 376 + _globals['_GETINFERENCERESPONSE']._serialized_start = 379 + _globals['_GETINFERENCERESPONSE']._serialized_end = 543 + _globals['_MLINFERENCESERVICE']._serialized_start = 546 + _globals['_MLINFERENCESERVICE']._serialized_end = 675 \ No newline at end of file diff --git a/src/viam/gen/app/mlinference/v1/ml_inference_pb2.pyi b/src/viam/gen/app/mlinference/v1/ml_inference_pb2.pyi new file mode 100644 index 000000000..b154ca9d2 --- /dev/null +++ b/src/viam/gen/app/mlinference/v1/ml_inference_pb2.pyi @@ -0,0 +1,63 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +from .... import app +import builtins +import google.protobuf.descriptor +import google.protobuf.message +from .... import service +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class GetInferenceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + REGISTRY_ITEM_ID_FIELD_NUMBER: builtins.int + REGISTRY_ITEM_VERSION_FIELD_NUMBER: builtins.int + BINARY_ID_FIELD_NUMBER: builtins.int + BINARY_DATA_ID_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + registry_item_id: builtins.str + 'The model framework and model type are inferred from the ML model registry item;\n For valid model types (classification, detections) we will return the formatted\n labels or annotations from the associated inference outputs.\n ' + registry_item_version: builtins.str + binary_data_id: builtins.str + organization_id: builtins.str + + @property + def binary_id(self) -> app.data.v1.data_pb2.BinaryID: + ... + + def __init__(self, *, registry_item_id: builtins.str=..., registry_item_version: builtins.str=..., binary_id: app.data.v1.data_pb2.BinaryID | None=..., binary_data_id: builtins.str=..., organization_id: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['binary_id', b'binary_id']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['binary_data_id', b'binary_data_id', 'binary_id', b'binary_id', 'organization_id', b'organization_id', 'registry_item_id', b'registry_item_id', 'registry_item_version', b'registry_item_version']) -> None: + ... +global___GetInferenceRequest = GetInferenceRequest + +@typing.final +class GetInferenceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + OUTPUT_TENSORS_FIELD_NUMBER: builtins.int + ANNOTATIONS_FIELD_NUMBER: builtins.int + + @property + def output_tensors(self) -> service.mlmodel.v1.mlmodel_pb2.FlatTensors: + ... + + @property + def annotations(self) -> app.data.v1.data_pb2.Annotations: + ... + + def __init__(self, *, output_tensors: service.mlmodel.v1.mlmodel_pb2.FlatTensors | None=..., annotations: app.data.v1.data_pb2.Annotations | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['annotations', b'annotations', 'output_tensors', b'output_tensors']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['annotations', b'annotations', 'output_tensors', b'output_tensors']) -> None: + ... +global___GetInferenceResponse = GetInferenceResponse \ No newline at end of file diff --git a/src/viam/gen/app/mltraining/__init__.py b/src/viam/gen/app/mltraining/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/mltraining/v1/__init__.py b/src/viam/gen/app/mltraining/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/mltraining/v1/ml_training_grpc.py b/src/viam/gen/app/mltraining/v1/ml_training_grpc.py new file mode 100644 index 000000000..16ea2ffbd --- /dev/null +++ b/src/viam/gen/app/mltraining/v1/ml_training_grpc.py @@ -0,0 +1,78 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +import google.protobuf.timestamp_pb2 +import google.rpc.status_pb2 +from .... import tagger +from .... import app + +class MLTrainingServiceBase(abc.ABC): + + @abc.abstractmethod + async def SubmitTrainingJob(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.SubmitTrainingJobRequest, app.mltraining.v1.ml_training_pb2.SubmitTrainingJobResponse]') -> None: + pass + + @abc.abstractmethod + async def SubmitCustomTrainingJob(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.SubmitCustomTrainingJobRequest, app.mltraining.v1.ml_training_pb2.SubmitCustomTrainingJobResponse]') -> None: + pass + + @abc.abstractmethod + async def GetTrainingJob(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.GetTrainingJobRequest, app.mltraining.v1.ml_training_pb2.GetTrainingJobResponse]') -> None: + pass + + @abc.abstractmethod + async def ListTrainingJobs(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.ListTrainingJobsRequest, app.mltraining.v1.ml_training_pb2.ListTrainingJobsResponse]') -> None: + pass + + @abc.abstractmethod + async def CancelTrainingJob(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.CancelTrainingJobRequest, app.mltraining.v1.ml_training_pb2.CancelTrainingJobResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteCompletedTrainingJob(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.DeleteCompletedTrainingJobRequest, app.mltraining.v1.ml_training_pb2.DeleteCompletedTrainingJobResponse]') -> None: + pass + + @abc.abstractmethod + async def GetTrainingJobLogs(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.GetTrainingJobLogsRequest, app.mltraining.v1.ml_training_pb2.GetTrainingJobLogsResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.app.mltraining.v1.MLTrainingService/SubmitTrainingJob': grpclib.const.Handler(self.SubmitTrainingJob, grpclib.const.Cardinality.UNARY_UNARY, app.mltraining.v1.ml_training_pb2.SubmitTrainingJobRequest, app.mltraining.v1.ml_training_pb2.SubmitTrainingJobResponse), '/viam.app.mltraining.v1.MLTrainingService/SubmitCustomTrainingJob': grpclib.const.Handler(self.SubmitCustomTrainingJob, grpclib.const.Cardinality.UNARY_UNARY, app.mltraining.v1.ml_training_pb2.SubmitCustomTrainingJobRequest, app.mltraining.v1.ml_training_pb2.SubmitCustomTrainingJobResponse), '/viam.app.mltraining.v1.MLTrainingService/GetTrainingJob': grpclib.const.Handler(self.GetTrainingJob, grpclib.const.Cardinality.UNARY_UNARY, app.mltraining.v1.ml_training_pb2.GetTrainingJobRequest, app.mltraining.v1.ml_training_pb2.GetTrainingJobResponse), '/viam.app.mltraining.v1.MLTrainingService/ListTrainingJobs': grpclib.const.Handler(self.ListTrainingJobs, grpclib.const.Cardinality.UNARY_UNARY, app.mltraining.v1.ml_training_pb2.ListTrainingJobsRequest, app.mltraining.v1.ml_training_pb2.ListTrainingJobsResponse), '/viam.app.mltraining.v1.MLTrainingService/CancelTrainingJob': grpclib.const.Handler(self.CancelTrainingJob, grpclib.const.Cardinality.UNARY_UNARY, app.mltraining.v1.ml_training_pb2.CancelTrainingJobRequest, app.mltraining.v1.ml_training_pb2.CancelTrainingJobResponse), '/viam.app.mltraining.v1.MLTrainingService/DeleteCompletedTrainingJob': grpclib.const.Handler(self.DeleteCompletedTrainingJob, grpclib.const.Cardinality.UNARY_UNARY, app.mltraining.v1.ml_training_pb2.DeleteCompletedTrainingJobRequest, app.mltraining.v1.ml_training_pb2.DeleteCompletedTrainingJobResponse), '/viam.app.mltraining.v1.MLTrainingService/GetTrainingJobLogs': grpclib.const.Handler(self.GetTrainingJobLogs, grpclib.const.Cardinality.UNARY_UNARY, app.mltraining.v1.ml_training_pb2.GetTrainingJobLogsRequest, app.mltraining.v1.ml_training_pb2.GetTrainingJobLogsResponse)} + +class UnimplementedMLTrainingServiceBase(MLTrainingServiceBase): + + async def SubmitTrainingJob(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.SubmitTrainingJobRequest, app.mltraining.v1.ml_training_pb2.SubmitTrainingJobResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SubmitCustomTrainingJob(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.SubmitCustomTrainingJobRequest, app.mltraining.v1.ml_training_pb2.SubmitCustomTrainingJobResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetTrainingJob(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.GetTrainingJobRequest, app.mltraining.v1.ml_training_pb2.GetTrainingJobResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListTrainingJobs(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.ListTrainingJobsRequest, app.mltraining.v1.ml_training_pb2.ListTrainingJobsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CancelTrainingJob(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.CancelTrainingJobRequest, app.mltraining.v1.ml_training_pb2.CancelTrainingJobResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteCompletedTrainingJob(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.DeleteCompletedTrainingJobRequest, app.mltraining.v1.ml_training_pb2.DeleteCompletedTrainingJobResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetTrainingJobLogs(self, stream: 'grpclib.server.Stream[app.mltraining.v1.ml_training_pb2.GetTrainingJobLogsRequest, app.mltraining.v1.ml_training_pb2.GetTrainingJobLogsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class MLTrainingServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.SubmitTrainingJob = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.mltraining.v1.MLTrainingService/SubmitTrainingJob', app.mltraining.v1.ml_training_pb2.SubmitTrainingJobRequest, app.mltraining.v1.ml_training_pb2.SubmitTrainingJobResponse) + self.SubmitCustomTrainingJob = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.mltraining.v1.MLTrainingService/SubmitCustomTrainingJob', app.mltraining.v1.ml_training_pb2.SubmitCustomTrainingJobRequest, app.mltraining.v1.ml_training_pb2.SubmitCustomTrainingJobResponse) + self.GetTrainingJob = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.mltraining.v1.MLTrainingService/GetTrainingJob', app.mltraining.v1.ml_training_pb2.GetTrainingJobRequest, app.mltraining.v1.ml_training_pb2.GetTrainingJobResponse) + self.ListTrainingJobs = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.mltraining.v1.MLTrainingService/ListTrainingJobs', app.mltraining.v1.ml_training_pb2.ListTrainingJobsRequest, app.mltraining.v1.ml_training_pb2.ListTrainingJobsResponse) + self.CancelTrainingJob = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.mltraining.v1.MLTrainingService/CancelTrainingJob', app.mltraining.v1.ml_training_pb2.CancelTrainingJobRequest, app.mltraining.v1.ml_training_pb2.CancelTrainingJobResponse) + self.DeleteCompletedTrainingJob = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.mltraining.v1.MLTrainingService/DeleteCompletedTrainingJob', app.mltraining.v1.ml_training_pb2.DeleteCompletedTrainingJobRequest, app.mltraining.v1.ml_training_pb2.DeleteCompletedTrainingJobResponse) + self.GetTrainingJobLogs = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.mltraining.v1.MLTrainingService/GetTrainingJobLogs', app.mltraining.v1.ml_training_pb2.GetTrainingJobLogsRequest, app.mltraining.v1.ml_training_pb2.GetTrainingJobLogsResponse) \ No newline at end of file diff --git a/src/viam/gen/app/mltraining/v1/ml_training_pb2.py b/src/viam/gen/app/mltraining/v1/ml_training_pb2.py new file mode 100644 index 000000000..6b2aaa6f8 --- /dev/null +++ b/src/viam/gen/app/mltraining/v1/ml_training_pb2.py @@ -0,0 +1,124 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/mltraining/v1/ml_training.proto') +_sym_db = _symbol_database.Default() +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from google.rpc import status_pb2 as google_dot_rpc_dot_status__pb2 +from ....tagger.v1 import tagger_pb2 as tagger_dot_v1_dot_tagger__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#app/mltraining/v1/ml_training.proto\x12\x16viam.app.mltraining.v1\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x17google/rpc/status.proto\x1a\x16tagger/v1/tagger.proto"\x90\x05\n\x18SubmitTrainingJobRequest\x12G\n\ndataset_id\x18\x07 \x01(\tB(\x9a\x84\x9e\x03#bson:"dataset_id" json:"dataset_id"R\tdatasetId\x12[\n\x0forganization_id\x18\x02 \x01(\tB2\x9a\x84\x9e\x03-bson:"organization_id" json:"organization_id"R\x0eorganizationId\x12G\n\nmodel_name\x18\x03 \x01(\tB(\x9a\x84\x9e\x03#bson:"model_name" json:"model_name"R\tmodelName\x12S\n\rmodel_version\x18\x04 \x01(\tB.\x9a\x84\x9e\x03)bson:"model_version" json:"model_version"R\x0cmodelVersion\x12j\n\nmodel_type\x18\x05 \x01(\x0e2!.viam.app.mltraining.v1.ModelTypeB(\x9a\x84\x9e\x03#bson:"model_type" json:"model_type"R\tmodelType\x12\x83\x01\n\x0fmodel_framework\x18\x08 \x01(\x0e2&.viam.app.mltraining.v1.ModelFrameworkB2\x9a\x84\x9e\x03-bson:"model_framework" json:"model_framework"R\x0emodelFramework\x120\n\x04tags\x18\x06 \x03(\tB\x1c\x9a\x84\x9e\x03\x17bson:"tags" json:"tags"R\x04tagsJ\x04\x08\x01\x10\x02R\x06filter"+\n\x19SubmitTrainingJobResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\xdb\x05\n\x1eSubmitCustomTrainingJobRequest\x12G\n\ndataset_id\x18\x01 \x01(\tB(\x9a\x84\x9e\x03#bson:"dataset_id" json:"dataset_id"R\tdatasetId\x12^\n\x10registry_item_id\x18\x02 \x01(\tB4\x9a\x84\x9e\x03/bson:"registry_item_id" json:"registry_item_id"R\x0eregistryItemId\x12r\n\x15registry_item_version\x18\x06 \x01(\tB>\x9a\x84\x9e\x039bson:"registry_item_version" json:"registry_item_version"R\x13registryItemVersion\x12[\n\x0forganization_id\x18\x03 \x01(\tB2\x9a\x84\x9e\x03-bson:"organization_id" json:"organization_id"R\x0eorganizationId\x12G\n\nmodel_name\x18\x04 \x01(\tB(\x9a\x84\x9e\x03#bson:"model_name" json:"model_name"R\tmodelName\x12S\n\rmodel_version\x18\x05 \x01(\tB.\x9a\x84\x9e\x03)bson:"model_version" json:"model_version"R\x0cmodelVersion\x12c\n\targuments\x18\x07 \x03(\x0b2E.viam.app.mltraining.v1.SubmitCustomTrainingJobRequest.ArgumentsEntryR\targuments\x1a<\n\x0eArgumentsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x028\x01"1\n\x1fSubmitCustomTrainingJobResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\'\n\x15GetTrainingJobRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"a\n\x16GetTrainingJobResponse\x12G\n\x08metadata\x18\x01 \x01(\x0b2+.viam.app.mltraining.v1.TrainingJobMetadataR\x08metadata"\x82\x01\n\x17ListTrainingJobsRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12>\n\x06status\x18\x02 \x01(\x0e2&.viam.app.mltraining.v1.TrainingStatusR\x06status"[\n\x18ListTrainingJobsResponse\x12?\n\x04jobs\x18\x01 \x03(\x0b2+.viam.app.mltraining.v1.TrainingJobMetadataR\x04jobs"\xe6\r\n\x13TrainingJobMetadata\x123\n\x02id\x18\x07 \x01(\tB#\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"R\x02id\x12G\n\ndataset_id\x18\x0b \x01(\tB(\x9a\x84\x9e\x03#bson:"dataset_id" json:"dataset_id"R\tdatasetId\x12[\n\x0forganization_id\x18\x0c \x01(\tB2\x9a\x84\x9e\x03-bson:"organization_id" json:"organization_id"R\x0eorganizationId\x12G\n\nmodel_name\x18\r \x01(\tB(\x9a\x84\x9e\x03#bson:"model_name" json:"model_name"R\tmodelName\x12S\n\rmodel_version\x18\x0e \x01(\tB.\x9a\x84\x9e\x03)bson:"model_version" json:"model_version"R\x0cmodelVersion\x12j\n\nmodel_type\x18\x0f \x01(\x0e2!.viam.app.mltraining.v1.ModelTypeB(\x9a\x84\x9e\x03#bson:"model_type" json:"model_type"R\tmodelType\x12\x83\x01\n\x0fmodel_framework\x18\x11 \x01(\x0e2&.viam.app.mltraining.v1.ModelFrameworkB2\x9a\x84\x9e\x03-bson:"model_framework" json:"model_framework"R\x0emodelFramework\x12R\n\ris_custom_job\x18\x12 \x01(\x08B.\x9a\x84\x9e\x03)bson:"is_custom_job" json:"is_custom_job"R\x0bisCustomJob\x12^\n\x10registry_item_id\x18\x13 \x01(\tB4\x9a\x84\x9e\x03/bson:"registry_item_id" json:"registry_item_id"R\x0eregistryItemId\x12r\n\x15registry_item_version\x18\x14 \x01(\tB>\x9a\x84\x9e\x039bson:"registry_item_version" json:"registry_item_version"R\x13registryItemVersion\x12`\n\x06status\x18\x02 \x01(\x0e2&.viam.app.mltraining.v1.TrainingStatusB \x9a\x84\x9e\x03\x1bbson:"status" json:"status"R\x06status\x12c\n\x0cerror_status\x18\x08 \x01(\x0b2\x12.google.rpc.StatusB,\x9a\x84\x9e\x03\'bson:"error_status" json:"error_status"R\x0berrorStatus\x12c\n\ncreated_on\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampB(\x9a\x84\x9e\x03#bson:"created_on" json:"created_on"R\tcreatedOn\x12o\n\rlast_modified\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampB.\x9a\x84\x9e\x03)bson:"last_modified" json:"last_modified"R\x0clastModified\x12{\n\x10training_started\x18\t \x01(\x0b2\x1a.google.protobuf.TimestampB4\x9a\x84\x9e\x03/bson:"training_started" json:"training_started"R\x0ftrainingStarted\x12s\n\x0etraining_ended\x18\n \x01(\x0b2\x1a.google.protobuf.TimestampB0\x9a\x84\x9e\x03+bson:"training_ended" json:"training_ended"R\rtrainingEnded\x12Z\n\x0fsynced_model_id\x18\x05 \x01(\tB2\x9a\x84\x9e\x03-bson:"synced_model_id" json:"synced_model_id"R\rsyncedModelId\x120\n\x04tags\x18\x10 \x03(\tB\x1c\x9a\x84\x9e\x03\x17bson:"tags" json:"tags"R\x04tagsJ\x04\x08\x01\x10\x02J\x04\x08\x06\x10\x07R\x07requestR\nuser_email"*\n\x18CancelTrainingJobRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x1b\n\x19CancelTrainingJobResponse"3\n!DeleteCompletedTrainingJobRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"$\n"DeleteCompletedTrainingJobResponse"u\n\x13TrainingJobLogEntry\x12\x14\n\x05level\x18\x01 \x01(\tR\x05level\x12.\n\x04time\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x04time\x12\x18\n\x07message\x18\x03 \x01(\tR\x07message"^\n\x19GetTrainingJobLogsRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12"\n\npage_token\x18\x02 \x01(\tH\x00R\tpageToken\x88\x01\x01B\r\n\x0b_page_token"\x85\x01\n\x1aGetTrainingJobLogsResponse\x12?\n\x04logs\x18\x01 \x03(\x0b2+.viam.app.mltraining.v1.TrainingJobLogEntryR\x04logs\x12&\n\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken*\x9f\x01\n\tModelType\x12\x1a\n\x16MODEL_TYPE_UNSPECIFIED\x10\x00\x12*\n&MODEL_TYPE_SINGLE_LABEL_CLASSIFICATION\x10\x01\x12)\n%MODEL_TYPE_MULTI_LABEL_CLASSIFICATION\x10\x02\x12\x1f\n\x1bMODEL_TYPE_OBJECT_DETECTION\x10\x03*\xa4\x01\n\x0eModelFramework\x12\x1f\n\x1bMODEL_FRAMEWORK_UNSPECIFIED\x10\x00\x12\x1a\n\x16MODEL_FRAMEWORK_TFLITE\x10\x01\x12\x1e\n\x1aMODEL_FRAMEWORK_TENSORFLOW\x10\x02\x12\x1b\n\x17MODEL_FRAMEWORK_PYTORCH\x10\x03\x12\x18\n\x14MODEL_FRAMEWORK_ONNX\x10\x04*\xe7\x01\n\x0eTrainingStatus\x12\x1f\n\x1bTRAINING_STATUS_UNSPECIFIED\x10\x00\x12\x1b\n\x17TRAINING_STATUS_PENDING\x10\x01\x12\x1f\n\x1bTRAINING_STATUS_IN_PROGRESS\x10\x02\x12\x1d\n\x19TRAINING_STATUS_COMPLETED\x10\x03\x12\x1a\n\x16TRAINING_STATUS_FAILED\x10\x04\x12\x1c\n\x18TRAINING_STATUS_CANCELED\x10\x05\x12\x1d\n\x19TRAINING_STATUS_CANCELING\x10\x062\x8f\x07\n\x11MLTrainingService\x12x\n\x11SubmitTrainingJob\x120.viam.app.mltraining.v1.SubmitTrainingJobRequest\x1a1.viam.app.mltraining.v1.SubmitTrainingJobResponse\x12\x8a\x01\n\x17SubmitCustomTrainingJob\x126.viam.app.mltraining.v1.SubmitCustomTrainingJobRequest\x1a7.viam.app.mltraining.v1.SubmitCustomTrainingJobResponse\x12o\n\x0eGetTrainingJob\x12-.viam.app.mltraining.v1.GetTrainingJobRequest\x1a..viam.app.mltraining.v1.GetTrainingJobResponse\x12u\n\x10ListTrainingJobs\x12/.viam.app.mltraining.v1.ListTrainingJobsRequest\x1a0.viam.app.mltraining.v1.ListTrainingJobsResponse\x12x\n\x11CancelTrainingJob\x120.viam.app.mltraining.v1.CancelTrainingJobRequest\x1a1.viam.app.mltraining.v1.CancelTrainingJobResponse\x12\x93\x01\n\x1aDeleteCompletedTrainingJob\x129.viam.app.mltraining.v1.DeleteCompletedTrainingJobRequest\x1a:.viam.app.mltraining.v1.DeleteCompletedTrainingJobResponse\x12{\n\x12GetTrainingJobLogs\x121.viam.app.mltraining.v1.GetTrainingJobLogsRequest\x1a2.viam.app.mltraining.v1.GetTrainingJobLogsResponseB#Z!go.viam.com/api/app/mltraining/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.mltraining.v1.ml_training_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z!go.viam.com/api/app/mltraining/v1' + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['dataset_id']._loaded_options = None + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['dataset_id']._serialized_options = b'\x9a\x84\x9e\x03#bson:"dataset_id" json:"dataset_id"' + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['organization_id']._loaded_options = None + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['organization_id']._serialized_options = b'\x9a\x84\x9e\x03-bson:"organization_id" json:"organization_id"' + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['model_name']._loaded_options = None + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['model_name']._serialized_options = b'\x9a\x84\x9e\x03#bson:"model_name" json:"model_name"' + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['model_version']._loaded_options = None + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['model_version']._serialized_options = b'\x9a\x84\x9e\x03)bson:"model_version" json:"model_version"' + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['model_type']._loaded_options = None + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['model_type']._serialized_options = b'\x9a\x84\x9e\x03#bson:"model_type" json:"model_type"' + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['model_framework']._loaded_options = None + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['model_framework']._serialized_options = b'\x9a\x84\x9e\x03-bson:"model_framework" json:"model_framework"' + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['tags']._loaded_options = None + _globals['_SUBMITTRAININGJOBREQUEST'].fields_by_name['tags']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"tags" json:"tags"' + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST_ARGUMENTSENTRY']._loaded_options = None + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST_ARGUMENTSENTRY']._serialized_options = b'8\x01' + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['dataset_id']._loaded_options = None + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['dataset_id']._serialized_options = b'\x9a\x84\x9e\x03#bson:"dataset_id" json:"dataset_id"' + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['registry_item_id']._loaded_options = None + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['registry_item_id']._serialized_options = b'\x9a\x84\x9e\x03/bson:"registry_item_id" json:"registry_item_id"' + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['registry_item_version']._loaded_options = None + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['registry_item_version']._serialized_options = b'\x9a\x84\x9e\x039bson:"registry_item_version" json:"registry_item_version"' + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['organization_id']._loaded_options = None + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['organization_id']._serialized_options = b'\x9a\x84\x9e\x03-bson:"organization_id" json:"organization_id"' + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['model_name']._loaded_options = None + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['model_name']._serialized_options = b'\x9a\x84\x9e\x03#bson:"model_name" json:"model_name"' + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['model_version']._loaded_options = None + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST'].fields_by_name['model_version']._serialized_options = b'\x9a\x84\x9e\x03)bson:"model_version" json:"model_version"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['id']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['id']._serialized_options = b'\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['dataset_id']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['dataset_id']._serialized_options = b'\x9a\x84\x9e\x03#bson:"dataset_id" json:"dataset_id"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['organization_id']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['organization_id']._serialized_options = b'\x9a\x84\x9e\x03-bson:"organization_id" json:"organization_id"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['model_name']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['model_name']._serialized_options = b'\x9a\x84\x9e\x03#bson:"model_name" json:"model_name"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['model_version']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['model_version']._serialized_options = b'\x9a\x84\x9e\x03)bson:"model_version" json:"model_version"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['model_type']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['model_type']._serialized_options = b'\x9a\x84\x9e\x03#bson:"model_type" json:"model_type"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['model_framework']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['model_framework']._serialized_options = b'\x9a\x84\x9e\x03-bson:"model_framework" json:"model_framework"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['is_custom_job']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['is_custom_job']._serialized_options = b'\x9a\x84\x9e\x03)bson:"is_custom_job" json:"is_custom_job"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['registry_item_id']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['registry_item_id']._serialized_options = b'\x9a\x84\x9e\x03/bson:"registry_item_id" json:"registry_item_id"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['registry_item_version']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['registry_item_version']._serialized_options = b'\x9a\x84\x9e\x039bson:"registry_item_version" json:"registry_item_version"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['status']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['status']._serialized_options = b'\x9a\x84\x9e\x03\x1bbson:"status" json:"status"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['error_status']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['error_status']._serialized_options = b'\x9a\x84\x9e\x03\'bson:"error_status" json:"error_status"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['created_on']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['created_on']._serialized_options = b'\x9a\x84\x9e\x03#bson:"created_on" json:"created_on"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['last_modified']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['last_modified']._serialized_options = b'\x9a\x84\x9e\x03)bson:"last_modified" json:"last_modified"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['training_started']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['training_started']._serialized_options = b'\x9a\x84\x9e\x03/bson:"training_started" json:"training_started"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['training_ended']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['training_ended']._serialized_options = b'\x9a\x84\x9e\x03+bson:"training_ended" json:"training_ended"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['synced_model_id']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['synced_model_id']._serialized_options = b'\x9a\x84\x9e\x03-bson:"synced_model_id" json:"synced_model_id"' + _globals['_TRAININGJOBMETADATA'].fields_by_name['tags']._loaded_options = None + _globals['_TRAININGJOBMETADATA'].fields_by_name['tags']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"tags" json:"tags"' + _globals['_MODELTYPE']._serialized_start = 4285 + _globals['_MODELTYPE']._serialized_end = 4444 + _globals['_MODELFRAMEWORK']._serialized_start = 4447 + _globals['_MODELFRAMEWORK']._serialized_end = 4611 + _globals['_TRAININGSTATUS']._serialized_start = 4614 + _globals['_TRAININGSTATUS']._serialized_end = 4845 + _globals['_SUBMITTRAININGJOBREQUEST']._serialized_start = 146 + _globals['_SUBMITTRAININGJOBREQUEST']._serialized_end = 802 + _globals['_SUBMITTRAININGJOBRESPONSE']._serialized_start = 804 + _globals['_SUBMITTRAININGJOBRESPONSE']._serialized_end = 847 + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST']._serialized_start = 850 + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST']._serialized_end = 1581 + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST_ARGUMENTSENTRY']._serialized_start = 1521 + _globals['_SUBMITCUSTOMTRAININGJOBREQUEST_ARGUMENTSENTRY']._serialized_end = 1581 + _globals['_SUBMITCUSTOMTRAININGJOBRESPONSE']._serialized_start = 1583 + _globals['_SUBMITCUSTOMTRAININGJOBRESPONSE']._serialized_end = 1632 + _globals['_GETTRAININGJOBREQUEST']._serialized_start = 1634 + _globals['_GETTRAININGJOBREQUEST']._serialized_end = 1673 + _globals['_GETTRAININGJOBRESPONSE']._serialized_start = 1675 + _globals['_GETTRAININGJOBRESPONSE']._serialized_end = 1772 + _globals['_LISTTRAININGJOBSREQUEST']._serialized_start = 1775 + _globals['_LISTTRAININGJOBSREQUEST']._serialized_end = 1905 + _globals['_LISTTRAININGJOBSRESPONSE']._serialized_start = 1907 + _globals['_LISTTRAININGJOBSRESPONSE']._serialized_end = 1998 + _globals['_TRAININGJOBMETADATA']._serialized_start = 2001 + _globals['_TRAININGJOBMETADATA']._serialized_end = 3767 + _globals['_CANCELTRAININGJOBREQUEST']._serialized_start = 3769 + _globals['_CANCELTRAININGJOBREQUEST']._serialized_end = 3811 + _globals['_CANCELTRAININGJOBRESPONSE']._serialized_start = 3813 + _globals['_CANCELTRAININGJOBRESPONSE']._serialized_end = 3840 + _globals['_DELETECOMPLETEDTRAININGJOBREQUEST']._serialized_start = 3842 + _globals['_DELETECOMPLETEDTRAININGJOBREQUEST']._serialized_end = 3893 + _globals['_DELETECOMPLETEDTRAININGJOBRESPONSE']._serialized_start = 3895 + _globals['_DELETECOMPLETEDTRAININGJOBRESPONSE']._serialized_end = 3931 + _globals['_TRAININGJOBLOGENTRY']._serialized_start = 3933 + _globals['_TRAININGJOBLOGENTRY']._serialized_end = 4050 + _globals['_GETTRAININGJOBLOGSREQUEST']._serialized_start = 4052 + _globals['_GETTRAININGJOBLOGSREQUEST']._serialized_end = 4146 + _globals['_GETTRAININGJOBLOGSRESPONSE']._serialized_start = 4149 + _globals['_GETTRAININGJOBLOGSRESPONSE']._serialized_end = 4282 + _globals['_MLTRAININGSERVICE']._serialized_start = 4848 + _globals['_MLTRAININGSERVICE']._serialized_end = 5759 \ No newline at end of file diff --git a/src/viam/gen/app/mltraining/v1/ml_training_pb2.pyi b/src/viam/gen/app/mltraining/v1/ml_training_pb2.pyi new file mode 100644 index 000000000..99869e514 --- /dev/null +++ b/src/viam/gen/app/mltraining/v1/ml_training_pb2.pyi @@ -0,0 +1,415 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import google.protobuf.timestamp_pb2 +import google.rpc.status_pb2 +import sys +import typing +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _ModelType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ModelTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ModelType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + MODEL_TYPE_UNSPECIFIED: _ModelType.ValueType + MODEL_TYPE_SINGLE_LABEL_CLASSIFICATION: _ModelType.ValueType + MODEL_TYPE_MULTI_LABEL_CLASSIFICATION: _ModelType.ValueType + MODEL_TYPE_OBJECT_DETECTION: _ModelType.ValueType + +class ModelType(_ModelType, metaclass=_ModelTypeEnumTypeWrapper): + ... +MODEL_TYPE_UNSPECIFIED: ModelType.ValueType +MODEL_TYPE_SINGLE_LABEL_CLASSIFICATION: ModelType.ValueType +MODEL_TYPE_MULTI_LABEL_CLASSIFICATION: ModelType.ValueType +MODEL_TYPE_OBJECT_DETECTION: ModelType.ValueType +global___ModelType = ModelType + +class _ModelFramework: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ModelFrameworkEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ModelFramework.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + MODEL_FRAMEWORK_UNSPECIFIED: _ModelFramework.ValueType + MODEL_FRAMEWORK_TFLITE: _ModelFramework.ValueType + MODEL_FRAMEWORK_TENSORFLOW: _ModelFramework.ValueType + MODEL_FRAMEWORK_PYTORCH: _ModelFramework.ValueType + MODEL_FRAMEWORK_ONNX: _ModelFramework.ValueType + +class ModelFramework(_ModelFramework, metaclass=_ModelFrameworkEnumTypeWrapper): + ... +MODEL_FRAMEWORK_UNSPECIFIED: ModelFramework.ValueType +MODEL_FRAMEWORK_TFLITE: ModelFramework.ValueType +MODEL_FRAMEWORK_TENSORFLOW: ModelFramework.ValueType +MODEL_FRAMEWORK_PYTORCH: ModelFramework.ValueType +MODEL_FRAMEWORK_ONNX: ModelFramework.ValueType +global___ModelFramework = ModelFramework + +class _TrainingStatus: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _TrainingStatusEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_TrainingStatus.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + TRAINING_STATUS_UNSPECIFIED: _TrainingStatus.ValueType + TRAINING_STATUS_PENDING: _TrainingStatus.ValueType + TRAINING_STATUS_IN_PROGRESS: _TrainingStatus.ValueType + TRAINING_STATUS_COMPLETED: _TrainingStatus.ValueType + TRAINING_STATUS_FAILED: _TrainingStatus.ValueType + TRAINING_STATUS_CANCELED: _TrainingStatus.ValueType + TRAINING_STATUS_CANCELING: _TrainingStatus.ValueType + +class TrainingStatus(_TrainingStatus, metaclass=_TrainingStatusEnumTypeWrapper): + ... +TRAINING_STATUS_UNSPECIFIED: TrainingStatus.ValueType +TRAINING_STATUS_PENDING: TrainingStatus.ValueType +TRAINING_STATUS_IN_PROGRESS: TrainingStatus.ValueType +TRAINING_STATUS_COMPLETED: TrainingStatus.ValueType +TRAINING_STATUS_FAILED: TrainingStatus.ValueType +TRAINING_STATUS_CANCELED: TrainingStatus.ValueType +TRAINING_STATUS_CANCELING: TrainingStatus.ValueType +global___TrainingStatus = TrainingStatus + +@typing.final +class SubmitTrainingJobRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATASET_ID_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + MODEL_NAME_FIELD_NUMBER: builtins.int + MODEL_VERSION_FIELD_NUMBER: builtins.int + MODEL_TYPE_FIELD_NUMBER: builtins.int + MODEL_FRAMEWORK_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + dataset_id: builtins.str + organization_id: builtins.str + model_name: builtins.str + model_version: builtins.str + model_type: global___ModelType.ValueType + model_framework: global___ModelFramework.ValueType + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, dataset_id: builtins.str=..., organization_id: builtins.str=..., model_name: builtins.str=..., model_version: builtins.str=..., model_type: global___ModelType.ValueType=..., model_framework: global___ModelFramework.ValueType=..., tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['dataset_id', b'dataset_id', 'model_framework', b'model_framework', 'model_name', b'model_name', 'model_type', b'model_type', 'model_version', b'model_version', 'organization_id', b'organization_id', 'tags', b'tags']) -> None: + ... +global___SubmitTrainingJobRequest = SubmitTrainingJobRequest + +@typing.final +class SubmitTrainingJobResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___SubmitTrainingJobResponse = SubmitTrainingJobResponse + +@typing.final +class SubmitCustomTrainingJobRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class ArgumentsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + + def __init__(self, *, key: builtins.str=..., value: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... + DATASET_ID_FIELD_NUMBER: builtins.int + REGISTRY_ITEM_ID_FIELD_NUMBER: builtins.int + REGISTRY_ITEM_VERSION_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + MODEL_NAME_FIELD_NUMBER: builtins.int + MODEL_VERSION_FIELD_NUMBER: builtins.int + ARGUMENTS_FIELD_NUMBER: builtins.int + dataset_id: builtins.str + registry_item_id: builtins.str + registry_item_version: builtins.str + organization_id: builtins.str + model_name: builtins.str + model_version: builtins.str + + @property + def arguments(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + ... + + def __init__(self, *, dataset_id: builtins.str=..., registry_item_id: builtins.str=..., registry_item_version: builtins.str=..., organization_id: builtins.str=..., model_name: builtins.str=..., model_version: builtins.str=..., arguments: collections.abc.Mapping[builtins.str, builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['arguments', b'arguments', 'dataset_id', b'dataset_id', 'model_name', b'model_name', 'model_version', b'model_version', 'organization_id', b'organization_id', 'registry_item_id', b'registry_item_id', 'registry_item_version', b'registry_item_version']) -> None: + ... +global___SubmitCustomTrainingJobRequest = SubmitCustomTrainingJobRequest + +@typing.final +class SubmitCustomTrainingJobResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___SubmitCustomTrainingJobResponse = SubmitCustomTrainingJobResponse + +@typing.final +class GetTrainingJobRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___GetTrainingJobRequest = GetTrainingJobRequest + +@typing.final +class GetTrainingJobResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + METADATA_FIELD_NUMBER: builtins.int + + @property + def metadata(self) -> global___TrainingJobMetadata: + ... + + def __init__(self, *, metadata: global___TrainingJobMetadata | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['metadata', b'metadata']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['metadata', b'metadata']) -> None: + ... +global___GetTrainingJobResponse = GetTrainingJobResponse + +@typing.final +class ListTrainingJobsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + organization_id: builtins.str + status: global___TrainingStatus.ValueType + + def __init__(self, *, organization_id: builtins.str=..., status: global___TrainingStatus.ValueType=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id', 'status', b'status']) -> None: + ... +global___ListTrainingJobsRequest = ListTrainingJobsRequest + +@typing.final +class ListTrainingJobsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + JOBS_FIELD_NUMBER: builtins.int + + @property + def jobs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TrainingJobMetadata]: + ... + + def __init__(self, *, jobs: collections.abc.Iterable[global___TrainingJobMetadata] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['jobs', b'jobs']) -> None: + ... +global___ListTrainingJobsResponse = ListTrainingJobsResponse + +@typing.final +class TrainingJobMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + DATASET_ID_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + MODEL_NAME_FIELD_NUMBER: builtins.int + MODEL_VERSION_FIELD_NUMBER: builtins.int + MODEL_TYPE_FIELD_NUMBER: builtins.int + MODEL_FRAMEWORK_FIELD_NUMBER: builtins.int + IS_CUSTOM_JOB_FIELD_NUMBER: builtins.int + REGISTRY_ITEM_ID_FIELD_NUMBER: builtins.int + REGISTRY_ITEM_VERSION_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + ERROR_STATUS_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + LAST_MODIFIED_FIELD_NUMBER: builtins.int + TRAINING_STARTED_FIELD_NUMBER: builtins.int + TRAINING_ENDED_FIELD_NUMBER: builtins.int + SYNCED_MODEL_ID_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + id: builtins.str + dataset_id: builtins.str + organization_id: builtins.str + model_name: builtins.str + model_version: builtins.str + model_type: global___ModelType.ValueType + model_framework: global___ModelFramework.ValueType + is_custom_job: builtins.bool + registry_item_id: builtins.str + registry_item_version: builtins.str + status: global___TrainingStatus.ValueType + synced_model_id: builtins.str + + @property + def error_status(self) -> google.rpc.status_pb2.Status: + ... + + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def last_modified(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def training_started(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def training_ended(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, id: builtins.str=..., dataset_id: builtins.str=..., organization_id: builtins.str=..., model_name: builtins.str=..., model_version: builtins.str=..., model_type: global___ModelType.ValueType=..., model_framework: global___ModelFramework.ValueType=..., is_custom_job: builtins.bool=..., registry_item_id: builtins.str=..., registry_item_version: builtins.str=..., status: global___TrainingStatus.ValueType=..., error_status: google.rpc.status_pb2.Status | None=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=..., last_modified: google.protobuf.timestamp_pb2.Timestamp | None=..., training_started: google.protobuf.timestamp_pb2.Timestamp | None=..., training_ended: google.protobuf.timestamp_pb2.Timestamp | None=..., synced_model_id: builtins.str=..., tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['created_on', b'created_on', 'error_status', b'error_status', 'last_modified', b'last_modified', 'training_ended', b'training_ended', 'training_started', b'training_started']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['created_on', b'created_on', 'dataset_id', b'dataset_id', 'error_status', b'error_status', 'id', b'id', 'is_custom_job', b'is_custom_job', 'last_modified', b'last_modified', 'model_framework', b'model_framework', 'model_name', b'model_name', 'model_type', b'model_type', 'model_version', b'model_version', 'organization_id', b'organization_id', 'registry_item_id', b'registry_item_id', 'registry_item_version', b'registry_item_version', 'status', b'status', 'synced_model_id', b'synced_model_id', 'tags', b'tags', 'training_ended', b'training_ended', 'training_started', b'training_started']) -> None: + ... +global___TrainingJobMetadata = TrainingJobMetadata + +@typing.final +class CancelTrainingJobRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___CancelTrainingJobRequest = CancelTrainingJobRequest + +@typing.final +class CancelTrainingJobResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___CancelTrainingJobResponse = CancelTrainingJobResponse + +@typing.final +class DeleteCompletedTrainingJobRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___DeleteCompletedTrainingJobRequest = DeleteCompletedTrainingJobRequest + +@typing.final +class DeleteCompletedTrainingJobResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteCompletedTrainingJobResponse = DeleteCompletedTrainingJobResponse + +@typing.final +class TrainingJobLogEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LEVEL_FIELD_NUMBER: builtins.int + TIME_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + level: builtins.str + message: builtins.str + + @property + def time(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, level: builtins.str=..., time: google.protobuf.timestamp_pb2.Timestamp | None=..., message: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['time', b'time']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['level', b'level', 'message', b'message', 'time', b'time']) -> None: + ... +global___TrainingJobLogEntry = TrainingJobLogEntry + +@typing.final +class GetTrainingJobLogsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + PAGE_TOKEN_FIELD_NUMBER: builtins.int + id: builtins.str + page_token: builtins.str + + def __init__(self, *, id: builtins.str=..., page_token: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_page_token', b'_page_token', 'page_token', b'page_token']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_page_token', b'_page_token', 'id', b'id', 'page_token', b'page_token']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_page_token', b'_page_token']) -> typing.Literal['page_token'] | None: + ... +global___GetTrainingJobLogsRequest = GetTrainingJobLogsRequest + +@typing.final +class GetTrainingJobLogsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOGS_FIELD_NUMBER: builtins.int + NEXT_PAGE_TOKEN_FIELD_NUMBER: builtins.int + next_page_token: builtins.str + + @property + def logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TrainingJobLogEntry]: + ... + + def __init__(self, *, logs: collections.abc.Iterable[global___TrainingJobLogEntry] | None=..., next_page_token: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['logs', b'logs', 'next_page_token', b'next_page_token']) -> None: + ... +global___GetTrainingJobLogsResponse = GetTrainingJobLogsResponse \ No newline at end of file diff --git a/src/viam/gen/app/model/v1/model_grpc.py b/src/viam/gen/app/model/v1/model_grpc.py deleted file mode 100644 index ffaeddcde..000000000 --- a/src/viam/gen/app/model/v1/model_grpc.py +++ /dev/null @@ -1,32 +0,0 @@ -import abc -import typing -import grpclib.const -import grpclib.client -if typing.TYPE_CHECKING: - import grpclib.server -import google.protobuf.timestamp_pb2 -from .... import app - -class ModelServiceBase(abc.ABC): - - @abc.abstractmethod - async def Upload(self, stream: 'grpclib.server.Stream[app.model.v1.model_pb2.UploadRequest, app.model.v1.model_pb2.UploadResponse]') -> None: - pass - - @abc.abstractmethod - async def Delete(self, stream: 'grpclib.server.Stream[app.model.v1.model_pb2.DeleteRequest, app.model.v1.model_pb2.DeleteResponse]') -> None: - pass - - @abc.abstractmethod - async def Deploy(self, stream: 'grpclib.server.Stream[app.model.v1.model_pb2.DeployRequest, app.model.v1.model_pb2.DeployResponse]') -> None: - pass - - def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.app.model.v1.ModelService/Upload': grpclib.const.Handler(self.Upload, grpclib.const.Cardinality.STREAM_UNARY, app.model.v1.model_pb2.UploadRequest, app.model.v1.model_pb2.UploadResponse), '/viam.app.model.v1.ModelService/Delete': grpclib.const.Handler(self.Delete, grpclib.const.Cardinality.UNARY_UNARY, app.model.v1.model_pb2.DeleteRequest, app.model.v1.model_pb2.DeleteResponse), '/viam.app.model.v1.ModelService/Deploy': grpclib.const.Handler(self.Deploy, grpclib.const.Cardinality.UNARY_UNARY, app.model.v1.model_pb2.DeployRequest, app.model.v1.model_pb2.DeployResponse)} - -class ModelServiceStub: - - def __init__(self, channel: grpclib.client.Channel) -> None: - self.Upload = grpclib.client.StreamUnaryMethod(channel, '/viam.app.model.v1.ModelService/Upload', app.model.v1.model_pb2.UploadRequest, app.model.v1.model_pb2.UploadResponse) - self.Delete = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.model.v1.ModelService/Delete', app.model.v1.model_pb2.DeleteRequest, app.model.v1.model_pb2.DeleteResponse) - self.Deploy = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.model.v1.ModelService/Deploy', app.model.v1.model_pb2.DeployRequest, app.model.v1.model_pb2.DeployResponse) \ No newline at end of file diff --git a/src/viam/gen/app/model/v1/model_pb2.py b/src/viam/gen/app/model/v1/model_pb2.py deleted file mode 100644 index 11a7bdfd7..000000000 --- a/src/viam/gen/app/model/v1/model_pb2.py +++ /dev/null @@ -1,39 +0,0 @@ -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -_sym_db = _symbol_database.Default() -from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x18app/model/v1/model.proto\x12\x11viam.app.model.v1\x1a\x1fgoogle/protobuf/timestamp.proto"\x1e\n\x08FileData\x12\x12\n\x04data\x18\x01 \x01(\x0cR\x04data"u\n\x0eUploadMetadata\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x1d\n\nmodel_name\x18\x02 \x01(\tR\tmodelName\x12-\n\x12associated_dataset\x18\x03 \x01(\tR\x11associatedDataset"\xa5\x01\n\rUploadRequest\x12?\n\x08metadata\x18\x01 \x01(\x0b2!.viam.app.model.v1.UploadMetadataH\x00R\x08metadata\x12B\n\rfile_contents\x18\x02 \x01(\x0b2\x1b.viam.app.model.v1.FileDataH\x00R\x0cfileContentsB\x0f\n\rupload_packet"F\n\x0eDeleteMetadata\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x1d\n\nmodel_name\x18\x02 \x01(\tR\tmodelName"N\n\rDeleteRequest\x12=\n\x08metadata\x18\x01 \x01(\x0b2!.viam.app.model.v1.DeleteMetadataR\x08metadata"/\n\x0eDeployMetadata\x12\x1d\n\nmodel_name\x18\x01 \x01(\tR\tmodelName"N\n\rDeployRequest\x12=\n\x08metadata\x18\x01 \x01(\x0b2!.viam.app.model.v1.DeployMetadataR\x08metadata"]\n\x0eUploadResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message\x121\n\x06status\x18\x02 \x01(\x0e2\x19.viam.app.model.v1.StatusR\x06status"]\n\x0eDeleteResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message\x121\n\x06status\x18\x02 \x01(\x0e2\x19.viam.app.model.v1.StatusR\x06status"]\n\x0eDeployResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message\x121\n\x06status\x18\x02 \x01(\x0e2\x19.viam.app.model.v1.StatusR\x06status"\xc8\x01\n\x0bSyncedModel\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x1d\n\nmodel_name\x18\x02 \x01(\tR\tmodelName\x12-\n\x12associated_dataset\x18\x03 \x01(\tR\x11associatedDataset\x12\x1b\n\tblob_path\x18\x04 \x01(\tR\x08blobPath\x127\n\tsync_time\x18\x05 \x01(\x0b2\x1a.google.protobuf.TimestampR\x08syncTime*+\n\x06Status\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x08\n\x04FAIL\x10\x01\x12\x06\n\x02OK\x10\x022\xfd\x01\n\x0cModelService\x12O\n\x06Upload\x12 .viam.app.model.v1.UploadRequest\x1a!.viam.app.model.v1.UploadResponse(\x01\x12M\n\x06Delete\x12 .viam.app.model.v1.DeleteRequest\x1a!.viam.app.model.v1.DeleteResponse\x12M\n\x06Deploy\x12 .viam.app.model.v1.DeployRequest\x1a!.viam.app.model.v1.DeployResponseB\x1eZ\x1cgo.viam.com/api/app/model/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.model.v1.model_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z\x1cgo.viam.com/api/app/model/v1' - _STATUS._serialized_start = 1168 - _STATUS._serialized_end = 1211 - _FILEDATA._serialized_start = 80 - _FILEDATA._serialized_end = 110 - _UPLOADMETADATA._serialized_start = 112 - _UPLOADMETADATA._serialized_end = 229 - _UPLOADREQUEST._serialized_start = 232 - _UPLOADREQUEST._serialized_end = 397 - _DELETEMETADATA._serialized_start = 399 - _DELETEMETADATA._serialized_end = 469 - _DELETEREQUEST._serialized_start = 471 - _DELETEREQUEST._serialized_end = 549 - _DEPLOYMETADATA._serialized_start = 551 - _DEPLOYMETADATA._serialized_end = 598 - _DEPLOYREQUEST._serialized_start = 600 - _DEPLOYREQUEST._serialized_end = 678 - _UPLOADRESPONSE._serialized_start = 680 - _UPLOADRESPONSE._serialized_end = 773 - _DELETERESPONSE._serialized_start = 775 - _DELETERESPONSE._serialized_end = 868 - _DEPLOYRESPONSE._serialized_start = 870 - _DEPLOYRESPONSE._serialized_end = 963 - _SYNCEDMODEL._serialized_start = 966 - _SYNCEDMODEL._serialized_end = 1166 - _MODELSERVICE._serialized_start = 1214 - _MODELSERVICE._serialized_end = 1467 \ No newline at end of file diff --git a/src/viam/gen/app/model/v1/model_pb2.pyi b/src/viam/gen/app/model/v1/model_pb2.pyi deleted file mode 100644 index 0759f66ec..000000000 --- a/src/viam/gen/app/model/v1/model_pb2.pyi +++ /dev/null @@ -1,224 +0,0 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -""" -import builtins -import google.protobuf.descriptor -import google.protobuf.internal.enum_type_wrapper -import google.protobuf.message -import google.protobuf.timestamp_pb2 -import sys -import typing -if sys.version_info >= (3, 10): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class _Status: - ValueType = typing.NewType('ValueType', builtins.int) - V: typing_extensions.TypeAlias = ValueType - -class _StatusEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Status.ValueType], builtins.type): - DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor - UNSPECIFIED: _Status.ValueType - 'buf:lint:ignore ENUM_VALUE_PREFIX\n buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX\n ' - FAIL: _Status.ValueType - 'buf:lint:ignore ENUM_VALUE_PREFIX' - OK: _Status.ValueType - 'buf:lint:ignore ENUM_VALUE_PREFIX' - -class Status(_Status, metaclass=_StatusEnumTypeWrapper): - ... -UNSPECIFIED: Status.ValueType -'buf:lint:ignore ENUM_VALUE_PREFIX\nbuf:lint:ignore ENUM_ZERO_VALUE_SUFFIX\n' -FAIL: Status.ValueType -'buf:lint:ignore ENUM_VALUE_PREFIX' -OK: Status.ValueType -'buf:lint:ignore ENUM_VALUE_PREFIX' -global___Status = Status - -class FileData(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - DATA_FIELD_NUMBER: builtins.int - data: builtins.bytes - - def __init__(self, *, data: builtins.bytes=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['data', b'data']) -> None: - ... -global___FileData = FileData - -class UploadMetadata(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - ORG_ID_FIELD_NUMBER: builtins.int - MODEL_NAME_FIELD_NUMBER: builtins.int - ASSOCIATED_DATASET_FIELD_NUMBER: builtins.int - org_id: builtins.str - model_name: builtins.str - associated_dataset: builtins.str - "TODO: Determine the format of the associated dataset. Update when it's decided\n whether it should be by ID or name.\n " - - def __init__(self, *, org_id: builtins.str=..., model_name: builtins.str=..., associated_dataset: builtins.str=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['associated_dataset', b'associated_dataset', 'model_name', b'model_name', 'org_id', b'org_id']) -> None: - ... -global___UploadMetadata = UploadMetadata - -class UploadRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - METADATA_FIELD_NUMBER: builtins.int - FILE_CONTENTS_FIELD_NUMBER: builtins.int - - @property - def metadata(self) -> global___UploadMetadata: - ... - - @property - def file_contents(self) -> global___FileData: - ... - - def __init__(self, *, metadata: global___UploadMetadata | None=..., file_contents: global___FileData | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['file_contents', b'file_contents', 'metadata', b'metadata', 'upload_packet', b'upload_packet']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['file_contents', b'file_contents', 'metadata', b'metadata', 'upload_packet', b'upload_packet']) -> None: - ... - - def WhichOneof(self, oneof_group: typing_extensions.Literal['upload_packet', b'upload_packet']) -> typing_extensions.Literal['metadata', 'file_contents'] | None: - ... -global___UploadRequest = UploadRequest - -class DeleteMetadata(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - ORG_ID_FIELD_NUMBER: builtins.int - MODEL_NAME_FIELD_NUMBER: builtins.int - org_id: builtins.str - model_name: builtins.str - - def __init__(self, *, org_id: builtins.str=..., model_name: builtins.str=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['model_name', b'model_name', 'org_id', b'org_id']) -> None: - ... -global___DeleteMetadata = DeleteMetadata - -class DeleteRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - METADATA_FIELD_NUMBER: builtins.int - - @property - def metadata(self) -> global___DeleteMetadata: - ... - - def __init__(self, *, metadata: global___DeleteMetadata | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['metadata', b'metadata']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['metadata', b'metadata']) -> None: - ... -global___DeleteRequest = DeleteRequest - -class DeployMetadata(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - MODEL_NAME_FIELD_NUMBER: builtins.int - model_name: builtins.str - - def __init__(self, *, model_name: builtins.str=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['model_name', b'model_name']) -> None: - ... -global___DeployMetadata = DeployMetadata - -class DeployRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - METADATA_FIELD_NUMBER: builtins.int - - @property - def metadata(self) -> global___DeployMetadata: - ... - - def __init__(self, *, metadata: global___DeployMetadata | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['metadata', b'metadata']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['metadata', b'metadata']) -> None: - ... -global___DeployRequest = DeployRequest - -class UploadResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - MESSAGE_FIELD_NUMBER: builtins.int - STATUS_FIELD_NUMBER: builtins.int - message: builtins.str - status: global___Status.ValueType - - def __init__(self, *, message: builtins.str=..., status: global___Status.ValueType=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['message', b'message', 'status', b'status']) -> None: - ... -global___UploadResponse = UploadResponse - -class DeleteResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - MESSAGE_FIELD_NUMBER: builtins.int - STATUS_FIELD_NUMBER: builtins.int - message: builtins.str - status: global___Status.ValueType - - def __init__(self, *, message: builtins.str=..., status: global___Status.ValueType=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['message', b'message', 'status', b'status']) -> None: - ... -global___DeleteResponse = DeleteResponse - -class DeployResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - MESSAGE_FIELD_NUMBER: builtins.int - STATUS_FIELD_NUMBER: builtins.int - message: builtins.str - status: global___Status.ValueType - - def __init__(self, *, message: builtins.str=..., status: global___Status.ValueType=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['message', b'message', 'status', b'status']) -> None: - ... -global___DeployResponse = DeployResponse - -class SyncedModel(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - ORG_ID_FIELD_NUMBER: builtins.int - MODEL_NAME_FIELD_NUMBER: builtins.int - ASSOCIATED_DATASET_FIELD_NUMBER: builtins.int - BLOB_PATH_FIELD_NUMBER: builtins.int - SYNC_TIME_FIELD_NUMBER: builtins.int - org_id: builtins.str - model_name: builtins.str - associated_dataset: builtins.str - blob_path: builtins.str - - @property - def sync_time(self) -> google.protobuf.timestamp_pb2.Timestamp: - ... - - def __init__(self, *, org_id: builtins.str=..., model_name: builtins.str=..., associated_dataset: builtins.str=..., blob_path: builtins.str=..., sync_time: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['sync_time', b'sync_time']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['associated_dataset', b'associated_dataset', 'blob_path', b'blob_path', 'model_name', b'model_name', 'org_id', b'org_id', 'sync_time', b'sync_time']) -> None: - ... -global___SyncedModel = SyncedModel \ No newline at end of file diff --git a/src/viam/gen/app/packages/__init__.py b/src/viam/gen/app/packages/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/packages/v1/__init__.py b/src/viam/gen/app/packages/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/app/packages/v1/packages_grpc.py b/src/viam/gen/app/packages/v1/packages_grpc.py new file mode 100644 index 000000000..0f0009c3c --- /dev/null +++ b/src/viam/gen/app/packages/v1/packages_grpc.py @@ -0,0 +1,54 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +import google.api.annotations_pb2 +import google.protobuf.struct_pb2 +import google.protobuf.timestamp_pb2 +from .... import app + +class PackageServiceBase(abc.ABC): + + @abc.abstractmethod + async def CreatePackage(self, stream: 'grpclib.server.Stream[app.packages.v1.packages_pb2.CreatePackageRequest, app.packages.v1.packages_pb2.CreatePackageResponse]') -> None: + pass + + @abc.abstractmethod + async def DeletePackage(self, stream: 'grpclib.server.Stream[app.packages.v1.packages_pb2.DeletePackageRequest, app.packages.v1.packages_pb2.DeletePackageResponse]') -> None: + pass + + @abc.abstractmethod + async def GetPackage(self, stream: 'grpclib.server.Stream[app.packages.v1.packages_pb2.GetPackageRequest, app.packages.v1.packages_pb2.GetPackageResponse]') -> None: + pass + + @abc.abstractmethod + async def ListPackages(self, stream: 'grpclib.server.Stream[app.packages.v1.packages_pb2.ListPackagesRequest, app.packages.v1.packages_pb2.ListPackagesResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.app.packages.v1.PackageService/CreatePackage': grpclib.const.Handler(self.CreatePackage, grpclib.const.Cardinality.STREAM_UNARY, app.packages.v1.packages_pb2.CreatePackageRequest, app.packages.v1.packages_pb2.CreatePackageResponse), '/viam.app.packages.v1.PackageService/DeletePackage': grpclib.const.Handler(self.DeletePackage, grpclib.const.Cardinality.UNARY_UNARY, app.packages.v1.packages_pb2.DeletePackageRequest, app.packages.v1.packages_pb2.DeletePackageResponse), '/viam.app.packages.v1.PackageService/GetPackage': grpclib.const.Handler(self.GetPackage, grpclib.const.Cardinality.UNARY_UNARY, app.packages.v1.packages_pb2.GetPackageRequest, app.packages.v1.packages_pb2.GetPackageResponse), '/viam.app.packages.v1.PackageService/ListPackages': grpclib.const.Handler(self.ListPackages, grpclib.const.Cardinality.UNARY_UNARY, app.packages.v1.packages_pb2.ListPackagesRequest, app.packages.v1.packages_pb2.ListPackagesResponse)} + +class UnimplementedPackageServiceBase(PackageServiceBase): + + async def CreatePackage(self, stream: 'grpclib.server.Stream[app.packages.v1.packages_pb2.CreatePackageRequest, app.packages.v1.packages_pb2.CreatePackageResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeletePackage(self, stream: 'grpclib.server.Stream[app.packages.v1.packages_pb2.DeletePackageRequest, app.packages.v1.packages_pb2.DeletePackageResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPackage(self, stream: 'grpclib.server.Stream[app.packages.v1.packages_pb2.GetPackageRequest, app.packages.v1.packages_pb2.GetPackageResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListPackages(self, stream: 'grpclib.server.Stream[app.packages.v1.packages_pb2.ListPackagesRequest, app.packages.v1.packages_pb2.ListPackagesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class PackageServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.CreatePackage = grpclib.client.StreamUnaryMethod(channel, '/viam.app.packages.v1.PackageService/CreatePackage', app.packages.v1.packages_pb2.CreatePackageRequest, app.packages.v1.packages_pb2.CreatePackageResponse) + self.DeletePackage = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.packages.v1.PackageService/DeletePackage', app.packages.v1.packages_pb2.DeletePackageRequest, app.packages.v1.packages_pb2.DeletePackageResponse) + self.GetPackage = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.packages.v1.PackageService/GetPackage', app.packages.v1.packages_pb2.GetPackageRequest, app.packages.v1.packages_pb2.GetPackageResponse) + self.ListPackages = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.packages.v1.PackageService/ListPackages', app.packages.v1.packages_pb2.ListPackagesRequest, app.packages.v1.packages_pb2.ListPackagesResponse) \ No newline at end of file diff --git a/src/viam/gen/app/packages/v1/packages_pb2.py b/src/viam/gen/app/packages/v1/packages_pb2.py new file mode 100644 index 000000000..2db022d22 --- /dev/null +++ b/src/viam/gen/app/packages/v1/packages_pb2.py @@ -0,0 +1,52 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/packages/v1/packages.proto') +_sym_db = _symbol_database.Default() +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eapp/packages/v1/packages.proto\x12\x14viam.app.packages.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"k\n\x08FileInfo\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04size\x18\x02 \x01(\x04R\x04size\x12&\n\x0cis_directory\x18\x03 \x01(\x08H\x00R\x0bisDirectory\x88\x01\x01B\x0f\n\r_is_directory"\xb4\x02\n\x0bPackageInfo\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x18\n\x07version\x18\x03 \x01(\tR\x07version\x125\n\x04type\x18\x04 \x01(\x0e2!.viam.app.packages.v1.PackageTypeR\x04type\x12\x1f\n\x08platform\x18\x07 \x01(\tH\x00R\x08platform\x88\x01\x01\x124\n\x05files\x18\x05 \x03(\x0b2\x1e.viam.app.packages.v1.FileInfoR\x05files\x123\n\x08metadata\x18\x06 \x01(\x0b2\x17.google.protobuf.StructR\x08metadataB\x0b\n\t_platform"x\n\x14CreatePackageRequest\x127\n\x04info\x18\x01 \x01(\x0b2!.viam.app.packages.v1.PackageInfoH\x00R\x04info\x12\x1c\n\x08contents\x18\x02 \x01(\x0cH\x00R\x08contentsB\t\n\x07package"A\n\x15CreatePackageResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x18\n\x07version\x18\x02 \x01(\tR\x07version"w\n\x14DeletePackageRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x18\n\x07version\x18\x02 \x01(\tR\x07version\x125\n\x04type\x18\x03 \x01(\x0e2!.viam.app.packages.v1.PackageTypeR\x04type"\x17\n\x15DeletePackageResponse"\xb9\x01\n\x07Package\x125\n\x04info\x18\x01 \x01(\x0b2!.viam.app.packages.v1.PackageInfoR\x04info\x12\x10\n\x03url\x18\x02 \x01(\tR\x03url\x129\n\ncreated_on\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\tcreatedOn\x12\x1a\n\x08checksum\x18\x04 \x01(\tR\x08checksum\x12\x0e\n\x02id\x18\x05 \x01(\tR\x02id"\xe6\x01\n\x11GetPackageRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x18\n\x07version\x18\x02 \x01(\tR\x07version\x12$\n\x0binclude_url\x18\x03 \x01(\x08H\x00R\nincludeUrl\x88\x01\x01\x12:\n\x04type\x18\x04 \x01(\x0e2!.viam.app.packages.v1.PackageTypeH\x01R\x04type\x88\x01\x01\x12\x1f\n\x08platform\x18\x05 \x01(\tH\x02R\x08platform\x88\x01\x01B\x0e\n\x0c_include_urlB\x07\n\x05_typeB\x0b\n\t_platform"M\n\x12GetPackageResponse\x127\n\x07package\x18\x01 \x01(\x0b2\x1d.viam.app.packages.v1.PackageR\x07package"\x86\x02\n\x13ListPackagesRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x17\n\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12\x1d\n\x07version\x18\x03 \x01(\tH\x01R\x07version\x88\x01\x01\x12:\n\x04type\x18\x04 \x01(\x0e2!.viam.app.packages.v1.PackageTypeH\x02R\x04type\x88\x01\x01\x12$\n\x0binclude_url\x18\x05 \x01(\x08H\x03R\nincludeUrl\x88\x01\x01B\x07\n\x05_nameB\n\n\x08_versionB\x07\n\x05_typeB\x0e\n\x0c_include_url"Q\n\x14ListPackagesResponse\x129\n\x08packages\x18\x01 \x03(\x0b2\x1d.viam.app.packages.v1.PackageR\x08packages*\xb2\x01\n\x0bPackageType\x12\x1c\n\x18PACKAGE_TYPE_UNSPECIFIED\x10\x00\x12\x18\n\x14PACKAGE_TYPE_ARCHIVE\x10\x01\x12\x19\n\x15PACKAGE_TYPE_ML_MODEL\x10\x02\x12\x17\n\x13PACKAGE_TYPE_MODULE\x10\x03\x12\x19\n\x15PACKAGE_TYPE_SLAM_MAP\x10\x04\x12\x1c\n\x18PACKAGE_TYPE_ML_TRAINING\x10\x052\xa0\x04\n\x0ePackageService\x12\x87\x01\n\rCreatePackage\x12*.viam.app.packages.v1.CreatePackageRequest\x1a+.viam.app.packages.v1.CreatePackageResponse"\x1b\x82\xd3\xe4\x93\x02\x15"\x13/packages/v1/create(\x01\x12\x85\x01\n\rDeletePackage\x12*.viam.app.packages.v1.DeletePackageRequest\x1a+.viam.app.packages.v1.DeletePackageResponse"\x1b\x82\xd3\xe4\x93\x02\x15*\x13/packages/v1/delete\x12y\n\nGetPackage\x12\'.viam.app.packages.v1.GetPackageRequest\x1a(.viam.app.packages.v1.GetPackageResponse"\x18\x82\xd3\xe4\x93\x02\x12\x12\x10/packages/v1/get\x12\x80\x01\n\x0cListPackages\x12).viam.app.packages.v1.ListPackagesRequest\x1a*.viam.app.packages.v1.ListPackagesResponse"\x19\x82\xd3\xe4\x93\x02\x13\x12\x11/packages/v1/listB!Z\x1fgo.viam.com/api/app/packages/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.packages.v1.packages_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x1fgo.viam.com/api/app/packages/v1' + _globals['_PACKAGESERVICE'].methods_by_name['CreatePackage']._loaded_options = None + _globals['_PACKAGESERVICE'].methods_by_name['CreatePackage']._serialized_options = b'\x82\xd3\xe4\x93\x02\x15"\x13/packages/v1/create' + _globals['_PACKAGESERVICE'].methods_by_name['DeletePackage']._loaded_options = None + _globals['_PACKAGESERVICE'].methods_by_name['DeletePackage']._serialized_options = b'\x82\xd3\xe4\x93\x02\x15*\x13/packages/v1/delete' + _globals['_PACKAGESERVICE'].methods_by_name['GetPackage']._loaded_options = None + _globals['_PACKAGESERVICE'].methods_by_name['GetPackage']._serialized_options = b'\x82\xd3\xe4\x93\x02\x12\x12\x10/packages/v1/get' + _globals['_PACKAGESERVICE'].methods_by_name['ListPackages']._loaded_options = None + _globals['_PACKAGESERVICE'].methods_by_name['ListPackages']._serialized_options = b'\x82\xd3\xe4\x93\x02\x13\x12\x11/packages/v1/list' + _globals['_PACKAGETYPE']._serialized_start = 1753 + _globals['_PACKAGETYPE']._serialized_end = 1931 + _globals['_FILEINFO']._serialized_start = 149 + _globals['_FILEINFO']._serialized_end = 256 + _globals['_PACKAGEINFO']._serialized_start = 259 + _globals['_PACKAGEINFO']._serialized_end = 567 + _globals['_CREATEPACKAGEREQUEST']._serialized_start = 569 + _globals['_CREATEPACKAGEREQUEST']._serialized_end = 689 + _globals['_CREATEPACKAGERESPONSE']._serialized_start = 691 + _globals['_CREATEPACKAGERESPONSE']._serialized_end = 756 + _globals['_DELETEPACKAGEREQUEST']._serialized_start = 758 + _globals['_DELETEPACKAGEREQUEST']._serialized_end = 877 + _globals['_DELETEPACKAGERESPONSE']._serialized_start = 879 + _globals['_DELETEPACKAGERESPONSE']._serialized_end = 902 + _globals['_PACKAGE']._serialized_start = 905 + _globals['_PACKAGE']._serialized_end = 1090 + _globals['_GETPACKAGEREQUEST']._serialized_start = 1093 + _globals['_GETPACKAGEREQUEST']._serialized_end = 1323 + _globals['_GETPACKAGERESPONSE']._serialized_start = 1325 + _globals['_GETPACKAGERESPONSE']._serialized_end = 1402 + _globals['_LISTPACKAGESREQUEST']._serialized_start = 1405 + _globals['_LISTPACKAGESREQUEST']._serialized_end = 1667 + _globals['_LISTPACKAGESRESPONSE']._serialized_start = 1669 + _globals['_LISTPACKAGESRESPONSE']._serialized_end = 1750 + _globals['_PACKAGESERVICE']._serialized_start = 1934 + _globals['_PACKAGESERVICE']._serialized_end = 2478 \ No newline at end of file diff --git a/src/viam/gen/app/packages/v1/packages_pb2.pyi b/src/viam/gen/app/packages/v1/packages_pb2.pyi new file mode 100644 index 000000000..16bee1649 --- /dev/null +++ b/src/viam/gen/app/packages/v1/packages_pb2.pyi @@ -0,0 +1,311 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import google.protobuf.struct_pb2 +import google.protobuf.timestamp_pb2 +import sys +import typing +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _PackageType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PackageTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PackageType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PACKAGE_TYPE_UNSPECIFIED: _PackageType.ValueType + PACKAGE_TYPE_ARCHIVE: _PackageType.ValueType + PACKAGE_TYPE_ML_MODEL: _PackageType.ValueType + PACKAGE_TYPE_MODULE: _PackageType.ValueType + PACKAGE_TYPE_SLAM_MAP: _PackageType.ValueType + PACKAGE_TYPE_ML_TRAINING: _PackageType.ValueType + +class PackageType(_PackageType, metaclass=_PackageTypeEnumTypeWrapper): + ... +PACKAGE_TYPE_UNSPECIFIED: PackageType.ValueType +PACKAGE_TYPE_ARCHIVE: PackageType.ValueType +PACKAGE_TYPE_ML_MODEL: PackageType.ValueType +PACKAGE_TYPE_MODULE: PackageType.ValueType +PACKAGE_TYPE_SLAM_MAP: PackageType.ValueType +PACKAGE_TYPE_ML_TRAINING: PackageType.ValueType +global___PackageType = PackageType + +@typing.final +class FileInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + SIZE_FIELD_NUMBER: builtins.int + IS_DIRECTORY_FIELD_NUMBER: builtins.int + name: builtins.str + size: builtins.int + is_directory: builtins.bool + + def __init__(self, *, name: builtins.str=..., size: builtins.int=..., is_directory: builtins.bool | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_is_directory', b'_is_directory', 'is_directory', b'is_directory']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_is_directory', b'_is_directory', 'is_directory', b'is_directory', 'name', b'name', 'size', b'size']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_is_directory', b'_is_directory']) -> typing.Literal['is_directory'] | None: + ... +global___FileInfo = FileInfo + +@typing.final +class PackageInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + PLATFORM_FIELD_NUMBER: builtins.int + FILES_FIELD_NUMBER: builtins.int + METADATA_FIELD_NUMBER: builtins.int + organization_id: builtins.str + name: builtins.str + version: builtins.str + type: global___PackageType.ValueType + platform: builtins.str + + @property + def files(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___FileInfo]: + ... + + @property + def metadata(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, organization_id: builtins.str=..., name: builtins.str=..., version: builtins.str=..., type: global___PackageType.ValueType=..., platform: builtins.str | None=..., files: collections.abc.Iterable[global___FileInfo] | None=..., metadata: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_platform', b'_platform', 'metadata', b'metadata', 'platform', b'platform']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_platform', b'_platform', 'files', b'files', 'metadata', b'metadata', 'name', b'name', 'organization_id', b'organization_id', 'platform', b'platform', 'type', b'type', 'version', b'version']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_platform', b'_platform']) -> typing.Literal['platform'] | None: + ... +global___PackageInfo = PackageInfo + +@typing.final +class CreatePackageRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + INFO_FIELD_NUMBER: builtins.int + CONTENTS_FIELD_NUMBER: builtins.int + contents: builtins.bytes + '.tar.gz file' + + @property + def info(self) -> global___PackageInfo: + ... + + def __init__(self, *, info: global___PackageInfo | None=..., contents: builtins.bytes=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['contents', b'contents', 'info', b'info', 'package', b'package']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['contents', b'contents', 'info', b'info', 'package', b'package']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['package', b'package']) -> typing.Literal['info', 'contents'] | None: + ... +global___CreatePackageRequest = CreatePackageRequest + +@typing.final +class CreatePackageResponse(google.protobuf.message.Message): + """Returns the package ID and version which are populated in GetPackageRequest and DeletePackageRequest to + retrieve or delete this package. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + id: builtins.str + version: builtins.str + + def __init__(self, *, id: builtins.str=..., version: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'version', b'version']) -> None: + ... +global___CreatePackageResponse = CreatePackageResponse + +@typing.final +class DeletePackageRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + id: builtins.str + version: builtins.str + type: global___PackageType.ValueType + + def __init__(self, *, id: builtins.str=..., version: builtins.str=..., type: global___PackageType.ValueType=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'type', b'type', 'version', b'version']) -> None: + ... +global___DeletePackageRequest = DeletePackageRequest + +@typing.final +class DeletePackageResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeletePackageResponse = DeletePackageResponse + +@typing.final +class Package(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + INFO_FIELD_NUMBER: builtins.int + URL_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + CHECKSUM_FIELD_NUMBER: builtins.int + ID_FIELD_NUMBER: builtins.int + url: builtins.str + checksum: builtins.str + id: builtins.str + + @property + def info(self) -> global___PackageInfo: + ... + + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, info: global___PackageInfo | None=..., url: builtins.str=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=..., checksum: builtins.str=..., id: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['created_on', b'created_on', 'info', b'info']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['checksum', b'checksum', 'created_on', b'created_on', 'id', b'id', 'info', b'info', 'url', b'url']) -> None: + ... +global___Package = Package + +@typing.final +class GetPackageRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + INCLUDE_URL_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + PLATFORM_FIELD_NUMBER: builtins.int + id: builtins.str + version: builtins.str + include_url: builtins.bool + type: global___PackageType.ValueType + platform: builtins.str + + def __init__(self, *, id: builtins.str=..., version: builtins.str=..., include_url: builtins.bool | None=..., type: global___PackageType.ValueType | None=..., platform: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_include_url', b'_include_url', '_platform', b'_platform', '_type', b'_type', 'include_url', b'include_url', 'platform', b'platform', 'type', b'type']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_include_url', b'_include_url', '_platform', b'_platform', '_type', b'_type', 'id', b'id', 'include_url', b'include_url', 'platform', b'platform', 'type', b'type', 'version', b'version']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_include_url', b'_include_url']) -> typing.Literal['include_url'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_platform', b'_platform']) -> typing.Literal['platform'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_type', b'_type']) -> typing.Literal['type'] | None: + ... +global___GetPackageRequest = GetPackageRequest + +@typing.final +class GetPackageResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PACKAGE_FIELD_NUMBER: builtins.int + + @property + def package(self) -> global___Package: + ... + + def __init__(self, *, package: global___Package | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['package', b'package']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['package', b'package']) -> None: + ... +global___GetPackageResponse = GetPackageResponse + +@typing.final +class ListPackagesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + INCLUDE_URL_FIELD_NUMBER: builtins.int + organization_id: builtins.str + name: builtins.str + version: builtins.str + type: global___PackageType.ValueType + include_url: builtins.bool + + def __init__(self, *, organization_id: builtins.str=..., name: builtins.str | None=..., version: builtins.str | None=..., type: global___PackageType.ValueType | None=..., include_url: builtins.bool | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_include_url', b'_include_url', '_name', b'_name', '_type', b'_type', '_version', b'_version', 'include_url', b'include_url', 'name', b'name', 'type', b'type', 'version', b'version']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_include_url', b'_include_url', '_name', b'_name', '_type', b'_type', '_version', b'_version', 'include_url', b'include_url', 'name', b'name', 'organization_id', b'organization_id', 'type', b'type', 'version', b'version']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_include_url', b'_include_url']) -> typing.Literal['include_url'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_name', b'_name']) -> typing.Literal['name'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_type', b'_type']) -> typing.Literal['type'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_version', b'_version']) -> typing.Literal['version'] | None: + ... +global___ListPackagesRequest = ListPackagesRequest + +@typing.final +class ListPackagesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PACKAGES_FIELD_NUMBER: builtins.int + + @property + def packages(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Package]: + ... + + def __init__(self, *, packages: collections.abc.Iterable[global___Package] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['packages', b'packages']) -> None: + ... +global___ListPackagesResponse = ListPackagesResponse \ No newline at end of file diff --git a/src/viam/gen/app/v1/app_grpc.py b/src/viam/gen/app/v1/app_grpc.py index 34e298d2f..96e96ffd2 100644 --- a/src/viam/gen/app/v1/app_grpc.py +++ b/src/viam/gen/app/v1/app_grpc.py @@ -2,31 +2,217 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from ... import app +from ... import common import google.protobuf.struct_pb2 import google.protobuf.timestamp_pb2 from ... import tagger -from ... import app class AppServiceBase(abc.ABC): + @abc.abstractmethod + async def GetUserIDByEmail(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetUserIDByEmailRequest, app.v1.app_pb2.GetUserIDByEmailResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateOrganization(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateOrganizationRequest, app.v1.app_pb2.CreateOrganizationResponse]') -> None: + pass + @abc.abstractmethod async def ListOrganizations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListOrganizationsRequest, app.v1.app_pb2.ListOrganizationsResponse]') -> None: pass + @abc.abstractmethod + async def GetOrganizationsWithAccessToLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetOrganizationsWithAccessToLocationRequest, app.v1.app_pb2.GetOrganizationsWithAccessToLocationResponse]') -> None: + pass + + @abc.abstractmethod + async def ListOrganizationsByUser(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListOrganizationsByUserRequest, app.v1.app_pb2.ListOrganizationsByUserResponse]') -> None: + pass + + @abc.abstractmethod + async def SearchOrganizations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.SearchOrganizationsRequest, app.v1.app_pb2.SearchOrganizationsResponse]') -> None: + pass + + @abc.abstractmethod + async def GetOrganization(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetOrganizationRequest, app.v1.app_pb2.GetOrganizationResponse]') -> None: + pass + + @abc.abstractmethod + async def GetOrganizationNamespaceAvailability(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetOrganizationNamespaceAvailabilityRequest, app.v1.app_pb2.GetOrganizationNamespaceAvailabilityResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateOrganization(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateOrganizationRequest, app.v1.app_pb2.UpdateOrganizationResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateOrganizationNamespace(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateOrganizationNamespaceRequest, app.v1.app_pb2.UpdateOrganizationNamespaceResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteOrganization(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteOrganizationRequest, app.v1.app_pb2.DeleteOrganizationResponse]') -> None: + pass + + @abc.abstractmethod + async def GetOrganizationMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetOrganizationMetadataRequest, app.v1.app_pb2.GetOrganizationMetadataResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateOrganizationMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateOrganizationMetadataRequest, app.v1.app_pb2.UpdateOrganizationMetadataResponse]') -> None: + pass + + @abc.abstractmethod + async def ListOrganizationMembers(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListOrganizationMembersRequest, app.v1.app_pb2.ListOrganizationMembersResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateOrganizationInvite(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateOrganizationInviteRequest, app.v1.app_pb2.CreateOrganizationInviteResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateOrganizationInviteAuthorizations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateOrganizationInviteAuthorizationsRequest, app.v1.app_pb2.UpdateOrganizationInviteAuthorizationsResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteOrganizationMember(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteOrganizationMemberRequest, app.v1.app_pb2.DeleteOrganizationMemberResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteOrganizationInvite(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteOrganizationInviteRequest, app.v1.app_pb2.DeleteOrganizationInviteResponse]') -> None: + pass + + @abc.abstractmethod + async def ResendOrganizationInvite(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ResendOrganizationInviteRequest, app.v1.app_pb2.ResendOrganizationInviteResponse]') -> None: + pass + + @abc.abstractmethod + async def EnableBillingService(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.EnableBillingServiceRequest, app.v1.app_pb2.EnableBillingServiceResponse]') -> None: + pass + + @abc.abstractmethod + async def DisableBillingService(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DisableBillingServiceRequest, app.v1.app_pb2.DisableBillingServiceResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateBillingService(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateBillingServiceRequest, app.v1.app_pb2.UpdateBillingServiceResponse]') -> None: + pass + + @abc.abstractmethod + async def GetBillingServiceConfig(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetBillingServiceConfigRequest, app.v1.app_pb2.GetBillingServiceConfigResponse]') -> None: + pass + + @abc.abstractmethod + async def OrganizationSetSupportEmail(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.OrganizationSetSupportEmailRequest, app.v1.app_pb2.OrganizationSetSupportEmailResponse]') -> None: + pass + + @abc.abstractmethod + async def OrganizationGetSupportEmail(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.OrganizationGetSupportEmailRequest, app.v1.app_pb2.OrganizationGetSupportEmailResponse]') -> None: + pass + + @abc.abstractmethod + async def OrganizationSetLogo(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.OrganizationSetLogoRequest, app.v1.app_pb2.OrganizationSetLogoResponse]') -> None: + pass + + @abc.abstractmethod + async def OrganizationGetLogo(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.OrganizationGetLogoRequest, app.v1.app_pb2.OrganizationGetLogoResponse]') -> None: + pass + + @abc.abstractmethod + async def EnableAuthService(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.EnableAuthServiceRequest, app.v1.app_pb2.EnableAuthServiceResponse]') -> None: + pass + + @abc.abstractmethod + async def DisableAuthService(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DisableAuthServiceRequest, app.v1.app_pb2.DisableAuthServiceResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateOAuthApp(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateOAuthAppRequest, app.v1.app_pb2.CreateOAuthAppResponse]') -> None: + pass + + @abc.abstractmethod + async def ReadOAuthApp(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ReadOAuthAppRequest, app.v1.app_pb2.ReadOAuthAppResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateOAuthApp(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateOAuthAppRequest, app.v1.app_pb2.UpdateOAuthAppResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteOAuthApp(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteOAuthAppRequest, app.v1.app_pb2.DeleteOAuthAppResponse]') -> None: + pass + + @abc.abstractmethod + async def ListOAuthApps(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListOAuthAppsRequest, app.v1.app_pb2.ListOAuthAppsResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateLocationRequest, app.v1.app_pb2.CreateLocationResponse]') -> None: + pass + + @abc.abstractmethod + async def GetLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetLocationRequest, app.v1.app_pb2.GetLocationResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateLocationRequest, app.v1.app_pb2.UpdateLocationResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteLocationRequest, app.v1.app_pb2.DeleteLocationResponse]') -> None: + pass + + @abc.abstractmethod + async def GetLocationMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetLocationMetadataRequest, app.v1.app_pb2.GetLocationMetadataResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateLocationMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateLocationMetadataRequest, app.v1.app_pb2.UpdateLocationMetadataResponse]') -> None: + pass + @abc.abstractmethod async def ListLocations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListLocationsRequest, app.v1.app_pb2.ListLocationsResponse]') -> None: pass + @abc.abstractmethod + async def ShareLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ShareLocationRequest, app.v1.app_pb2.ShareLocationResponse]') -> None: + pass + + @abc.abstractmethod + async def UnshareLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UnshareLocationRequest, app.v1.app_pb2.UnshareLocationResponse]') -> None: + pass + @abc.abstractmethod async def LocationAuth(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.LocationAuthRequest, app.v1.app_pb2.LocationAuthResponse]') -> None: pass + @abc.abstractmethod + async def CreateLocationSecret(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateLocationSecretRequest, app.v1.app_pb2.CreateLocationSecretResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteLocationSecret(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteLocationSecretRequest, app.v1.app_pb2.DeleteLocationSecretResponse]') -> None: + pass + @abc.abstractmethod async def GetRobot(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotRequest, app.v1.app_pb2.GetRobotResponse]') -> None: pass + @abc.abstractmethod + async def GetRobotMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotMetadataRequest, app.v1.app_pb2.GetRobotMetadataResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateRobotMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateRobotMetadataRequest, app.v1.app_pb2.UpdateRobotMetadataResponse]') -> None: + pass + + @abc.abstractmethod + async def GetRoverRentalRobots(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRoverRentalRobotsRequest, app.v1.app_pb2.GetRoverRentalRobotsResponse]') -> None: + pass + @abc.abstractmethod async def GetRobotParts(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotPartsRequest, app.v1.app_pb2.GetRobotPartsResponse]') -> None: pass @@ -59,12 +245,36 @@ async def NewRobotPart(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.NewRo async def DeleteRobotPart(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteRobotPartRequest, app.v1.app_pb2.DeleteRobotPartResponse]') -> None: pass + @abc.abstractmethod + async def GetRobotPartMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotPartMetadataRequest, app.v1.app_pb2.GetRobotPartMetadataResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateRobotPartMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateRobotPartMetadataRequest, app.v1.app_pb2.UpdateRobotPartMetadataResponse]') -> None: + pass + + @abc.abstractmethod + async def GetRobotAPIKeys(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotAPIKeysRequest, app.v1.app_pb2.GetRobotAPIKeysResponse]') -> None: + pass + @abc.abstractmethod async def MarkPartAsMain(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.MarkPartAsMainRequest, app.v1.app_pb2.MarkPartAsMainResponse]') -> None: pass @abc.abstractmethod - async def FindRobots(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.FindRobotsRequest, app.v1.app_pb2.FindRobotsResponse]') -> None: + async def MarkPartForRestart(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.MarkPartForRestartRequest, app.v1.app_pb2.MarkPartForRestartResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateRobotPartSecret(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateRobotPartSecretRequest, app.v1.app_pb2.CreateRobotPartSecretResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteRobotPartSecret(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteRobotPartSecretRequest, app.v1.app_pb2.DeleteRobotPartSecretResponse]') -> None: + pass + + @abc.abstractmethod + async def ListRobots(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListRobotsRequest, app.v1.app_pb2.ListRobotsResponse]') -> None: pass @abc.abstractmethod @@ -79,16 +289,523 @@ async def UpdateRobot(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.Update async def DeleteRobot(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteRobotRequest, app.v1.app_pb2.DeleteRobotResponse]') -> None: pass + @abc.abstractmethod + async def ListFragments(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListFragmentsRequest, app.v1.app_pb2.ListFragmentsResponse]') -> None: + pass + + @abc.abstractmethod + async def GetFragment(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetFragmentRequest, app.v1.app_pb2.GetFragmentResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateFragment(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateFragmentRequest, app.v1.app_pb2.CreateFragmentResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateFragment(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateFragmentRequest, app.v1.app_pb2.UpdateFragmentResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteFragment(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteFragmentRequest, app.v1.app_pb2.DeleteFragmentResponse]') -> None: + pass + + @abc.abstractmethod + async def ListNestedFragments(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListNestedFragmentsRequest, app.v1.app_pb2.ListNestedFragmentsResponse]') -> None: + pass + + @abc.abstractmethod + async def ListMachineFragments(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListMachineFragmentsRequest, app.v1.app_pb2.ListMachineFragmentsResponse]') -> None: + pass + + @abc.abstractmethod + async def ListMachineSummaries(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListMachineSummariesRequest, app.v1.app_pb2.ListMachineSummariesResponse]') -> None: + pass + + @abc.abstractmethod + async def GetFragmentHistory(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetFragmentHistoryRequest, app.v1.app_pb2.GetFragmentHistoryResponse]') -> None: + pass + + @abc.abstractmethod + async def GetFragmentUsage(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetFragmentUsageRequest, app.v1.app_pb2.GetFragmentUsageResponse]') -> None: + pass + + @abc.abstractmethod + async def SetFragmentTag(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.SetFragmentTagRequest, app.v1.app_pb2.SetFragmentTagResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteFragmentTag(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteFragmentTagRequest, app.v1.app_pb2.DeleteFragmentTagResponse]') -> None: + pass + + @abc.abstractmethod + async def AddRole(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.AddRoleRequest, app.v1.app_pb2.AddRoleResponse]') -> None: + pass + + @abc.abstractmethod + async def RemoveRole(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.RemoveRoleRequest, app.v1.app_pb2.RemoveRoleResponse]') -> None: + pass + + @abc.abstractmethod + async def ChangeRole(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ChangeRoleRequest, app.v1.app_pb2.ChangeRoleResponse]') -> None: + pass + + @abc.abstractmethod + async def ListAuthorizations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListAuthorizationsRequest, app.v1.app_pb2.ListAuthorizationsResponse]') -> None: + pass + + @abc.abstractmethod + async def CheckPermissions(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CheckPermissionsRequest, app.v1.app_pb2.CheckPermissionsResponse]') -> None: + pass + + @abc.abstractmethod + async def GetRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRegistryItemRequest, app.v1.app_pb2.GetRegistryItemResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateRegistryItemRequest, app.v1.app_pb2.CreateRegistryItemResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateRegistryItemRequest, app.v1.app_pb2.UpdateRegistryItemResponse]') -> None: + pass + + @abc.abstractmethod + async def ListRegistryItems(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListRegistryItemsRequest, app.v1.app_pb2.ListRegistryItemsResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteRegistryItemRequest, app.v1.app_pb2.DeleteRegistryItemResponse]') -> None: + pass + + @abc.abstractmethod + async def RenameRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.RenameRegistryItemRequest, app.v1.app_pb2.RenameRegistryItemResponse]') -> None: + pass + + @abc.abstractmethod + async def TransferRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.TransferRegistryItemRequest, app.v1.app_pb2.TransferRegistryItemResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateModule(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateModuleRequest, app.v1.app_pb2.CreateModuleResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateModule(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateModuleRequest, app.v1.app_pb2.UpdateModuleResponse]') -> None: + pass + + @abc.abstractmethod + async def UploadModuleFile(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UploadModuleFileRequest, app.v1.app_pb2.UploadModuleFileResponse]') -> None: + pass + + @abc.abstractmethod + async def GetModule(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetModuleRequest, app.v1.app_pb2.GetModuleResponse]') -> None: + pass + + @abc.abstractmethod + async def ListModules(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListModulesRequest, app.v1.app_pb2.ListModulesResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateKey(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateKeyRequest, app.v1.app_pb2.CreateKeyResponse]') -> None: + pass + + @abc.abstractmethod + async def DeleteKey(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteKeyRequest, app.v1.app_pb2.DeleteKeyResponse]') -> None: + pass + + @abc.abstractmethod + async def ListKeys(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListKeysRequest, app.v1.app_pb2.ListKeysResponse]') -> None: + pass + + @abc.abstractmethod + async def RenameKey(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.RenameKeyRequest, app.v1.app_pb2.RenameKeyResponse]') -> None: + pass + + @abc.abstractmethod + async def RotateKey(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.RotateKeyRequest, app.v1.app_pb2.RotateKeyResponse]') -> None: + pass + + @abc.abstractmethod + async def CreateKeyFromExistingKeyAuthorizations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateKeyFromExistingKeyAuthorizationsRequest, app.v1.app_pb2.CreateKeyFromExistingKeyAuthorizationsResponse]') -> None: + pass + + @abc.abstractmethod + async def GetAppContent(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetAppContentRequest, app.v1.app_pb2.GetAppContentResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.app.v1.AppService/ListOrganizations': grpclib.const.Handler(self.ListOrganizations, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListOrganizationsRequest, app.v1.app_pb2.ListOrganizationsResponse), '/viam.app.v1.AppService/ListLocations': grpclib.const.Handler(self.ListLocations, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListLocationsRequest, app.v1.app_pb2.ListLocationsResponse), '/viam.app.v1.AppService/LocationAuth': grpclib.const.Handler(self.LocationAuth, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.LocationAuthRequest, app.v1.app_pb2.LocationAuthResponse), '/viam.app.v1.AppService/GetRobot': grpclib.const.Handler(self.GetRobot, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotRequest, app.v1.app_pb2.GetRobotResponse), '/viam.app.v1.AppService/GetRobotParts': grpclib.const.Handler(self.GetRobotParts, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotPartsRequest, app.v1.app_pb2.GetRobotPartsResponse), '/viam.app.v1.AppService/GetRobotPart': grpclib.const.Handler(self.GetRobotPart, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotPartRequest, app.v1.app_pb2.GetRobotPartResponse), '/viam.app.v1.AppService/GetRobotPartLogs': grpclib.const.Handler(self.GetRobotPartLogs, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotPartLogsRequest, app.v1.app_pb2.GetRobotPartLogsResponse), '/viam.app.v1.AppService/TailRobotPartLogs': grpclib.const.Handler(self.TailRobotPartLogs, grpclib.const.Cardinality.UNARY_STREAM, app.v1.app_pb2.TailRobotPartLogsRequest, app.v1.app_pb2.TailRobotPartLogsResponse), '/viam.app.v1.AppService/GetRobotPartHistory': grpclib.const.Handler(self.GetRobotPartHistory, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotPartHistoryRequest, app.v1.app_pb2.GetRobotPartHistoryResponse), '/viam.app.v1.AppService/UpdateRobotPart': grpclib.const.Handler(self.UpdateRobotPart, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateRobotPartRequest, app.v1.app_pb2.UpdateRobotPartResponse), '/viam.app.v1.AppService/NewRobotPart': grpclib.const.Handler(self.NewRobotPart, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.NewRobotPartRequest, app.v1.app_pb2.NewRobotPartResponse), '/viam.app.v1.AppService/DeleteRobotPart': grpclib.const.Handler(self.DeleteRobotPart, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteRobotPartRequest, app.v1.app_pb2.DeleteRobotPartResponse), '/viam.app.v1.AppService/MarkPartAsMain': grpclib.const.Handler(self.MarkPartAsMain, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.MarkPartAsMainRequest, app.v1.app_pb2.MarkPartAsMainResponse), '/viam.app.v1.AppService/FindRobots': grpclib.const.Handler(self.FindRobots, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.FindRobotsRequest, app.v1.app_pb2.FindRobotsResponse), '/viam.app.v1.AppService/NewRobot': grpclib.const.Handler(self.NewRobot, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.NewRobotRequest, app.v1.app_pb2.NewRobotResponse), '/viam.app.v1.AppService/UpdateRobot': grpclib.const.Handler(self.UpdateRobot, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateRobotRequest, app.v1.app_pb2.UpdateRobotResponse), '/viam.app.v1.AppService/DeleteRobot': grpclib.const.Handler(self.DeleteRobot, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteRobotRequest, app.v1.app_pb2.DeleteRobotResponse)} + return {'/viam.app.v1.AppService/GetUserIDByEmail': grpclib.const.Handler(self.GetUserIDByEmail, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetUserIDByEmailRequest, app.v1.app_pb2.GetUserIDByEmailResponse), '/viam.app.v1.AppService/CreateOrganization': grpclib.const.Handler(self.CreateOrganization, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateOrganizationRequest, app.v1.app_pb2.CreateOrganizationResponse), '/viam.app.v1.AppService/ListOrganizations': grpclib.const.Handler(self.ListOrganizations, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListOrganizationsRequest, app.v1.app_pb2.ListOrganizationsResponse), '/viam.app.v1.AppService/GetOrganizationsWithAccessToLocation': grpclib.const.Handler(self.GetOrganizationsWithAccessToLocation, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetOrganizationsWithAccessToLocationRequest, app.v1.app_pb2.GetOrganizationsWithAccessToLocationResponse), '/viam.app.v1.AppService/ListOrganizationsByUser': grpclib.const.Handler(self.ListOrganizationsByUser, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListOrganizationsByUserRequest, app.v1.app_pb2.ListOrganizationsByUserResponse), '/viam.app.v1.AppService/SearchOrganizations': grpclib.const.Handler(self.SearchOrganizations, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.SearchOrganizationsRequest, app.v1.app_pb2.SearchOrganizationsResponse), '/viam.app.v1.AppService/GetOrganization': grpclib.const.Handler(self.GetOrganization, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetOrganizationRequest, app.v1.app_pb2.GetOrganizationResponse), '/viam.app.v1.AppService/GetOrganizationNamespaceAvailability': grpclib.const.Handler(self.GetOrganizationNamespaceAvailability, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetOrganizationNamespaceAvailabilityRequest, app.v1.app_pb2.GetOrganizationNamespaceAvailabilityResponse), '/viam.app.v1.AppService/UpdateOrganization': grpclib.const.Handler(self.UpdateOrganization, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateOrganizationRequest, app.v1.app_pb2.UpdateOrganizationResponse), '/viam.app.v1.AppService/UpdateOrganizationNamespace': grpclib.const.Handler(self.UpdateOrganizationNamespace, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateOrganizationNamespaceRequest, app.v1.app_pb2.UpdateOrganizationNamespaceResponse), '/viam.app.v1.AppService/DeleteOrganization': grpclib.const.Handler(self.DeleteOrganization, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteOrganizationRequest, app.v1.app_pb2.DeleteOrganizationResponse), '/viam.app.v1.AppService/GetOrganizationMetadata': grpclib.const.Handler(self.GetOrganizationMetadata, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetOrganizationMetadataRequest, app.v1.app_pb2.GetOrganizationMetadataResponse), '/viam.app.v1.AppService/UpdateOrganizationMetadata': grpclib.const.Handler(self.UpdateOrganizationMetadata, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateOrganizationMetadataRequest, app.v1.app_pb2.UpdateOrganizationMetadataResponse), '/viam.app.v1.AppService/ListOrganizationMembers': grpclib.const.Handler(self.ListOrganizationMembers, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListOrganizationMembersRequest, app.v1.app_pb2.ListOrganizationMembersResponse), '/viam.app.v1.AppService/CreateOrganizationInvite': grpclib.const.Handler(self.CreateOrganizationInvite, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateOrganizationInviteRequest, app.v1.app_pb2.CreateOrganizationInviteResponse), '/viam.app.v1.AppService/UpdateOrganizationInviteAuthorizations': grpclib.const.Handler(self.UpdateOrganizationInviteAuthorizations, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateOrganizationInviteAuthorizationsRequest, app.v1.app_pb2.UpdateOrganizationInviteAuthorizationsResponse), '/viam.app.v1.AppService/DeleteOrganizationMember': grpclib.const.Handler(self.DeleteOrganizationMember, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteOrganizationMemberRequest, app.v1.app_pb2.DeleteOrganizationMemberResponse), '/viam.app.v1.AppService/DeleteOrganizationInvite': grpclib.const.Handler(self.DeleteOrganizationInvite, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteOrganizationInviteRequest, app.v1.app_pb2.DeleteOrganizationInviteResponse), '/viam.app.v1.AppService/ResendOrganizationInvite': grpclib.const.Handler(self.ResendOrganizationInvite, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ResendOrganizationInviteRequest, app.v1.app_pb2.ResendOrganizationInviteResponse), '/viam.app.v1.AppService/EnableBillingService': grpclib.const.Handler(self.EnableBillingService, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.EnableBillingServiceRequest, app.v1.app_pb2.EnableBillingServiceResponse), '/viam.app.v1.AppService/DisableBillingService': grpclib.const.Handler(self.DisableBillingService, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DisableBillingServiceRequest, app.v1.app_pb2.DisableBillingServiceResponse), '/viam.app.v1.AppService/UpdateBillingService': grpclib.const.Handler(self.UpdateBillingService, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateBillingServiceRequest, app.v1.app_pb2.UpdateBillingServiceResponse), '/viam.app.v1.AppService/GetBillingServiceConfig': grpclib.const.Handler(self.GetBillingServiceConfig, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetBillingServiceConfigRequest, app.v1.app_pb2.GetBillingServiceConfigResponse), '/viam.app.v1.AppService/OrganizationSetSupportEmail': grpclib.const.Handler(self.OrganizationSetSupportEmail, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.OrganizationSetSupportEmailRequest, app.v1.app_pb2.OrganizationSetSupportEmailResponse), '/viam.app.v1.AppService/OrganizationGetSupportEmail': grpclib.const.Handler(self.OrganizationGetSupportEmail, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.OrganizationGetSupportEmailRequest, app.v1.app_pb2.OrganizationGetSupportEmailResponse), '/viam.app.v1.AppService/OrganizationSetLogo': grpclib.const.Handler(self.OrganizationSetLogo, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.OrganizationSetLogoRequest, app.v1.app_pb2.OrganizationSetLogoResponse), '/viam.app.v1.AppService/OrganizationGetLogo': grpclib.const.Handler(self.OrganizationGetLogo, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.OrganizationGetLogoRequest, app.v1.app_pb2.OrganizationGetLogoResponse), '/viam.app.v1.AppService/EnableAuthService': grpclib.const.Handler(self.EnableAuthService, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.EnableAuthServiceRequest, app.v1.app_pb2.EnableAuthServiceResponse), '/viam.app.v1.AppService/DisableAuthService': grpclib.const.Handler(self.DisableAuthService, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DisableAuthServiceRequest, app.v1.app_pb2.DisableAuthServiceResponse), '/viam.app.v1.AppService/CreateOAuthApp': grpclib.const.Handler(self.CreateOAuthApp, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateOAuthAppRequest, app.v1.app_pb2.CreateOAuthAppResponse), '/viam.app.v1.AppService/ReadOAuthApp': grpclib.const.Handler(self.ReadOAuthApp, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ReadOAuthAppRequest, app.v1.app_pb2.ReadOAuthAppResponse), '/viam.app.v1.AppService/UpdateOAuthApp': grpclib.const.Handler(self.UpdateOAuthApp, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateOAuthAppRequest, app.v1.app_pb2.UpdateOAuthAppResponse), '/viam.app.v1.AppService/DeleteOAuthApp': grpclib.const.Handler(self.DeleteOAuthApp, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteOAuthAppRequest, app.v1.app_pb2.DeleteOAuthAppResponse), '/viam.app.v1.AppService/ListOAuthApps': grpclib.const.Handler(self.ListOAuthApps, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListOAuthAppsRequest, app.v1.app_pb2.ListOAuthAppsResponse), '/viam.app.v1.AppService/CreateLocation': grpclib.const.Handler(self.CreateLocation, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateLocationRequest, app.v1.app_pb2.CreateLocationResponse), '/viam.app.v1.AppService/GetLocation': grpclib.const.Handler(self.GetLocation, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetLocationRequest, app.v1.app_pb2.GetLocationResponse), '/viam.app.v1.AppService/UpdateLocation': grpclib.const.Handler(self.UpdateLocation, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateLocationRequest, app.v1.app_pb2.UpdateLocationResponse), '/viam.app.v1.AppService/DeleteLocation': grpclib.const.Handler(self.DeleteLocation, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteLocationRequest, app.v1.app_pb2.DeleteLocationResponse), '/viam.app.v1.AppService/GetLocationMetadata': grpclib.const.Handler(self.GetLocationMetadata, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetLocationMetadataRequest, app.v1.app_pb2.GetLocationMetadataResponse), '/viam.app.v1.AppService/UpdateLocationMetadata': grpclib.const.Handler(self.UpdateLocationMetadata, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateLocationMetadataRequest, app.v1.app_pb2.UpdateLocationMetadataResponse), '/viam.app.v1.AppService/ListLocations': grpclib.const.Handler(self.ListLocations, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListLocationsRequest, app.v1.app_pb2.ListLocationsResponse), '/viam.app.v1.AppService/ShareLocation': grpclib.const.Handler(self.ShareLocation, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ShareLocationRequest, app.v1.app_pb2.ShareLocationResponse), '/viam.app.v1.AppService/UnshareLocation': grpclib.const.Handler(self.UnshareLocation, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UnshareLocationRequest, app.v1.app_pb2.UnshareLocationResponse), '/viam.app.v1.AppService/LocationAuth': grpclib.const.Handler(self.LocationAuth, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.LocationAuthRequest, app.v1.app_pb2.LocationAuthResponse), '/viam.app.v1.AppService/CreateLocationSecret': grpclib.const.Handler(self.CreateLocationSecret, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateLocationSecretRequest, app.v1.app_pb2.CreateLocationSecretResponse), '/viam.app.v1.AppService/DeleteLocationSecret': grpclib.const.Handler(self.DeleteLocationSecret, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteLocationSecretRequest, app.v1.app_pb2.DeleteLocationSecretResponse), '/viam.app.v1.AppService/GetRobot': grpclib.const.Handler(self.GetRobot, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotRequest, app.v1.app_pb2.GetRobotResponse), '/viam.app.v1.AppService/GetRobotMetadata': grpclib.const.Handler(self.GetRobotMetadata, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotMetadataRequest, app.v1.app_pb2.GetRobotMetadataResponse), '/viam.app.v1.AppService/UpdateRobotMetadata': grpclib.const.Handler(self.UpdateRobotMetadata, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateRobotMetadataRequest, app.v1.app_pb2.UpdateRobotMetadataResponse), '/viam.app.v1.AppService/GetRoverRentalRobots': grpclib.const.Handler(self.GetRoverRentalRobots, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRoverRentalRobotsRequest, app.v1.app_pb2.GetRoverRentalRobotsResponse), '/viam.app.v1.AppService/GetRobotParts': grpclib.const.Handler(self.GetRobotParts, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotPartsRequest, app.v1.app_pb2.GetRobotPartsResponse), '/viam.app.v1.AppService/GetRobotPart': grpclib.const.Handler(self.GetRobotPart, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotPartRequest, app.v1.app_pb2.GetRobotPartResponse), '/viam.app.v1.AppService/GetRobotPartLogs': grpclib.const.Handler(self.GetRobotPartLogs, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotPartLogsRequest, app.v1.app_pb2.GetRobotPartLogsResponse), '/viam.app.v1.AppService/TailRobotPartLogs': grpclib.const.Handler(self.TailRobotPartLogs, grpclib.const.Cardinality.UNARY_STREAM, app.v1.app_pb2.TailRobotPartLogsRequest, app.v1.app_pb2.TailRobotPartLogsResponse), '/viam.app.v1.AppService/GetRobotPartHistory': grpclib.const.Handler(self.GetRobotPartHistory, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotPartHistoryRequest, app.v1.app_pb2.GetRobotPartHistoryResponse), '/viam.app.v1.AppService/UpdateRobotPart': grpclib.const.Handler(self.UpdateRobotPart, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateRobotPartRequest, app.v1.app_pb2.UpdateRobotPartResponse), '/viam.app.v1.AppService/NewRobotPart': grpclib.const.Handler(self.NewRobotPart, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.NewRobotPartRequest, app.v1.app_pb2.NewRobotPartResponse), '/viam.app.v1.AppService/DeleteRobotPart': grpclib.const.Handler(self.DeleteRobotPart, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteRobotPartRequest, app.v1.app_pb2.DeleteRobotPartResponse), '/viam.app.v1.AppService/GetRobotPartMetadata': grpclib.const.Handler(self.GetRobotPartMetadata, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotPartMetadataRequest, app.v1.app_pb2.GetRobotPartMetadataResponse), '/viam.app.v1.AppService/UpdateRobotPartMetadata': grpclib.const.Handler(self.UpdateRobotPartMetadata, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateRobotPartMetadataRequest, app.v1.app_pb2.UpdateRobotPartMetadataResponse), '/viam.app.v1.AppService/GetRobotAPIKeys': grpclib.const.Handler(self.GetRobotAPIKeys, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRobotAPIKeysRequest, app.v1.app_pb2.GetRobotAPIKeysResponse), '/viam.app.v1.AppService/MarkPartAsMain': grpclib.const.Handler(self.MarkPartAsMain, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.MarkPartAsMainRequest, app.v1.app_pb2.MarkPartAsMainResponse), '/viam.app.v1.AppService/MarkPartForRestart': grpclib.const.Handler(self.MarkPartForRestart, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.MarkPartForRestartRequest, app.v1.app_pb2.MarkPartForRestartResponse), '/viam.app.v1.AppService/CreateRobotPartSecret': grpclib.const.Handler(self.CreateRobotPartSecret, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateRobotPartSecretRequest, app.v1.app_pb2.CreateRobotPartSecretResponse), '/viam.app.v1.AppService/DeleteRobotPartSecret': grpclib.const.Handler(self.DeleteRobotPartSecret, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteRobotPartSecretRequest, app.v1.app_pb2.DeleteRobotPartSecretResponse), '/viam.app.v1.AppService/ListRobots': grpclib.const.Handler(self.ListRobots, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListRobotsRequest, app.v1.app_pb2.ListRobotsResponse), '/viam.app.v1.AppService/NewRobot': grpclib.const.Handler(self.NewRobot, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.NewRobotRequest, app.v1.app_pb2.NewRobotResponse), '/viam.app.v1.AppService/UpdateRobot': grpclib.const.Handler(self.UpdateRobot, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateRobotRequest, app.v1.app_pb2.UpdateRobotResponse), '/viam.app.v1.AppService/DeleteRobot': grpclib.const.Handler(self.DeleteRobot, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteRobotRequest, app.v1.app_pb2.DeleteRobotResponse), '/viam.app.v1.AppService/ListFragments': grpclib.const.Handler(self.ListFragments, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListFragmentsRequest, app.v1.app_pb2.ListFragmentsResponse), '/viam.app.v1.AppService/GetFragment': grpclib.const.Handler(self.GetFragment, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetFragmentRequest, app.v1.app_pb2.GetFragmentResponse), '/viam.app.v1.AppService/CreateFragment': grpclib.const.Handler(self.CreateFragment, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateFragmentRequest, app.v1.app_pb2.CreateFragmentResponse), '/viam.app.v1.AppService/UpdateFragment': grpclib.const.Handler(self.UpdateFragment, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateFragmentRequest, app.v1.app_pb2.UpdateFragmentResponse), '/viam.app.v1.AppService/DeleteFragment': grpclib.const.Handler(self.DeleteFragment, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteFragmentRequest, app.v1.app_pb2.DeleteFragmentResponse), '/viam.app.v1.AppService/ListNestedFragments': grpclib.const.Handler(self.ListNestedFragments, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListNestedFragmentsRequest, app.v1.app_pb2.ListNestedFragmentsResponse), '/viam.app.v1.AppService/ListMachineFragments': grpclib.const.Handler(self.ListMachineFragments, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListMachineFragmentsRequest, app.v1.app_pb2.ListMachineFragmentsResponse), '/viam.app.v1.AppService/ListMachineSummaries': grpclib.const.Handler(self.ListMachineSummaries, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListMachineSummariesRequest, app.v1.app_pb2.ListMachineSummariesResponse), '/viam.app.v1.AppService/GetFragmentHistory': grpclib.const.Handler(self.GetFragmentHistory, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetFragmentHistoryRequest, app.v1.app_pb2.GetFragmentHistoryResponse), '/viam.app.v1.AppService/GetFragmentUsage': grpclib.const.Handler(self.GetFragmentUsage, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetFragmentUsageRequest, app.v1.app_pb2.GetFragmentUsageResponse), '/viam.app.v1.AppService/SetFragmentTag': grpclib.const.Handler(self.SetFragmentTag, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.SetFragmentTagRequest, app.v1.app_pb2.SetFragmentTagResponse), '/viam.app.v1.AppService/DeleteFragmentTag': grpclib.const.Handler(self.DeleteFragmentTag, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteFragmentTagRequest, app.v1.app_pb2.DeleteFragmentTagResponse), '/viam.app.v1.AppService/AddRole': grpclib.const.Handler(self.AddRole, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.AddRoleRequest, app.v1.app_pb2.AddRoleResponse), '/viam.app.v1.AppService/RemoveRole': grpclib.const.Handler(self.RemoveRole, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.RemoveRoleRequest, app.v1.app_pb2.RemoveRoleResponse), '/viam.app.v1.AppService/ChangeRole': grpclib.const.Handler(self.ChangeRole, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ChangeRoleRequest, app.v1.app_pb2.ChangeRoleResponse), '/viam.app.v1.AppService/ListAuthorizations': grpclib.const.Handler(self.ListAuthorizations, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListAuthorizationsRequest, app.v1.app_pb2.ListAuthorizationsResponse), '/viam.app.v1.AppService/CheckPermissions': grpclib.const.Handler(self.CheckPermissions, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CheckPermissionsRequest, app.v1.app_pb2.CheckPermissionsResponse), '/viam.app.v1.AppService/GetRegistryItem': grpclib.const.Handler(self.GetRegistryItem, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetRegistryItemRequest, app.v1.app_pb2.GetRegistryItemResponse), '/viam.app.v1.AppService/CreateRegistryItem': grpclib.const.Handler(self.CreateRegistryItem, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateRegistryItemRequest, app.v1.app_pb2.CreateRegistryItemResponse), '/viam.app.v1.AppService/UpdateRegistryItem': grpclib.const.Handler(self.UpdateRegistryItem, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateRegistryItemRequest, app.v1.app_pb2.UpdateRegistryItemResponse), '/viam.app.v1.AppService/ListRegistryItems': grpclib.const.Handler(self.ListRegistryItems, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListRegistryItemsRequest, app.v1.app_pb2.ListRegistryItemsResponse), '/viam.app.v1.AppService/DeleteRegistryItem': grpclib.const.Handler(self.DeleteRegistryItem, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteRegistryItemRequest, app.v1.app_pb2.DeleteRegistryItemResponse), '/viam.app.v1.AppService/RenameRegistryItem': grpclib.const.Handler(self.RenameRegistryItem, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.RenameRegistryItemRequest, app.v1.app_pb2.RenameRegistryItemResponse), '/viam.app.v1.AppService/TransferRegistryItem': grpclib.const.Handler(self.TransferRegistryItem, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.TransferRegistryItemRequest, app.v1.app_pb2.TransferRegistryItemResponse), '/viam.app.v1.AppService/CreateModule': grpclib.const.Handler(self.CreateModule, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateModuleRequest, app.v1.app_pb2.CreateModuleResponse), '/viam.app.v1.AppService/UpdateModule': grpclib.const.Handler(self.UpdateModule, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.UpdateModuleRequest, app.v1.app_pb2.UpdateModuleResponse), '/viam.app.v1.AppService/UploadModuleFile': grpclib.const.Handler(self.UploadModuleFile, grpclib.const.Cardinality.STREAM_UNARY, app.v1.app_pb2.UploadModuleFileRequest, app.v1.app_pb2.UploadModuleFileResponse), '/viam.app.v1.AppService/GetModule': grpclib.const.Handler(self.GetModule, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetModuleRequest, app.v1.app_pb2.GetModuleResponse), '/viam.app.v1.AppService/ListModules': grpclib.const.Handler(self.ListModules, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListModulesRequest, app.v1.app_pb2.ListModulesResponse), '/viam.app.v1.AppService/CreateKey': grpclib.const.Handler(self.CreateKey, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateKeyRequest, app.v1.app_pb2.CreateKeyResponse), '/viam.app.v1.AppService/DeleteKey': grpclib.const.Handler(self.DeleteKey, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.DeleteKeyRequest, app.v1.app_pb2.DeleteKeyResponse), '/viam.app.v1.AppService/ListKeys': grpclib.const.Handler(self.ListKeys, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.ListKeysRequest, app.v1.app_pb2.ListKeysResponse), '/viam.app.v1.AppService/RenameKey': grpclib.const.Handler(self.RenameKey, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.RenameKeyRequest, app.v1.app_pb2.RenameKeyResponse), '/viam.app.v1.AppService/RotateKey': grpclib.const.Handler(self.RotateKey, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.RotateKeyRequest, app.v1.app_pb2.RotateKeyResponse), '/viam.app.v1.AppService/CreateKeyFromExistingKeyAuthorizations': grpclib.const.Handler(self.CreateKeyFromExistingKeyAuthorizations, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.CreateKeyFromExistingKeyAuthorizationsRequest, app.v1.app_pb2.CreateKeyFromExistingKeyAuthorizationsResponse), '/viam.app.v1.AppService/GetAppContent': grpclib.const.Handler(self.GetAppContent, grpclib.const.Cardinality.UNARY_UNARY, app.v1.app_pb2.GetAppContentRequest, app.v1.app_pb2.GetAppContentResponse)} + +class UnimplementedAppServiceBase(AppServiceBase): + + async def GetUserIDByEmail(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetUserIDByEmailRequest, app.v1.app_pb2.GetUserIDByEmailResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateOrganization(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateOrganizationRequest, app.v1.app_pb2.CreateOrganizationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListOrganizations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListOrganizationsRequest, app.v1.app_pb2.ListOrganizationsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetOrganizationsWithAccessToLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetOrganizationsWithAccessToLocationRequest, app.v1.app_pb2.GetOrganizationsWithAccessToLocationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListOrganizationsByUser(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListOrganizationsByUserRequest, app.v1.app_pb2.ListOrganizationsByUserResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SearchOrganizations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.SearchOrganizationsRequest, app.v1.app_pb2.SearchOrganizationsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetOrganization(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetOrganizationRequest, app.v1.app_pb2.GetOrganizationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetOrganizationNamespaceAvailability(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetOrganizationNamespaceAvailabilityRequest, app.v1.app_pb2.GetOrganizationNamespaceAvailabilityResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateOrganization(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateOrganizationRequest, app.v1.app_pb2.UpdateOrganizationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateOrganizationNamespace(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateOrganizationNamespaceRequest, app.v1.app_pb2.UpdateOrganizationNamespaceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteOrganization(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteOrganizationRequest, app.v1.app_pb2.DeleteOrganizationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetOrganizationMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetOrganizationMetadataRequest, app.v1.app_pb2.GetOrganizationMetadataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateOrganizationMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateOrganizationMetadataRequest, app.v1.app_pb2.UpdateOrganizationMetadataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListOrganizationMembers(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListOrganizationMembersRequest, app.v1.app_pb2.ListOrganizationMembersResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateOrganizationInvite(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateOrganizationInviteRequest, app.v1.app_pb2.CreateOrganizationInviteResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateOrganizationInviteAuthorizations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateOrganizationInviteAuthorizationsRequest, app.v1.app_pb2.UpdateOrganizationInviteAuthorizationsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteOrganizationMember(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteOrganizationMemberRequest, app.v1.app_pb2.DeleteOrganizationMemberResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteOrganizationInvite(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteOrganizationInviteRequest, app.v1.app_pb2.DeleteOrganizationInviteResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ResendOrganizationInvite(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ResendOrganizationInviteRequest, app.v1.app_pb2.ResendOrganizationInviteResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def EnableBillingService(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.EnableBillingServiceRequest, app.v1.app_pb2.EnableBillingServiceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DisableBillingService(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DisableBillingServiceRequest, app.v1.app_pb2.DisableBillingServiceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateBillingService(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateBillingServiceRequest, app.v1.app_pb2.UpdateBillingServiceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetBillingServiceConfig(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetBillingServiceConfigRequest, app.v1.app_pb2.GetBillingServiceConfigResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def OrganizationSetSupportEmail(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.OrganizationSetSupportEmailRequest, app.v1.app_pb2.OrganizationSetSupportEmailResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def OrganizationGetSupportEmail(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.OrganizationGetSupportEmailRequest, app.v1.app_pb2.OrganizationGetSupportEmailResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def OrganizationSetLogo(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.OrganizationSetLogoRequest, app.v1.app_pb2.OrganizationSetLogoResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def OrganizationGetLogo(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.OrganizationGetLogoRequest, app.v1.app_pb2.OrganizationGetLogoResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def EnableAuthService(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.EnableAuthServiceRequest, app.v1.app_pb2.EnableAuthServiceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DisableAuthService(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DisableAuthServiceRequest, app.v1.app_pb2.DisableAuthServiceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateOAuthApp(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateOAuthAppRequest, app.v1.app_pb2.CreateOAuthAppResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ReadOAuthApp(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ReadOAuthAppRequest, app.v1.app_pb2.ReadOAuthAppResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateOAuthApp(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateOAuthAppRequest, app.v1.app_pb2.UpdateOAuthAppResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteOAuthApp(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteOAuthAppRequest, app.v1.app_pb2.DeleteOAuthAppResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListOAuthApps(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListOAuthAppsRequest, app.v1.app_pb2.ListOAuthAppsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateLocationRequest, app.v1.app_pb2.CreateLocationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetLocationRequest, app.v1.app_pb2.GetLocationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateLocationRequest, app.v1.app_pb2.UpdateLocationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteLocationRequest, app.v1.app_pb2.DeleteLocationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetLocationMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetLocationMetadataRequest, app.v1.app_pb2.GetLocationMetadataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateLocationMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateLocationMetadataRequest, app.v1.app_pb2.UpdateLocationMetadataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListLocations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListLocationsRequest, app.v1.app_pb2.ListLocationsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ShareLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ShareLocationRequest, app.v1.app_pb2.ShareLocationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UnshareLocation(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UnshareLocationRequest, app.v1.app_pb2.UnshareLocationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def LocationAuth(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.LocationAuthRequest, app.v1.app_pb2.LocationAuthResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateLocationSecret(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateLocationSecretRequest, app.v1.app_pb2.CreateLocationSecretResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteLocationSecret(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteLocationSecretRequest, app.v1.app_pb2.DeleteLocationSecretResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetRobot(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotRequest, app.v1.app_pb2.GetRobotResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetRobotMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotMetadataRequest, app.v1.app_pb2.GetRobotMetadataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateRobotMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateRobotMetadataRequest, app.v1.app_pb2.UpdateRobotMetadataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetRoverRentalRobots(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRoverRentalRobotsRequest, app.v1.app_pb2.GetRoverRentalRobotsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetRobotParts(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotPartsRequest, app.v1.app_pb2.GetRobotPartsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetRobotPart(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotPartRequest, app.v1.app_pb2.GetRobotPartResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetRobotPartLogs(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotPartLogsRequest, app.v1.app_pb2.GetRobotPartLogsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def TailRobotPartLogs(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.TailRobotPartLogsRequest, app.v1.app_pb2.TailRobotPartLogsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetRobotPartHistory(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotPartHistoryRequest, app.v1.app_pb2.GetRobotPartHistoryResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateRobotPart(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateRobotPartRequest, app.v1.app_pb2.UpdateRobotPartResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def NewRobotPart(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.NewRobotPartRequest, app.v1.app_pb2.NewRobotPartResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteRobotPart(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteRobotPartRequest, app.v1.app_pb2.DeleteRobotPartResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetRobotPartMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotPartMetadataRequest, app.v1.app_pb2.GetRobotPartMetadataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateRobotPartMetadata(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateRobotPartMetadataRequest, app.v1.app_pb2.UpdateRobotPartMetadataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetRobotAPIKeys(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRobotAPIKeysRequest, app.v1.app_pb2.GetRobotAPIKeysResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def MarkPartAsMain(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.MarkPartAsMainRequest, app.v1.app_pb2.MarkPartAsMainResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def MarkPartForRestart(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.MarkPartForRestartRequest, app.v1.app_pb2.MarkPartForRestartResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateRobotPartSecret(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateRobotPartSecretRequest, app.v1.app_pb2.CreateRobotPartSecretResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteRobotPartSecret(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteRobotPartSecretRequest, app.v1.app_pb2.DeleteRobotPartSecretResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListRobots(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListRobotsRequest, app.v1.app_pb2.ListRobotsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def NewRobot(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.NewRobotRequest, app.v1.app_pb2.NewRobotResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateRobot(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateRobotRequest, app.v1.app_pb2.UpdateRobotResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteRobot(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteRobotRequest, app.v1.app_pb2.DeleteRobotResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListFragments(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListFragmentsRequest, app.v1.app_pb2.ListFragmentsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetFragment(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetFragmentRequest, app.v1.app_pb2.GetFragmentResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateFragment(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateFragmentRequest, app.v1.app_pb2.CreateFragmentResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateFragment(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateFragmentRequest, app.v1.app_pb2.UpdateFragmentResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteFragment(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteFragmentRequest, app.v1.app_pb2.DeleteFragmentResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListNestedFragments(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListNestedFragmentsRequest, app.v1.app_pb2.ListNestedFragmentsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListMachineFragments(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListMachineFragmentsRequest, app.v1.app_pb2.ListMachineFragmentsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListMachineSummaries(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListMachineSummariesRequest, app.v1.app_pb2.ListMachineSummariesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetFragmentHistory(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetFragmentHistoryRequest, app.v1.app_pb2.GetFragmentHistoryResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetFragmentUsage(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetFragmentUsageRequest, app.v1.app_pb2.GetFragmentUsageResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetFragmentTag(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.SetFragmentTagRequest, app.v1.app_pb2.SetFragmentTagResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteFragmentTag(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteFragmentTagRequest, app.v1.app_pb2.DeleteFragmentTagResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def AddRole(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.AddRoleRequest, app.v1.app_pb2.AddRoleResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RemoveRole(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.RemoveRoleRequest, app.v1.app_pb2.RemoveRoleResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ChangeRole(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ChangeRoleRequest, app.v1.app_pb2.ChangeRoleResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListAuthorizations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListAuthorizationsRequest, app.v1.app_pb2.ListAuthorizationsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CheckPermissions(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CheckPermissionsRequest, app.v1.app_pb2.CheckPermissionsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetRegistryItemRequest, app.v1.app_pb2.GetRegistryItemResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateRegistryItemRequest, app.v1.app_pb2.CreateRegistryItemResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateRegistryItemRequest, app.v1.app_pb2.UpdateRegistryItemResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListRegistryItems(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListRegistryItemsRequest, app.v1.app_pb2.ListRegistryItemsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteRegistryItemRequest, app.v1.app_pb2.DeleteRegistryItemResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RenameRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.RenameRegistryItemRequest, app.v1.app_pb2.RenameRegistryItemResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def TransferRegistryItem(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.TransferRegistryItemRequest, app.v1.app_pb2.TransferRegistryItemResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateModule(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateModuleRequest, app.v1.app_pb2.CreateModuleResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateModule(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UpdateModuleRequest, app.v1.app_pb2.UpdateModuleResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UploadModuleFile(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.UploadModuleFileRequest, app.v1.app_pb2.UploadModuleFileResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetModule(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetModuleRequest, app.v1.app_pb2.GetModuleResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListModules(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListModulesRequest, app.v1.app_pb2.ListModulesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateKey(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateKeyRequest, app.v1.app_pb2.CreateKeyResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DeleteKey(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.DeleteKeyRequest, app.v1.app_pb2.DeleteKeyResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListKeys(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.ListKeysRequest, app.v1.app_pb2.ListKeysResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RenameKey(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.RenameKeyRequest, app.v1.app_pb2.RenameKeyResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RotateKey(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.RotateKeyRequest, app.v1.app_pb2.RotateKeyResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CreateKeyFromExistingKeyAuthorizations(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.CreateKeyFromExistingKeyAuthorizationsRequest, app.v1.app_pb2.CreateKeyFromExistingKeyAuthorizationsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetAppContent(self, stream: 'grpclib.server.Stream[app.v1.app_pb2.GetAppContentRequest, app.v1.app_pb2.GetAppContentResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class AppServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: + self.GetUserIDByEmail = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetUserIDByEmail', app.v1.app_pb2.GetUserIDByEmailRequest, app.v1.app_pb2.GetUserIDByEmailResponse) + self.CreateOrganization = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateOrganization', app.v1.app_pb2.CreateOrganizationRequest, app.v1.app_pb2.CreateOrganizationResponse) self.ListOrganizations = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListOrganizations', app.v1.app_pb2.ListOrganizationsRequest, app.v1.app_pb2.ListOrganizationsResponse) + self.GetOrganizationsWithAccessToLocation = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetOrganizationsWithAccessToLocation', app.v1.app_pb2.GetOrganizationsWithAccessToLocationRequest, app.v1.app_pb2.GetOrganizationsWithAccessToLocationResponse) + self.ListOrganizationsByUser = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListOrganizationsByUser', app.v1.app_pb2.ListOrganizationsByUserRequest, app.v1.app_pb2.ListOrganizationsByUserResponse) + self.SearchOrganizations = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/SearchOrganizations', app.v1.app_pb2.SearchOrganizationsRequest, app.v1.app_pb2.SearchOrganizationsResponse) + self.GetOrganization = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetOrganization', app.v1.app_pb2.GetOrganizationRequest, app.v1.app_pb2.GetOrganizationResponse) + self.GetOrganizationNamespaceAvailability = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetOrganizationNamespaceAvailability', app.v1.app_pb2.GetOrganizationNamespaceAvailabilityRequest, app.v1.app_pb2.GetOrganizationNamespaceAvailabilityResponse) + self.UpdateOrganization = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateOrganization', app.v1.app_pb2.UpdateOrganizationRequest, app.v1.app_pb2.UpdateOrganizationResponse) + self.UpdateOrganizationNamespace = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateOrganizationNamespace', app.v1.app_pb2.UpdateOrganizationNamespaceRequest, app.v1.app_pb2.UpdateOrganizationNamespaceResponse) + self.DeleteOrganization = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteOrganization', app.v1.app_pb2.DeleteOrganizationRequest, app.v1.app_pb2.DeleteOrganizationResponse) + self.GetOrganizationMetadata = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetOrganizationMetadata', app.v1.app_pb2.GetOrganizationMetadataRequest, app.v1.app_pb2.GetOrganizationMetadataResponse) + self.UpdateOrganizationMetadata = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateOrganizationMetadata', app.v1.app_pb2.UpdateOrganizationMetadataRequest, app.v1.app_pb2.UpdateOrganizationMetadataResponse) + self.ListOrganizationMembers = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListOrganizationMembers', app.v1.app_pb2.ListOrganizationMembersRequest, app.v1.app_pb2.ListOrganizationMembersResponse) + self.CreateOrganizationInvite = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateOrganizationInvite', app.v1.app_pb2.CreateOrganizationInviteRequest, app.v1.app_pb2.CreateOrganizationInviteResponse) + self.UpdateOrganizationInviteAuthorizations = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateOrganizationInviteAuthorizations', app.v1.app_pb2.UpdateOrganizationInviteAuthorizationsRequest, app.v1.app_pb2.UpdateOrganizationInviteAuthorizationsResponse) + self.DeleteOrganizationMember = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteOrganizationMember', app.v1.app_pb2.DeleteOrganizationMemberRequest, app.v1.app_pb2.DeleteOrganizationMemberResponse) + self.DeleteOrganizationInvite = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteOrganizationInvite', app.v1.app_pb2.DeleteOrganizationInviteRequest, app.v1.app_pb2.DeleteOrganizationInviteResponse) + self.ResendOrganizationInvite = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ResendOrganizationInvite', app.v1.app_pb2.ResendOrganizationInviteRequest, app.v1.app_pb2.ResendOrganizationInviteResponse) + self.EnableBillingService = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/EnableBillingService', app.v1.app_pb2.EnableBillingServiceRequest, app.v1.app_pb2.EnableBillingServiceResponse) + self.DisableBillingService = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DisableBillingService', app.v1.app_pb2.DisableBillingServiceRequest, app.v1.app_pb2.DisableBillingServiceResponse) + self.UpdateBillingService = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateBillingService', app.v1.app_pb2.UpdateBillingServiceRequest, app.v1.app_pb2.UpdateBillingServiceResponse) + self.GetBillingServiceConfig = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetBillingServiceConfig', app.v1.app_pb2.GetBillingServiceConfigRequest, app.v1.app_pb2.GetBillingServiceConfigResponse) + self.OrganizationSetSupportEmail = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/OrganizationSetSupportEmail', app.v1.app_pb2.OrganizationSetSupportEmailRequest, app.v1.app_pb2.OrganizationSetSupportEmailResponse) + self.OrganizationGetSupportEmail = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/OrganizationGetSupportEmail', app.v1.app_pb2.OrganizationGetSupportEmailRequest, app.v1.app_pb2.OrganizationGetSupportEmailResponse) + self.OrganizationSetLogo = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/OrganizationSetLogo', app.v1.app_pb2.OrganizationSetLogoRequest, app.v1.app_pb2.OrganizationSetLogoResponse) + self.OrganizationGetLogo = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/OrganizationGetLogo', app.v1.app_pb2.OrganizationGetLogoRequest, app.v1.app_pb2.OrganizationGetLogoResponse) + self.EnableAuthService = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/EnableAuthService', app.v1.app_pb2.EnableAuthServiceRequest, app.v1.app_pb2.EnableAuthServiceResponse) + self.DisableAuthService = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DisableAuthService', app.v1.app_pb2.DisableAuthServiceRequest, app.v1.app_pb2.DisableAuthServiceResponse) + self.CreateOAuthApp = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateOAuthApp', app.v1.app_pb2.CreateOAuthAppRequest, app.v1.app_pb2.CreateOAuthAppResponse) + self.ReadOAuthApp = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ReadOAuthApp', app.v1.app_pb2.ReadOAuthAppRequest, app.v1.app_pb2.ReadOAuthAppResponse) + self.UpdateOAuthApp = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateOAuthApp', app.v1.app_pb2.UpdateOAuthAppRequest, app.v1.app_pb2.UpdateOAuthAppResponse) + self.DeleteOAuthApp = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteOAuthApp', app.v1.app_pb2.DeleteOAuthAppRequest, app.v1.app_pb2.DeleteOAuthAppResponse) + self.ListOAuthApps = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListOAuthApps', app.v1.app_pb2.ListOAuthAppsRequest, app.v1.app_pb2.ListOAuthAppsResponse) + self.CreateLocation = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateLocation', app.v1.app_pb2.CreateLocationRequest, app.v1.app_pb2.CreateLocationResponse) + self.GetLocation = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetLocation', app.v1.app_pb2.GetLocationRequest, app.v1.app_pb2.GetLocationResponse) + self.UpdateLocation = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateLocation', app.v1.app_pb2.UpdateLocationRequest, app.v1.app_pb2.UpdateLocationResponse) + self.DeleteLocation = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteLocation', app.v1.app_pb2.DeleteLocationRequest, app.v1.app_pb2.DeleteLocationResponse) + self.GetLocationMetadata = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetLocationMetadata', app.v1.app_pb2.GetLocationMetadataRequest, app.v1.app_pb2.GetLocationMetadataResponse) + self.UpdateLocationMetadata = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateLocationMetadata', app.v1.app_pb2.UpdateLocationMetadataRequest, app.v1.app_pb2.UpdateLocationMetadataResponse) self.ListLocations = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListLocations', app.v1.app_pb2.ListLocationsRequest, app.v1.app_pb2.ListLocationsResponse) + self.ShareLocation = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ShareLocation', app.v1.app_pb2.ShareLocationRequest, app.v1.app_pb2.ShareLocationResponse) + self.UnshareLocation = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UnshareLocation', app.v1.app_pb2.UnshareLocationRequest, app.v1.app_pb2.UnshareLocationResponse) self.LocationAuth = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/LocationAuth', app.v1.app_pb2.LocationAuthRequest, app.v1.app_pb2.LocationAuthResponse) + self.CreateLocationSecret = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateLocationSecret', app.v1.app_pb2.CreateLocationSecretRequest, app.v1.app_pb2.CreateLocationSecretResponse) + self.DeleteLocationSecret = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteLocationSecret', app.v1.app_pb2.DeleteLocationSecretRequest, app.v1.app_pb2.DeleteLocationSecretResponse) self.GetRobot = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetRobot', app.v1.app_pb2.GetRobotRequest, app.v1.app_pb2.GetRobotResponse) + self.GetRobotMetadata = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetRobotMetadata', app.v1.app_pb2.GetRobotMetadataRequest, app.v1.app_pb2.GetRobotMetadataResponse) + self.UpdateRobotMetadata = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateRobotMetadata', app.v1.app_pb2.UpdateRobotMetadataRequest, app.v1.app_pb2.UpdateRobotMetadataResponse) + self.GetRoverRentalRobots = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetRoverRentalRobots', app.v1.app_pb2.GetRoverRentalRobotsRequest, app.v1.app_pb2.GetRoverRentalRobotsResponse) self.GetRobotParts = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetRobotParts', app.v1.app_pb2.GetRobotPartsRequest, app.v1.app_pb2.GetRobotPartsResponse) self.GetRobotPart = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetRobotPart', app.v1.app_pb2.GetRobotPartRequest, app.v1.app_pb2.GetRobotPartResponse) self.GetRobotPartLogs = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetRobotPartLogs', app.v1.app_pb2.GetRobotPartLogsRequest, app.v1.app_pb2.GetRobotPartLogsResponse) @@ -97,8 +814,50 @@ def __init__(self, channel: grpclib.client.Channel) -> None: self.UpdateRobotPart = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateRobotPart', app.v1.app_pb2.UpdateRobotPartRequest, app.v1.app_pb2.UpdateRobotPartResponse) self.NewRobotPart = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/NewRobotPart', app.v1.app_pb2.NewRobotPartRequest, app.v1.app_pb2.NewRobotPartResponse) self.DeleteRobotPart = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteRobotPart', app.v1.app_pb2.DeleteRobotPartRequest, app.v1.app_pb2.DeleteRobotPartResponse) + self.GetRobotPartMetadata = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetRobotPartMetadata', app.v1.app_pb2.GetRobotPartMetadataRequest, app.v1.app_pb2.GetRobotPartMetadataResponse) + self.UpdateRobotPartMetadata = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateRobotPartMetadata', app.v1.app_pb2.UpdateRobotPartMetadataRequest, app.v1.app_pb2.UpdateRobotPartMetadataResponse) + self.GetRobotAPIKeys = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetRobotAPIKeys', app.v1.app_pb2.GetRobotAPIKeysRequest, app.v1.app_pb2.GetRobotAPIKeysResponse) self.MarkPartAsMain = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/MarkPartAsMain', app.v1.app_pb2.MarkPartAsMainRequest, app.v1.app_pb2.MarkPartAsMainResponse) - self.FindRobots = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/FindRobots', app.v1.app_pb2.FindRobotsRequest, app.v1.app_pb2.FindRobotsResponse) + self.MarkPartForRestart = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/MarkPartForRestart', app.v1.app_pb2.MarkPartForRestartRequest, app.v1.app_pb2.MarkPartForRestartResponse) + self.CreateRobotPartSecret = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateRobotPartSecret', app.v1.app_pb2.CreateRobotPartSecretRequest, app.v1.app_pb2.CreateRobotPartSecretResponse) + self.DeleteRobotPartSecret = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteRobotPartSecret', app.v1.app_pb2.DeleteRobotPartSecretRequest, app.v1.app_pb2.DeleteRobotPartSecretResponse) + self.ListRobots = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListRobots', app.v1.app_pb2.ListRobotsRequest, app.v1.app_pb2.ListRobotsResponse) self.NewRobot = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/NewRobot', app.v1.app_pb2.NewRobotRequest, app.v1.app_pb2.NewRobotResponse) self.UpdateRobot = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateRobot', app.v1.app_pb2.UpdateRobotRequest, app.v1.app_pb2.UpdateRobotResponse) - self.DeleteRobot = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteRobot', app.v1.app_pb2.DeleteRobotRequest, app.v1.app_pb2.DeleteRobotResponse) \ No newline at end of file + self.DeleteRobot = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteRobot', app.v1.app_pb2.DeleteRobotRequest, app.v1.app_pb2.DeleteRobotResponse) + self.ListFragments = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListFragments', app.v1.app_pb2.ListFragmentsRequest, app.v1.app_pb2.ListFragmentsResponse) + self.GetFragment = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetFragment', app.v1.app_pb2.GetFragmentRequest, app.v1.app_pb2.GetFragmentResponse) + self.CreateFragment = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateFragment', app.v1.app_pb2.CreateFragmentRequest, app.v1.app_pb2.CreateFragmentResponse) + self.UpdateFragment = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateFragment', app.v1.app_pb2.UpdateFragmentRequest, app.v1.app_pb2.UpdateFragmentResponse) + self.DeleteFragment = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteFragment', app.v1.app_pb2.DeleteFragmentRequest, app.v1.app_pb2.DeleteFragmentResponse) + self.ListNestedFragments = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListNestedFragments', app.v1.app_pb2.ListNestedFragmentsRequest, app.v1.app_pb2.ListNestedFragmentsResponse) + self.ListMachineFragments = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListMachineFragments', app.v1.app_pb2.ListMachineFragmentsRequest, app.v1.app_pb2.ListMachineFragmentsResponse) + self.ListMachineSummaries = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListMachineSummaries', app.v1.app_pb2.ListMachineSummariesRequest, app.v1.app_pb2.ListMachineSummariesResponse) + self.GetFragmentHistory = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetFragmentHistory', app.v1.app_pb2.GetFragmentHistoryRequest, app.v1.app_pb2.GetFragmentHistoryResponse) + self.GetFragmentUsage = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetFragmentUsage', app.v1.app_pb2.GetFragmentUsageRequest, app.v1.app_pb2.GetFragmentUsageResponse) + self.SetFragmentTag = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/SetFragmentTag', app.v1.app_pb2.SetFragmentTagRequest, app.v1.app_pb2.SetFragmentTagResponse) + self.DeleteFragmentTag = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteFragmentTag', app.v1.app_pb2.DeleteFragmentTagRequest, app.v1.app_pb2.DeleteFragmentTagResponse) + self.AddRole = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/AddRole', app.v1.app_pb2.AddRoleRequest, app.v1.app_pb2.AddRoleResponse) + self.RemoveRole = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/RemoveRole', app.v1.app_pb2.RemoveRoleRequest, app.v1.app_pb2.RemoveRoleResponse) + self.ChangeRole = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ChangeRole', app.v1.app_pb2.ChangeRoleRequest, app.v1.app_pb2.ChangeRoleResponse) + self.ListAuthorizations = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListAuthorizations', app.v1.app_pb2.ListAuthorizationsRequest, app.v1.app_pb2.ListAuthorizationsResponse) + self.CheckPermissions = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CheckPermissions', app.v1.app_pb2.CheckPermissionsRequest, app.v1.app_pb2.CheckPermissionsResponse) + self.GetRegistryItem = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetRegistryItem', app.v1.app_pb2.GetRegistryItemRequest, app.v1.app_pb2.GetRegistryItemResponse) + self.CreateRegistryItem = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateRegistryItem', app.v1.app_pb2.CreateRegistryItemRequest, app.v1.app_pb2.CreateRegistryItemResponse) + self.UpdateRegistryItem = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateRegistryItem', app.v1.app_pb2.UpdateRegistryItemRequest, app.v1.app_pb2.UpdateRegistryItemResponse) + self.ListRegistryItems = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListRegistryItems', app.v1.app_pb2.ListRegistryItemsRequest, app.v1.app_pb2.ListRegistryItemsResponse) + self.DeleteRegistryItem = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteRegistryItem', app.v1.app_pb2.DeleteRegistryItemRequest, app.v1.app_pb2.DeleteRegistryItemResponse) + self.RenameRegistryItem = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/RenameRegistryItem', app.v1.app_pb2.RenameRegistryItemRequest, app.v1.app_pb2.RenameRegistryItemResponse) + self.TransferRegistryItem = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/TransferRegistryItem', app.v1.app_pb2.TransferRegistryItemRequest, app.v1.app_pb2.TransferRegistryItemResponse) + self.CreateModule = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateModule', app.v1.app_pb2.CreateModuleRequest, app.v1.app_pb2.CreateModuleResponse) + self.UpdateModule = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/UpdateModule', app.v1.app_pb2.UpdateModuleRequest, app.v1.app_pb2.UpdateModuleResponse) + self.UploadModuleFile = grpclib.client.StreamUnaryMethod(channel, '/viam.app.v1.AppService/UploadModuleFile', app.v1.app_pb2.UploadModuleFileRequest, app.v1.app_pb2.UploadModuleFileResponse) + self.GetModule = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetModule', app.v1.app_pb2.GetModuleRequest, app.v1.app_pb2.GetModuleResponse) + self.ListModules = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListModules', app.v1.app_pb2.ListModulesRequest, app.v1.app_pb2.ListModulesResponse) + self.CreateKey = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateKey', app.v1.app_pb2.CreateKeyRequest, app.v1.app_pb2.CreateKeyResponse) + self.DeleteKey = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/DeleteKey', app.v1.app_pb2.DeleteKeyRequest, app.v1.app_pb2.DeleteKeyResponse) + self.ListKeys = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/ListKeys', app.v1.app_pb2.ListKeysRequest, app.v1.app_pb2.ListKeysResponse) + self.RenameKey = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/RenameKey', app.v1.app_pb2.RenameKeyRequest, app.v1.app_pb2.RenameKeyResponse) + self.RotateKey = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/RotateKey', app.v1.app_pb2.RotateKeyRequest, app.v1.app_pb2.RotateKeyResponse) + self.CreateKeyFromExistingKeyAuthorizations = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/CreateKeyFromExistingKeyAuthorizations', app.v1.app_pb2.CreateKeyFromExistingKeyAuthorizationsRequest, app.v1.app_pb2.CreateKeyFromExistingKeyAuthorizationsResponse) + self.GetAppContent = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.AppService/GetAppContent', app.v1.app_pb2.GetAppContentRequest, app.v1.app_pb2.GetAppContentResponse) \ No newline at end of file diff --git a/src/viam/gen/app/v1/app_pb2.py b/src/viam/gen/app/v1/app_pb2.py index 877c25633..c78064e15 100644 --- a/src/viam/gen/app/v1/app_pb2.py +++ b/src/viam/gen/app/v1/app_pb2.py @@ -1,147 +1,649 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/v1/app.proto') _sym_db = _symbol_database.Default() +from ...app.mltraining.v1 import ml_training_pb2 as app_dot_mltraining_dot_v1_dot_ml__training__pb2 +from ...app.packages.v1 import packages_pb2 as app_dot_packages_dot_v1_dot_packages__pb2 +from ...common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 from ...tagger.v1 import tagger_pb2 as tagger_dot_v1_dot_tagger__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10app/v1/app.proto\x12\x0bviam.app.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x16tagger/v1/tagger.proto"\x99\x02\n\x05Robot\x123\n\x02id\x18\x01 \x01(\tB#\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"R\x02id\x120\n\x04name\x18\x02 \x01(\tB\x1c\x9a\x84\x9e\x03\x17bson:"name" json:"name"R\x04name\x12@\n\x08location\x18\x03 \x01(\tB$\x9a\x84\x9e\x03\x1fbson:"location" json:"location"R\x08location\x12g\n\x0blast_access\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampB*\x9a\x84\x9e\x03%bson:"last_access" json:"last_access"R\nlastAccess"\xb6\x06\n\tRobotPart\x123\n\x02id\x18\x01 \x01(\tB#\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"R\x02id\x120\n\x04name\x18\x02 \x01(\tB\x1c\x9a\x84\x9e\x03\x17bson:"name" json:"name"R\x04name\x12?\n\x08dns_name\x18\n \x01(\tB$\x9a\x84\x9e\x03\x1fbson:"dns_name" json:"dns_name"R\x07dnsName\x12B\n\x06secret\x18\x03 \x01(\tB*\x9a\x84\x9e\x03%bson:"secret" json:"secret,omitempty"R\x06secret\x124\n\x05robot\x18\x04 \x01(\tB\x1e\x9a\x84\x9e\x03\x19bson:"robot" json:"robot"R\x05robot\x12A\n\x0blocation_id\x18\x0c \x01(\tB \x9a\x84\x9e\x03\x1bbson:"location_id" json:"-"R\nlocationId\x12b\n\x0crobot_config\x18\x05 \x01(\x0b2\x17.google.protobuf.StructB&\x9a\x84\x9e\x03!bson:"config" json:"robot_config"R\x0brobotConfig\x12g\n\x0blast_access\x18\x06 \x01(\x0b2\x1a.google.protobuf.TimestampB*\x9a\x84\x9e\x03%bson:"last_access" json:"last_access"R\nlastAccess\x12\x7f\n\x12user_supplied_info\x18\x07 \x01(\x0b2\x17.google.protobuf.StructB8\x9a\x84\x9e\x033bson:"user_supplied_info" json:"user_supplied_info"R\x10userSuppliedInfo\x12C\n\tmain_part\x18\x08 \x01(\x08B&\x9a\x84\x9e\x03!bson:"main_part" json:"main_part"R\x08mainPart\x12\x12\n\x04fqdn\x18\t \x01(\tR\x04fqdn\x12\x1d\n\nlocal_fqdn\x18\x0b \x01(\tR\tlocalFqdn"\x93\x02\n\x15RobotPartHistoryEntry\x120\n\x04part\x18\x01 \x01(\tB\x1c\x9a\x84\x9e\x03\x17bson:"part" json:"part"R\x04part\x124\n\x05robot\x18\x02 \x01(\tB\x1e\x9a\x84\x9e\x03\x19bson:"robot" json:"robot"R\x05robot\x12L\n\x04when\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampB\x1c\x9a\x84\x9e\x03\x17bson:"when" json:"when"R\x04when\x12D\n\x03old\x18\x04 \x01(\x0b2\x16.viam.app.v1.RobotPartB\x1a\x9a\x84\x9e\x03\x15bson:"old" json:"old"R\x03old"\x1a\n\x18ListOrganizationsRequest"2\n\x0cOrganization\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name"\\\n\x19ListOrganizationsResponse\x12?\n\rorganizations\x18\x01 \x03(\x0b2\x19.viam.app.v1.OrganizationR\rorganizations".\n\x08Location\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name"?\n\x14ListLocationsRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId"L\n\x15ListLocationsResponse\x123\n\tlocations\x18\x01 \x03(\x0b2\x15.viam.app.v1.LocationR\tlocations"&\n\x0cLocationAuth\x12\x16\n\x06secret\x18\x01 \x01(\tR\x06secret"6\n\x13LocationAuthRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId"E\n\x14LocationAuthResponse\x12-\n\x04auth\x18\x01 \x01(\x0b2\x19.viam.app.v1.LocationAuthR\x04auth"!\n\x0fGetRobotRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"<\n\x10GetRobotResponse\x12(\n\x05robot\x18\x01 \x01(\x0b2\x12.viam.app.v1.RobotR\x05robot"1\n\x14GetRobotPartsRequest\x12\x19\n\x08robot_id\x18\x01 \x01(\tR\x07robotId"E\n\x15GetRobotPartsResponse\x12,\n\x05parts\x18\x01 \x03(\x0b2\x16.viam.app.v1.RobotPartR\x05parts"%\n\x13GetRobotPartRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"c\n\x14GetRobotPartResponse\x12*\n\x04part\x18\x01 \x01(\x0b2\x16.viam.app.v1.RobotPartR\x04part\x12\x1f\n\x0bconfig_json\x18\x02 \x01(\tR\nconfigJson"J\n\x17GetRobotPartLogsRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1f\n\x0berrors_only\x18\x02 \x01(\x08R\nerrorsOnly"\x97\x02\n\x08LogEntry\x12\x12\n\x04host\x18\x01 \x01(\tR\x04host\x12\x14\n\x05level\x18\x02 \x01(\tR\x05level\x12.\n\x04time\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\x04time\x12\x1f\n\x0blogger_name\x18\x04 \x01(\tR\nloggerName\x12\x18\n\x07message\x18\x05 \x01(\tR\x07message\x12/\n\x06caller\x18\x06 \x01(\x0b2\x17.google.protobuf.StructR\x06caller\x12\x14\n\x05stack\x18\x07 \x01(\tR\x05stack\x12/\n\x06fields\x18\x08 \x03(\x0b2\x17.google.protobuf.StructR\x06fields"E\n\x18GetRobotPartLogsResponse\x12)\n\x04logs\x18\x01 \x03(\x0b2\x15.viam.app.v1.LogEntryR\x04logs"K\n\x18TailRobotPartLogsRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1f\n\x0berrors_only\x18\x02 \x01(\x08R\nerrorsOnly"F\n\x19TailRobotPartLogsResponse\x12)\n\x04logs\x18\x01 \x03(\x0b2\x15.viam.app.v1.LogEntryR\x04logs",\n\x1aGetRobotPartHistoryRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"[\n\x1bGetRobotPartHistoryResponse\x12<\n\x07history\x18\x01 \x03(\x0b2".viam.app.v1.RobotPartHistoryEntryR\x07history"x\n\x16UpdateRobotPartRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12:\n\x0crobot_config\x18\x03 \x01(\x0b2\x17.google.protobuf.StructR\x0brobotConfig"E\n\x17UpdateRobotPartResponse\x12*\n\x04part\x18\x01 \x01(\x0b2\x16.viam.app.v1.RobotPartR\x04part"M\n\x13NewRobotPartRequest\x12\x19\n\x08robot_id\x18\x01 \x01(\tR\x07robotId\x12\x1b\n\tpart_name\x18\x02 \x01(\tR\x08partName"/\n\x14NewRobotPartResponse\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId"1\n\x16DeleteRobotPartRequest\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId"\x19\n\x17DeleteRobotPartResponse"\xe2\x02\n\x08Fragment\x123\n\x02id\x18\x01 \x01(\tB#\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"R\x02id\x120\n\x04name\x18\x02 \x01(\tB\x1c\x9a\x84\x9e\x03\x17bson:"name" json:"name"R\x04name\x12Y\n\x08fragment\x18\x03 \x01(\x0b2\x17.google.protobuf.StructB$\x9a\x84\x9e\x03\x1fbson:"fragment" json:"fragment"R\x08fragment\x12Z\n\x12organization_owner\x18\x04 \x01(\tB+\x9a\x84\x9e\x03&bson:"organization_owner" json:"owner"R\x11organizationOwner\x128\n\x06public\x18\x05 \x01(\x08B \x9a\x84\x9e\x03\x1bbson:"public" json:"public"R\x06public"4\n\x11FindRobotsRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId"@\n\x12FindRobotsResponse\x12*\n\x06robots\x18\x01 \x03(\x0b2\x12.viam.app.v1.RobotR\x06robots"A\n\x0fNewRobotRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1a\n\x08location\x18\x02 \x01(\tR\x08location""\n\x10NewRobotResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"T\n\x12UpdateRobotRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x1a\n\x08location\x18\x03 \x01(\tR\x08location"?\n\x13UpdateRobotResponse\x12(\n\x05robot\x18\x01 \x01(\x0b2\x12.viam.app.v1.RobotR\x05robot"$\n\x12DeleteRobotRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x15\n\x13DeleteRobotResponse"0\n\x15MarkPartAsMainRequest\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId"\x18\n\x16MarkPartAsMainResponse2\xec\x0b\n\nAppService\x12b\n\x11ListOrganizations\x12%.viam.app.v1.ListOrganizationsRequest\x1a&.viam.app.v1.ListOrganizationsResponse\x12V\n\rListLocations\x12!.viam.app.v1.ListLocationsRequest\x1a".viam.app.v1.ListLocationsResponse\x12S\n\x0cLocationAuth\x12 .viam.app.v1.LocationAuthRequest\x1a!.viam.app.v1.LocationAuthResponse\x12G\n\x08GetRobot\x12\x1c.viam.app.v1.GetRobotRequest\x1a\x1d.viam.app.v1.GetRobotResponse\x12V\n\rGetRobotParts\x12!.viam.app.v1.GetRobotPartsRequest\x1a".viam.app.v1.GetRobotPartsResponse\x12S\n\x0cGetRobotPart\x12 .viam.app.v1.GetRobotPartRequest\x1a!.viam.app.v1.GetRobotPartResponse\x12_\n\x10GetRobotPartLogs\x12$.viam.app.v1.GetRobotPartLogsRequest\x1a%.viam.app.v1.GetRobotPartLogsResponse\x12d\n\x11TailRobotPartLogs\x12%.viam.app.v1.TailRobotPartLogsRequest\x1a&.viam.app.v1.TailRobotPartLogsResponse0\x01\x12h\n\x13GetRobotPartHistory\x12\'.viam.app.v1.GetRobotPartHistoryRequest\x1a(.viam.app.v1.GetRobotPartHistoryResponse\x12\\\n\x0fUpdateRobotPart\x12#.viam.app.v1.UpdateRobotPartRequest\x1a$.viam.app.v1.UpdateRobotPartResponse\x12S\n\x0cNewRobotPart\x12 .viam.app.v1.NewRobotPartRequest\x1a!.viam.app.v1.NewRobotPartResponse\x12\\\n\x0fDeleteRobotPart\x12#.viam.app.v1.DeleteRobotPartRequest\x1a$.viam.app.v1.DeleteRobotPartResponse\x12Y\n\x0eMarkPartAsMain\x12".viam.app.v1.MarkPartAsMainRequest\x1a#.viam.app.v1.MarkPartAsMainResponse\x12M\n\nFindRobots\x12\x1e.viam.app.v1.FindRobotsRequest\x1a\x1f.viam.app.v1.FindRobotsResponse\x12G\n\x08NewRobot\x12\x1c.viam.app.v1.NewRobotRequest\x1a\x1d.viam.app.v1.NewRobotResponse\x12P\n\x0bUpdateRobot\x12\x1f.viam.app.v1.UpdateRobotRequest\x1a .viam.app.v1.UpdateRobotResponse\x12P\n\x0bDeleteRobot\x12\x1f.viam.app.v1.DeleteRobotRequest\x1a .viam.app.v1.DeleteRobotResponseB\x18Z\x16go.viam.com/api/app/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.v1.app_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z\x16go.viam.com/api/app/v1' - _ROBOT.fields_by_name['id']._options = None - _ROBOT.fields_by_name['id']._serialized_options = b'\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"' - _ROBOT.fields_by_name['name']._options = None - _ROBOT.fields_by_name['name']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"name" json:"name"' - _ROBOT.fields_by_name['location']._options = None - _ROBOT.fields_by_name['location']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"location" json:"location"' - _ROBOT.fields_by_name['last_access']._options = None - _ROBOT.fields_by_name['last_access']._serialized_options = b'\x9a\x84\x9e\x03%bson:"last_access" json:"last_access"' - _ROBOTPART.fields_by_name['id']._options = None - _ROBOTPART.fields_by_name['id']._serialized_options = b'\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"' - _ROBOTPART.fields_by_name['name']._options = None - _ROBOTPART.fields_by_name['name']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"name" json:"name"' - _ROBOTPART.fields_by_name['dns_name']._options = None - _ROBOTPART.fields_by_name['dns_name']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"dns_name" json:"dns_name"' - _ROBOTPART.fields_by_name['secret']._options = None - _ROBOTPART.fields_by_name['secret']._serialized_options = b'\x9a\x84\x9e\x03%bson:"secret" json:"secret,omitempty"' - _ROBOTPART.fields_by_name['robot']._options = None - _ROBOTPART.fields_by_name['robot']._serialized_options = b'\x9a\x84\x9e\x03\x19bson:"robot" json:"robot"' - _ROBOTPART.fields_by_name['location_id']._options = None - _ROBOTPART.fields_by_name['location_id']._serialized_options = b'\x9a\x84\x9e\x03\x1bbson:"location_id" json:"-"' - _ROBOTPART.fields_by_name['robot_config']._options = None - _ROBOTPART.fields_by_name['robot_config']._serialized_options = b'\x9a\x84\x9e\x03!bson:"config" json:"robot_config"' - _ROBOTPART.fields_by_name['last_access']._options = None - _ROBOTPART.fields_by_name['last_access']._serialized_options = b'\x9a\x84\x9e\x03%bson:"last_access" json:"last_access"' - _ROBOTPART.fields_by_name['user_supplied_info']._options = None - _ROBOTPART.fields_by_name['user_supplied_info']._serialized_options = b'\x9a\x84\x9e\x033bson:"user_supplied_info" json:"user_supplied_info"' - _ROBOTPART.fields_by_name['main_part']._options = None - _ROBOTPART.fields_by_name['main_part']._serialized_options = b'\x9a\x84\x9e\x03!bson:"main_part" json:"main_part"' - _ROBOTPARTHISTORYENTRY.fields_by_name['part']._options = None - _ROBOTPARTHISTORYENTRY.fields_by_name['part']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"part" json:"part"' - _ROBOTPARTHISTORYENTRY.fields_by_name['robot']._options = None - _ROBOTPARTHISTORYENTRY.fields_by_name['robot']._serialized_options = b'\x9a\x84\x9e\x03\x19bson:"robot" json:"robot"' - _ROBOTPARTHISTORYENTRY.fields_by_name['when']._options = None - _ROBOTPARTHISTORYENTRY.fields_by_name['when']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"when" json:"when"' - _ROBOTPARTHISTORYENTRY.fields_by_name['old']._options = None - _ROBOTPARTHISTORYENTRY.fields_by_name['old']._serialized_options = b'\x9a\x84\x9e\x03\x15bson:"old" json:"old"' - _FRAGMENT.fields_by_name['id']._options = None - _FRAGMENT.fields_by_name['id']._serialized_options = b'\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"' - _FRAGMENT.fields_by_name['name']._options = None - _FRAGMENT.fields_by_name['name']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"name" json:"name"' - _FRAGMENT.fields_by_name['fragment']._options = None - _FRAGMENT.fields_by_name['fragment']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"fragment" json:"fragment"' - _FRAGMENT.fields_by_name['organization_owner']._options = None - _FRAGMENT.fields_by_name['organization_owner']._serialized_options = b'\x9a\x84\x9e\x03&bson:"organization_owner" json:"owner"' - _FRAGMENT.fields_by_name['public']._options = None - _FRAGMENT.fields_by_name['public']._serialized_options = b'\x9a\x84\x9e\x03\x1bbson:"public" json:"public"' - _ROBOT._serialized_start = 121 - _ROBOT._serialized_end = 402 - _ROBOTPART._serialized_start = 405 - _ROBOTPART._serialized_end = 1227 - _ROBOTPARTHISTORYENTRY._serialized_start = 1230 - _ROBOTPARTHISTORYENTRY._serialized_end = 1505 - _LISTORGANIZATIONSREQUEST._serialized_start = 1507 - _LISTORGANIZATIONSREQUEST._serialized_end = 1533 - _ORGANIZATION._serialized_start = 1535 - _ORGANIZATION._serialized_end = 1585 - _LISTORGANIZATIONSRESPONSE._serialized_start = 1587 - _LISTORGANIZATIONSRESPONSE._serialized_end = 1679 - _LOCATION._serialized_start = 1681 - _LOCATION._serialized_end = 1727 - _LISTLOCATIONSREQUEST._serialized_start = 1729 - _LISTLOCATIONSREQUEST._serialized_end = 1792 - _LISTLOCATIONSRESPONSE._serialized_start = 1794 - _LISTLOCATIONSRESPONSE._serialized_end = 1870 - _LOCATIONAUTH._serialized_start = 1872 - _LOCATIONAUTH._serialized_end = 1910 - _LOCATIONAUTHREQUEST._serialized_start = 1912 - _LOCATIONAUTHREQUEST._serialized_end = 1966 - _LOCATIONAUTHRESPONSE._serialized_start = 1968 - _LOCATIONAUTHRESPONSE._serialized_end = 2037 - _GETROBOTREQUEST._serialized_start = 2039 - _GETROBOTREQUEST._serialized_end = 2072 - _GETROBOTRESPONSE._serialized_start = 2074 - _GETROBOTRESPONSE._serialized_end = 2134 - _GETROBOTPARTSREQUEST._serialized_start = 2136 - _GETROBOTPARTSREQUEST._serialized_end = 2185 - _GETROBOTPARTSRESPONSE._serialized_start = 2187 - _GETROBOTPARTSRESPONSE._serialized_end = 2256 - _GETROBOTPARTREQUEST._serialized_start = 2258 - _GETROBOTPARTREQUEST._serialized_end = 2295 - _GETROBOTPARTRESPONSE._serialized_start = 2297 - _GETROBOTPARTRESPONSE._serialized_end = 2396 - _GETROBOTPARTLOGSREQUEST._serialized_start = 2398 - _GETROBOTPARTLOGSREQUEST._serialized_end = 2472 - _LOGENTRY._serialized_start = 2475 - _LOGENTRY._serialized_end = 2754 - _GETROBOTPARTLOGSRESPONSE._serialized_start = 2756 - _GETROBOTPARTLOGSRESPONSE._serialized_end = 2825 - _TAILROBOTPARTLOGSREQUEST._serialized_start = 2827 - _TAILROBOTPARTLOGSREQUEST._serialized_end = 2902 - _TAILROBOTPARTLOGSRESPONSE._serialized_start = 2904 - _TAILROBOTPARTLOGSRESPONSE._serialized_end = 2974 - _GETROBOTPARTHISTORYREQUEST._serialized_start = 2976 - _GETROBOTPARTHISTORYREQUEST._serialized_end = 3020 - _GETROBOTPARTHISTORYRESPONSE._serialized_start = 3022 - _GETROBOTPARTHISTORYRESPONSE._serialized_end = 3113 - _UPDATEROBOTPARTREQUEST._serialized_start = 3115 - _UPDATEROBOTPARTREQUEST._serialized_end = 3235 - _UPDATEROBOTPARTRESPONSE._serialized_start = 3237 - _UPDATEROBOTPARTRESPONSE._serialized_end = 3306 - _NEWROBOTPARTREQUEST._serialized_start = 3308 - _NEWROBOTPARTREQUEST._serialized_end = 3385 - _NEWROBOTPARTRESPONSE._serialized_start = 3387 - _NEWROBOTPARTRESPONSE._serialized_end = 3434 - _DELETEROBOTPARTREQUEST._serialized_start = 3436 - _DELETEROBOTPARTREQUEST._serialized_end = 3485 - _DELETEROBOTPARTRESPONSE._serialized_start = 3487 - _DELETEROBOTPARTRESPONSE._serialized_end = 3512 - _FRAGMENT._serialized_start = 3515 - _FRAGMENT._serialized_end = 3869 - _FINDROBOTSREQUEST._serialized_start = 3871 - _FINDROBOTSREQUEST._serialized_end = 3923 - _FINDROBOTSRESPONSE._serialized_start = 3925 - _FINDROBOTSRESPONSE._serialized_end = 3989 - _NEWROBOTREQUEST._serialized_start = 3991 - _NEWROBOTREQUEST._serialized_end = 4056 - _NEWROBOTRESPONSE._serialized_start = 4058 - _NEWROBOTRESPONSE._serialized_end = 4092 - _UPDATEROBOTREQUEST._serialized_start = 4094 - _UPDATEROBOTREQUEST._serialized_end = 4178 - _UPDATEROBOTRESPONSE._serialized_start = 4180 - _UPDATEROBOTRESPONSE._serialized_end = 4243 - _DELETEROBOTREQUEST._serialized_start = 4245 - _DELETEROBOTREQUEST._serialized_end = 4281 - _DELETEROBOTRESPONSE._serialized_start = 4283 - _DELETEROBOTRESPONSE._serialized_end = 4304 - _MARKPARTASMAINREQUEST._serialized_start = 4306 - _MARKPARTASMAINREQUEST._serialized_end = 4354 - _MARKPARTASMAINRESPONSE._serialized_start = 4356 - _MARKPARTASMAINRESPONSE._serialized_end = 4380 - _APPSERVICE._serialized_start = 4383 - _APPSERVICE._serialized_end = 5899 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10app/v1/app.proto\x12\x0bviam.app.v1\x1a#app/mltraining/v1/ml_training.proto\x1a\x1eapp/packages/v1/packages.proto\x1a\x16common/v1/common.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x16tagger/v1/tagger.proto"\xec\x02\n\x05Robot\x123\n\x02id\x18\x01 \x01(\tB#\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"R\x02id\x120\n\x04name\x18\x02 \x01(\tB\x1c\x9a\x84\x9e\x03\x17bson:"name" json:"name"R\x04name\x12@\n\x08location\x18\x03 \x01(\tB$\x9a\x84\x9e\x03\x1fbson:"location" json:"location"R\x08location\x12g\n\x0blast_access\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampB*\x9a\x84\x9e\x03%bson:"last_access" json:"last_access"R\nlastAccess\x12Q\n\ncreated_on\x18\x05 \x01(\x0b2\x1a.google.protobuf.TimestampB\x16\x9a\x84\x9e\x03\x11bson:"created_on"R\tcreatedOn"\xaf\x08\n\tRobotPart\x123\n\x02id\x18\x01 \x01(\tB#\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"R\x02id\x120\n\x04name\x18\x02 \x01(\tB\x1c\x9a\x84\x9e\x03\x17bson:"name" json:"name"R\x04name\x12?\n\x08dns_name\x18\n \x01(\tB$\x9a\x84\x9e\x03\x1fbson:"dns_name" json:"dns_name"R\x07dnsName\x12B\n\x06secret\x18\x03 \x01(\tB*\x9a\x84\x9e\x03%bson:"secret" json:"secret,omitempty"R\x06secret\x124\n\x05robot\x18\x04 \x01(\tB\x1e\x9a\x84\x9e\x03\x19bson:"robot" json:"robot"R\x05robot\x12A\n\x0blocation_id\x18\x0c \x01(\tB \x9a\x84\x9e\x03\x1bbson:"location_id" json:"-"R\nlocationId\x12b\n\x0crobot_config\x18\x05 \x01(\x0b2\x17.google.protobuf.StructB&\x9a\x84\x9e\x03!bson:"config" json:"robot_config"R\x0brobotConfig\x12g\n\x0blast_access\x18\x06 \x01(\x0b2\x1a.google.protobuf.TimestampB*\x9a\x84\x9e\x03%bson:"last_access" json:"last_access"R\nlastAccess\x12\x7f\n\x12user_supplied_info\x18\x07 \x01(\x0b2\x17.google.protobuf.StructB8\x9a\x84\x9e\x033bson:"user_supplied_info" json:"user_supplied_info"R\x10userSuppliedInfo\x12C\n\tmain_part\x18\x08 \x01(\x08B&\x9a\x84\x9e\x03!bson:"main_part" json:"main_part"R\x08mainPart\x12\x12\n\x04fqdn\x18\t \x01(\tR\x04fqdn\x12\x1d\n\nlocal_fqdn\x18\x0b \x01(\tR\tlocalFqdn\x12Q\n\ncreated_on\x18\r \x01(\x0b2\x1a.google.protobuf.TimestampB\x16\x9a\x84\x9e\x03\x11bson:"created_on"R\tcreatedOn\x12H\n\x07secrets\x18\x0e \x03(\x0b2\x19.viam.app.v1.SharedSecretB\x13\x9a\x84\x9e\x03\x0ebson:"secrets"R\x07secrets\x12Z\n\x0clast_updated\x18\x0f \x01(\x0b2\x1a.google.protobuf.TimestampB\x1b\x9a\x84\x9e\x03\x16bson:"last_updated_at"R\x0blastUpdated"\xf8\x02\n\x15RobotPartHistoryEntry\x120\n\x04part\x18\x01 \x01(\tB\x1c\x9a\x84\x9e\x03\x17bson:"part" json:"part"R\x04part\x124\n\x05robot\x18\x02 \x01(\tB\x1e\x9a\x84\x9e\x03\x19bson:"robot" json:"robot"R\x05robot\x12L\n\x04when\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampB\x1c\x9a\x84\x9e\x03\x17bson:"when" json:"when"R\x04when\x12D\n\x03old\x18\x04 \x01(\x0b2\x16.viam.app.v1.RobotPartB\x1a\x9a\x84\x9e\x03\x15bson:"old" json:"old"R\x03old\x12c\n\tedited_by\x18\x05 \x01(\x0b2\x1e.viam.app.v1.AuthenticatorInfoB&\x9a\x84\x9e\x03!bson:"edited_by" json:"edited_by"R\x08editedBy"\x85\x01\n\x11AuthenticatorInfo\x123\n\x04type\x18\x01 \x01(\x0e2\x1f.viam.app.v1.AuthenticationTypeR\x04type\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value\x12%\n\x0eis_deactivated\x18\x03 \x01(\x08R\risDeactivated"\x1a\n\x18ListOrganizationsRequest"\xde\x01\n\x0cOrganization\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x129\n\ncreated_on\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\tcreatedOn\x12)\n\x10public_namespace\x18\x04 \x01(\tR\x0fpublicNamespace\x12%\n\x0edefault_region\x18\x05 \x01(\tR\rdefaultRegion\x12\x15\n\x03cid\x18\x06 \x01(\tH\x00R\x03cid\x88\x01\x01B\x06\n\x04_cid"\xcf\x01\n\x12OrganizationMember\x12\x17\n\x07user_id\x18\x01 \x01(\tR\x06userId\x12\x16\n\x06emails\x18\x02 \x03(\tR\x06emails\x129\n\ndate_added\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\tdateAdded\x12>\n\nlast_login\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampH\x00R\tlastLogin\x88\x01\x01B\r\n\x0b_last_login"\\\n\x19ListOrganizationsResponse\x12?\n\rorganizations\x18\x01 \x03(\x0b2\x19.viam.app.v1.OrganizationR\rorganizations"\xd2\x01\n\x12OrganizationInvite\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x14\n\x05email\x18\x02 \x01(\tR\x05email\x129\n\ncreated_on\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\tcreatedOn\x12B\n\x0eauthorizations\x18\x04 \x03(\x0b2\x1a.viam.app.v1.AuthorizationR\x0eauthorizations"/\n\x19CreateOrganizationRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"[\n\x1aCreateOrganizationResponse\x12=\n\x0corganization\x18\x01 \x01(\x0b2\x19.viam.app.v1.OrganizationR\x0corganization"A\n\x16GetOrganizationRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId"X\n\x17GetOrganizationResponse\x12=\n\x0corganization\x18\x01 \x01(\x0b2\x19.viam.app.v1.OrganizationR\x0corganization"X\n+GetOrganizationNamespaceAvailabilityRequest\x12)\n\x10public_namespace\x18\x01 \x01(\tR\x0fpublicNamespace"L\n,GetOrganizationNamespaceAvailabilityResponse\x12\x1c\n\tavailable\x18\x01 \x01(\x08R\tavailable"\xf2\x01\n\x19UpdateOrganizationRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x17\n\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x12.\n\x10public_namespace\x18\x03 \x01(\tH\x01R\x0fpublicNamespace\x88\x01\x01\x12\x1b\n\x06region\x18\x04 \x01(\tH\x02R\x06region\x88\x01\x01\x12\x15\n\x03cid\x18\x05 \x01(\tH\x03R\x03cid\x88\x01\x01B\x07\n\x05_nameB\x13\n\x11_public_namespaceB\t\n\x07_regionB\x06\n\x04_cid"[\n\x1aUpdateOrganizationResponse\x12=\n\x0corganization\x18\x01 \x01(\x0b2\x19.viam.app.v1.OrganizationR\x0corganization"\x7f\n"UpdateOrganizationNamespaceRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x120\n\x14new_public_namespace\x18\x02 \x01(\tR\x12newPublicNamespace"d\n#UpdateOrganizationNamespaceResponse\x12=\n\x0corganization\x18\x01 \x01(\x0b2\x19.viam.app.v1.OrganizationR\x0corganization"D\n\x19DeleteOrganizationRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId"\x1c\n\x1aDeleteOrganizationResponse"I\n\x1eGetOrganizationMetadataRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId"N\n\x1fGetOrganizationMetadataResponse\x12+\n\x04data\x18\x01 \x01(\x0b2\x17.google.protobuf.StructR\x04data"y\n!UpdateOrganizationMetadataRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12+\n\x04data\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x04data"$\n"UpdateOrganizationMetadataResponse"I\n\x1eListOrganizationMembersRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId"\xc0\x01\n\x1fListOrganizationMembersResponse\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x129\n\x07members\x18\x02 \x03(\x0b2\x1f.viam.app.v1.OrganizationMemberR\x07members\x129\n\x07invites\x18\x03 \x03(\x0b2\x1f.viam.app.v1.OrganizationInviteR\x07invites"\xeb\x01\n\x1fCreateOrganizationInviteRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x14\n\x05email\x18\x02 \x01(\tR\x05email\x12B\n\x0eauthorizations\x18\x03 \x03(\x0b2\x1a.viam.app.v1.AuthorizationR\x0eauthorizations\x12/\n\x11send_email_invite\x18\x04 \x01(\x08H\x00R\x0fsendEmailInvite\x88\x01\x01B\x14\n\x12_send_email_invite"[\n CreateOrganizationInviteResponse\x127\n\x06invite\x18\x01 \x01(\x0b2\x1f.viam.app.v1.OrganizationInviteR\x06invite"\x8a\x02\n-UpdateOrganizationInviteAuthorizationsRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x14\n\x05email\x18\x02 \x01(\tR\x05email\x12I\n\x12add_authorizations\x18\x03 \x03(\x0b2\x1a.viam.app.v1.AuthorizationR\x11addAuthorizations\x12O\n\x15remove_authorizations\x18\x04 \x03(\x0b2\x1a.viam.app.v1.AuthorizationR\x14removeAuthorizations"i\n.UpdateOrganizationInviteAuthorizationsResponse\x127\n\x06invite\x18\x01 \x01(\x0b2\x1f.viam.app.v1.OrganizationInviteR\x06invite"`\n\x1fDeleteOrganizationInviteRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x14\n\x05email\x18\x02 \x01(\tR\x05email""\n DeleteOrganizationInviteResponse"`\n\x1fResendOrganizationInviteRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x14\n\x05email\x18\x02 \x01(\tR\x05email"[\n ResendOrganizationInviteResponse\x127\n\x06invite\x18\x01 \x01(\x0b2\x1f.viam.app.v1.OrganizationInviteR\x06invite"c\n\x1fDeleteOrganizationMemberRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x17\n\x07user_id\x18\x02 \x01(\tR\x06userId""\n DeleteOrganizationMemberResponse"\xd2\x01\n\x0eBillingAddress\x12$\n\x0eaddress_line_1\x18\x01 \x01(\tR\x0caddressLine1\x12)\n\x0eaddress_line_2\x18\x02 \x01(\tH\x00R\x0caddressLine2\x88\x01\x01\x12\x12\n\x04city\x18\x03 \x01(\tR\x04city\x12\x14\n\x05state\x18\x04 \x01(\tR\x05state\x12\x18\n\x07zipcode\x18\x05 \x01(\tR\x07zipcode\x12\x18\n\x07country\x18\x06 \x01(\tR\x07countryB\x11\n\x0f_address_line_2"z\n\x1bEnableBillingServiceRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12D\n\x0fbilling_address\x18\x02 \x01(\x0b2\x1b.viam.app.v1.BillingAddressR\x0ebillingAddress"\x1e\n\x1cEnableBillingServiceResponse"z\n\x1bUpdateBillingServiceRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12D\n\x0fbilling_address\x18\x02 \x01(\x0b2\x1b.viam.app.v1.BillingAddressR\x0ebillingAddress"\x1e\n\x1cUpdateBillingServiceResponse"7\n\x1eGetBillingServiceConfigRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"\xdb\x01\n\x1fGetBillingServiceConfigResponse\x12D\n\x0fbilling_address\x18\x01 \x01(\x0b2\x1b.viam.app.v1.BillingAddressR\x0ebillingAddress\x12#\n\rsupport_email\x18\x02 \x01(\tR\x0csupportEmail\x12\x19\n\x08logo_url\x18\x03 \x01(\tR\x07logoUrl\x122\n\x15billing_dashboard_url\x18\x04 \x01(\tR\x13billingDashboardUrl"5\n\x1cDisableBillingServiceRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"\x1f\n\x1dDisableBillingServiceResponse"Q\n"OrganizationSetSupportEmailRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x14\n\x05email\x18\x02 \x01(\tR\x05email"%\n#OrganizationSetSupportEmailResponse";\n"OrganizationGetSupportEmailRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId";\n#OrganizationGetSupportEmailResponse\x12\x14\n\x05email\x18\x01 \x01(\tR\x05email":\n\x14OrganizationIdentity\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name"Y\n\x14LocationOrganization\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x18\n\x07primary\x18\x02 \x01(\x08R\x07primary"\x80\x01\n\x0cLocationAuth\x12\x1a\n\x06secret\x18\x01 \x01(\tB\x02\x18\x01R\x06secret\x12\x1f\n\x0blocation_id\x18\x02 \x01(\tR\nlocationId\x123\n\x07secrets\x18\x03 \x03(\x0b2\x19.viam.app.v1.SharedSecretR\x07secrets"\'\n\rStorageConfig\x12\x16\n\x06region\x18\x01 \x01(\tR\x06region"\xd7\x03\n\x08Location\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12,\n\x12parent_location_id\x18\x04 \x01(\tR\x10parentLocationId\x12-\n\x04auth\x18\x05 \x01(\x0b2\x19.viam.app.v1.LocationAuthR\x04auth\x12G\n\rorganizations\x18\x06 \x03(\x0b2!.viam.app.v1.LocationOrganizationR\rorganizations\x129\n\ncreated_on\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\tcreatedOn\x12\x1f\n\x0brobot_count\x18\x07 \x01(\x05R\nrobotCount\x122\n\x06config\x18\x08 \x01(\x0b2\x1a.viam.app.v1.StorageConfigR\x06config\x12X\n\x14primary_org_identity\x18\t \x01(\x0b2!.viam.app.v1.OrganizationIdentityH\x00R\x12primaryOrgIdentity\x88\x01\x01B\x17\n\x15_primary_org_identity"\xd0\x02\n\x0cSharedSecret\x12\x1e\n\x02id\x18\x01 \x01(\tB\x0e\x9a\x84\x9e\x03\tbson:"id"R\x02id\x12*\n\x06secret\x18\x02 \x01(\tB\x12\x9a\x84\x9e\x03\rbson:"secret"R\x06secret\x12c\n\ncreated_on\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampB(\x9a\x84\x9e\x03#bson:"created_on" json:"created_on"R\tcreatedOn\x12H\n\x05state\x18\x04 \x01(\x0e2\x1f.viam.app.v1.SharedSecret.StateB\x11\x9a\x84\x9e\x03\x0cbson:"state"R\x05state"E\n\x05State\x12\x15\n\x11STATE_UNSPECIFIED\x10\x00\x12\x11\n\rSTATE_ENABLED\x10\x01\x12\x12\n\x0eSTATE_DISABLED\x10\x02"\x9e\x01\n\x15CreateLocationRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x121\n\x12parent_location_id\x18\x03 \x01(\tH\x00R\x10parentLocationId\x88\x01\x01B\x15\n\x13_parent_location_id"K\n\x16CreateLocationResponse\x121\n\x08location\x18\x01 \x01(\x0b2\x15.viam.app.v1.LocationR\x08location"5\n\x12GetLocationRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId"H\n\x13GetLocationResponse\x121\n\x08location\x18\x01 \x01(\x0b2\x15.viam.app.v1.LocationR\x08location"\xcc\x01\n\x15UpdateLocationRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId\x12\x17\n\x04name\x18\x02 \x01(\tH\x00R\x04name\x88\x01\x01\x121\n\x12parent_location_id\x18\x03 \x01(\tH\x01R\x10parentLocationId\x88\x01\x01\x12\x1b\n\x06region\x18\x04 \x01(\tH\x02R\x06region\x88\x01\x01B\x07\n\x05_nameB\x15\n\x13_parent_location_idB\t\n\x07_region"K\n\x16UpdateLocationResponse\x121\n\x08location\x18\x01 \x01(\x0b2\x15.viam.app.v1.LocationR\x08location"8\n\x15DeleteLocationRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId"\x18\n\x16DeleteLocationResponse"=\n\x1aGetLocationMetadataRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId"J\n\x1bGetLocationMetadataResponse\x12+\n\x04data\x18\x01 \x01(\x0b2\x17.google.protobuf.StructR\x04data"m\n\x1dUpdateLocationMetadataRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId\x12+\n\x04data\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x04data" \n\x1eUpdateLocationMetadataResponse"N\n+GetOrganizationsWithAccessToLocationRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId"\x8a\x01\n,GetOrganizationsWithAccessToLocationResponse\x12Z\n\x17organization_identities\x18\x01 \x03(\x0b2!.viam.app.v1.OrganizationIdentityR\x16organizationIdentities"?\n\x14ListLocationsRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId"`\n\x14ShareLocationRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId\x12\'\n\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId"\x17\n\x15ShareLocationResponse"b\n\x16UnshareLocationRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId\x12\'\n\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId"\x19\n\x17UnshareLocationResponse"L\n\x15ListLocationsResponse\x123\n\tlocations\x18\x01 \x03(\x0b2\x15.viam.app.v1.LocationR\tlocations">\n\x1bCreateLocationSecretRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId"M\n\x1cCreateLocationSecretResponse\x12-\n\x04auth\x18\x01 \x01(\x0b2\x19.viam.app.v1.LocationAuthR\x04auth"[\n\x1bDeleteLocationSecretRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId\x12\x1b\n\tsecret_id\x18\x02 \x01(\tR\x08secretId"\x1e\n\x1cDeleteLocationSecretResponse"6\n\x13LocationAuthRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId"E\n\x14LocationAuthResponse\x12-\n\x04auth\x18\x01 \x01(\x0b2\x19.viam.app.v1.LocationAuthR\x04auth"!\n\x0fGetRobotRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"4\n\x1bGetRoverRentalRobotsRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"\x9a\x01\n\x10RoverRentalRobot\x12\x19\n\x08robot_id\x18\x01 \x01(\tR\x07robotId\x12\x1f\n\x0blocation_id\x18\x02 \x01(\tR\nlocationId\x12\x1d\n\nrobot_name\x18\x03 \x01(\tR\trobotName\x12+\n\x12robot_main_part_id\x18\x04 \x01(\tR\x0frobotMainPartId"U\n\x1cGetRoverRentalRobotsResponse\x125\n\x06robots\x18\x01 \x03(\x0b2\x1d.viam.app.v1.RoverRentalRobotR\x06robots"<\n\x10GetRobotResponse\x12(\n\x05robot\x18\x01 \x01(\x0b2\x12.viam.app.v1.RobotR\x05robot"1\n\x14GetRobotPartsRequest\x12\x19\n\x08robot_id\x18\x01 \x01(\tR\x07robotId"E\n\x15GetRobotPartsResponse\x12,\n\x05parts\x18\x01 \x03(\x0b2\x16.viam.app.v1.RobotPartR\x05parts"%\n\x13GetRobotPartRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"c\n\x14GetRobotPartResponse\x12*\n\x04part\x18\x01 \x01(\x0b2\x16.viam.app.v1.RobotPartR\x04part\x12\x1f\n\x0bconfig_json\x18\x02 \x01(\tR\nconfigJson"\x8a\x03\n\x17GetRobotPartLogsRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12#\n\x0berrors_only\x18\x02 \x01(\x08B\x02\x18\x01R\nerrorsOnly\x12\x1b\n\x06filter\x18\x03 \x01(\tH\x00R\x06filter\x88\x01\x01\x12"\n\npage_token\x18\x04 \x01(\tH\x01R\tpageToken\x88\x01\x01\x12\x16\n\x06levels\x18\x05 \x03(\tR\x06levels\x125\n\x05start\x18\x06 \x01(\x0b2\x1a.google.protobuf.TimestampH\x02R\x05start\x88\x01\x01\x121\n\x03end\x18\x07 \x01(\x0b2\x1a.google.protobuf.TimestampH\x03R\x03end\x88\x01\x01\x12\x19\n\x05limit\x18\x08 \x01(\x03H\x04R\x05limit\x88\x01\x01\x12\x1b\n\x06source\x18\t \x01(\tH\x05R\x06source\x88\x01\x01B\t\n\x07_filterB\r\n\x0b_page_tokenB\x08\n\x06_startB\x06\n\x04_endB\x08\n\x06_limitB\t\n\x07_source"p\n\x18GetRobotPartLogsResponse\x12,\n\x04logs\x18\x01 \x03(\x0b2\x18.viam.common.v1.LogEntryR\x04logs\x12&\n\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken"s\n\x18TailRobotPartLogsRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x1f\n\x0berrors_only\x18\x02 \x01(\x08R\nerrorsOnly\x12\x1b\n\x06filter\x18\x03 \x01(\tH\x00R\x06filter\x88\x01\x01B\t\n\x07_filter"I\n\x19TailRobotPartLogsResponse\x12,\n\x04logs\x18\x01 \x03(\x0b2\x18.viam.common.v1.LogEntryR\x04logs",\n\x1aGetRobotPartHistoryRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"[\n\x1bGetRobotPartHistoryResponse\x12<\n\x07history\x18\x01 \x03(\x0b2".viam.app.v1.RobotPartHistoryEntryR\x07history"\xdb\x01\n\x16UpdateRobotPartRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12:\n\x0crobot_config\x18\x03 \x01(\x0b2\x17.google.protobuf.StructR\x0brobotConfig\x12K\n\x11last_known_update\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampH\x00R\x0flastKnownUpdate\x88\x01\x01B\x14\n\x12_last_known_update"E\n\x17UpdateRobotPartResponse\x12*\n\x04part\x18\x01 \x01(\x0b2\x16.viam.app.v1.RobotPartR\x04part"M\n\x13NewRobotPartRequest\x12\x19\n\x08robot_id\x18\x01 \x01(\tR\x07robotId\x12\x1b\n\tpart_name\x18\x02 \x01(\tR\x08partName"/\n\x14NewRobotPartResponse\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId"1\n\x16DeleteRobotPartRequest\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId"-\n\x1bGetRobotPartMetadataRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"K\n\x1cGetRobotPartMetadataResponse\x12+\n\x04data\x18\x01 \x01(\x0b2\x17.google.protobuf.StructR\x04data"]\n\x1eUpdateRobotPartMetadataRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12+\n\x04data\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x04data"!\n\x1fUpdateRobotPartMetadataResponse"3\n\x16GetRobotAPIKeysRequest\x12\x19\n\x08robot_id\x18\x01 \x01(\tR\x07robotId"y\n\x06APIKey\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x10\n\x03key\x18\x02 \x01(\tR\x03key\x12\x12\n\x04name\x18\x03 \x01(\tR\x04name\x129\n\ncreated_on\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampR\tcreatedOn"[\n\x17GetRobotAPIKeysResponse\x12@\n\x08api_keys\x18\x01 \x03(\x0b2%.viam.app.v1.APIKeyWithAuthorizationsR\x07apiKeys"\x19\n\x17DeleteRobotPartResponse"\xa1\x06\n\x08Fragment\x123\n\x02id\x18\x01 \x01(\tB#\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"R\x02id\x120\n\x04name\x18\x02 \x01(\tB\x1c\x9a\x84\x9e\x03\x17bson:"name" json:"name"R\x04name\x12Y\n\x08fragment\x18\x03 \x01(\x0b2\x17.google.protobuf.StructB$\x9a\x84\x9e\x03\x1fbson:"fragment" json:"fragment"R\x08fragment\x12Z\n\x12organization_owner\x18\x04 \x01(\tB+\x9a\x84\x9e\x03&bson:"organization_owner" json:"owner"R\x11organizationOwner\x128\n\x06public\x18\x05 \x01(\x08B \x9a\x84\x9e\x03\x1bbson:"public" json:"public"R\x06public\x12Q\n\ncreated_on\x18\x06 \x01(\x0b2\x1a.google.protobuf.TimestampB\x16\x9a\x84\x9e\x03\x11bson:"created_on"R\tcreatedOn\x12+\n\x11organization_name\x18\x07 \x01(\tR\x10organizationName\x12(\n\x10robot_part_count\x18\t \x01(\x05R\x0erobotPartCount\x12-\n\x12organization_count\x18\n \x01(\x05R\x11organizationCount\x12+\n\x12only_used_by_owner\x18\x0b \x01(\x08R\x0fonlyUsedByOwner\x12?\n\nvisibility\x18\x0c \x01(\x0e2\x1f.viam.app.v1.FragmentVisibilityR\nvisibility\x12Z\n\x0clast_updated\x18\r \x01(\x0b2\x1a.google.protobuf.TimestampB\x1b\x9a\x84\x9e\x03\x16bson:"last_updated_at"R\x0blastUpdated\x12\x1a\n\x08revision\x18\x0e \x01(\tR\x08revision"\xf8\x03\n\x14FragmentHistoryEntry\x12@\n\x08fragment\x18\x01 \x01(\tB$\x9a\x84\x9e\x03\x1fbson:"fragment" json:"fragment"R\x08fragment\x12_\n\tedited_on\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampB&\x9a\x84\x9e\x03!bson:"edited_on" json:"edited_on"R\x08editedOn\x12C\n\x03old\x18\x03 \x01(\x0b2\x15.viam.app.v1.FragmentB\x1a\x9a\x84\x9e\x03\x15bson:"old" json:"old"R\x03old\x12c\n\tedited_by\x18\x04 \x01(\x0b2\x1e.viam.app.v1.AuthenticatorInfoB&\x9a\x84\x9e\x03!bson:"edited_by" json:"edited_by"R\x08editedBy\x12@\n\x08revision\x18\x05 \x01(\tB$\x9a\x84\x9e\x03\x1fbson:"revision" json:"revision"R\x08revision\x12Q\n\x06config\x18\x06 \x01(\x0b2\x17.google.protobuf.StructB \x9a\x84\x9e\x03\x1bbson:"config" json:"config"R\x06config"i\n\x10FragmentRevision\x12\x1a\n\x08revision\x18\x01 \x01(\tR\x08revision\x129\n\ncreated_at\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\tcreatedAt";\n\x0bFragmentTag\x12\x10\n\x03tag\x18\x01 \x01(\tR\x03tag\x12\x1a\n\x08revision\x18\x02 \x01(\tR\x08revision"\x87\x01\n\rFragmentError\x12=\n\nerror_type\x18\x01 \x01(\x0e2\x1e.viam.app.v1.FragmentErrorTypeR\terrorType\x12\x1f\n\x0bfragment_id\x18\x02 \x01(\tR\nfragmentId\x12\x16\n\x06detail\x18\x03 \x01(\tR\x06detail"\xd4\x01\n\rFragmentUsage\x12\x1f\n\x0bfragment_id\x18\x01 \x01(\tR\nfragmentId\x12$\n\rorganizations\x18\x02 \x01(\x05R\rorganizations\x12\x1a\n\x08machines\x18\x03 \x01(\x05R\x08machines\x125\n\x17machines_in_current_org\x18\x04 \x01(\x05R\x14machinesInCurrentOrg\x12\x1d\n\x07version\x18\x05 \x01(\tH\x00R\x07version\x88\x01\x01B\n\n\x08_version"\xc3\x01\n\x10ResolvedFragment\x12\x1f\n\x0bfragment_id\x18\x01 \x01(\tR\nfragmentId\x12@\n\x0fresolved_config\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x0eresolvedConfig\x120\n\x05error\x18\x03 \x01(\x0b2\x1a.viam.app.v1.FragmentErrorR\x05error\x12\x1a\n\x08revision\x18\x04 \x01(\tR\x08revision"\xb2\x01\n\x14ListFragmentsRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x1f\n\x0bshow_public\x18\x02 \x01(\x08R\nshowPublic\x12P\n\x13fragment_visibility\x18\x03 \x03(\x0e2\x1f.viam.app.v1.FragmentVisibilityR\x12fragmentVisibility"\x91\x01\n\x15ListFragmentsResponse\x123\n\tfragments\x18\x01 \x03(\x0b2\x15.viam.app.v1.FragmentR\tfragments\x12C\n\x0ffragment_usages\x18\x02 \x03(\x0b2\x1a.viam.app.v1.FragmentUsageR\x0efragmentUsages"\x87\x01\n\x12GetFragmentRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x126\n\x17current_organization_id\x18\x02 \x01(\tR\x15currentOrganizationId\x12\x1d\n\x07version\x18\x03 \x01(\tH\x00R\x07version\x88\x01\x01B\n\n\x08_version"\xf6\x01\n\x13GetFragmentResponse\x121\n\x08fragment\x18\x01 \x01(\x0b2\x15.viam.app.v1.FragmentR\x08fragment\x12A\n\x0efragment_usage\x18\x02 \x01(\x0b2\x1a.viam.app.v1.FragmentUsageR\rfragmentUsage\x12;\n\trevisions\x18\x03 \x03(\x0b2\x1d.viam.app.v1.FragmentRevisionR\trevisions\x12,\n\x04tags\x18\x04 \x03(\x0b2\x18.viam.app.v1.FragmentTagR\x04tags"\xda\x01\n\x15CreateFragmentRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12/\n\x06config\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x06config\x12\'\n\x0forganization_id\x18\x03 \x01(\tR\x0eorganizationId\x12D\n\nvisibility\x18\x04 \x01(\x0e2\x1f.viam.app.v1.FragmentVisibilityH\x00R\nvisibility\x88\x01\x01B\r\n\x0b_visibility"K\n\x16CreateFragmentResponse\x121\n\x08fragment\x18\x01 \x01(\x0b2\x15.viam.app.v1.FragmentR\x08fragment"\xcc\x02\n\x15UpdateFragmentRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12/\n\x06config\x18\x03 \x01(\x0b2\x17.google.protobuf.StructR\x06config\x12\x1b\n\x06public\x18\x04 \x01(\x08H\x00R\x06public\x88\x01\x01\x12D\n\nvisibility\x18\x05 \x01(\x0e2\x1f.viam.app.v1.FragmentVisibilityH\x01R\nvisibility\x88\x01\x01\x12K\n\x11last_known_update\x18\x06 \x01(\x0b2\x1a.google.protobuf.TimestampH\x02R\x0flastKnownUpdate\x88\x01\x01B\t\n\x07_publicB\r\n\x0b_visibilityB\x14\n\x12_last_known_update"K\n\x16UpdateFragmentResponse\x121\n\x08fragment\x18\x01 \x01(\x0b2\x15.viam.app.v1.FragmentR\x08fragment"\'\n\x15DeleteFragmentRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x18\n\x16DeleteFragmentResponse"\x91\x01\n\x19GetFragmentHistoryRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12"\n\npage_token\x18\x02 \x01(\tH\x00R\tpageToken\x88\x01\x01\x12"\n\npage_limit\x18\x03 \x01(\x03H\x01R\tpageLimit\x88\x01\x01B\r\n\x0b_page_tokenB\r\n\x0b_page_limit"\x81\x01\n\x1aGetFragmentHistoryResponse\x12;\n\x07history\x18\x01 \x03(\x0b2!.viam.app.v1.FragmentHistoryEntryR\x07history\x12&\n\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken":\n\x17GetFragmentUsageRequest\x12\x1f\n\x0bfragment_id\x18\x01 \x01(\tR\nfragmentId"]\n\x18GetFragmentUsageResponse\x12A\n\x0eversion_usages\x18\x01 \x03(\x0b2\x1a.viam.app.v1.FragmentUsageR\rversionUsages"f\n\x15SetFragmentTagRequest\x12\x1f\n\x0bfragment_id\x18\x01 \x01(\tR\nfragmentId\x12\x10\n\x03tag\x18\x02 \x01(\tR\x03tag\x12\x1a\n\x08revision\x18\x03 \x01(\tR\x08revision"F\n\x16SetFragmentTagResponse\x12,\n\x04tags\x18\x01 \x03(\x0b2\x18.viam.app.v1.FragmentTagR\x04tags"M\n\x18DeleteFragmentTagRequest\x12\x1f\n\x0bfragment_id\x18\x01 \x01(\tR\nfragmentId\x12\x10\n\x03tag\x18\x02 \x01(\tR\x03tag"I\n\x19DeleteFragmentTagResponse\x12,\n\x04tags\x18\x01 \x03(\x0b2\x18.viam.app.v1.FragmentTagR\x04tags"4\n\x11ListRobotsRequest\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId"`\n\x12AdditionalFragment\x12\x1f\n\x0bfragment_id\x18\x01 \x01(\tR\nfragmentId\x12\x1d\n\x07version\x18\x02 \x01(\tH\x00R\x07version\x88\x01\x01B\n\n\x08_version"\xa6\x01\n\x1aListNestedFragmentsRequest\x12$\n\x0bfragment_id\x18\x01 \x01(\tH\x00R\nfragmentId\x88\x01\x01\x12R\n\x14additional_fragments\x18\x02 \x03(\x0b2\x1f.viam.app.v1.AdditionalFragmentR\x13additionalFragmentsB\x0e\n\x0c_fragment_id"\xa0\x01\n\x1bListNestedFragmentsResponse\x123\n\tfragments\x18\x01 \x03(\x0b2\x15.viam.app.v1.FragmentR\tfragments\x12L\n\x12resolved_fragments\x18\x02 \x03(\x0b2\x1d.viam.app.v1.ResolvedFragmentR\x11resolvedFragments"\xc8\x01\n\x1bListMachineFragmentsRequest\x12\x1d\n\nmachine_id\x18\x01 \x01(\tR\tmachineId\x126\n\x17additional_fragment_ids\x18\x02 \x03(\tR\x15additionalFragmentIds\x12R\n\x14additional_fragments\x18\x03 \x03(\x0b2\x1f.viam.app.v1.AdditionalFragmentR\x13additionalFragments"\xa1\x01\n\x1cListMachineFragmentsResponse\x123\n\tfragments\x18\x01 \x03(\x0b2\x15.viam.app.v1.FragmentR\tfragments\x12L\n\x12resolved_fragments\x18\x02 \x03(\x0b2\x1d.viam.app.v1.ResolvedFragmentR\x11resolvedFragments"F\n\x1bListMachineSummariesRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId"k\n\x1cListMachineSummariesResponse\x12K\n\x12location_summaries\x18\x01 \x03(\x0b2\x1c.viam.app.v1.LocationSummaryR\x11locationSummaries"\xa1\x01\n\x0fLocationSummary\x12\x1f\n\x0blocation_id\x18\x01 \x01(\tR\nlocationId\x12#\n\rlocation_name\x18\x02 \x01(\tR\x0clocationName\x12H\n\x11machine_summaries\x18\x03 \x03(\x0b2\x1b.viam.app.v1.MachineSummaryR\x10machineSummaries"\x93\x01\n\x0eMachineSummary\x12\x1d\n\nmachine_id\x18\x01 \x01(\tR\tmachineId\x12!\n\x0cmachine_name\x18\x02 \x01(\tR\x0bmachineName\x12?\n\x0epart_summaries\x18\x03 \x03(\x0b2\x18.viam.app.v1.PartSummaryR\rpartSummaries";\n\x0fFragmentSummary\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x03 \x01(\tR\x04nameJ\x04\x08\x02\x10\x03"N\n\x11ViamServerVersion\x12\x16\n\x05major\x18\x01 \x01(\tH\x00R\x05major\x12\x16\n\x05minor\x18\x02 \x01(\tH\x00R\x05minorB\t\n\x07version"M\n\x10ViamAgentVersion\x12\x16\n\x05major\x18\x01 \x01(\tH\x00R\x05major\x12\x16\n\x05minor\x18\x02 \x01(\tH\x00R\x05minorB\t\n\x07version"\xb8\x04\n\x0bPartSummary\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId\x12\x1b\n\tpart_name\x18\x02 \x01(\tR\x08partName\x12@\n\x0blast_online\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampH\x00R\nlastOnline\x88\x01\x01\x12S\n\x13viam_server_version\x18\x04 \x01(\x0b2\x1e.viam.app.v1.ViamServerVersionH\x01R\x11viamServerVersion\x88\x01\x01\x12P\n\x12viam_agent_version\x18\x05 \x01(\x0b2\x1d.viam.app.v1.ViamAgentVersionH\x02R\x10viamAgentVersion\x88\x01\x01\x12\x13\n\x02os\x18\x06 \x01(\tH\x03R\x02os\x88\x01\x01\x12\x1f\n\x08platform\x18\x07 \x01(\tH\x04R\x08platform\x88\x01\x01\x12/\n\x11public_ip_address\x18\x08 \x01(\tH\x05R\x0fpublicIpAddress\x88\x01\x01\x12:\n\tfragments\x18\t \x03(\x0b2\x1c.viam.app.v1.FragmentSummaryR\tfragmentsB\x0e\n\x0c_last_onlineB\x16\n\x14_viam_server_versionB\x15\n\x13_viam_agent_versionB\x05\n\x03_osB\x0b\n\t_platformB\x14\n\x12_public_ip_address"@\n\x12ListRobotsResponse\x12*\n\x06robots\x18\x01 \x03(\x0b2\x12.viam.app.v1.RobotR\x06robots"A\n\x0fNewRobotRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1a\n\x08location\x18\x02 \x01(\tR\x08location""\n\x10NewRobotResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"T\n\x12UpdateRobotRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x1a\n\x08location\x18\x03 \x01(\tR\x08location"?\n\x13UpdateRobotResponse\x12(\n\x05robot\x18\x01 \x01(\x0b2\x12.viam.app.v1.RobotR\x05robot"$\n\x12DeleteRobotRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x15\n\x13DeleteRobotResponse")\n\x17GetRobotMetadataRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"G\n\x18GetRobotMetadataResponse\x12+\n\x04data\x18\x01 \x01(\x0b2\x17.google.protobuf.StructR\x04data"Y\n\x1aUpdateRobotMetadataRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12+\n\x04data\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x04data"\x1d\n\x1bUpdateRobotMetadataResponse"0\n\x15MarkPartAsMainRequest\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId"\x18\n\x16MarkPartAsMainResponse"4\n\x19MarkPartForRestartRequest\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId"\x1c\n\x1aMarkPartForRestartResponse"7\n\x1cCreateRobotPartSecretRequest\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId"K\n\x1dCreateRobotPartSecretResponse\x12*\n\x04part\x18\x01 \x01(\x0b2\x16.viam.app.v1.RobotPartR\x04part"T\n\x1cDeleteRobotPartSecretRequest\x12\x17\n\x07part_id\x18\x01 \x01(\tR\x06partId\x12\x1b\n\tsecret_id\x18\x02 \x01(\tR\x08secretId"\x1f\n\x1dDeleteRobotPartSecretResponse"\x9e\x02\n\rAuthorization\x12-\n\x12authorization_type\x18\x01 \x01(\tR\x11authorizationType\x12)\n\x10authorization_id\x18\x02 \x01(\tR\x0fauthorizationId\x12#\n\rresource_type\x18\x03 \x01(\tR\x0cresourceType\x12\x1f\n\x0bresource_id\x18\x04 \x01(\tR\nresourceId\x12\x1f\n\x0bidentity_id\x18\x05 \x01(\tR\nidentityId\x12\'\n\x0forganization_id\x18\x06 \x01(\tR\x0eorganizationId\x12#\n\ridentity_type\x18\x07 \x01(\tR\x0cidentityType"R\n\x0eAddRoleRequest\x12@\n\rauthorization\x18\x01 \x01(\x0b2\x1a.viam.app.v1.AuthorizationR\rauthorization"\x11\n\x0fAddRoleResponse"U\n\x11RemoveRoleRequest\x12@\n\rauthorization\x18\x01 \x01(\x0b2\x1a.viam.app.v1.AuthorizationR\rauthorization"\x14\n\x12RemoveRoleResponse"\xa5\x01\n\x11ChangeRoleRequest\x12G\n\x11old_authorization\x18\x01 \x01(\x0b2\x1a.viam.app.v1.AuthorizationR\x10oldAuthorization\x12G\n\x11new_authorization\x18\x02 \x01(\x0b2\x1a.viam.app.v1.AuthorizationR\x10newAuthorization"\x14\n\x12ChangeRoleResponse"g\n\x19ListAuthorizationsRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12!\n\x0cresource_ids\x18\x02 \x03(\tR\x0bresourceIds"`\n\x1aListAuthorizationsResponse\x12B\n\x0eauthorizations\x18\x01 \x03(\x0b2\x1a.viam.app.v1.AuthorizationR\x0eauthorizations"_\n\x17CheckPermissionsRequest\x12D\n\x0bpermissions\x18\x01 \x03(\x0b2".viam.app.v1.AuthorizedPermissionsR\x0bpermissions"\x7f\n\x15AuthorizedPermissions\x12#\n\rresource_type\x18\x01 \x01(\tR\x0cresourceType\x12\x1f\n\x0bresource_id\x18\x02 \x01(\tR\nresourceId\x12 \n\x0bpermissions\x18\x03 \x03(\tR\x0bpermissions"u\n\x18CheckPermissionsResponse\x12Y\n\x16authorized_permissions\x18\x01 \x03(\x0b2".viam.app.v1.AuthorizedPermissionsR\x15authorizedPermissions"\xa2\x02\n\rModuleVersion\x12\x18\n\x07version\x18\x01 \x01(\tR\x07version\x12*\n\x05files\x18\x02 \x03(\x0b2\x14.viam.app.v1.UploadsR\x05files\x12*\n\x06models\x18\x03 \x03(\x0b2\x12.viam.app.v1.ModelR\x06models\x12\x1e\n\nentrypoint\x18\x04 \x01(\tR\nentrypoint\x12 \n\tfirst_run\x18\x05 \x01(\tH\x00R\x08firstRun\x88\x01\x01\x126\n\x14markdown_description\x18\x06 \x01(\tH\x01R\x13markdownDescription\x88\x01\x01B\x0c\n\n_first_runB\x17\n\x15_markdown_description"\x95\x02\n\x0eModuleMetadata\x12*\n\x06models\x18\x01 \x03(\x0b2\x12.viam.app.v1.ModelR\x06models\x126\n\x08versions\x18\x02 \x03(\x0b2\x1a.viam.app.v1.ModuleVersionR\x08versions\x12\x1e\n\nentrypoint\x18\x03 \x01(\tR\nentrypoint\x12 \n\tfirst_run\x18\x04 \x01(\tH\x00R\x08firstRun\x88\x01\x01\x126\n\x14markdown_description\x18\x05 \x01(\tH\x01R\x13markdownDescription\x88\x01\x01B\x0c\n\n_first_runB\x17\n\x15_markdown_description"\xc0\x01\n\x0fMLModelMetadata\x12\x1a\n\x08versions\x18\x01 \x03(\tR\x08versions\x12@\n\nmodel_type\x18\x02 \x01(\x0e2!.viam.app.mltraining.v1.ModelTypeR\tmodelType\x12O\n\x0fmodel_framework\x18\x03 \x01(\x0e2&.viam.app.mltraining.v1.ModelFrameworkR\x0emodelFramework"h\n\x11MLTrainingVersion\x12\x18\n\x07version\x18\x01 \x01(\tR\x07version\x129\n\ncreated_on\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\tcreatedOn"\xff\x01\n\x12MLTrainingMetadata\x12:\n\x08versions\x18\x05 \x03(\x0b2\x1e.viam.app.v1.MLTrainingVersionR\x08versions\x12@\n\nmodel_type\x18\x02 \x01(\x0e2!.viam.app.mltraining.v1.ModelTypeR\tmodelType\x12O\n\x0fmodel_framework\x18\x03 \x01(\x0e2&.viam.app.mltraining.v1.ModelFrameworkR\x0emodelFramework\x12\x14\n\x05draft\x18\x04 \x01(\x08R\x05draftJ\x04\x08\x01\x10\x02"\x8c\x07\n\x0cRegistryItem\x12\x17\n\x07item_id\x18\x01 \x01(\tR\x06itemId\x12\'\n\x0forganization_id\x18\x02 \x01(\tR\x0eorganizationId\x12)\n\x10public_namespace\x18\x03 \x01(\tR\x0fpublicNamespace\x12\x12\n\x04name\x18\x04 \x01(\tR\x04name\x125\n\x04type\x18\x05 \x01(\x0e2!.viam.app.packages.v1.PackageTypeR\x04type\x127\n\nvisibility\x18\x06 \x01(\x0e2\x17.viam.app.v1.VisibilityR\nvisibility\x12\x10\n\x03url\x18\x07 \x01(\tR\x03url\x12 \n\x0bdescription\x18\x08 \x01(\tR\x0bdescription\x12*\n\x11total_robot_usage\x18\t \x01(\x03R\x0ftotalRobotUsage\x12;\n\x1atotal_external_robot_usage\x18\r \x01(\x03R\x17totalExternalRobotUsage\x128\n\x18total_organization_usage\x18\n \x01(\x03R\x16totalOrganizationUsage\x12I\n!total_external_organization_usage\x18\x0e \x01(\x03R\x1etotalExternalOrganizationUsage\x12F\n\x0fmodule_metadata\x18\x0b \x01(\x0b2\x1b.viam.app.v1.ModuleMetadataH\x00R\x0emoduleMetadata\x12J\n\x11ml_model_metadata\x18\x0c \x01(\x0b2\x1c.viam.app.v1.MLModelMetadataH\x00R\x0fmlModelMetadata\x12S\n\x14ml_training_metadata\x18\x12 \x01(\x0b2\x1f.viam.app.v1.MLTrainingMetadataH\x00R\x12mlTrainingMetadata\x129\n\ncreated_at\x18\x0f \x01(\x0b2\x1a.google.protobuf.TimestampR\tcreatedAt\x129\n\nupdated_at\x18\x10 \x01(\x0b2\x1a.google.protobuf.TimestampR\tupdatedAtB\n\n\x08metadata"\x9f\x01\n\x16GetRegistryItemRequest\x12\x17\n\x07item_id\x18\x01 \x01(\tR\x06itemId\x12I\n\x1einclude_markdown_documentation\x18\x02 \x01(\x08H\x00R\x1cincludeMarkdownDocumentation\x88\x01\x01B!\n\x1f_include_markdown_documentation"H\n\x17GetRegistryItemResponse\x12-\n\x04item\x18\x01 \x01(\x0b2\x19.viam.app.v1.RegistryItemR\x04item"\x8f\x01\n\x19CreateRegistryItemRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x125\n\x04type\x18\x03 \x01(\x0e2!.viam.app.packages.v1.PackageTypeR\x04type"\x1c\n\x1aCreateRegistryItemResponse"\xe4\x04\n\x19UpdateRegistryItemRequest\x12\x17\n\x07item_id\x18\x01 \x01(\tR\x06itemId\x125\n\x04type\x18\x02 \x01(\x0e2!.viam.app.packages.v1.PackageTypeR\x04type\x12 \n\x0bdescription\x18\x03 \x01(\tR\x0bdescription\x127\n\nvisibility\x18\x04 \x01(\x0e2\x17.viam.app.v1.VisibilityR\nvisibility\x12\x15\n\x03url\x18\x05 \x01(\tH\x01R\x03url\x88\x01\x01\x12Y\n\x16update_module_metadata\x18\x06 \x01(\x0b2!.viam.app.v1.UpdateModuleMetadataH\x00R\x14updateModuleMetadata\x12]\n\x18update_ml_model_metadata\x18\x07 \x01(\x0b2".viam.app.v1.UpdateMLModelMetadataH\x00R\x15updateMlModelMetadata\x12f\n\x1bupdate_ml_training_metadata\x18\x08 \x01(\x0b2%.viam.app.v1.UpdateMLTrainingMetadataH\x00R\x18updateMlTrainingMetadata\x126\n\x14markdown_description\x18\t \x01(\tH\x02R\x13markdownDescription\x88\x01\x01B\n\n\x08metadataB\x06\n\x04_urlB\x17\n\x15_markdown_description"\x1c\n\x1aUpdateRegistryItemResponse"\xb1\x04\n\x18ListRegistryItemsRequest\x12,\n\x0forganization_id\x18\x01 \x01(\tH\x00R\x0eorganizationId\x88\x01\x01\x127\n\x05types\x18\x02 \x03(\x0e2!.viam.app.packages.v1.PackageTypeR\x05types\x12;\n\x0cvisibilities\x18\x03 \x03(\x0e2\x17.viam.app.v1.VisibilityR\x0cvisibilities\x12\x1c\n\tplatforms\x18\x04 \x03(\tR\tplatforms\x12;\n\x08statuses\x18\x05 \x03(\x0e2\x1f.viam.app.v1.RegistryItemStatusR\x08statuses\x12$\n\x0bsearch_term\x18\x06 \x01(\tH\x01R\nsearchTerm\x88\x01\x01\x12"\n\npage_token\x18\x07 \x01(\tH\x02R\tpageToken\x88\x01\x01\x12+\n\x11public_namespaces\x18\x08 \x03(\tR\x10publicNamespaces\x12I\n\x1einclude_markdown_documentation\x18\t \x01(\x08H\x03R\x1cincludeMarkdownDocumentation\x88\x01\x01B\x12\n\x10_organization_idB\x0e\n\x0c_search_termB\r\n\x0b_page_tokenB!\n\x1f_include_markdown_documentation"L\n\x19ListRegistryItemsResponse\x12/\n\x05items\x18\x01 \x03(\x0b2\x19.viam.app.v1.RegistryItemR\x05items"4\n\x19DeleteRegistryItemRequest\x12\x17\n\x07item_id\x18\x01 \x01(\tR\x06itemId"\x1c\n\x1aDeleteRegistryItemResponse"O\n\x19RenameRegistryItemRequest\x12\x17\n\x07item_id\x18\x01 \x01(\tR\x06itemId\x12\x19\n\x08new_name\x18\x02 \x01(\tR\x07newName"K\n\x1aRenameRegistryItemResponse\x12-\n\x04item\x18\x01 \x01(\x0b2\x19.viam.app.v1.RegistryItemR\x04item"h\n\x1bTransferRegistryItemRequest\x12\x17\n\x07item_id\x18\x01 \x01(\tR\x06itemId\x120\n\x14new_public_namespace\x18\x02 \x01(\tR\x12newPublicNamespace"\x1e\n\x1cTransferRegistryItemResponse"R\n\x13CreateModuleRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name"E\n\x14CreateModuleResponse\x12\x1b\n\tmodule_id\x18\x01 \x01(\tR\x08moduleId\x12\x10\n\x03url\x18\x02 \x01(\tR\x03url"\x92\x03\n\x13UpdateModuleRequest\x12\x1b\n\tmodule_id\x18\x01 \x01(\tR\x08moduleId\x127\n\nvisibility\x18\x02 \x01(\x0e2\x17.viam.app.v1.VisibilityR\nvisibility\x12\x10\n\x03url\x18\x03 \x01(\tR\x03url\x12 \n\x0bdescription\x18\x04 \x01(\tR\x0bdescription\x12*\n\x06models\x18\x05 \x03(\x0b2\x12.viam.app.v1.ModelR\x06models\x12\x1e\n\nentrypoint\x18\x06 \x01(\tR\nentrypoint\x12 \n\tfirst_run\x18\x07 \x01(\tH\x00R\x08firstRun\x88\x01\x01\x12$\n\x04apps\x18\x08 \x03(\x0b2\x10.viam.app.v1.AppR\x04apps\x126\n\x14markdown_description\x18\t \x01(\tH\x01R\x13markdownDescription\x88\x01\x01B\x0c\n\n_first_runB\x17\n\x15_markdown_description"M\n\x03App\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04type\x18\x02 \x01(\tR\x04type\x12\x1e\n\nentrypoint\x18\x03 \x01(\tR\nentrypoint"(\n\x14UpdateModuleResponse\x12\x10\n\x03url\x18\x01 \x01(\tR\x03url"b\n\x14UpdateModuleMetadata\x12*\n\x06models\x18\x01 \x03(\x0b2\x12.viam.app.v1.ModelR\x06models\x12\x1e\n\nentrypoint\x18\x02 \x01(\tR\nentrypoint"\xaa\x01\n\x15UpdateMLModelMetadata\x12@\n\nmodel_type\x18\x01 \x01(\x0e2!.viam.app.mltraining.v1.ModelTypeR\tmodelType\x12O\n\x0fmodel_framework\x18\x02 \x01(\x0e2&.viam.app.mltraining.v1.ModelFrameworkR\x0emodelFramework"\xc3\x01\n\x18UpdateMLTrainingMetadata\x12@\n\nmodel_type\x18\x01 \x01(\x0e2!.viam.app.mltraining.v1.ModelTypeR\tmodelType\x12O\n\x0fmodel_framework\x18\x02 \x01(\x0e2&.viam.app.mltraining.v1.ModelFrameworkR\x0emodelFramework\x12\x14\n\x05draft\x18\x03 \x01(\x08R\x05draft"\xec\x01\n\x05Model\x12\x10\n\x03api\x18\x01 \x01(\tR\x03api\x12\x14\n\x05model\x18\x02 \x01(\tR\x05model\x12:\n\x16markdown_documentation\x18\x03 \x01(\tH\x00R\x15markdownDocumentation\x88\x01\x01\x12%\n\x0bdescription\x18\x04 \x01(\tH\x01R\x0bdescription\x88\x01\x01\x12-\n\x12supported_hardware\x18\x05 \x03(\tR\x11supportedHardwareB\x19\n\x17_markdown_documentationB\x0e\n\x0c_description"\x88\x01\n\x0eModuleFileInfo\x12\x1b\n\tmodule_id\x18\x01 \x01(\tR\x08moduleId\x12\x18\n\x07version\x18\x02 \x01(\tR\x07version\x12\x1a\n\x08platform\x18\x03 \x01(\tR\x08platform\x12#\n\rplatform_tags\x18\x05 \x03(\tR\x0cplatformTags"\x87\x01\n\x17UploadModuleFileRequest\x12G\n\x10module_file_info\x18\x01 \x01(\x0b2\x1b.viam.app.v1.ModuleFileInfoH\x00R\x0emoduleFileInfo\x12\x14\n\x04file\x18\x02 \x01(\x0cH\x00R\x04fileB\r\n\x0bmodule_file",\n\x18UploadModuleFileResponse\x12\x10\n\x03url\x18\x01 \x01(\tR\x03url"\x9d\x01\n\x10GetModuleRequest\x12\x1b\n\tmodule_id\x18\x01 \x01(\tR\x08moduleId\x12I\n\x1einclude_markdown_documentation\x18\x02 \x01(\x08H\x00R\x1cincludeMarkdownDocumentation\x88\x01\x01B!\n\x1f_include_markdown_documentation"@\n\x11GetModuleResponse\x12+\n\x06module\x18\x01 \x01(\x0b2\x13.viam.app.v1.ModuleR\x06module"\xe6\x04\n\x06Module\x12\x1b\n\tmodule_id\x18\x01 \x01(\tR\x08moduleId\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x127\n\nvisibility\x18\x03 \x01(\x0e2\x17.viam.app.v1.VisibilityR\nvisibility\x127\n\x08versions\x18\x04 \x03(\x0b2\x1b.viam.app.v1.VersionHistoryR\x08versions\x12\x10\n\x03url\x18\x05 \x01(\tR\x03url\x12 \n\x0bdescription\x18\x06 \x01(\tR\x0bdescription\x12*\n\x06models\x18\x07 \x03(\x0b2\x12.viam.app.v1.ModelR\x06models\x12*\n\x11total_robot_usage\x18\x08 \x01(\x03R\x0ftotalRobotUsage\x128\n\x18total_organization_usage\x18\t \x01(\x03R\x16totalOrganizationUsage\x12\'\n\x0forganization_id\x18\n \x01(\tR\x0eorganizationId\x12\x1e\n\nentrypoint\x18\x0b \x01(\tR\nentrypoint\x12)\n\x10public_namespace\x18\x0c \x01(\tR\x0fpublicNamespace\x12 \n\tfirst_run\x18\r \x01(\tH\x00R\x08firstRun\x88\x01\x01\x126\n\x14markdown_description\x18\x0e \x01(\tH\x01R\x13markdownDescription\x88\x01\x01B\x0c\n\n_first_runB\x17\n\x15_markdown_description"\xa3\x02\n\x0eVersionHistory\x12\x18\n\x07version\x18\x01 \x01(\tR\x07version\x12*\n\x05files\x18\x02 \x03(\x0b2\x14.viam.app.v1.UploadsR\x05files\x12*\n\x06models\x18\x03 \x03(\x0b2\x12.viam.app.v1.ModelR\x06models\x12\x1e\n\nentrypoint\x18\x04 \x01(\tR\nentrypoint\x12 \n\tfirst_run\x18\x05 \x01(\tH\x00R\x08firstRun\x88\x01\x01\x126\n\x14markdown_description\x18\x06 \x01(\tH\x01R\x13markdownDescription\x88\x01\x01B\x0c\n\n_first_runB\x17\n\x15_markdown_description"b\n\x07Uploads\x12\x1a\n\x08platform\x18\x01 \x01(\tR\x08platform\x12;\n\x0buploaded_at\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\nuploadedAt"\xc4\x01\n\x12ListModulesRequest\x12,\n\x0forganization_id\x18\x01 \x01(\tH\x00R\x0eorganizationId\x88\x01\x01\x12I\n\x1einclude_markdown_documentation\x18\x02 \x01(\x08H\x01R\x1cincludeMarkdownDocumentation\x88\x01\x01B\x12\n\x10_organization_idB!\n\x1f_include_markdown_documentation"D\n\x13ListModulesResponse\x12-\n\x07modules\x18\x01 \x03(\x0b2\x13.viam.app.v1.ModuleR\x07modules"/\n\x17GetUserIDByEmailRequest\x12\x14\n\x05email\x18\x01 \x01(\tR\x05email"3\n\x18GetUserIDByEmailResponse\x12\x17\n\x07user_id\x18\x01 \x01(\tR\x06userId"9\n\x1eListOrganizationsByUserRequest\x12\x17\n\x07user_id\x18\x01 \x01(\tR\x06userId"\xe6\x01\n\nOrgDetails\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x19\n\x08org_name\x18\x02 \x01(\tR\x07orgName\x12\x1c\n\x07org_cid\x18\x03 \x01(\tH\x00R\x06orgCid\x88\x01\x01\x12.\n\x10public_namespace\x18\x04 \x01(\tH\x01R\x0fpublicNamespace\x88\x01\x01\x12&\n\x0cbilling_tier\x18\x05 \x01(\tH\x02R\x0bbillingTier\x88\x01\x01B\n\n\x08_org_cidB\x13\n\x11_public_namespaceB\x0f\n\r_billing_tier"N\n\x1fListOrganizationsByUserResponse\x12+\n\x04orgs\x18\x01 \x03(\x0b2\x17.viam.app.v1.OrgDetailsR\x04orgs"\xd4\x01\n\x1aSearchOrganizationsRequest\x12\x1a\n\x06org_id\x18\x01 \x01(\tH\x00R\x05orgId\x88\x01\x01\x12\x1e\n\x08org_name\x18\x02 \x01(\tH\x01R\x07orgName\x88\x01\x01\x12\x15\n\x03cid\x18\x03 \x01(\tH\x02R\x03cid\x88\x01\x01\x12.\n\x10public_namespace\x18\x04 \x01(\tH\x03R\x0fpublicNamespace\x88\x01\x01B\t\n\x07_org_idB\x0b\n\t_org_nameB\x06\n\x04_cidB\x13\n\x11_public_namespace"\\\n\x1bSearchOrganizationsResponse\x12=\n\rorganizations\x18\x01 \x03(\x0b2\x17.viam.app.v1.OrgDetailsR\rorganizations"j\n\x10CreateKeyRequest\x12B\n\x0eauthorizations\x18\x01 \x03(\x0b2\x1a.viam.app.v1.AuthorizationR\x0eauthorizations\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name"5\n\x11CreateKeyResponse\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x0e\n\x02id\x18\x02 \x01(\tR\x02id""\n\x10DeleteKeyRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x13\n\x11DeleteKeyResponse"6\n\x10RenameKeyRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name"7\n\x11RenameKeyResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name"\xcd\x01\n\x14AuthorizationDetails\x12-\n\x12authorization_type\x18\x01 \x01(\tR\x11authorizationType\x12)\n\x10authorization_id\x18\x02 \x01(\tR\x0fauthorizationId\x12#\n\rresource_type\x18\x03 \x01(\tR\x0cresourceType\x12\x1f\n\x0bresource_id\x18\x04 \x01(\tR\nresourceId\x12\x15\n\x06org_id\x18\x05 \x01(\tR\x05orgId"\x93\x01\n\x18APIKeyWithAuthorizations\x12,\n\x07api_key\x18\x01 \x01(\x0b2\x13.viam.app.v1.APIKeyR\x06apiKey\x12I\n\x0eauthorizations\x18\x02 \x03(\x0b2!.viam.app.v1.AuthorizationDetailsR\x0eauthorizations"(\n\x0fListKeysRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"T\n\x10ListKeysResponse\x12@\n\x08api_keys\x18\x01 \x03(\x0b2%.viam.app.v1.APIKeyWithAuthorizationsR\x07apiKeys""\n\x10RotateKeyRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"5\n\x11RotateKeyResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x10\n\x03key\x18\x02 \x01(\tR\x03key"?\n-CreateKeyFromExistingKeyAuthorizationsRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"R\n.CreateKeyFromExistingKeyAuthorizationsResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x10\n\x03key\x18\x02 \x01(\tR\x03key"U\n\x14GetAppContentRequest\x12)\n\x10public_namespace\x18\x01 \x01(\tR\x0fpublicNamespace\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name"T\n\x15GetAppContentResponse\x12\x1b\n\tblob_path\x18\x01 \x01(\tR\x08blobPath\x12\x1e\n\nentrypoint\x18\x02 \x01(\tR\nentrypoint"G\n\x1aOrganizationSetLogoRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x12\n\x04logo\x18\x02 \x01(\x0cR\x04logo"\x1d\n\x1bOrganizationSetLogoResponse"3\n\x1aOrganizationGetLogoRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"/\n\x1bOrganizationGetLogoResponse\x12\x10\n\x03url\x18\x01 \x01(\tR\x03url"1\n\x18EnableAuthServiceRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"\x1b\n\x19EnableAuthServiceResponse"2\n\x19DisableAuthServiceRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"\x1c\n\x1aDisableAuthServiceResponse"\x8c\x01\n\x15CreateOAuthAppRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x1f\n\x0bclient_name\x18\x02 \x01(\tR\nclientName\x12;\n\x0coauth_config\x18\x03 \x01(\x0b2\x18.viam.app.v1.OAuthConfigR\x0boauthConfig"Z\n\x16CreateOAuthAppResponse\x12\x1b\n\tclient_id\x18\x01 \x01(\tR\x08clientId\x12#\n\rclient_secret\x18\x02 \x01(\tR\x0cclientSecret"I\n\x13ReadOAuthAppRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x1b\n\tclient_id\x18\x02 \x01(\tR\x08clientId"\x99\x01\n\x14ReadOAuthAppResponse\x12\x1f\n\x0bclient_name\x18\x01 \x01(\tR\nclientName\x12#\n\rclient_secret\x18\x02 \x01(\tR\x0cclientSecret\x12;\n\x0coauth_config\x18\x03 \x01(\x0b2\x18.viam.app.v1.OAuthConfigR\x0boauthConfig"\xa9\x01\n\x15UpdateOAuthAppRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x1b\n\tclient_id\x18\x02 \x01(\tR\x08clientId\x12\x1f\n\x0bclient_name\x18\x03 \x01(\tR\nclientName\x12;\n\x0coauth_config\x18\x04 \x01(\x0b2\x18.viam.app.v1.OAuthConfigR\x0boauthConfig"\x18\n\x16UpdateOAuthAppResponse"K\n\x15DeleteOAuthAppRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12\x1b\n\tclient_id\x18\x02 \x01(\tR\x08clientId"\x18\n\x16DeleteOAuthAppResponse"-\n\x14ListOAuthAppsRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"6\n\x15ListOAuthAppsResponse\x12\x1d\n\nclient_ids\x18\x01 \x03(\tR\tclientIds"\xf6\x02\n\x0bOAuthConfig\x12V\n\x15client_authentication\x18\x01 \x01(\x0e2!.viam.app.v1.ClientAuthenticationR\x14clientAuthentication\x12%\n\x04pkce\x18\x02 \x01(\x0e2\x11.viam.app.v1.PKCER\x04pkce\x12A\n\x0eurl_validation\x18\x03 \x01(\x0e2\x1a.viam.app.v1.URLValidationR\rurlValidation\x12\x1f\n\x0borigin_uris\x18\x04 \x03(\tR\noriginUris\x12#\n\rredirect_uris\x18\x05 \x03(\tR\x0credirectUris\x12\x1d\n\nlogout_uri\x18\x06 \x01(\tR\tlogoutUri\x12@\n\x0eenabled_grants\x18\x07 \x03(\x0e2\x19.viam.app.v1.EnabledGrantR\renabledGrants*\xd1\x01\n\x12AuthenticationType\x12#\n\x1fAUTHENTICATION_TYPE_UNSPECIFIED\x10\x00\x12!\n\x1dAUTHENTICATION_TYPE_WEB_OAUTH\x10\x01\x12\x1f\n\x1bAUTHENTICATION_TYPE_API_KEY\x10\x02\x12)\n%AUTHENTICATION_TYPE_ROBOT_PART_SECRET\x10\x03\x12\'\n#AUTHENTICATION_TYPE_LOCATION_SECRET\x10\x04*\xa3\x01\n\x12FragmentVisibility\x12#\n\x1fFRAGMENT_VISIBILITY_UNSPECIFIED\x10\x00\x12\x1f\n\x1bFRAGMENT_VISIBILITY_PRIVATE\x10\x01\x12\x1e\n\x1aFRAGMENT_VISIBILITY_PUBLIC\x10\x02\x12\'\n#FRAGMENT_VISIBILITY_PUBLIC_UNLISTED\x10\x03*\xdd\x01\n\x11FragmentErrorType\x12#\n\x1fFRAGMENT_ERROR_TYPE_UNSPECIFIED\x10\x00\x12!\n\x1dFRAGMENT_ERROR_TYPE_NO_ACCESS\x10\x01\x12.\n*FRAGMENT_ERROR_TYPE_NESTING_LIMIT_EXCEEDED\x10\x02\x12(\n$FRAGMENT_ERROR_TYPE_CHILD_ID_INVALID\x10\x03\x12&\n"FRAGMENT_ERROR_TYPE_CYCLE_DETECTED\x10\x04*\x87\x01\n\x12RegistryItemStatus\x12$\n REGISTRY_ITEM_STATUS_UNSPECIFIED\x10\x00\x12"\n\x1eREGISTRY_ITEM_STATUS_PUBLISHED\x10\x01\x12\'\n#REGISTRY_ITEM_STATUS_IN_DEVELOPMENT\x10\x02*w\n\nVisibility\x12\x1a\n\x16VISIBILITY_UNSPECIFIED\x10\x00\x12\x16\n\x12VISIBILITY_PRIVATE\x10\x01\x12\x15\n\x11VISIBILITY_PUBLIC\x10\x02\x12\x1e\n\x1aVISIBILITY_PUBLIC_UNLISTED\x10\x03*\xc1\x01\n\x14ClientAuthentication\x12%\n!CLIENT_AUTHENTICATION_UNSPECIFIED\x10\x00\x12"\n\x1eCLIENT_AUTHENTICATION_REQUIRED\x10\x01\x12&\n"CLIENT_AUTHENTICATION_NOT_REQUIRED\x10\x02\x126\n2CLIENT_AUTHENTICATION_NOT_REQUIRED_WHEN_USING_PKCE\x10\x03*~\n\x04PKCE\x12\x14\n\x10PKCE_UNSPECIFIED\x10\x00\x12\x11\n\rPKCE_REQUIRED\x10\x01\x12\x15\n\x11PKCE_NOT_REQUIRED\x10\x02\x126\n2PKCE_NOT_REQUIRED_WHEN_USING_CLIENT_AUTHENTICATION\x10\x03*s\n\rURLValidation\x12\x1e\n\x1aURL_VALIDATION_UNSPECIFIED\x10\x00\x12\x1e\n\x1aURL_VALIDATION_EXACT_MATCH\x10\x01\x12"\n\x1eURL_VALIDATION_ALLOW_WILDCARDS\x10\x02*\xcb\x01\n\x0cEnabledGrant\x12\x1d\n\x19ENABLED_GRANT_UNSPECIFIED\x10\x00\x12$\n ENABLED_GRANT_AUTHORIZATION_CODE\x10\x01\x12\x1a\n\x16ENABLED_GRANT_IMPLICIT\x10\x02\x12\x1a\n\x16ENABLED_GRANT_PASSWORD\x10\x03\x12\x1f\n\x1bENABLED_GRANT_REFRESH_TOKEN\x10\x04\x12\x1d\n\x19ENABLED_GRANT_DEVICE_CODE\x10\x052\xc2R\n\nAppService\x12_\n\x10GetUserIDByEmail\x12$.viam.app.v1.GetUserIDByEmailRequest\x1a%.viam.app.v1.GetUserIDByEmailResponse\x12e\n\x12CreateOrganization\x12&.viam.app.v1.CreateOrganizationRequest\x1a\'.viam.app.v1.CreateOrganizationResponse\x12b\n\x11ListOrganizations\x12%.viam.app.v1.ListOrganizationsRequest\x1a&.viam.app.v1.ListOrganizationsResponse\x12\x9b\x01\n$GetOrganizationsWithAccessToLocation\x128.viam.app.v1.GetOrganizationsWithAccessToLocationRequest\x1a9.viam.app.v1.GetOrganizationsWithAccessToLocationResponse\x12t\n\x17ListOrganizationsByUser\x12+.viam.app.v1.ListOrganizationsByUserRequest\x1a,.viam.app.v1.ListOrganizationsByUserResponse\x12h\n\x13SearchOrganizations\x12\'.viam.app.v1.SearchOrganizationsRequest\x1a(.viam.app.v1.SearchOrganizationsResponse\x12\\\n\x0fGetOrganization\x12#.viam.app.v1.GetOrganizationRequest\x1a$.viam.app.v1.GetOrganizationResponse\x12\x9b\x01\n$GetOrganizationNamespaceAvailability\x128.viam.app.v1.GetOrganizationNamespaceAvailabilityRequest\x1a9.viam.app.v1.GetOrganizationNamespaceAvailabilityResponse\x12e\n\x12UpdateOrganization\x12&.viam.app.v1.UpdateOrganizationRequest\x1a\'.viam.app.v1.UpdateOrganizationResponse\x12\x80\x01\n\x1bUpdateOrganizationNamespace\x12/.viam.app.v1.UpdateOrganizationNamespaceRequest\x1a0.viam.app.v1.UpdateOrganizationNamespaceResponse\x12e\n\x12DeleteOrganization\x12&.viam.app.v1.DeleteOrganizationRequest\x1a\'.viam.app.v1.DeleteOrganizationResponse\x12t\n\x17GetOrganizationMetadata\x12+.viam.app.v1.GetOrganizationMetadataRequest\x1a,.viam.app.v1.GetOrganizationMetadataResponse\x12}\n\x1aUpdateOrganizationMetadata\x12..viam.app.v1.UpdateOrganizationMetadataRequest\x1a/.viam.app.v1.UpdateOrganizationMetadataResponse\x12t\n\x17ListOrganizationMembers\x12+.viam.app.v1.ListOrganizationMembersRequest\x1a,.viam.app.v1.ListOrganizationMembersResponse\x12w\n\x18CreateOrganizationInvite\x12,.viam.app.v1.CreateOrganizationInviteRequest\x1a-.viam.app.v1.CreateOrganizationInviteResponse\x12\xa1\x01\n&UpdateOrganizationInviteAuthorizations\x12:.viam.app.v1.UpdateOrganizationInviteAuthorizationsRequest\x1a;.viam.app.v1.UpdateOrganizationInviteAuthorizationsResponse\x12w\n\x18DeleteOrganizationMember\x12,.viam.app.v1.DeleteOrganizationMemberRequest\x1a-.viam.app.v1.DeleteOrganizationMemberResponse\x12w\n\x18DeleteOrganizationInvite\x12,.viam.app.v1.DeleteOrganizationInviteRequest\x1a-.viam.app.v1.DeleteOrganizationInviteResponse\x12w\n\x18ResendOrganizationInvite\x12,.viam.app.v1.ResendOrganizationInviteRequest\x1a-.viam.app.v1.ResendOrganizationInviteResponse\x12k\n\x14EnableBillingService\x12(.viam.app.v1.EnableBillingServiceRequest\x1a).viam.app.v1.EnableBillingServiceResponse\x12n\n\x15DisableBillingService\x12).viam.app.v1.DisableBillingServiceRequest\x1a*.viam.app.v1.DisableBillingServiceResponse\x12k\n\x14UpdateBillingService\x12(.viam.app.v1.UpdateBillingServiceRequest\x1a).viam.app.v1.UpdateBillingServiceResponse\x12t\n\x17GetBillingServiceConfig\x12+.viam.app.v1.GetBillingServiceConfigRequest\x1a,.viam.app.v1.GetBillingServiceConfigResponse\x12\x80\x01\n\x1bOrganizationSetSupportEmail\x12/.viam.app.v1.OrganizationSetSupportEmailRequest\x1a0.viam.app.v1.OrganizationSetSupportEmailResponse\x12\x80\x01\n\x1bOrganizationGetSupportEmail\x12/.viam.app.v1.OrganizationGetSupportEmailRequest\x1a0.viam.app.v1.OrganizationGetSupportEmailResponse\x12h\n\x13OrganizationSetLogo\x12\'.viam.app.v1.OrganizationSetLogoRequest\x1a(.viam.app.v1.OrganizationSetLogoResponse\x12h\n\x13OrganizationGetLogo\x12\'.viam.app.v1.OrganizationGetLogoRequest\x1a(.viam.app.v1.OrganizationGetLogoResponse\x12b\n\x11EnableAuthService\x12%.viam.app.v1.EnableAuthServiceRequest\x1a&.viam.app.v1.EnableAuthServiceResponse\x12e\n\x12DisableAuthService\x12&.viam.app.v1.DisableAuthServiceRequest\x1a\'.viam.app.v1.DisableAuthServiceResponse\x12Y\n\x0eCreateOAuthApp\x12".viam.app.v1.CreateOAuthAppRequest\x1a#.viam.app.v1.CreateOAuthAppResponse\x12S\n\x0cReadOAuthApp\x12 .viam.app.v1.ReadOAuthAppRequest\x1a!.viam.app.v1.ReadOAuthAppResponse\x12Y\n\x0eUpdateOAuthApp\x12".viam.app.v1.UpdateOAuthAppRequest\x1a#.viam.app.v1.UpdateOAuthAppResponse\x12Y\n\x0eDeleteOAuthApp\x12".viam.app.v1.DeleteOAuthAppRequest\x1a#.viam.app.v1.DeleteOAuthAppResponse\x12V\n\rListOAuthApps\x12!.viam.app.v1.ListOAuthAppsRequest\x1a".viam.app.v1.ListOAuthAppsResponse\x12Y\n\x0eCreateLocation\x12".viam.app.v1.CreateLocationRequest\x1a#.viam.app.v1.CreateLocationResponse\x12P\n\x0bGetLocation\x12\x1f.viam.app.v1.GetLocationRequest\x1a .viam.app.v1.GetLocationResponse\x12Y\n\x0eUpdateLocation\x12".viam.app.v1.UpdateLocationRequest\x1a#.viam.app.v1.UpdateLocationResponse\x12Y\n\x0eDeleteLocation\x12".viam.app.v1.DeleteLocationRequest\x1a#.viam.app.v1.DeleteLocationResponse\x12h\n\x13GetLocationMetadata\x12\'.viam.app.v1.GetLocationMetadataRequest\x1a(.viam.app.v1.GetLocationMetadataResponse\x12q\n\x16UpdateLocationMetadata\x12*.viam.app.v1.UpdateLocationMetadataRequest\x1a+.viam.app.v1.UpdateLocationMetadataResponse\x12V\n\rListLocations\x12!.viam.app.v1.ListLocationsRequest\x1a".viam.app.v1.ListLocationsResponse\x12V\n\rShareLocation\x12!.viam.app.v1.ShareLocationRequest\x1a".viam.app.v1.ShareLocationResponse\x12\\\n\x0fUnshareLocation\x12#.viam.app.v1.UnshareLocationRequest\x1a$.viam.app.v1.UnshareLocationResponse\x12S\n\x0cLocationAuth\x12 .viam.app.v1.LocationAuthRequest\x1a!.viam.app.v1.LocationAuthResponse\x12k\n\x14CreateLocationSecret\x12(.viam.app.v1.CreateLocationSecretRequest\x1a).viam.app.v1.CreateLocationSecretResponse\x12k\n\x14DeleteLocationSecret\x12(.viam.app.v1.DeleteLocationSecretRequest\x1a).viam.app.v1.DeleteLocationSecretResponse\x12G\n\x08GetRobot\x12\x1c.viam.app.v1.GetRobotRequest\x1a\x1d.viam.app.v1.GetRobotResponse\x12_\n\x10GetRobotMetadata\x12$.viam.app.v1.GetRobotMetadataRequest\x1a%.viam.app.v1.GetRobotMetadataResponse\x12h\n\x13UpdateRobotMetadata\x12\'.viam.app.v1.UpdateRobotMetadataRequest\x1a(.viam.app.v1.UpdateRobotMetadataResponse\x12k\n\x14GetRoverRentalRobots\x12(.viam.app.v1.GetRoverRentalRobotsRequest\x1a).viam.app.v1.GetRoverRentalRobotsResponse\x12V\n\rGetRobotParts\x12!.viam.app.v1.GetRobotPartsRequest\x1a".viam.app.v1.GetRobotPartsResponse\x12S\n\x0cGetRobotPart\x12 .viam.app.v1.GetRobotPartRequest\x1a!.viam.app.v1.GetRobotPartResponse\x12_\n\x10GetRobotPartLogs\x12$.viam.app.v1.GetRobotPartLogsRequest\x1a%.viam.app.v1.GetRobotPartLogsResponse\x12d\n\x11TailRobotPartLogs\x12%.viam.app.v1.TailRobotPartLogsRequest\x1a&.viam.app.v1.TailRobotPartLogsResponse0\x01\x12h\n\x13GetRobotPartHistory\x12\'.viam.app.v1.GetRobotPartHistoryRequest\x1a(.viam.app.v1.GetRobotPartHistoryResponse\x12\\\n\x0fUpdateRobotPart\x12#.viam.app.v1.UpdateRobotPartRequest\x1a$.viam.app.v1.UpdateRobotPartResponse\x12S\n\x0cNewRobotPart\x12 .viam.app.v1.NewRobotPartRequest\x1a!.viam.app.v1.NewRobotPartResponse\x12\\\n\x0fDeleteRobotPart\x12#.viam.app.v1.DeleteRobotPartRequest\x1a$.viam.app.v1.DeleteRobotPartResponse\x12k\n\x14GetRobotPartMetadata\x12(.viam.app.v1.GetRobotPartMetadataRequest\x1a).viam.app.v1.GetRobotPartMetadataResponse\x12t\n\x17UpdateRobotPartMetadata\x12+.viam.app.v1.UpdateRobotPartMetadataRequest\x1a,.viam.app.v1.UpdateRobotPartMetadataResponse\x12\\\n\x0fGetRobotAPIKeys\x12#.viam.app.v1.GetRobotAPIKeysRequest\x1a$.viam.app.v1.GetRobotAPIKeysResponse\x12Y\n\x0eMarkPartAsMain\x12".viam.app.v1.MarkPartAsMainRequest\x1a#.viam.app.v1.MarkPartAsMainResponse\x12e\n\x12MarkPartForRestart\x12&.viam.app.v1.MarkPartForRestartRequest\x1a\'.viam.app.v1.MarkPartForRestartResponse\x12n\n\x15CreateRobotPartSecret\x12).viam.app.v1.CreateRobotPartSecretRequest\x1a*.viam.app.v1.CreateRobotPartSecretResponse\x12n\n\x15DeleteRobotPartSecret\x12).viam.app.v1.DeleteRobotPartSecretRequest\x1a*.viam.app.v1.DeleteRobotPartSecretResponse\x12M\n\nListRobots\x12\x1e.viam.app.v1.ListRobotsRequest\x1a\x1f.viam.app.v1.ListRobotsResponse\x12G\n\x08NewRobot\x12\x1c.viam.app.v1.NewRobotRequest\x1a\x1d.viam.app.v1.NewRobotResponse\x12P\n\x0bUpdateRobot\x12\x1f.viam.app.v1.UpdateRobotRequest\x1a .viam.app.v1.UpdateRobotResponse\x12P\n\x0bDeleteRobot\x12\x1f.viam.app.v1.DeleteRobotRequest\x1a .viam.app.v1.DeleteRobotResponse\x12V\n\rListFragments\x12!.viam.app.v1.ListFragmentsRequest\x1a".viam.app.v1.ListFragmentsResponse\x12P\n\x0bGetFragment\x12\x1f.viam.app.v1.GetFragmentRequest\x1a .viam.app.v1.GetFragmentResponse\x12Y\n\x0eCreateFragment\x12".viam.app.v1.CreateFragmentRequest\x1a#.viam.app.v1.CreateFragmentResponse\x12Y\n\x0eUpdateFragment\x12".viam.app.v1.UpdateFragmentRequest\x1a#.viam.app.v1.UpdateFragmentResponse\x12Y\n\x0eDeleteFragment\x12".viam.app.v1.DeleteFragmentRequest\x1a#.viam.app.v1.DeleteFragmentResponse\x12h\n\x13ListNestedFragments\x12\'.viam.app.v1.ListNestedFragmentsRequest\x1a(.viam.app.v1.ListNestedFragmentsResponse\x12k\n\x14ListMachineFragments\x12(.viam.app.v1.ListMachineFragmentsRequest\x1a).viam.app.v1.ListMachineFragmentsResponse\x12k\n\x14ListMachineSummaries\x12(.viam.app.v1.ListMachineSummariesRequest\x1a).viam.app.v1.ListMachineSummariesResponse\x12e\n\x12GetFragmentHistory\x12&.viam.app.v1.GetFragmentHistoryRequest\x1a\'.viam.app.v1.GetFragmentHistoryResponse\x12_\n\x10GetFragmentUsage\x12$.viam.app.v1.GetFragmentUsageRequest\x1a%.viam.app.v1.GetFragmentUsageResponse\x12Y\n\x0eSetFragmentTag\x12".viam.app.v1.SetFragmentTagRequest\x1a#.viam.app.v1.SetFragmentTagResponse\x12b\n\x11DeleteFragmentTag\x12%.viam.app.v1.DeleteFragmentTagRequest\x1a&.viam.app.v1.DeleteFragmentTagResponse\x12D\n\x07AddRole\x12\x1b.viam.app.v1.AddRoleRequest\x1a\x1c.viam.app.v1.AddRoleResponse\x12M\n\nRemoveRole\x12\x1e.viam.app.v1.RemoveRoleRequest\x1a\x1f.viam.app.v1.RemoveRoleResponse\x12M\n\nChangeRole\x12\x1e.viam.app.v1.ChangeRoleRequest\x1a\x1f.viam.app.v1.ChangeRoleResponse\x12e\n\x12ListAuthorizations\x12&.viam.app.v1.ListAuthorizationsRequest\x1a\'.viam.app.v1.ListAuthorizationsResponse\x12_\n\x10CheckPermissions\x12$.viam.app.v1.CheckPermissionsRequest\x1a%.viam.app.v1.CheckPermissionsResponse\x12\\\n\x0fGetRegistryItem\x12#.viam.app.v1.GetRegistryItemRequest\x1a$.viam.app.v1.GetRegistryItemResponse\x12e\n\x12CreateRegistryItem\x12&.viam.app.v1.CreateRegistryItemRequest\x1a\'.viam.app.v1.CreateRegistryItemResponse\x12e\n\x12UpdateRegistryItem\x12&.viam.app.v1.UpdateRegistryItemRequest\x1a\'.viam.app.v1.UpdateRegistryItemResponse\x12b\n\x11ListRegistryItems\x12%.viam.app.v1.ListRegistryItemsRequest\x1a&.viam.app.v1.ListRegistryItemsResponse\x12e\n\x12DeleteRegistryItem\x12&.viam.app.v1.DeleteRegistryItemRequest\x1a\'.viam.app.v1.DeleteRegistryItemResponse\x12e\n\x12RenameRegistryItem\x12&.viam.app.v1.RenameRegistryItemRequest\x1a\'.viam.app.v1.RenameRegistryItemResponse\x12k\n\x14TransferRegistryItem\x12(.viam.app.v1.TransferRegistryItemRequest\x1a).viam.app.v1.TransferRegistryItemResponse\x12S\n\x0cCreateModule\x12 .viam.app.v1.CreateModuleRequest\x1a!.viam.app.v1.CreateModuleResponse\x12S\n\x0cUpdateModule\x12 .viam.app.v1.UpdateModuleRequest\x1a!.viam.app.v1.UpdateModuleResponse\x12a\n\x10UploadModuleFile\x12$.viam.app.v1.UploadModuleFileRequest\x1a%.viam.app.v1.UploadModuleFileResponse(\x01\x12J\n\tGetModule\x12\x1d.viam.app.v1.GetModuleRequest\x1a\x1e.viam.app.v1.GetModuleResponse\x12P\n\x0bListModules\x12\x1f.viam.app.v1.ListModulesRequest\x1a .viam.app.v1.ListModulesResponse\x12J\n\tCreateKey\x12\x1d.viam.app.v1.CreateKeyRequest\x1a\x1e.viam.app.v1.CreateKeyResponse\x12J\n\tDeleteKey\x12\x1d.viam.app.v1.DeleteKeyRequest\x1a\x1e.viam.app.v1.DeleteKeyResponse\x12G\n\x08ListKeys\x12\x1c.viam.app.v1.ListKeysRequest\x1a\x1d.viam.app.v1.ListKeysResponse\x12J\n\tRenameKey\x12\x1d.viam.app.v1.RenameKeyRequest\x1a\x1e.viam.app.v1.RenameKeyResponse\x12J\n\tRotateKey\x12\x1d.viam.app.v1.RotateKeyRequest\x1a\x1e.viam.app.v1.RotateKeyResponse\x12\xa1\x01\n&CreateKeyFromExistingKeyAuthorizations\x12:.viam.app.v1.CreateKeyFromExistingKeyAuthorizationsRequest\x1a;.viam.app.v1.CreateKeyFromExistingKeyAuthorizationsResponse\x12V\n\rGetAppContent\x12!.viam.app.v1.GetAppContentRequest\x1a".viam.app.v1.GetAppContentResponseB\x18Z\x16go.viam.com/api/app/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.v1.app_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x16go.viam.com/api/app/v1' + _globals['_ROBOT'].fields_by_name['id']._loaded_options = None + _globals['_ROBOT'].fields_by_name['id']._serialized_options = b'\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"' + _globals['_ROBOT'].fields_by_name['name']._loaded_options = None + _globals['_ROBOT'].fields_by_name['name']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"name" json:"name"' + _globals['_ROBOT'].fields_by_name['location']._loaded_options = None + _globals['_ROBOT'].fields_by_name['location']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"location" json:"location"' + _globals['_ROBOT'].fields_by_name['last_access']._loaded_options = None + _globals['_ROBOT'].fields_by_name['last_access']._serialized_options = b'\x9a\x84\x9e\x03%bson:"last_access" json:"last_access"' + _globals['_ROBOT'].fields_by_name['created_on']._loaded_options = None + _globals['_ROBOT'].fields_by_name['created_on']._serialized_options = b'\x9a\x84\x9e\x03\x11bson:"created_on"' + _globals['_ROBOTPART'].fields_by_name['id']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['id']._serialized_options = b'\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"' + _globals['_ROBOTPART'].fields_by_name['name']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['name']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"name" json:"name"' + _globals['_ROBOTPART'].fields_by_name['dns_name']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['dns_name']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"dns_name" json:"dns_name"' + _globals['_ROBOTPART'].fields_by_name['secret']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['secret']._serialized_options = b'\x9a\x84\x9e\x03%bson:"secret" json:"secret,omitempty"' + _globals['_ROBOTPART'].fields_by_name['robot']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['robot']._serialized_options = b'\x9a\x84\x9e\x03\x19bson:"robot" json:"robot"' + _globals['_ROBOTPART'].fields_by_name['location_id']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['location_id']._serialized_options = b'\x9a\x84\x9e\x03\x1bbson:"location_id" json:"-"' + _globals['_ROBOTPART'].fields_by_name['robot_config']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['robot_config']._serialized_options = b'\x9a\x84\x9e\x03!bson:"config" json:"robot_config"' + _globals['_ROBOTPART'].fields_by_name['last_access']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['last_access']._serialized_options = b'\x9a\x84\x9e\x03%bson:"last_access" json:"last_access"' + _globals['_ROBOTPART'].fields_by_name['user_supplied_info']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['user_supplied_info']._serialized_options = b'\x9a\x84\x9e\x033bson:"user_supplied_info" json:"user_supplied_info"' + _globals['_ROBOTPART'].fields_by_name['main_part']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['main_part']._serialized_options = b'\x9a\x84\x9e\x03!bson:"main_part" json:"main_part"' + _globals['_ROBOTPART'].fields_by_name['created_on']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['created_on']._serialized_options = b'\x9a\x84\x9e\x03\x11bson:"created_on"' + _globals['_ROBOTPART'].fields_by_name['secrets']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['secrets']._serialized_options = b'\x9a\x84\x9e\x03\x0ebson:"secrets"' + _globals['_ROBOTPART'].fields_by_name['last_updated']._loaded_options = None + _globals['_ROBOTPART'].fields_by_name['last_updated']._serialized_options = b'\x9a\x84\x9e\x03\x16bson:"last_updated_at"' + _globals['_ROBOTPARTHISTORYENTRY'].fields_by_name['part']._loaded_options = None + _globals['_ROBOTPARTHISTORYENTRY'].fields_by_name['part']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"part" json:"part"' + _globals['_ROBOTPARTHISTORYENTRY'].fields_by_name['robot']._loaded_options = None + _globals['_ROBOTPARTHISTORYENTRY'].fields_by_name['robot']._serialized_options = b'\x9a\x84\x9e\x03\x19bson:"robot" json:"robot"' + _globals['_ROBOTPARTHISTORYENTRY'].fields_by_name['when']._loaded_options = None + _globals['_ROBOTPARTHISTORYENTRY'].fields_by_name['when']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"when" json:"when"' + _globals['_ROBOTPARTHISTORYENTRY'].fields_by_name['old']._loaded_options = None + _globals['_ROBOTPARTHISTORYENTRY'].fields_by_name['old']._serialized_options = b'\x9a\x84\x9e\x03\x15bson:"old" json:"old"' + _globals['_ROBOTPARTHISTORYENTRY'].fields_by_name['edited_by']._loaded_options = None + _globals['_ROBOTPARTHISTORYENTRY'].fields_by_name['edited_by']._serialized_options = b'\x9a\x84\x9e\x03!bson:"edited_by" json:"edited_by"' + _globals['_LOCATIONAUTH'].fields_by_name['secret']._loaded_options = None + _globals['_LOCATIONAUTH'].fields_by_name['secret']._serialized_options = b'\x18\x01' + _globals['_SHAREDSECRET'].fields_by_name['id']._loaded_options = None + _globals['_SHAREDSECRET'].fields_by_name['id']._serialized_options = b'\x9a\x84\x9e\x03\tbson:"id"' + _globals['_SHAREDSECRET'].fields_by_name['secret']._loaded_options = None + _globals['_SHAREDSECRET'].fields_by_name['secret']._serialized_options = b'\x9a\x84\x9e\x03\rbson:"secret"' + _globals['_SHAREDSECRET'].fields_by_name['created_on']._loaded_options = None + _globals['_SHAREDSECRET'].fields_by_name['created_on']._serialized_options = b'\x9a\x84\x9e\x03#bson:"created_on" json:"created_on"' + _globals['_SHAREDSECRET'].fields_by_name['state']._loaded_options = None + _globals['_SHAREDSECRET'].fields_by_name['state']._serialized_options = b'\x9a\x84\x9e\x03\x0cbson:"state"' + _globals['_GETROBOTPARTLOGSREQUEST'].fields_by_name['errors_only']._loaded_options = None + _globals['_GETROBOTPARTLOGSREQUEST'].fields_by_name['errors_only']._serialized_options = b'\x18\x01' + _globals['_FRAGMENT'].fields_by_name['id']._loaded_options = None + _globals['_FRAGMENT'].fields_by_name['id']._serialized_options = b'\x9a\x84\x9e\x03\x1ebson:"_id" json:"id,omitempty"' + _globals['_FRAGMENT'].fields_by_name['name']._loaded_options = None + _globals['_FRAGMENT'].fields_by_name['name']._serialized_options = b'\x9a\x84\x9e\x03\x17bson:"name" json:"name"' + _globals['_FRAGMENT'].fields_by_name['fragment']._loaded_options = None + _globals['_FRAGMENT'].fields_by_name['fragment']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"fragment" json:"fragment"' + _globals['_FRAGMENT'].fields_by_name['organization_owner']._loaded_options = None + _globals['_FRAGMENT'].fields_by_name['organization_owner']._serialized_options = b'\x9a\x84\x9e\x03&bson:"organization_owner" json:"owner"' + _globals['_FRAGMENT'].fields_by_name['public']._loaded_options = None + _globals['_FRAGMENT'].fields_by_name['public']._serialized_options = b'\x9a\x84\x9e\x03\x1bbson:"public" json:"public"' + _globals['_FRAGMENT'].fields_by_name['created_on']._loaded_options = None + _globals['_FRAGMENT'].fields_by_name['created_on']._serialized_options = b'\x9a\x84\x9e\x03\x11bson:"created_on"' + _globals['_FRAGMENT'].fields_by_name['last_updated']._loaded_options = None + _globals['_FRAGMENT'].fields_by_name['last_updated']._serialized_options = b'\x9a\x84\x9e\x03\x16bson:"last_updated_at"' + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['fragment']._loaded_options = None + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['fragment']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"fragment" json:"fragment"' + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['edited_on']._loaded_options = None + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['edited_on']._serialized_options = b'\x9a\x84\x9e\x03!bson:"edited_on" json:"edited_on"' + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['old']._loaded_options = None + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['old']._serialized_options = b'\x9a\x84\x9e\x03\x15bson:"old" json:"old"' + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['edited_by']._loaded_options = None + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['edited_by']._serialized_options = b'\x9a\x84\x9e\x03!bson:"edited_by" json:"edited_by"' + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['revision']._loaded_options = None + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['revision']._serialized_options = b'\x9a\x84\x9e\x03\x1fbson:"revision" json:"revision"' + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['config']._loaded_options = None + _globals['_FRAGMENTHISTORYENTRY'].fields_by_name['config']._serialized_options = b'\x9a\x84\x9e\x03\x1bbson:"config" json:"config"' + _globals['_AUTHENTICATIONTYPE']._serialized_start = 32320 + _globals['_AUTHENTICATIONTYPE']._serialized_end = 32529 + _globals['_FRAGMENTVISIBILITY']._serialized_start = 32532 + _globals['_FRAGMENTVISIBILITY']._serialized_end = 32695 + _globals['_FRAGMENTERRORTYPE']._serialized_start = 32698 + _globals['_FRAGMENTERRORTYPE']._serialized_end = 32919 + _globals['_REGISTRYITEMSTATUS']._serialized_start = 32922 + _globals['_REGISTRYITEMSTATUS']._serialized_end = 33057 + _globals['_VISIBILITY']._serialized_start = 33059 + _globals['_VISIBILITY']._serialized_end = 33178 + _globals['_CLIENTAUTHENTICATION']._serialized_start = 33181 + _globals['_CLIENTAUTHENTICATION']._serialized_end = 33374 + _globals['_PKCE']._serialized_start = 33376 + _globals['_PKCE']._serialized_end = 33502 + _globals['_URLVALIDATION']._serialized_start = 33504 + _globals['_URLVALIDATION']._serialized_end = 33619 + _globals['_ENABLEDGRANT']._serialized_start = 33622 + _globals['_ENABLEDGRANT']._serialized_end = 33825 + _globals['_ROBOT']._serialized_start = 214 + _globals['_ROBOT']._serialized_end = 578 + _globals['_ROBOTPART']._serialized_start = 581 + _globals['_ROBOTPART']._serialized_end = 1652 + _globals['_ROBOTPARTHISTORYENTRY']._serialized_start = 1655 + _globals['_ROBOTPARTHISTORYENTRY']._serialized_end = 2031 + _globals['_AUTHENTICATORINFO']._serialized_start = 2034 + _globals['_AUTHENTICATORINFO']._serialized_end = 2167 + _globals['_LISTORGANIZATIONSREQUEST']._serialized_start = 2169 + _globals['_LISTORGANIZATIONSREQUEST']._serialized_end = 2195 + _globals['_ORGANIZATION']._serialized_start = 2198 + _globals['_ORGANIZATION']._serialized_end = 2420 + _globals['_ORGANIZATIONMEMBER']._serialized_start = 2423 + _globals['_ORGANIZATIONMEMBER']._serialized_end = 2630 + _globals['_LISTORGANIZATIONSRESPONSE']._serialized_start = 2632 + _globals['_LISTORGANIZATIONSRESPONSE']._serialized_end = 2724 + _globals['_ORGANIZATIONINVITE']._serialized_start = 2727 + _globals['_ORGANIZATIONINVITE']._serialized_end = 2937 + _globals['_CREATEORGANIZATIONREQUEST']._serialized_start = 2939 + _globals['_CREATEORGANIZATIONREQUEST']._serialized_end = 2986 + _globals['_CREATEORGANIZATIONRESPONSE']._serialized_start = 2988 + _globals['_CREATEORGANIZATIONRESPONSE']._serialized_end = 3079 + _globals['_GETORGANIZATIONREQUEST']._serialized_start = 3081 + _globals['_GETORGANIZATIONREQUEST']._serialized_end = 3146 + _globals['_GETORGANIZATIONRESPONSE']._serialized_start = 3148 + _globals['_GETORGANIZATIONRESPONSE']._serialized_end = 3236 + _globals['_GETORGANIZATIONNAMESPACEAVAILABILITYREQUEST']._serialized_start = 3238 + _globals['_GETORGANIZATIONNAMESPACEAVAILABILITYREQUEST']._serialized_end = 3326 + _globals['_GETORGANIZATIONNAMESPACEAVAILABILITYRESPONSE']._serialized_start = 3328 + _globals['_GETORGANIZATIONNAMESPACEAVAILABILITYRESPONSE']._serialized_end = 3404 + _globals['_UPDATEORGANIZATIONREQUEST']._serialized_start = 3407 + _globals['_UPDATEORGANIZATIONREQUEST']._serialized_end = 3649 + _globals['_UPDATEORGANIZATIONRESPONSE']._serialized_start = 3651 + _globals['_UPDATEORGANIZATIONRESPONSE']._serialized_end = 3742 + _globals['_UPDATEORGANIZATIONNAMESPACEREQUEST']._serialized_start = 3744 + _globals['_UPDATEORGANIZATIONNAMESPACEREQUEST']._serialized_end = 3871 + _globals['_UPDATEORGANIZATIONNAMESPACERESPONSE']._serialized_start = 3873 + _globals['_UPDATEORGANIZATIONNAMESPACERESPONSE']._serialized_end = 3973 + _globals['_DELETEORGANIZATIONREQUEST']._serialized_start = 3975 + _globals['_DELETEORGANIZATIONREQUEST']._serialized_end = 4043 + _globals['_DELETEORGANIZATIONRESPONSE']._serialized_start = 4045 + _globals['_DELETEORGANIZATIONRESPONSE']._serialized_end = 4073 + _globals['_GETORGANIZATIONMETADATAREQUEST']._serialized_start = 4075 + _globals['_GETORGANIZATIONMETADATAREQUEST']._serialized_end = 4148 + _globals['_GETORGANIZATIONMETADATARESPONSE']._serialized_start = 4150 + _globals['_GETORGANIZATIONMETADATARESPONSE']._serialized_end = 4228 + _globals['_UPDATEORGANIZATIONMETADATAREQUEST']._serialized_start = 4230 + _globals['_UPDATEORGANIZATIONMETADATAREQUEST']._serialized_end = 4351 + _globals['_UPDATEORGANIZATIONMETADATARESPONSE']._serialized_start = 4353 + _globals['_UPDATEORGANIZATIONMETADATARESPONSE']._serialized_end = 4389 + _globals['_LISTORGANIZATIONMEMBERSREQUEST']._serialized_start = 4391 + _globals['_LISTORGANIZATIONMEMBERSREQUEST']._serialized_end = 4464 + _globals['_LISTORGANIZATIONMEMBERSRESPONSE']._serialized_start = 4467 + _globals['_LISTORGANIZATIONMEMBERSRESPONSE']._serialized_end = 4659 + _globals['_CREATEORGANIZATIONINVITEREQUEST']._serialized_start = 4662 + _globals['_CREATEORGANIZATIONINVITEREQUEST']._serialized_end = 4897 + _globals['_CREATEORGANIZATIONINVITERESPONSE']._serialized_start = 4899 + _globals['_CREATEORGANIZATIONINVITERESPONSE']._serialized_end = 4990 + _globals['_UPDATEORGANIZATIONINVITEAUTHORIZATIONSREQUEST']._serialized_start = 4993 + _globals['_UPDATEORGANIZATIONINVITEAUTHORIZATIONSREQUEST']._serialized_end = 5259 + _globals['_UPDATEORGANIZATIONINVITEAUTHORIZATIONSRESPONSE']._serialized_start = 5261 + _globals['_UPDATEORGANIZATIONINVITEAUTHORIZATIONSRESPONSE']._serialized_end = 5366 + _globals['_DELETEORGANIZATIONINVITEREQUEST']._serialized_start = 5368 + _globals['_DELETEORGANIZATIONINVITEREQUEST']._serialized_end = 5464 + _globals['_DELETEORGANIZATIONINVITERESPONSE']._serialized_start = 5466 + _globals['_DELETEORGANIZATIONINVITERESPONSE']._serialized_end = 5500 + _globals['_RESENDORGANIZATIONINVITEREQUEST']._serialized_start = 5502 + _globals['_RESENDORGANIZATIONINVITEREQUEST']._serialized_end = 5598 + _globals['_RESENDORGANIZATIONINVITERESPONSE']._serialized_start = 5600 + _globals['_RESENDORGANIZATIONINVITERESPONSE']._serialized_end = 5691 + _globals['_DELETEORGANIZATIONMEMBERREQUEST']._serialized_start = 5693 + _globals['_DELETEORGANIZATIONMEMBERREQUEST']._serialized_end = 5792 + _globals['_DELETEORGANIZATIONMEMBERRESPONSE']._serialized_start = 5794 + _globals['_DELETEORGANIZATIONMEMBERRESPONSE']._serialized_end = 5828 + _globals['_BILLINGADDRESS']._serialized_start = 5831 + _globals['_BILLINGADDRESS']._serialized_end = 6041 + _globals['_ENABLEBILLINGSERVICEREQUEST']._serialized_start = 6043 + _globals['_ENABLEBILLINGSERVICEREQUEST']._serialized_end = 6165 + _globals['_ENABLEBILLINGSERVICERESPONSE']._serialized_start = 6167 + _globals['_ENABLEBILLINGSERVICERESPONSE']._serialized_end = 6197 + _globals['_UPDATEBILLINGSERVICEREQUEST']._serialized_start = 6199 + _globals['_UPDATEBILLINGSERVICEREQUEST']._serialized_end = 6321 + _globals['_UPDATEBILLINGSERVICERESPONSE']._serialized_start = 6323 + _globals['_UPDATEBILLINGSERVICERESPONSE']._serialized_end = 6353 + _globals['_GETBILLINGSERVICECONFIGREQUEST']._serialized_start = 6355 + _globals['_GETBILLINGSERVICECONFIGREQUEST']._serialized_end = 6410 + _globals['_GETBILLINGSERVICECONFIGRESPONSE']._serialized_start = 6413 + _globals['_GETBILLINGSERVICECONFIGRESPONSE']._serialized_end = 6632 + _globals['_DISABLEBILLINGSERVICEREQUEST']._serialized_start = 6634 + _globals['_DISABLEBILLINGSERVICEREQUEST']._serialized_end = 6687 + _globals['_DISABLEBILLINGSERVICERESPONSE']._serialized_start = 6689 + _globals['_DISABLEBILLINGSERVICERESPONSE']._serialized_end = 6720 + _globals['_ORGANIZATIONSETSUPPORTEMAILREQUEST']._serialized_start = 6722 + _globals['_ORGANIZATIONSETSUPPORTEMAILREQUEST']._serialized_end = 6803 + _globals['_ORGANIZATIONSETSUPPORTEMAILRESPONSE']._serialized_start = 6805 + _globals['_ORGANIZATIONSETSUPPORTEMAILRESPONSE']._serialized_end = 6842 + _globals['_ORGANIZATIONGETSUPPORTEMAILREQUEST']._serialized_start = 6844 + _globals['_ORGANIZATIONGETSUPPORTEMAILREQUEST']._serialized_end = 6903 + _globals['_ORGANIZATIONGETSUPPORTEMAILRESPONSE']._serialized_start = 6905 + _globals['_ORGANIZATIONGETSUPPORTEMAILRESPONSE']._serialized_end = 6964 + _globals['_ORGANIZATIONIDENTITY']._serialized_start = 6966 + _globals['_ORGANIZATIONIDENTITY']._serialized_end = 7024 + _globals['_LOCATIONORGANIZATION']._serialized_start = 7026 + _globals['_LOCATIONORGANIZATION']._serialized_end = 7115 + _globals['_LOCATIONAUTH']._serialized_start = 7118 + _globals['_LOCATIONAUTH']._serialized_end = 7246 + _globals['_STORAGECONFIG']._serialized_start = 7248 + _globals['_STORAGECONFIG']._serialized_end = 7287 + _globals['_LOCATION']._serialized_start = 7290 + _globals['_LOCATION']._serialized_end = 7761 + _globals['_SHAREDSECRET']._serialized_start = 7764 + _globals['_SHAREDSECRET']._serialized_end = 8100 + _globals['_SHAREDSECRET_STATE']._serialized_start = 8031 + _globals['_SHAREDSECRET_STATE']._serialized_end = 8100 + _globals['_CREATELOCATIONREQUEST']._serialized_start = 8103 + _globals['_CREATELOCATIONREQUEST']._serialized_end = 8261 + _globals['_CREATELOCATIONRESPONSE']._serialized_start = 8263 + _globals['_CREATELOCATIONRESPONSE']._serialized_end = 8338 + _globals['_GETLOCATIONREQUEST']._serialized_start = 8340 + _globals['_GETLOCATIONREQUEST']._serialized_end = 8393 + _globals['_GETLOCATIONRESPONSE']._serialized_start = 8395 + _globals['_GETLOCATIONRESPONSE']._serialized_end = 8467 + _globals['_UPDATELOCATIONREQUEST']._serialized_start = 8470 + _globals['_UPDATELOCATIONREQUEST']._serialized_end = 8674 + _globals['_UPDATELOCATIONRESPONSE']._serialized_start = 8676 + _globals['_UPDATELOCATIONRESPONSE']._serialized_end = 8751 + _globals['_DELETELOCATIONREQUEST']._serialized_start = 8753 + _globals['_DELETELOCATIONREQUEST']._serialized_end = 8809 + _globals['_DELETELOCATIONRESPONSE']._serialized_start = 8811 + _globals['_DELETELOCATIONRESPONSE']._serialized_end = 8835 + _globals['_GETLOCATIONMETADATAREQUEST']._serialized_start = 8837 + _globals['_GETLOCATIONMETADATAREQUEST']._serialized_end = 8898 + _globals['_GETLOCATIONMETADATARESPONSE']._serialized_start = 8900 + _globals['_GETLOCATIONMETADATARESPONSE']._serialized_end = 8974 + _globals['_UPDATELOCATIONMETADATAREQUEST']._serialized_start = 8976 + _globals['_UPDATELOCATIONMETADATAREQUEST']._serialized_end = 9085 + _globals['_UPDATELOCATIONMETADATARESPONSE']._serialized_start = 9087 + _globals['_UPDATELOCATIONMETADATARESPONSE']._serialized_end = 9119 + _globals['_GETORGANIZATIONSWITHACCESSTOLOCATIONREQUEST']._serialized_start = 9121 + _globals['_GETORGANIZATIONSWITHACCESSTOLOCATIONREQUEST']._serialized_end = 9199 + _globals['_GETORGANIZATIONSWITHACCESSTOLOCATIONRESPONSE']._serialized_start = 9202 + _globals['_GETORGANIZATIONSWITHACCESSTOLOCATIONRESPONSE']._serialized_end = 9340 + _globals['_LISTLOCATIONSREQUEST']._serialized_start = 9342 + _globals['_LISTLOCATIONSREQUEST']._serialized_end = 9405 + _globals['_SHARELOCATIONREQUEST']._serialized_start = 9407 + _globals['_SHARELOCATIONREQUEST']._serialized_end = 9503 + _globals['_SHARELOCATIONRESPONSE']._serialized_start = 9505 + _globals['_SHARELOCATIONRESPONSE']._serialized_end = 9528 + _globals['_UNSHARELOCATIONREQUEST']._serialized_start = 9530 + _globals['_UNSHARELOCATIONREQUEST']._serialized_end = 9628 + _globals['_UNSHARELOCATIONRESPONSE']._serialized_start = 9630 + _globals['_UNSHARELOCATIONRESPONSE']._serialized_end = 9655 + _globals['_LISTLOCATIONSRESPONSE']._serialized_start = 9657 + _globals['_LISTLOCATIONSRESPONSE']._serialized_end = 9733 + _globals['_CREATELOCATIONSECRETREQUEST']._serialized_start = 9735 + _globals['_CREATELOCATIONSECRETREQUEST']._serialized_end = 9797 + _globals['_CREATELOCATIONSECRETRESPONSE']._serialized_start = 9799 + _globals['_CREATELOCATIONSECRETRESPONSE']._serialized_end = 9876 + _globals['_DELETELOCATIONSECRETREQUEST']._serialized_start = 9878 + _globals['_DELETELOCATIONSECRETREQUEST']._serialized_end = 9969 + _globals['_DELETELOCATIONSECRETRESPONSE']._serialized_start = 9971 + _globals['_DELETELOCATIONSECRETRESPONSE']._serialized_end = 10001 + _globals['_LOCATIONAUTHREQUEST']._serialized_start = 10003 + _globals['_LOCATIONAUTHREQUEST']._serialized_end = 10057 + _globals['_LOCATIONAUTHRESPONSE']._serialized_start = 10059 + _globals['_LOCATIONAUTHRESPONSE']._serialized_end = 10128 + _globals['_GETROBOTREQUEST']._serialized_start = 10130 + _globals['_GETROBOTREQUEST']._serialized_end = 10163 + _globals['_GETROVERRENTALROBOTSREQUEST']._serialized_start = 10165 + _globals['_GETROVERRENTALROBOTSREQUEST']._serialized_end = 10217 + _globals['_ROVERRENTALROBOT']._serialized_start = 10220 + _globals['_ROVERRENTALROBOT']._serialized_end = 10374 + _globals['_GETROVERRENTALROBOTSRESPONSE']._serialized_start = 10376 + _globals['_GETROVERRENTALROBOTSRESPONSE']._serialized_end = 10461 + _globals['_GETROBOTRESPONSE']._serialized_start = 10463 + _globals['_GETROBOTRESPONSE']._serialized_end = 10523 + _globals['_GETROBOTPARTSREQUEST']._serialized_start = 10525 + _globals['_GETROBOTPARTSREQUEST']._serialized_end = 10574 + _globals['_GETROBOTPARTSRESPONSE']._serialized_start = 10576 + _globals['_GETROBOTPARTSRESPONSE']._serialized_end = 10645 + _globals['_GETROBOTPARTREQUEST']._serialized_start = 10647 + _globals['_GETROBOTPARTREQUEST']._serialized_end = 10684 + _globals['_GETROBOTPARTRESPONSE']._serialized_start = 10686 + _globals['_GETROBOTPARTRESPONSE']._serialized_end = 10785 + _globals['_GETROBOTPARTLOGSREQUEST']._serialized_start = 10788 + _globals['_GETROBOTPARTLOGSREQUEST']._serialized_end = 11182 + _globals['_GETROBOTPARTLOGSRESPONSE']._serialized_start = 11184 + _globals['_GETROBOTPARTLOGSRESPONSE']._serialized_end = 11296 + _globals['_TAILROBOTPARTLOGSREQUEST']._serialized_start = 11298 + _globals['_TAILROBOTPARTLOGSREQUEST']._serialized_end = 11413 + _globals['_TAILROBOTPARTLOGSRESPONSE']._serialized_start = 11415 + _globals['_TAILROBOTPARTLOGSRESPONSE']._serialized_end = 11488 + _globals['_GETROBOTPARTHISTORYREQUEST']._serialized_start = 11490 + _globals['_GETROBOTPARTHISTORYREQUEST']._serialized_end = 11534 + _globals['_GETROBOTPARTHISTORYRESPONSE']._serialized_start = 11536 + _globals['_GETROBOTPARTHISTORYRESPONSE']._serialized_end = 11627 + _globals['_UPDATEROBOTPARTREQUEST']._serialized_start = 11630 + _globals['_UPDATEROBOTPARTREQUEST']._serialized_end = 11849 + _globals['_UPDATEROBOTPARTRESPONSE']._serialized_start = 11851 + _globals['_UPDATEROBOTPARTRESPONSE']._serialized_end = 11920 + _globals['_NEWROBOTPARTREQUEST']._serialized_start = 11922 + _globals['_NEWROBOTPARTREQUEST']._serialized_end = 11999 + _globals['_NEWROBOTPARTRESPONSE']._serialized_start = 12001 + _globals['_NEWROBOTPARTRESPONSE']._serialized_end = 12048 + _globals['_DELETEROBOTPARTREQUEST']._serialized_start = 12050 + _globals['_DELETEROBOTPARTREQUEST']._serialized_end = 12099 + _globals['_GETROBOTPARTMETADATAREQUEST']._serialized_start = 12101 + _globals['_GETROBOTPARTMETADATAREQUEST']._serialized_end = 12146 + _globals['_GETROBOTPARTMETADATARESPONSE']._serialized_start = 12148 + _globals['_GETROBOTPARTMETADATARESPONSE']._serialized_end = 12223 + _globals['_UPDATEROBOTPARTMETADATAREQUEST']._serialized_start = 12225 + _globals['_UPDATEROBOTPARTMETADATAREQUEST']._serialized_end = 12318 + _globals['_UPDATEROBOTPARTMETADATARESPONSE']._serialized_start = 12320 + _globals['_UPDATEROBOTPARTMETADATARESPONSE']._serialized_end = 12353 + _globals['_GETROBOTAPIKEYSREQUEST']._serialized_start = 12355 + _globals['_GETROBOTAPIKEYSREQUEST']._serialized_end = 12406 + _globals['_APIKEY']._serialized_start = 12408 + _globals['_APIKEY']._serialized_end = 12529 + _globals['_GETROBOTAPIKEYSRESPONSE']._serialized_start = 12531 + _globals['_GETROBOTAPIKEYSRESPONSE']._serialized_end = 12622 + _globals['_DELETEROBOTPARTRESPONSE']._serialized_start = 12624 + _globals['_DELETEROBOTPARTRESPONSE']._serialized_end = 12649 + _globals['_FRAGMENT']._serialized_start = 12652 + _globals['_FRAGMENT']._serialized_end = 13453 + _globals['_FRAGMENTHISTORYENTRY']._serialized_start = 13456 + _globals['_FRAGMENTHISTORYENTRY']._serialized_end = 13960 + _globals['_FRAGMENTREVISION']._serialized_start = 13962 + _globals['_FRAGMENTREVISION']._serialized_end = 14067 + _globals['_FRAGMENTTAG']._serialized_start = 14069 + _globals['_FRAGMENTTAG']._serialized_end = 14128 + _globals['_FRAGMENTERROR']._serialized_start = 14131 + _globals['_FRAGMENTERROR']._serialized_end = 14266 + _globals['_FRAGMENTUSAGE']._serialized_start = 14269 + _globals['_FRAGMENTUSAGE']._serialized_end = 14481 + _globals['_RESOLVEDFRAGMENT']._serialized_start = 14484 + _globals['_RESOLVEDFRAGMENT']._serialized_end = 14679 + _globals['_LISTFRAGMENTSREQUEST']._serialized_start = 14682 + _globals['_LISTFRAGMENTSREQUEST']._serialized_end = 14860 + _globals['_LISTFRAGMENTSRESPONSE']._serialized_start = 14863 + _globals['_LISTFRAGMENTSRESPONSE']._serialized_end = 15008 + _globals['_GETFRAGMENTREQUEST']._serialized_start = 15011 + _globals['_GETFRAGMENTREQUEST']._serialized_end = 15146 + _globals['_GETFRAGMENTRESPONSE']._serialized_start = 15149 + _globals['_GETFRAGMENTRESPONSE']._serialized_end = 15395 + _globals['_CREATEFRAGMENTREQUEST']._serialized_start = 15398 + _globals['_CREATEFRAGMENTREQUEST']._serialized_end = 15616 + _globals['_CREATEFRAGMENTRESPONSE']._serialized_start = 15618 + _globals['_CREATEFRAGMENTRESPONSE']._serialized_end = 15693 + _globals['_UPDATEFRAGMENTREQUEST']._serialized_start = 15696 + _globals['_UPDATEFRAGMENTREQUEST']._serialized_end = 16028 + _globals['_UPDATEFRAGMENTRESPONSE']._serialized_start = 16030 + _globals['_UPDATEFRAGMENTRESPONSE']._serialized_end = 16105 + _globals['_DELETEFRAGMENTREQUEST']._serialized_start = 16107 + _globals['_DELETEFRAGMENTREQUEST']._serialized_end = 16146 + _globals['_DELETEFRAGMENTRESPONSE']._serialized_start = 16148 + _globals['_DELETEFRAGMENTRESPONSE']._serialized_end = 16172 + _globals['_GETFRAGMENTHISTORYREQUEST']._serialized_start = 16175 + _globals['_GETFRAGMENTHISTORYREQUEST']._serialized_end = 16320 + _globals['_GETFRAGMENTHISTORYRESPONSE']._serialized_start = 16323 + _globals['_GETFRAGMENTHISTORYRESPONSE']._serialized_end = 16452 + _globals['_GETFRAGMENTUSAGEREQUEST']._serialized_start = 16454 + _globals['_GETFRAGMENTUSAGEREQUEST']._serialized_end = 16512 + _globals['_GETFRAGMENTUSAGERESPONSE']._serialized_start = 16514 + _globals['_GETFRAGMENTUSAGERESPONSE']._serialized_end = 16607 + _globals['_SETFRAGMENTTAGREQUEST']._serialized_start = 16609 + _globals['_SETFRAGMENTTAGREQUEST']._serialized_end = 16711 + _globals['_SETFRAGMENTTAGRESPONSE']._serialized_start = 16713 + _globals['_SETFRAGMENTTAGRESPONSE']._serialized_end = 16783 + _globals['_DELETEFRAGMENTTAGREQUEST']._serialized_start = 16785 + _globals['_DELETEFRAGMENTTAGREQUEST']._serialized_end = 16862 + _globals['_DELETEFRAGMENTTAGRESPONSE']._serialized_start = 16864 + _globals['_DELETEFRAGMENTTAGRESPONSE']._serialized_end = 16937 + _globals['_LISTROBOTSREQUEST']._serialized_start = 16939 + _globals['_LISTROBOTSREQUEST']._serialized_end = 16991 + _globals['_ADDITIONALFRAGMENT']._serialized_start = 16993 + _globals['_ADDITIONALFRAGMENT']._serialized_end = 17089 + _globals['_LISTNESTEDFRAGMENTSREQUEST']._serialized_start = 17092 + _globals['_LISTNESTEDFRAGMENTSREQUEST']._serialized_end = 17258 + _globals['_LISTNESTEDFRAGMENTSRESPONSE']._serialized_start = 17261 + _globals['_LISTNESTEDFRAGMENTSRESPONSE']._serialized_end = 17421 + _globals['_LISTMACHINEFRAGMENTSREQUEST']._serialized_start = 17424 + _globals['_LISTMACHINEFRAGMENTSREQUEST']._serialized_end = 17624 + _globals['_LISTMACHINEFRAGMENTSRESPONSE']._serialized_start = 17627 + _globals['_LISTMACHINEFRAGMENTSRESPONSE']._serialized_end = 17788 + _globals['_LISTMACHINESUMMARIESREQUEST']._serialized_start = 17790 + _globals['_LISTMACHINESUMMARIESREQUEST']._serialized_end = 17860 + _globals['_LISTMACHINESUMMARIESRESPONSE']._serialized_start = 17862 + _globals['_LISTMACHINESUMMARIESRESPONSE']._serialized_end = 17969 + _globals['_LOCATIONSUMMARY']._serialized_start = 17972 + _globals['_LOCATIONSUMMARY']._serialized_end = 18133 + _globals['_MACHINESUMMARY']._serialized_start = 18136 + _globals['_MACHINESUMMARY']._serialized_end = 18283 + _globals['_FRAGMENTSUMMARY']._serialized_start = 18285 + _globals['_FRAGMENTSUMMARY']._serialized_end = 18344 + _globals['_VIAMSERVERVERSION']._serialized_start = 18346 + _globals['_VIAMSERVERVERSION']._serialized_end = 18424 + _globals['_VIAMAGENTVERSION']._serialized_start = 18426 + _globals['_VIAMAGENTVERSION']._serialized_end = 18503 + _globals['_PARTSUMMARY']._serialized_start = 18506 + _globals['_PARTSUMMARY']._serialized_end = 19074 + _globals['_LISTROBOTSRESPONSE']._serialized_start = 19076 + _globals['_LISTROBOTSRESPONSE']._serialized_end = 19140 + _globals['_NEWROBOTREQUEST']._serialized_start = 19142 + _globals['_NEWROBOTREQUEST']._serialized_end = 19207 + _globals['_NEWROBOTRESPONSE']._serialized_start = 19209 + _globals['_NEWROBOTRESPONSE']._serialized_end = 19243 + _globals['_UPDATEROBOTREQUEST']._serialized_start = 19245 + _globals['_UPDATEROBOTREQUEST']._serialized_end = 19329 + _globals['_UPDATEROBOTRESPONSE']._serialized_start = 19331 + _globals['_UPDATEROBOTRESPONSE']._serialized_end = 19394 + _globals['_DELETEROBOTREQUEST']._serialized_start = 19396 + _globals['_DELETEROBOTREQUEST']._serialized_end = 19432 + _globals['_DELETEROBOTRESPONSE']._serialized_start = 19434 + _globals['_DELETEROBOTRESPONSE']._serialized_end = 19455 + _globals['_GETROBOTMETADATAREQUEST']._serialized_start = 19457 + _globals['_GETROBOTMETADATAREQUEST']._serialized_end = 19498 + _globals['_GETROBOTMETADATARESPONSE']._serialized_start = 19500 + _globals['_GETROBOTMETADATARESPONSE']._serialized_end = 19571 + _globals['_UPDATEROBOTMETADATAREQUEST']._serialized_start = 19573 + _globals['_UPDATEROBOTMETADATAREQUEST']._serialized_end = 19662 + _globals['_UPDATEROBOTMETADATARESPONSE']._serialized_start = 19664 + _globals['_UPDATEROBOTMETADATARESPONSE']._serialized_end = 19693 + _globals['_MARKPARTASMAINREQUEST']._serialized_start = 19695 + _globals['_MARKPARTASMAINREQUEST']._serialized_end = 19743 + _globals['_MARKPARTASMAINRESPONSE']._serialized_start = 19745 + _globals['_MARKPARTASMAINRESPONSE']._serialized_end = 19769 + _globals['_MARKPARTFORRESTARTREQUEST']._serialized_start = 19771 + _globals['_MARKPARTFORRESTARTREQUEST']._serialized_end = 19823 + _globals['_MARKPARTFORRESTARTRESPONSE']._serialized_start = 19825 + _globals['_MARKPARTFORRESTARTRESPONSE']._serialized_end = 19853 + _globals['_CREATEROBOTPARTSECRETREQUEST']._serialized_start = 19855 + _globals['_CREATEROBOTPARTSECRETREQUEST']._serialized_end = 19910 + _globals['_CREATEROBOTPARTSECRETRESPONSE']._serialized_start = 19912 + _globals['_CREATEROBOTPARTSECRETRESPONSE']._serialized_end = 19987 + _globals['_DELETEROBOTPARTSECRETREQUEST']._serialized_start = 19989 + _globals['_DELETEROBOTPARTSECRETREQUEST']._serialized_end = 20073 + _globals['_DELETEROBOTPARTSECRETRESPONSE']._serialized_start = 20075 + _globals['_DELETEROBOTPARTSECRETRESPONSE']._serialized_end = 20106 + _globals['_AUTHORIZATION']._serialized_start = 20109 + _globals['_AUTHORIZATION']._serialized_end = 20395 + _globals['_ADDROLEREQUEST']._serialized_start = 20397 + _globals['_ADDROLEREQUEST']._serialized_end = 20479 + _globals['_ADDROLERESPONSE']._serialized_start = 20481 + _globals['_ADDROLERESPONSE']._serialized_end = 20498 + _globals['_REMOVEROLEREQUEST']._serialized_start = 20500 + _globals['_REMOVEROLEREQUEST']._serialized_end = 20585 + _globals['_REMOVEROLERESPONSE']._serialized_start = 20587 + _globals['_REMOVEROLERESPONSE']._serialized_end = 20607 + _globals['_CHANGEROLEREQUEST']._serialized_start = 20610 + _globals['_CHANGEROLEREQUEST']._serialized_end = 20775 + _globals['_CHANGEROLERESPONSE']._serialized_start = 20777 + _globals['_CHANGEROLERESPONSE']._serialized_end = 20797 + _globals['_LISTAUTHORIZATIONSREQUEST']._serialized_start = 20799 + _globals['_LISTAUTHORIZATIONSREQUEST']._serialized_end = 20902 + _globals['_LISTAUTHORIZATIONSRESPONSE']._serialized_start = 20904 + _globals['_LISTAUTHORIZATIONSRESPONSE']._serialized_end = 21000 + _globals['_CHECKPERMISSIONSREQUEST']._serialized_start = 21002 + _globals['_CHECKPERMISSIONSREQUEST']._serialized_end = 21097 + _globals['_AUTHORIZEDPERMISSIONS']._serialized_start = 21099 + _globals['_AUTHORIZEDPERMISSIONS']._serialized_end = 21226 + _globals['_CHECKPERMISSIONSRESPONSE']._serialized_start = 21228 + _globals['_CHECKPERMISSIONSRESPONSE']._serialized_end = 21345 + _globals['_MODULEVERSION']._serialized_start = 21348 + _globals['_MODULEVERSION']._serialized_end = 21638 + _globals['_MODULEMETADATA']._serialized_start = 21641 + _globals['_MODULEMETADATA']._serialized_end = 21918 + _globals['_MLMODELMETADATA']._serialized_start = 21921 + _globals['_MLMODELMETADATA']._serialized_end = 22113 + _globals['_MLTRAININGVERSION']._serialized_start = 22115 + _globals['_MLTRAININGVERSION']._serialized_end = 22219 + _globals['_MLTRAININGMETADATA']._serialized_start = 22222 + _globals['_MLTRAININGMETADATA']._serialized_end = 22477 + _globals['_REGISTRYITEM']._serialized_start = 22480 + _globals['_REGISTRYITEM']._serialized_end = 23388 + _globals['_GETREGISTRYITEMREQUEST']._serialized_start = 23391 + _globals['_GETREGISTRYITEMREQUEST']._serialized_end = 23550 + _globals['_GETREGISTRYITEMRESPONSE']._serialized_start = 23552 + _globals['_GETREGISTRYITEMRESPONSE']._serialized_end = 23624 + _globals['_CREATEREGISTRYITEMREQUEST']._serialized_start = 23627 + _globals['_CREATEREGISTRYITEMREQUEST']._serialized_end = 23770 + _globals['_CREATEREGISTRYITEMRESPONSE']._serialized_start = 23772 + _globals['_CREATEREGISTRYITEMRESPONSE']._serialized_end = 23800 + _globals['_UPDATEREGISTRYITEMREQUEST']._serialized_start = 23803 + _globals['_UPDATEREGISTRYITEMREQUEST']._serialized_end = 24415 + _globals['_UPDATEREGISTRYITEMRESPONSE']._serialized_start = 24417 + _globals['_UPDATEREGISTRYITEMRESPONSE']._serialized_end = 24445 + _globals['_LISTREGISTRYITEMSREQUEST']._serialized_start = 24448 + _globals['_LISTREGISTRYITEMSREQUEST']._serialized_end = 25009 + _globals['_LISTREGISTRYITEMSRESPONSE']._serialized_start = 25011 + _globals['_LISTREGISTRYITEMSRESPONSE']._serialized_end = 25087 + _globals['_DELETEREGISTRYITEMREQUEST']._serialized_start = 25089 + _globals['_DELETEREGISTRYITEMREQUEST']._serialized_end = 25141 + _globals['_DELETEREGISTRYITEMRESPONSE']._serialized_start = 25143 + _globals['_DELETEREGISTRYITEMRESPONSE']._serialized_end = 25171 + _globals['_RENAMEREGISTRYITEMREQUEST']._serialized_start = 25173 + _globals['_RENAMEREGISTRYITEMREQUEST']._serialized_end = 25252 + _globals['_RENAMEREGISTRYITEMRESPONSE']._serialized_start = 25254 + _globals['_RENAMEREGISTRYITEMRESPONSE']._serialized_end = 25329 + _globals['_TRANSFERREGISTRYITEMREQUEST']._serialized_start = 25331 + _globals['_TRANSFERREGISTRYITEMREQUEST']._serialized_end = 25435 + _globals['_TRANSFERREGISTRYITEMRESPONSE']._serialized_start = 25437 + _globals['_TRANSFERREGISTRYITEMRESPONSE']._serialized_end = 25467 + _globals['_CREATEMODULEREQUEST']._serialized_start = 25469 + _globals['_CREATEMODULEREQUEST']._serialized_end = 25551 + _globals['_CREATEMODULERESPONSE']._serialized_start = 25553 + _globals['_CREATEMODULERESPONSE']._serialized_end = 25622 + _globals['_UPDATEMODULEREQUEST']._serialized_start = 25625 + _globals['_UPDATEMODULEREQUEST']._serialized_end = 26027 + _globals['_APP']._serialized_start = 26029 + _globals['_APP']._serialized_end = 26106 + _globals['_UPDATEMODULERESPONSE']._serialized_start = 26108 + _globals['_UPDATEMODULERESPONSE']._serialized_end = 26148 + _globals['_UPDATEMODULEMETADATA']._serialized_start = 26150 + _globals['_UPDATEMODULEMETADATA']._serialized_end = 26248 + _globals['_UPDATEMLMODELMETADATA']._serialized_start = 26251 + _globals['_UPDATEMLMODELMETADATA']._serialized_end = 26421 + _globals['_UPDATEMLTRAININGMETADATA']._serialized_start = 26424 + _globals['_UPDATEMLTRAININGMETADATA']._serialized_end = 26619 + _globals['_MODEL']._serialized_start = 26622 + _globals['_MODEL']._serialized_end = 26858 + _globals['_MODULEFILEINFO']._serialized_start = 26861 + _globals['_MODULEFILEINFO']._serialized_end = 26997 + _globals['_UPLOADMODULEFILEREQUEST']._serialized_start = 27000 + _globals['_UPLOADMODULEFILEREQUEST']._serialized_end = 27135 + _globals['_UPLOADMODULEFILERESPONSE']._serialized_start = 27137 + _globals['_UPLOADMODULEFILERESPONSE']._serialized_end = 27181 + _globals['_GETMODULEREQUEST']._serialized_start = 27184 + _globals['_GETMODULEREQUEST']._serialized_end = 27341 + _globals['_GETMODULERESPONSE']._serialized_start = 27343 + _globals['_GETMODULERESPONSE']._serialized_end = 27407 + _globals['_MODULE']._serialized_start = 27410 + _globals['_MODULE']._serialized_end = 28024 + _globals['_VERSIONHISTORY']._serialized_start = 28027 + _globals['_VERSIONHISTORY']._serialized_end = 28318 + _globals['_UPLOADS']._serialized_start = 28320 + _globals['_UPLOADS']._serialized_end = 28418 + _globals['_LISTMODULESREQUEST']._serialized_start = 28421 + _globals['_LISTMODULESREQUEST']._serialized_end = 28617 + _globals['_LISTMODULESRESPONSE']._serialized_start = 28619 + _globals['_LISTMODULESRESPONSE']._serialized_end = 28687 + _globals['_GETUSERIDBYEMAILREQUEST']._serialized_start = 28689 + _globals['_GETUSERIDBYEMAILREQUEST']._serialized_end = 28736 + _globals['_GETUSERIDBYEMAILRESPONSE']._serialized_start = 28738 + _globals['_GETUSERIDBYEMAILRESPONSE']._serialized_end = 28789 + _globals['_LISTORGANIZATIONSBYUSERREQUEST']._serialized_start = 28791 + _globals['_LISTORGANIZATIONSBYUSERREQUEST']._serialized_end = 28848 + _globals['_ORGDETAILS']._serialized_start = 28851 + _globals['_ORGDETAILS']._serialized_end = 29081 + _globals['_LISTORGANIZATIONSBYUSERRESPONSE']._serialized_start = 29083 + _globals['_LISTORGANIZATIONSBYUSERRESPONSE']._serialized_end = 29161 + _globals['_SEARCHORGANIZATIONSREQUEST']._serialized_start = 29164 + _globals['_SEARCHORGANIZATIONSREQUEST']._serialized_end = 29376 + _globals['_SEARCHORGANIZATIONSRESPONSE']._serialized_start = 29378 + _globals['_SEARCHORGANIZATIONSRESPONSE']._serialized_end = 29470 + _globals['_CREATEKEYREQUEST']._serialized_start = 29472 + _globals['_CREATEKEYREQUEST']._serialized_end = 29578 + _globals['_CREATEKEYRESPONSE']._serialized_start = 29580 + _globals['_CREATEKEYRESPONSE']._serialized_end = 29633 + _globals['_DELETEKEYREQUEST']._serialized_start = 29635 + _globals['_DELETEKEYREQUEST']._serialized_end = 29669 + _globals['_DELETEKEYRESPONSE']._serialized_start = 29671 + _globals['_DELETEKEYRESPONSE']._serialized_end = 29690 + _globals['_RENAMEKEYREQUEST']._serialized_start = 29692 + _globals['_RENAMEKEYREQUEST']._serialized_end = 29746 + _globals['_RENAMEKEYRESPONSE']._serialized_start = 29748 + _globals['_RENAMEKEYRESPONSE']._serialized_end = 29803 + _globals['_AUTHORIZATIONDETAILS']._serialized_start = 29806 + _globals['_AUTHORIZATIONDETAILS']._serialized_end = 30011 + _globals['_APIKEYWITHAUTHORIZATIONS']._serialized_start = 30014 + _globals['_APIKEYWITHAUTHORIZATIONS']._serialized_end = 30161 + _globals['_LISTKEYSREQUEST']._serialized_start = 30163 + _globals['_LISTKEYSREQUEST']._serialized_end = 30203 + _globals['_LISTKEYSRESPONSE']._serialized_start = 30205 + _globals['_LISTKEYSRESPONSE']._serialized_end = 30289 + _globals['_ROTATEKEYREQUEST']._serialized_start = 30291 + _globals['_ROTATEKEYREQUEST']._serialized_end = 30325 + _globals['_ROTATEKEYRESPONSE']._serialized_start = 30327 + _globals['_ROTATEKEYRESPONSE']._serialized_end = 30380 + _globals['_CREATEKEYFROMEXISTINGKEYAUTHORIZATIONSREQUEST']._serialized_start = 30382 + _globals['_CREATEKEYFROMEXISTINGKEYAUTHORIZATIONSREQUEST']._serialized_end = 30445 + _globals['_CREATEKEYFROMEXISTINGKEYAUTHORIZATIONSRESPONSE']._serialized_start = 30447 + _globals['_CREATEKEYFROMEXISTINGKEYAUTHORIZATIONSRESPONSE']._serialized_end = 30529 + _globals['_GETAPPCONTENTREQUEST']._serialized_start = 30531 + _globals['_GETAPPCONTENTREQUEST']._serialized_end = 30616 + _globals['_GETAPPCONTENTRESPONSE']._serialized_start = 30618 + _globals['_GETAPPCONTENTRESPONSE']._serialized_end = 30702 + _globals['_ORGANIZATIONSETLOGOREQUEST']._serialized_start = 30704 + _globals['_ORGANIZATIONSETLOGOREQUEST']._serialized_end = 30775 + _globals['_ORGANIZATIONSETLOGORESPONSE']._serialized_start = 30777 + _globals['_ORGANIZATIONSETLOGORESPONSE']._serialized_end = 30806 + _globals['_ORGANIZATIONGETLOGOREQUEST']._serialized_start = 30808 + _globals['_ORGANIZATIONGETLOGOREQUEST']._serialized_end = 30859 + _globals['_ORGANIZATIONGETLOGORESPONSE']._serialized_start = 30861 + _globals['_ORGANIZATIONGETLOGORESPONSE']._serialized_end = 30908 + _globals['_ENABLEAUTHSERVICEREQUEST']._serialized_start = 30910 + _globals['_ENABLEAUTHSERVICEREQUEST']._serialized_end = 30959 + _globals['_ENABLEAUTHSERVICERESPONSE']._serialized_start = 30961 + _globals['_ENABLEAUTHSERVICERESPONSE']._serialized_end = 30988 + _globals['_DISABLEAUTHSERVICEREQUEST']._serialized_start = 30990 + _globals['_DISABLEAUTHSERVICEREQUEST']._serialized_end = 31040 + _globals['_DISABLEAUTHSERVICERESPONSE']._serialized_start = 31042 + _globals['_DISABLEAUTHSERVICERESPONSE']._serialized_end = 31070 + _globals['_CREATEOAUTHAPPREQUEST']._serialized_start = 31073 + _globals['_CREATEOAUTHAPPREQUEST']._serialized_end = 31213 + _globals['_CREATEOAUTHAPPRESPONSE']._serialized_start = 31215 + _globals['_CREATEOAUTHAPPRESPONSE']._serialized_end = 31305 + _globals['_READOAUTHAPPREQUEST']._serialized_start = 31307 + _globals['_READOAUTHAPPREQUEST']._serialized_end = 31380 + _globals['_READOAUTHAPPRESPONSE']._serialized_start = 31383 + _globals['_READOAUTHAPPRESPONSE']._serialized_end = 31536 + _globals['_UPDATEOAUTHAPPREQUEST']._serialized_start = 31539 + _globals['_UPDATEOAUTHAPPREQUEST']._serialized_end = 31708 + _globals['_UPDATEOAUTHAPPRESPONSE']._serialized_start = 31710 + _globals['_UPDATEOAUTHAPPRESPONSE']._serialized_end = 31734 + _globals['_DELETEOAUTHAPPREQUEST']._serialized_start = 31736 + _globals['_DELETEOAUTHAPPREQUEST']._serialized_end = 31811 + _globals['_DELETEOAUTHAPPRESPONSE']._serialized_start = 31813 + _globals['_DELETEOAUTHAPPRESPONSE']._serialized_end = 31837 + _globals['_LISTOAUTHAPPSREQUEST']._serialized_start = 31839 + _globals['_LISTOAUTHAPPSREQUEST']._serialized_end = 31884 + _globals['_LISTOAUTHAPPSRESPONSE']._serialized_start = 31886 + _globals['_LISTOAUTHAPPSRESPONSE']._serialized_end = 31940 + _globals['_OAUTHCONFIG']._serialized_start = 31943 + _globals['_OAUTHCONFIG']._serialized_end = 32317 + _globals['_APPSERVICE']._serialized_start = 33828 + _globals['_APPSERVICE']._serialized_end = 44390 \ No newline at end of file diff --git a/src/viam/gen/app/v1/app_pb2.pyi b/src/viam/gen/app/v1/app_pb2.pyi index 7a274e2ae..4427da430 100644 --- a/src/viam/gen/app/v1/app_pb2.pyi +++ b/src/viam/gen/app/v1/app_pb2.pyi @@ -2,26 +2,213 @@ @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ +from ... import app import builtins import collections.abc +from ... import common import google.protobuf.descriptor import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import google.protobuf.struct_pb2 import google.protobuf.timestamp_pb2 import sys -if sys.version_info >= (3, 8): +import typing +if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _AuthenticationType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _AuthenticationTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_AuthenticationType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + AUTHENTICATION_TYPE_UNSPECIFIED: _AuthenticationType.ValueType + AUTHENTICATION_TYPE_WEB_OAUTH: _AuthenticationType.ValueType + AUTHENTICATION_TYPE_API_KEY: _AuthenticationType.ValueType + AUTHENTICATION_TYPE_ROBOT_PART_SECRET: _AuthenticationType.ValueType + AUTHENTICATION_TYPE_LOCATION_SECRET: _AuthenticationType.ValueType + +class AuthenticationType(_AuthenticationType, metaclass=_AuthenticationTypeEnumTypeWrapper): + ... +AUTHENTICATION_TYPE_UNSPECIFIED: AuthenticationType.ValueType +AUTHENTICATION_TYPE_WEB_OAUTH: AuthenticationType.ValueType +AUTHENTICATION_TYPE_API_KEY: AuthenticationType.ValueType +AUTHENTICATION_TYPE_ROBOT_PART_SECRET: AuthenticationType.ValueType +AUTHENTICATION_TYPE_LOCATION_SECRET: AuthenticationType.ValueType +global___AuthenticationType = AuthenticationType + +class _FragmentVisibility: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _FragmentVisibilityEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_FragmentVisibility.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + FRAGMENT_VISIBILITY_UNSPECIFIED: _FragmentVisibility.ValueType + FRAGMENT_VISIBILITY_PRIVATE: _FragmentVisibility.ValueType + FRAGMENT_VISIBILITY_PUBLIC: _FragmentVisibility.ValueType + FRAGMENT_VISIBILITY_PUBLIC_UNLISTED: _FragmentVisibility.ValueType + +class FragmentVisibility(_FragmentVisibility, metaclass=_FragmentVisibilityEnumTypeWrapper): + ... +FRAGMENT_VISIBILITY_UNSPECIFIED: FragmentVisibility.ValueType +FRAGMENT_VISIBILITY_PRIVATE: FragmentVisibility.ValueType +FRAGMENT_VISIBILITY_PUBLIC: FragmentVisibility.ValueType +FRAGMENT_VISIBILITY_PUBLIC_UNLISTED: FragmentVisibility.ValueType +global___FragmentVisibility = FragmentVisibility + +class _FragmentErrorType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _FragmentErrorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_FragmentErrorType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + FRAGMENT_ERROR_TYPE_UNSPECIFIED: _FragmentErrorType.ValueType + FRAGMENT_ERROR_TYPE_NO_ACCESS: _FragmentErrorType.ValueType + FRAGMENT_ERROR_TYPE_NESTING_LIMIT_EXCEEDED: _FragmentErrorType.ValueType + FRAGMENT_ERROR_TYPE_CHILD_ID_INVALID: _FragmentErrorType.ValueType + FRAGMENT_ERROR_TYPE_CYCLE_DETECTED: _FragmentErrorType.ValueType + +class FragmentErrorType(_FragmentErrorType, metaclass=_FragmentErrorTypeEnumTypeWrapper): + ... +FRAGMENT_ERROR_TYPE_UNSPECIFIED: FragmentErrorType.ValueType +FRAGMENT_ERROR_TYPE_NO_ACCESS: FragmentErrorType.ValueType +FRAGMENT_ERROR_TYPE_NESTING_LIMIT_EXCEEDED: FragmentErrorType.ValueType +FRAGMENT_ERROR_TYPE_CHILD_ID_INVALID: FragmentErrorType.ValueType +FRAGMENT_ERROR_TYPE_CYCLE_DETECTED: FragmentErrorType.ValueType +global___FragmentErrorType = FragmentErrorType + +class _RegistryItemStatus: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _RegistryItemStatusEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_RegistryItemStatus.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + REGISTRY_ITEM_STATUS_UNSPECIFIED: _RegistryItemStatus.ValueType + REGISTRY_ITEM_STATUS_PUBLISHED: _RegistryItemStatus.ValueType + REGISTRY_ITEM_STATUS_IN_DEVELOPMENT: _RegistryItemStatus.ValueType + +class RegistryItemStatus(_RegistryItemStatus, metaclass=_RegistryItemStatusEnumTypeWrapper): + ... +REGISTRY_ITEM_STATUS_UNSPECIFIED: RegistryItemStatus.ValueType +REGISTRY_ITEM_STATUS_PUBLISHED: RegistryItemStatus.ValueType +REGISTRY_ITEM_STATUS_IN_DEVELOPMENT: RegistryItemStatus.ValueType +global___RegistryItemStatus = RegistryItemStatus + +class _Visibility: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _VisibilityEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Visibility.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + VISIBILITY_UNSPECIFIED: _Visibility.ValueType + VISIBILITY_PRIVATE: _Visibility.ValueType + 'Private registry items are visible only within the owning org' + VISIBILITY_PUBLIC: _Visibility.ValueType + 'Public registry items are visible to everyone' + VISIBILITY_PUBLIC_UNLISTED: _Visibility.ValueType + "Public Unlisted registry items are usable in everyone's robot but are hidden from the registry page as if they are private" + +class Visibility(_Visibility, metaclass=_VisibilityEnumTypeWrapper): + ... +VISIBILITY_UNSPECIFIED: Visibility.ValueType +VISIBILITY_PRIVATE: Visibility.ValueType +'Private registry items are visible only within the owning org' +VISIBILITY_PUBLIC: Visibility.ValueType +'Public registry items are visible to everyone' +VISIBILITY_PUBLIC_UNLISTED: Visibility.ValueType +"Public Unlisted registry items are usable in everyone's robot but are hidden from the registry page as if they are private" +global___Visibility = Visibility + +class _ClientAuthentication: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ClientAuthenticationEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ClientAuthentication.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CLIENT_AUTHENTICATION_UNSPECIFIED: _ClientAuthentication.ValueType + CLIENT_AUTHENTICATION_REQUIRED: _ClientAuthentication.ValueType + CLIENT_AUTHENTICATION_NOT_REQUIRED: _ClientAuthentication.ValueType + CLIENT_AUTHENTICATION_NOT_REQUIRED_WHEN_USING_PKCE: _ClientAuthentication.ValueType + +class ClientAuthentication(_ClientAuthentication, metaclass=_ClientAuthenticationEnumTypeWrapper): + ... +CLIENT_AUTHENTICATION_UNSPECIFIED: ClientAuthentication.ValueType +CLIENT_AUTHENTICATION_REQUIRED: ClientAuthentication.ValueType +CLIENT_AUTHENTICATION_NOT_REQUIRED: ClientAuthentication.ValueType +CLIENT_AUTHENTICATION_NOT_REQUIRED_WHEN_USING_PKCE: ClientAuthentication.ValueType +global___ClientAuthentication = ClientAuthentication + +class _PKCE: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PKCEEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PKCE.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PKCE_UNSPECIFIED: _PKCE.ValueType + PKCE_REQUIRED: _PKCE.ValueType + PKCE_NOT_REQUIRED: _PKCE.ValueType + PKCE_NOT_REQUIRED_WHEN_USING_CLIENT_AUTHENTICATION: _PKCE.ValueType + +class PKCE(_PKCE, metaclass=_PKCEEnumTypeWrapper): + ... +PKCE_UNSPECIFIED: PKCE.ValueType +PKCE_REQUIRED: PKCE.ValueType +PKCE_NOT_REQUIRED: PKCE.ValueType +PKCE_NOT_REQUIRED_WHEN_USING_CLIENT_AUTHENTICATION: PKCE.ValueType +global___PKCE = PKCE + +class _URLValidation: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _URLValidationEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_URLValidation.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + URL_VALIDATION_UNSPECIFIED: _URLValidation.ValueType + URL_VALIDATION_EXACT_MATCH: _URLValidation.ValueType + URL_VALIDATION_ALLOW_WILDCARDS: _URLValidation.ValueType + +class URLValidation(_URLValidation, metaclass=_URLValidationEnumTypeWrapper): + ... +URL_VALIDATION_UNSPECIFIED: URLValidation.ValueType +URL_VALIDATION_EXACT_MATCH: URLValidation.ValueType +URL_VALIDATION_ALLOW_WILDCARDS: URLValidation.ValueType +global___URLValidation = URLValidation + +class _EnabledGrant: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _EnabledGrantEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_EnabledGrant.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ENABLED_GRANT_UNSPECIFIED: _EnabledGrant.ValueType + ENABLED_GRANT_AUTHORIZATION_CODE: _EnabledGrant.ValueType + ENABLED_GRANT_IMPLICIT: _EnabledGrant.ValueType + ENABLED_GRANT_PASSWORD: _EnabledGrant.ValueType + ENABLED_GRANT_REFRESH_TOKEN: _EnabledGrant.ValueType + ENABLED_GRANT_DEVICE_CODE: _EnabledGrant.ValueType + +class EnabledGrant(_EnabledGrant, metaclass=_EnabledGrantEnumTypeWrapper): + ... +ENABLED_GRANT_UNSPECIFIED: EnabledGrant.ValueType +ENABLED_GRANT_AUTHORIZATION_CODE: EnabledGrant.ValueType +ENABLED_GRANT_IMPLICIT: EnabledGrant.ValueType +ENABLED_GRANT_PASSWORD: EnabledGrant.ValueType +ENABLED_GRANT_REFRESH_TOKEN: EnabledGrant.ValueType +ENABLED_GRANT_DEVICE_CODE: EnabledGrant.ValueType +global___EnabledGrant = EnabledGrant + +@typing.final class Robot(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int LOCATION_FIELD_NUMBER: builtins.int LAST_ACCESS_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int id: builtins.str name: builtins.str location: builtins.str @@ -30,16 +217,21 @@ class Robot(google.protobuf.message.Message): def last_access(self) -> google.protobuf.timestamp_pb2.Timestamp: ... - def __init__(self, *, id: builtins.str=..., name: builtins.str=..., location: builtins.str=..., last_access: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., location: builtins.str=..., last_access: google.protobuf.timestamp_pb2.Timestamp | None=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['last_access', b'last_access']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['created_on', b'created_on', 'last_access', b'last_access']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id', 'last_access', b'last_access', 'location', b'location', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['created_on', b'created_on', 'id', b'id', 'last_access', b'last_access', 'location', b'location', 'name', b'name']) -> None: ... global___Robot = Robot +@typing.final class RobotPart(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -54,6 +246,9 @@ class RobotPart(google.protobuf.message.Message): MAIN_PART_FIELD_NUMBER: builtins.int FQDN_FIELD_NUMBER: builtins.int LOCAL_FQDN_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + SECRETS_FIELD_NUMBER: builtins.int + LAST_UPDATED_FIELD_NUMBER: builtins.int id: builtins.str name: builtins.str dns_name: builtins.str @@ -62,6 +257,9 @@ class RobotPart(google.protobuf.message.Message): robot: builtins.str location_id: builtins.str 'Store the location_id to allow for unique indexes across parts and locations. This filed MUST be updated each time the robots location\n changes.\n ' + main_part: builtins.bool + fqdn: builtins.str + local_fqdn: builtins.str @property def robot_config(self) -> google.protobuf.struct_pb2.Struct: @@ -74,26 +272,37 @@ class RobotPart(google.protobuf.message.Message): @property def user_supplied_info(self) -> google.protobuf.struct_pb2.Struct: ... - main_part: builtins.bool - fqdn: builtins.str - local_fqdn: builtins.str - def __init__(self, *, id: builtins.str=..., name: builtins.str=..., dns_name: builtins.str=..., secret: builtins.str=..., robot: builtins.str=..., location_id: builtins.str=..., robot_config: google.protobuf.struct_pb2.Struct | None=..., last_access: google.protobuf.timestamp_pb2.Timestamp | None=..., user_supplied_info: google.protobuf.struct_pb2.Struct | None=..., main_part: builtins.bool=..., fqdn: builtins.str=..., local_fqdn: builtins.str=...) -> None: + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def secrets(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SharedSecret]: + """List of secrets allowed for authentication.""" + + @property + def last_updated(self) -> google.protobuf.timestamp_pb2.Timestamp: + """latest timestamp when a robot part was updated""" + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., dns_name: builtins.str=..., secret: builtins.str=..., robot: builtins.str=..., location_id: builtins.str=..., robot_config: google.protobuf.struct_pb2.Struct | None=..., last_access: google.protobuf.timestamp_pb2.Timestamp | None=..., user_supplied_info: google.protobuf.struct_pb2.Struct | None=..., main_part: builtins.bool=..., fqdn: builtins.str=..., local_fqdn: builtins.str=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=..., secrets: collections.abc.Iterable[global___SharedSecret] | None=..., last_updated: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['last_access', b'last_access', 'robot_config', b'robot_config', 'user_supplied_info', b'user_supplied_info']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['created_on', b'created_on', 'last_access', b'last_access', 'last_updated', b'last_updated', 'robot_config', b'robot_config', 'user_supplied_info', b'user_supplied_info']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['dns_name', b'dns_name', 'fqdn', b'fqdn', 'id', b'id', 'last_access', b'last_access', 'local_fqdn', b'local_fqdn', 'location_id', b'location_id', 'main_part', b'main_part', 'name', b'name', 'robot', b'robot', 'robot_config', b'robot_config', 'secret', b'secret', 'user_supplied_info', b'user_supplied_info']) -> None: + def ClearField(self, field_name: typing.Literal['created_on', b'created_on', 'dns_name', b'dns_name', 'fqdn', b'fqdn', 'id', b'id', 'last_access', b'last_access', 'last_updated', b'last_updated', 'local_fqdn', b'local_fqdn', 'location_id', b'location_id', 'main_part', b'main_part', 'name', b'name', 'robot', b'robot', 'robot_config', b'robot_config', 'secret', b'secret', 'secrets', b'secrets', 'user_supplied_info', b'user_supplied_info']) -> None: ... global___RobotPart = RobotPart +@typing.final class RobotPartHistoryEntry(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor PART_FIELD_NUMBER: builtins.int ROBOT_FIELD_NUMBER: builtins.int WHEN_FIELD_NUMBER: builtins.int OLD_FIELD_NUMBER: builtins.int + EDITED_BY_FIELD_NUMBER: builtins.int part: builtins.str robot: builtins.str @@ -105,16 +314,38 @@ class RobotPartHistoryEntry(google.protobuf.message.Message): def old(self) -> global___RobotPart: ... - def __init__(self, *, part: builtins.str=..., robot: builtins.str=..., when: google.protobuf.timestamp_pb2.Timestamp | None=..., old: global___RobotPart | None=...) -> None: + @property + def edited_by(self) -> global___AuthenticatorInfo: + ... + + def __init__(self, *, part: builtins.str=..., robot: builtins.str=..., when: google.protobuf.timestamp_pb2.Timestamp | None=..., old: global___RobotPart | None=..., edited_by: global___AuthenticatorInfo | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['old', b'old', 'when', b'when']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['edited_by', b'edited_by', 'old', b'old', 'when', b'when']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['old', b'old', 'part', b'part', 'robot', b'robot', 'when', b'when']) -> None: + def ClearField(self, field_name: typing.Literal['edited_by', b'edited_by', 'old', b'old', 'part', b'part', 'robot', b'robot', 'when', b'when']) -> None: ... global___RobotPartHistoryEntry = RobotPartHistoryEntry +@typing.final +class AuthenticatorInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TYPE_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + IS_DEACTIVATED_FIELD_NUMBER: builtins.int + type: global___AuthenticationType.ValueType + value: builtins.str + is_deactivated: builtins.bool + + def __init__(self, *, type: global___AuthenticationType.ValueType=..., value: builtins.str=..., is_deactivated: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['is_deactivated', b'is_deactivated', 'type', b'type', 'value', b'value']) -> None: + ... +global___AuthenticatorInfo = AuthenticatorInfo + +@typing.final class ListOrganizationsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -122,20 +353,74 @@ class ListOrganizationsRequest(google.protobuf.message.Message): ... global___ListOrganizationsRequest = ListOrganizationsRequest +@typing.final class Organization(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + PUBLIC_NAMESPACE_FIELD_NUMBER: builtins.int + DEFAULT_REGION_FIELD_NUMBER: builtins.int + CID_FIELD_NUMBER: builtins.int id: builtins.str name: builtins.str + public_namespace: builtins.str + default_region: builtins.str + 'GCS region of the organization. Locations created under this org will have their GCS region set to this by default and packages\n associated with this org will be stored in this region.\n ' + cid: builtins.str - def __init__(self, *, id: builtins.str=..., name: builtins.str=...) -> None: + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=..., public_namespace: builtins.str=..., default_region: builtins.str=..., cid: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_cid', b'_cid', 'cid', b'cid', 'created_on', b'created_on']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_cid', b'_cid', 'cid', b'cid', 'created_on', b'created_on', 'default_region', b'default_region', 'id', b'id', 'name', b'name', 'public_namespace', b'public_namespace']) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id', 'name', b'name']) -> None: + def WhichOneof(self, oneof_group: typing.Literal['_cid', b'_cid']) -> typing.Literal['cid'] | None: ... global___Organization = Organization +@typing.final +class OrganizationMember(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + USER_ID_FIELD_NUMBER: builtins.int + EMAILS_FIELD_NUMBER: builtins.int + DATE_ADDED_FIELD_NUMBER: builtins.int + LAST_LOGIN_FIELD_NUMBER: builtins.int + user_id: builtins.str + + @property + def emails(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def date_added(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def last_login(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, user_id: builtins.str=..., emails: collections.abc.Iterable[builtins.str] | None=..., date_added: google.protobuf.timestamp_pb2.Timestamp | None=..., last_login: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_last_login', b'_last_login', 'date_added', b'date_added', 'last_login', b'last_login']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_last_login', b'_last_login', 'date_added', b'date_added', 'emails', b'emails', 'last_login', b'last_login', 'user_id', b'user_id']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_last_login', b'_last_login']) -> typing.Literal['last_login'] | None: + ... +global___OrganizationMember = OrganizationMember + +@typing.final class ListOrganizationsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ORGANIZATIONS_FIELD_NUMBER: builtins.int @@ -147,458 +432,4351 @@ class ListOrganizationsResponse(google.protobuf.message.Message): def __init__(self, *, organizations: collections.abc.Iterable[global___Organization] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['organizations', b'organizations']) -> None: + def ClearField(self, field_name: typing.Literal['organizations', b'organizations']) -> None: ... global___ListOrganizationsResponse = ListOrganizationsResponse -class Location(google.protobuf.message.Message): +@typing.final +class OrganizationInvite(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - NAME_FIELD_NUMBER: builtins.int - id: builtins.str - name: builtins.str + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + EMAIL_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + AUTHORIZATIONS_FIELD_NUMBER: builtins.int + organization_id: builtins.str + email: builtins.str - def __init__(self, *, id: builtins.str=..., name: builtins.str=...) -> None: + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id', 'name', b'name']) -> None: + @property + def authorizations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Authorization]: ... -global___Location = Location -class ListLocationsRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - ORGANIZATION_ID_FIELD_NUMBER: builtins.int - organization_id: builtins.str + def __init__(self, *, organization_id: builtins.str=..., email: builtins.str=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=..., authorizations: collections.abc.Iterable[global___Authorization] | None=...) -> None: + ... - def __init__(self, *, organization_id: builtins.str=...) -> None: + def HasField(self, field_name: typing.Literal['created_on', b'created_on']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['organization_id', b'organization_id']) -> None: + def ClearField(self, field_name: typing.Literal['authorizations', b'authorizations', 'created_on', b'created_on', 'email', b'email', 'organization_id', b'organization_id']) -> None: ... -global___ListLocationsRequest = ListLocationsRequest +global___OrganizationInvite = OrganizationInvite -class ListLocationsResponse(google.protobuf.message.Message): +@typing.final +class CreateOrganizationRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - LOCATIONS_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + name: builtins.str - @property - def locations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Location]: + def __init__(self, *, name: builtins.str=...) -> None: ... - def __init__(self, *, locations: collections.abc.Iterable[global___Location] | None=...) -> None: + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: ... +global___CreateOrganizationRequest = CreateOrganizationRequest - def ClearField(self, field_name: typing_extensions.Literal['locations', b'locations']) -> None: +@typing.final +class CreateOrganizationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_FIELD_NUMBER: builtins.int + + @property + def organization(self) -> global___Organization: ... -global___ListLocationsResponse = ListLocationsResponse -class LocationAuth(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - SECRET_FIELD_NUMBER: builtins.int - secret: builtins.str + def __init__(self, *, organization: global___Organization | None=...) -> None: + ... - def __init__(self, *, secret: builtins.str=...) -> None: + def HasField(self, field_name: typing.Literal['organization', b'organization']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['secret', b'secret']) -> None: + def ClearField(self, field_name: typing.Literal['organization', b'organization']) -> None: ... -global___LocationAuth = LocationAuth +global___CreateOrganizationResponse = CreateOrganizationResponse -class LocationAuthRequest(google.protobuf.message.Message): +@typing.final +class GetOrganizationRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - LOCATION_ID_FIELD_NUMBER: builtins.int - location_id: builtins.str + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str - def __init__(self, *, location_id: builtins.str=...) -> None: + def __init__(self, *, organization_id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['location_id', b'location_id']) -> None: + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id']) -> None: ... -global___LocationAuthRequest = LocationAuthRequest +global___GetOrganizationRequest = GetOrganizationRequest -class LocationAuthResponse(google.protobuf.message.Message): +@typing.final +class GetOrganizationResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - AUTH_FIELD_NUMBER: builtins.int + ORGANIZATION_FIELD_NUMBER: builtins.int @property - def auth(self) -> global___LocationAuth: + def organization(self) -> global___Organization: ... - def __init__(self, *, auth: global___LocationAuth | None=...) -> None: + def __init__(self, *, organization: global___Organization | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['auth', b'auth']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['organization', b'organization']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['auth', b'auth']) -> None: + def ClearField(self, field_name: typing.Literal['organization', b'organization']) -> None: ... -global___LocationAuthResponse = LocationAuthResponse +global___GetOrganizationResponse = GetOrganizationResponse -class GetRobotRequest(google.protobuf.message.Message): +@typing.final +class GetOrganizationNamespaceAvailabilityRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - id: builtins.str + PUBLIC_NAMESPACE_FIELD_NUMBER: builtins.int + public_namespace: builtins.str - def __init__(self, *, id: builtins.str=...) -> None: + def __init__(self, *, public_namespace: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['public_namespace', b'public_namespace']) -> None: ... -global___GetRobotRequest = GetRobotRequest +global___GetOrganizationNamespaceAvailabilityRequest = GetOrganizationNamespaceAvailabilityRequest -class GetRobotResponse(google.protobuf.message.Message): +@typing.final +class GetOrganizationNamespaceAvailabilityResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ROBOT_FIELD_NUMBER: builtins.int + AVAILABLE_FIELD_NUMBER: builtins.int + available: builtins.bool - @property - def robot(self) -> global___Robot: + def __init__(self, *, available: builtins.bool=...) -> None: ... - def __init__(self, *, robot: global___Robot | None=...) -> None: + def ClearField(self, field_name: typing.Literal['available', b'available']) -> None: + ... +global___GetOrganizationNamespaceAvailabilityResponse = GetOrganizationNamespaceAvailabilityResponse + +@typing.final +class UpdateOrganizationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + PUBLIC_NAMESPACE_FIELD_NUMBER: builtins.int + REGION_FIELD_NUMBER: builtins.int + CID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + name: builtins.str + public_namespace: builtins.str + region: builtins.str + 'The new GCS region to associate the org with.' + cid: builtins.str + + def __init__(self, *, organization_id: builtins.str=..., name: builtins.str | None=..., public_namespace: builtins.str | None=..., region: builtins.str | None=..., cid: builtins.str | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['robot', b'robot']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['_cid', b'_cid', '_name', b'_name', '_public_namespace', b'_public_namespace', '_region', b'_region', 'cid', b'cid', 'name', b'name', 'public_namespace', b'public_namespace', 'region', b'region']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['robot', b'robot']) -> None: + def ClearField(self, field_name: typing.Literal['_cid', b'_cid', '_name', b'_name', '_public_namespace', b'_public_namespace', '_region', b'_region', 'cid', b'cid', 'name', b'name', 'organization_id', b'organization_id', 'public_namespace', b'public_namespace', 'region', b'region']) -> None: ... -global___GetRobotResponse = GetRobotResponse -class GetRobotPartsRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - ROBOT_ID_FIELD_NUMBER: builtins.int - robot_id: builtins.str + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_cid', b'_cid']) -> typing.Literal['cid'] | None: + ... - def __init__(self, *, robot_id: builtins.str=...) -> None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_name', b'_name']) -> typing.Literal['name'] | None: ... - def ClearField(self, field_name: typing_extensions.Literal['robot_id', b'robot_id']) -> None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_public_namespace', b'_public_namespace']) -> typing.Literal['public_namespace'] | None: ... -global___GetRobotPartsRequest = GetRobotPartsRequest -class GetRobotPartsResponse(google.protobuf.message.Message): + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_region', b'_region']) -> typing.Literal['region'] | None: + ... +global___UpdateOrganizationRequest = UpdateOrganizationRequest + +@typing.final +class UpdateOrganizationResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTS_FIELD_NUMBER: builtins.int + ORGANIZATION_FIELD_NUMBER: builtins.int @property - def parts(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___RobotPart]: + def organization(self) -> global___Organization: ... - def __init__(self, *, parts: collections.abc.Iterable[global___RobotPart] | None=...) -> None: + def __init__(self, *, organization: global___Organization | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['parts', b'parts']) -> None: + def HasField(self, field_name: typing.Literal['organization', b'organization']) -> builtins.bool: ... -global___GetRobotPartsResponse = GetRobotPartsResponse -class GetRobotPartRequest(google.protobuf.message.Message): + def ClearField(self, field_name: typing.Literal['organization', b'organization']) -> None: + ... +global___UpdateOrganizationResponse = UpdateOrganizationResponse + +@typing.final +class UpdateOrganizationNamespaceRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - id: builtins.str + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + NEW_PUBLIC_NAMESPACE_FIELD_NUMBER: builtins.int + organization_id: builtins.str + new_public_namespace: builtins.str - def __init__(self, *, id: builtins.str=...) -> None: + def __init__(self, *, organization_id: builtins.str=..., new_public_namespace: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['new_public_namespace', b'new_public_namespace', 'organization_id', b'organization_id']) -> None: ... -global___GetRobotPartRequest = GetRobotPartRequest +global___UpdateOrganizationNamespaceRequest = UpdateOrganizationNamespaceRequest -class GetRobotPartResponse(google.protobuf.message.Message): +@typing.final +class UpdateOrganizationNamespaceResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PART_FIELD_NUMBER: builtins.int - CONFIG_JSON_FIELD_NUMBER: builtins.int + ORGANIZATION_FIELD_NUMBER: builtins.int @property - def part(self) -> global___RobotPart: + def organization(self) -> global___Organization: ... - config_json: builtins.str - def __init__(self, *, part: global___RobotPart | None=..., config_json: builtins.str=...) -> None: + def __init__(self, *, organization: global___Organization | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['part', b'part']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['organization', b'organization']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['config_json', b'config_json', 'part', b'part']) -> None: + def ClearField(self, field_name: typing.Literal['organization', b'organization']) -> None: ... -global___GetRobotPartResponse = GetRobotPartResponse +global___UpdateOrganizationNamespaceResponse = UpdateOrganizationNamespaceResponse -class GetRobotPartLogsRequest(google.protobuf.message.Message): +@typing.final +class DeleteOrganizationRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - ERRORS_ONLY_FIELD_NUMBER: builtins.int - id: builtins.str - errors_only: builtins.bool + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str - def __init__(self, *, id: builtins.str=..., errors_only: builtins.bool=...) -> None: + def __init__(self, *, organization_id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['errors_only', b'errors_only', 'id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id']) -> None: ... -global___GetRobotPartLogsRequest = GetRobotPartLogsRequest +global___DeleteOrganizationRequest = DeleteOrganizationRequest -class LogEntry(google.protobuf.message.Message): +@typing.final +class DeleteOrganizationResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - HOST_FIELD_NUMBER: builtins.int - LEVEL_FIELD_NUMBER: builtins.int - TIME_FIELD_NUMBER: builtins.int - LOGGER_NAME_FIELD_NUMBER: builtins.int - MESSAGE_FIELD_NUMBER: builtins.int - CALLER_FIELD_NUMBER: builtins.int - STACK_FIELD_NUMBER: builtins.int - FIELDS_FIELD_NUMBER: builtins.int - host: builtins.str - level: builtins.str - @property - def time(self) -> google.protobuf.timestamp_pb2.Timestamp: + def __init__(self) -> None: ... - logger_name: builtins.str - message: builtins.str +global___DeleteOrganizationResponse = DeleteOrganizationResponse - @property - def caller(self) -> google.protobuf.struct_pb2.Struct: +@typing.final +class GetOrganizationMetadataRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + + def __init__(self, *, organization_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id']) -> None: ... - stack: builtins.str +global___GetOrganizationMetadataRequest = GetOrganizationMetadataRequest + +@typing.final +class GetOrganizationMetadataResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int @property - def fields(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[google.protobuf.struct_pb2.Struct]: + def data(self) -> google.protobuf.struct_pb2.Struct: ... - def __init__(self, *, host: builtins.str=..., level: builtins.str=..., time: google.protobuf.timestamp_pb2.Timestamp | None=..., logger_name: builtins.str=..., message: builtins.str=..., caller: google.protobuf.struct_pb2.Struct | None=..., stack: builtins.str=..., fields: collections.abc.Iterable[google.protobuf.struct_pb2.Struct] | None=...) -> None: + def __init__(self, *, data: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['caller', b'caller', 'time', b'time']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['data', b'data']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['caller', b'caller', 'fields', b'fields', 'host', b'host', 'level', b'level', 'logger_name', b'logger_name', 'message', b'message', 'stack', b'stack', 'time', b'time']) -> None: + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: ... -global___LogEntry = LogEntry +global___GetOrganizationMetadataResponse = GetOrganizationMetadataResponse -class GetRobotPartLogsResponse(google.protobuf.message.Message): +@typing.final +class UpdateOrganizationMetadataRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - LOGS_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + organization_id: builtins.str @property - def logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___LogEntry]: + def data(self) -> google.protobuf.struct_pb2.Struct: ... - def __init__(self, *, logs: collections.abc.Iterable[global___LogEntry] | None=...) -> None: + def __init__(self, *, organization_id: builtins.str=..., data: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['logs', b'logs']) -> None: + def HasField(self, field_name: typing.Literal['data', b'data']) -> builtins.bool: ... -global___GetRobotPartLogsResponse = GetRobotPartLogsResponse -class TailRobotPartLogsRequest(google.protobuf.message.Message): + def ClearField(self, field_name: typing.Literal['data', b'data', 'organization_id', b'organization_id']) -> None: + ... +global___UpdateOrganizationMetadataRequest = UpdateOrganizationMetadataRequest + +@typing.final +class UpdateOrganizationMetadataResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - ERRORS_ONLY_FIELD_NUMBER: builtins.int - id: builtins.str - errors_only: builtins.bool - def __init__(self, *, id: builtins.str=..., errors_only: builtins.bool=...) -> None: + def __init__(self) -> None: + ... +global___UpdateOrganizationMetadataResponse = UpdateOrganizationMetadataResponse + +@typing.final +class ListOrganizationMembersRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + + def __init__(self, *, organization_id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['errors_only', b'errors_only', 'id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id']) -> None: ... -global___TailRobotPartLogsRequest = TailRobotPartLogsRequest +global___ListOrganizationMembersRequest = ListOrganizationMembersRequest -class TailRobotPartLogsResponse(google.protobuf.message.Message): +@typing.final +class ListOrganizationMembersResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - LOGS_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + MEMBERS_FIELD_NUMBER: builtins.int + INVITES_FIELD_NUMBER: builtins.int + organization_id: builtins.str @property - def logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___LogEntry]: + def members(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___OrganizationMember]: ... - def __init__(self, *, logs: collections.abc.Iterable[global___LogEntry] | None=...) -> None: + @property + def invites(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___OrganizationInvite]: ... - def ClearField(self, field_name: typing_extensions.Literal['logs', b'logs']) -> None: + def __init__(self, *, organization_id: builtins.str=..., members: collections.abc.Iterable[global___OrganizationMember] | None=..., invites: collections.abc.Iterable[global___OrganizationInvite] | None=...) -> None: ... -global___TailRobotPartLogsResponse = TailRobotPartLogsResponse -class GetRobotPartHistoryRequest(google.protobuf.message.Message): + def ClearField(self, field_name: typing.Literal['invites', b'invites', 'members', b'members', 'organization_id', b'organization_id']) -> None: + ... +global___ListOrganizationMembersResponse = ListOrganizationMembersResponse + +@typing.final +class CreateOrganizationInviteRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - id: builtins.str + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + EMAIL_FIELD_NUMBER: builtins.int + AUTHORIZATIONS_FIELD_NUMBER: builtins.int + SEND_EMAIL_INVITE_FIELD_NUMBER: builtins.int + organization_id: builtins.str + email: builtins.str + send_email_invite: builtins.bool + 'Set to true (the default) to send an email to the recipient of an invite. The user must accept the email to be added to the associated authorizations.\n When set to false, the user automatically receives the associated authorization on the next login of the user with the associated email address.\n ' - def __init__(self, *, id: builtins.str=...) -> None: + @property + def authorizations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Authorization]: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id']) -> None: + def __init__(self, *, organization_id: builtins.str=..., email: builtins.str=..., authorizations: collections.abc.Iterable[global___Authorization] | None=..., send_email_invite: builtins.bool | None=...) -> None: ... -global___GetRobotPartHistoryRequest = GetRobotPartHistoryRequest - -class GetRobotPartHistoryResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - HISTORY_FIELD_NUMBER: builtins.int - @property - def history(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___RobotPartHistoryEntry]: + def HasField(self, field_name: typing.Literal['_send_email_invite', b'_send_email_invite', 'send_email_invite', b'send_email_invite']) -> builtins.bool: ... - def __init__(self, *, history: collections.abc.Iterable[global___RobotPartHistoryEntry] | None=...) -> None: + def ClearField(self, field_name: typing.Literal['_send_email_invite', b'_send_email_invite', 'authorizations', b'authorizations', 'email', b'email', 'organization_id', b'organization_id', 'send_email_invite', b'send_email_invite']) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['history', b'history']) -> None: + def WhichOneof(self, oneof_group: typing.Literal['_send_email_invite', b'_send_email_invite']) -> typing.Literal['send_email_invite'] | None: ... -global___GetRobotPartHistoryResponse = GetRobotPartHistoryResponse +global___CreateOrganizationInviteRequest = CreateOrganizationInviteRequest -class UpdateRobotPartRequest(google.protobuf.message.Message): +@typing.final +class CreateOrganizationInviteResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int - NAME_FIELD_NUMBER: builtins.int - ROBOT_CONFIG_FIELD_NUMBER: builtins.int - id: builtins.str - name: builtins.str + INVITE_FIELD_NUMBER: builtins.int @property - def robot_config(self) -> google.protobuf.struct_pb2.Struct: + def invite(self) -> global___OrganizationInvite: ... - def __init__(self, *, id: builtins.str=..., name: builtins.str=..., robot_config: google.protobuf.struct_pb2.Struct | None=...) -> None: + def __init__(self, *, invite: global___OrganizationInvite | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['robot_config', b'robot_config']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['invite', b'invite']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id', 'name', b'name', 'robot_config', b'robot_config']) -> None: + def ClearField(self, field_name: typing.Literal['invite', b'invite']) -> None: ... -global___UpdateRobotPartRequest = UpdateRobotPartRequest +global___CreateOrganizationInviteResponse = CreateOrganizationInviteResponse -class UpdateRobotPartResponse(google.protobuf.message.Message): +@typing.final +class UpdateOrganizationInviteAuthorizationsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PART_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + EMAIL_FIELD_NUMBER: builtins.int + ADD_AUTHORIZATIONS_FIELD_NUMBER: builtins.int + REMOVE_AUTHORIZATIONS_FIELD_NUMBER: builtins.int + organization_id: builtins.str + email: builtins.str @property - def part(self) -> global___RobotPart: + def add_authorizations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Authorization]: ... - def __init__(self, *, part: global___RobotPart | None=...) -> None: + @property + def remove_authorizations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Authorization]: ... - def HasField(self, field_name: typing_extensions.Literal['part', b'part']) -> builtins.bool: + def __init__(self, *, organization_id: builtins.str=..., email: builtins.str=..., add_authorizations: collections.abc.Iterable[global___Authorization] | None=..., remove_authorizations: collections.abc.Iterable[global___Authorization] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['part', b'part']) -> None: + def ClearField(self, field_name: typing.Literal['add_authorizations', b'add_authorizations', 'email', b'email', 'organization_id', b'organization_id', 'remove_authorizations', b'remove_authorizations']) -> None: ... -global___UpdateRobotPartResponse = UpdateRobotPartResponse +global___UpdateOrganizationInviteAuthorizationsRequest = UpdateOrganizationInviteAuthorizationsRequest -class NewRobotPartRequest(google.protobuf.message.Message): +@typing.final +class UpdateOrganizationInviteAuthorizationsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ROBOT_ID_FIELD_NUMBER: builtins.int - PART_NAME_FIELD_NUMBER: builtins.int - robot_id: builtins.str - part_name: builtins.str + INVITE_FIELD_NUMBER: builtins.int - def __init__(self, *, robot_id: builtins.str=..., part_name: builtins.str=...) -> None: + @property + def invite(self) -> global___OrganizationInvite: ... - def ClearField(self, field_name: typing_extensions.Literal['part_name', b'part_name', 'robot_id', b'robot_id']) -> None: + def __init__(self, *, invite: global___OrganizationInvite | None=...) -> None: ... -global___NewRobotPartRequest = NewRobotPartRequest - -class NewRobotPartResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - PART_ID_FIELD_NUMBER: builtins.int - part_id: builtins.str - def __init__(self, *, part_id: builtins.str=...) -> None: + def HasField(self, field_name: typing.Literal['invite', b'invite']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['part_id', b'part_id']) -> None: + def ClearField(self, field_name: typing.Literal['invite', b'invite']) -> None: ... -global___NewRobotPartResponse = NewRobotPartResponse +global___UpdateOrganizationInviteAuthorizationsResponse = UpdateOrganizationInviteAuthorizationsResponse -class DeleteRobotPartRequest(google.protobuf.message.Message): +@typing.final +class DeleteOrganizationInviteRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PART_ID_FIELD_NUMBER: builtins.int - part_id: builtins.str + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + EMAIL_FIELD_NUMBER: builtins.int + organization_id: builtins.str + email: builtins.str - def __init__(self, *, part_id: builtins.str=...) -> None: + def __init__(self, *, organization_id: builtins.str=..., email: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['part_id', b'part_id']) -> None: + def ClearField(self, field_name: typing.Literal['email', b'email', 'organization_id', b'organization_id']) -> None: ... -global___DeleteRobotPartRequest = DeleteRobotPartRequest +global___DeleteOrganizationInviteRequest = DeleteOrganizationInviteRequest -class DeleteRobotPartResponse(google.protobuf.message.Message): +@typing.final +class DeleteOrganizationInviteResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor def __init__(self) -> None: ... -global___DeleteRobotPartResponse = DeleteRobotPartResponse +global___DeleteOrganizationInviteResponse = DeleteOrganizationInviteResponse -class Fragment(google.protobuf.message.Message): +@typing.final +class ResendOrganizationInviteRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ID_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + EMAIL_FIELD_NUMBER: builtins.int + organization_id: builtins.str + email: builtins.str + + def __init__(self, *, organization_id: builtins.str=..., email: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['email', b'email', 'organization_id', b'organization_id']) -> None: + ... +global___ResendOrganizationInviteRequest = ResendOrganizationInviteRequest + +@typing.final +class ResendOrganizationInviteResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + INVITE_FIELD_NUMBER: builtins.int + + @property + def invite(self) -> global___OrganizationInvite: + ... + + def __init__(self, *, invite: global___OrganizationInvite | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['invite', b'invite']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['invite', b'invite']) -> None: + ... +global___ResendOrganizationInviteResponse = ResendOrganizationInviteResponse + +@typing.final +class DeleteOrganizationMemberRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + USER_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + user_id: builtins.str + + def __init__(self, *, organization_id: builtins.str=..., user_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id', 'user_id', b'user_id']) -> None: + ... +global___DeleteOrganizationMemberRequest = DeleteOrganizationMemberRequest + +@typing.final +class DeleteOrganizationMemberResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteOrganizationMemberResponse = DeleteOrganizationMemberResponse + +@typing.final +class BillingAddress(google.protobuf.message.Message): + """Third Party Org Services""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ADDRESS_LINE_1_FIELD_NUMBER: builtins.int + ADDRESS_LINE_2_FIELD_NUMBER: builtins.int + CITY_FIELD_NUMBER: builtins.int + STATE_FIELD_NUMBER: builtins.int + ZIPCODE_FIELD_NUMBER: builtins.int + COUNTRY_FIELD_NUMBER: builtins.int + address_line_1: builtins.str + address_line_2: builtins.str + city: builtins.str + state: builtins.str + zipcode: builtins.str + country: builtins.str + + def __init__(self, *, address_line_1: builtins.str=..., address_line_2: builtins.str | None=..., city: builtins.str=..., state: builtins.str=..., zipcode: builtins.str=..., country: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_address_line_2', b'_address_line_2', 'address_line_2', b'address_line_2']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_address_line_2', b'_address_line_2', 'address_line_1', b'address_line_1', 'address_line_2', b'address_line_2', 'city', b'city', 'country', b'country', 'state', b'state', 'zipcode', b'zipcode']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_address_line_2', b'_address_line_2']) -> typing.Literal['address_line_2'] | None: + ... +global___BillingAddress = BillingAddress + +@typing.final +class EnableBillingServiceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + BILLING_ADDRESS_FIELD_NUMBER: builtins.int + org_id: builtins.str + + @property + def billing_address(self) -> global___BillingAddress: + ... + + def __init__(self, *, org_id: builtins.str=..., billing_address: global___BillingAddress | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['billing_address', b'billing_address']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['billing_address', b'billing_address', 'org_id', b'org_id']) -> None: + ... +global___EnableBillingServiceRequest = EnableBillingServiceRequest + +@typing.final +class EnableBillingServiceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___EnableBillingServiceResponse = EnableBillingServiceResponse + +@typing.final +class UpdateBillingServiceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + BILLING_ADDRESS_FIELD_NUMBER: builtins.int + org_id: builtins.str + + @property + def billing_address(self) -> global___BillingAddress: + ... + + def __init__(self, *, org_id: builtins.str=..., billing_address: global___BillingAddress | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['billing_address', b'billing_address']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['billing_address', b'billing_address', 'org_id', b'org_id']) -> None: + ... +global___UpdateBillingServiceRequest = UpdateBillingServiceRequest + +@typing.final +class UpdateBillingServiceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___UpdateBillingServiceResponse = UpdateBillingServiceResponse + +@typing.final +class GetBillingServiceConfigRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___GetBillingServiceConfigRequest = GetBillingServiceConfigRequest + +@typing.final +class GetBillingServiceConfigResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BILLING_ADDRESS_FIELD_NUMBER: builtins.int + SUPPORT_EMAIL_FIELD_NUMBER: builtins.int + LOGO_URL_FIELD_NUMBER: builtins.int + BILLING_DASHBOARD_URL_FIELD_NUMBER: builtins.int + support_email: builtins.str + logo_url: builtins.str + billing_dashboard_url: builtins.str + + @property + def billing_address(self) -> global___BillingAddress: + ... + + def __init__(self, *, billing_address: global___BillingAddress | None=..., support_email: builtins.str=..., logo_url: builtins.str=..., billing_dashboard_url: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['billing_address', b'billing_address']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['billing_address', b'billing_address', 'billing_dashboard_url', b'billing_dashboard_url', 'logo_url', b'logo_url', 'support_email', b'support_email']) -> None: + ... +global___GetBillingServiceConfigResponse = GetBillingServiceConfigResponse + +@typing.final +class DisableBillingServiceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___DisableBillingServiceRequest = DisableBillingServiceRequest + +@typing.final +class DisableBillingServiceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DisableBillingServiceResponse = DisableBillingServiceResponse + +@typing.final +class OrganizationSetSupportEmailRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + EMAIL_FIELD_NUMBER: builtins.int + org_id: builtins.str + email: builtins.str + + def __init__(self, *, org_id: builtins.str=..., email: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['email', b'email', 'org_id', b'org_id']) -> None: + ... +global___OrganizationSetSupportEmailRequest = OrganizationSetSupportEmailRequest + +@typing.final +class OrganizationSetSupportEmailResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___OrganizationSetSupportEmailResponse = OrganizationSetSupportEmailResponse + +@typing.final +class OrganizationGetSupportEmailRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___OrganizationGetSupportEmailRequest = OrganizationGetSupportEmailRequest + +@typing.final +class OrganizationGetSupportEmailResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + EMAIL_FIELD_NUMBER: builtins.int + email: builtins.str + + def __init__(self, *, email: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['email', b'email']) -> None: + ... +global___OrganizationGetSupportEmailResponse = OrganizationGetSupportEmailResponse + +@typing.final +class OrganizationIdentity(google.protobuf.message.Message): + """Location + + Used for rendering an organization's information on the frontend (limited + to id, name, or both). + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int - FRAGMENT_FIELD_NUMBER: builtins.int - ORGANIZATION_OWNER_FIELD_NUMBER: builtins.int - PUBLIC_FIELD_NUMBER: builtins.int id: builtins.str + 'Organization ID.' + name: builtins.str + 'Organization name.' + + def __init__(self, *, id: builtins.str=..., name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'name', b'name']) -> None: + ... +global___OrganizationIdentity = OrganizationIdentity + +@typing.final +class LocationOrganization(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + PRIMARY_FIELD_NUMBER: builtins.int + organization_id: builtins.str + 'Organization ID the location is shared with.' + primary: builtins.bool + 'Whether the organization is the primary owner or not.' + + def __init__(self, *, organization_id: builtins.str=..., primary: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id', 'primary', b'primary']) -> None: + ... +global___LocationOrganization = LocationOrganization + +@typing.final +class LocationAuth(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SECRET_FIELD_NUMBER: builtins.int + LOCATION_ID_FIELD_NUMBER: builtins.int + SECRETS_FIELD_NUMBER: builtins.int + secret: builtins.str + 'Deprecated: use secrets field.' + location_id: builtins.str + 'Location ID containing this LocationAuth.' + + @property + def secrets(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SharedSecret]: + """List of secrets used to authenticate to the Location.""" + + def __init__(self, *, secret: builtins.str=..., location_id: builtins.str=..., secrets: collections.abc.Iterable[global___SharedSecret] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id', 'secret', b'secret', 'secrets', b'secrets']) -> None: + ... +global___LocationAuth = LocationAuth + +@typing.final +class StorageConfig(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + REGION_FIELD_NUMBER: builtins.int + region: builtins.str + 'GCS region that data is stored in.' + + def __init__(self, *, region: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['region', b'region']) -> None: + ... +global___StorageConfig = StorageConfig + +@typing.final +class Location(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + PARENT_LOCATION_ID_FIELD_NUMBER: builtins.int + AUTH_FIELD_NUMBER: builtins.int + ORGANIZATIONS_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + ROBOT_COUNT_FIELD_NUMBER: builtins.int + CONFIG_FIELD_NUMBER: builtins.int + PRIMARY_ORG_IDENTITY_FIELD_NUMBER: builtins.int + id: builtins.str + 'Location ID.' + name: builtins.str + 'Location name.' + parent_location_id: builtins.str + 'Location ID of the parent location.' + robot_count: builtins.int + '' + + @property + def auth(self) -> global___LocationAuth: + """Location authentication secrets.""" + + @property + def organizations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___LocationOrganization]: + """Organizations that the location is shared with.""" + + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + """Location creation timestamp.""" + + @property + def config(self) -> global___StorageConfig: + """Config for how data in this location is stored.""" + + @property + def primary_org_identity(self) -> global___OrganizationIdentity: + """The organization that is the primary owner of the location.""" + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., parent_location_id: builtins.str=..., auth: global___LocationAuth | None=..., organizations: collections.abc.Iterable[global___LocationOrganization] | None=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=..., robot_count: builtins.int=..., config: global___StorageConfig | None=..., primary_org_identity: global___OrganizationIdentity | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_primary_org_identity', b'_primary_org_identity', 'auth', b'auth', 'config', b'config', 'created_on', b'created_on', 'primary_org_identity', b'primary_org_identity']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_primary_org_identity', b'_primary_org_identity', 'auth', b'auth', 'config', b'config', 'created_on', b'created_on', 'id', b'id', 'name', b'name', 'organizations', b'organizations', 'parent_location_id', b'parent_location_id', 'primary_org_identity', b'primary_org_identity', 'robot_count', b'robot_count']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_primary_org_identity', b'_primary_org_identity']) -> typing.Literal['primary_org_identity'] | None: + ... +global___Location = Location + +@typing.final +class SharedSecret(google.protobuf.message.Message): + """SharedSecret is a secret used for LocationAuth and RobotParts.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _State: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _StateEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[SharedSecret._State.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + STATE_UNSPECIFIED: SharedSecret._State.ValueType + STATE_ENABLED: SharedSecret._State.ValueType + 'Secret is enabled and can be used in authentication.' + STATE_DISABLED: SharedSecret._State.ValueType + 'Secret is disabled and must not be used to authenticate to rpc.' + + class State(_State, metaclass=_StateEnumTypeWrapper): + ... + STATE_UNSPECIFIED: SharedSecret.State.ValueType + STATE_ENABLED: SharedSecret.State.ValueType + 'Secret is enabled and can be used in authentication.' + STATE_DISABLED: SharedSecret.State.ValueType + 'Secret is disabled and must not be used to authenticate to rpc.' + ID_FIELD_NUMBER: builtins.int + SECRET_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + STATE_FIELD_NUMBER: builtins.int + id: builtins.str + secret: builtins.str + 'The payload of the secret. Used during authentication to the rpc framework.' + state: global___SharedSecret.State.ValueType + 'State of the shared secret. In most cases it should be enabled. We may support\n disabling a specific secret while keeping it in the database.\n ' + + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + """Date/time the secret was first created.""" + + def __init__(self, *, id: builtins.str=..., secret: builtins.str=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=..., state: global___SharedSecret.State.ValueType=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['created_on', b'created_on']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['created_on', b'created_on', 'id', b'id', 'secret', b'secret', 'state', b'state']) -> None: + ... +global___SharedSecret = SharedSecret + +@typing.final +class CreateLocationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + PARENT_LOCATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + 'Organization ID to create the location under.' name: builtins.str + 'Name of the location.' + parent_location_id: builtins.str + 'The new parent location to move the location under.' + + def __init__(self, *, organization_id: builtins.str=..., name: builtins.str=..., parent_location_id: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_parent_location_id', b'_parent_location_id', 'parent_location_id', b'parent_location_id']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_parent_location_id', b'_parent_location_id', 'name', b'name', 'organization_id', b'organization_id', 'parent_location_id', b'parent_location_id']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_parent_location_id', b'_parent_location_id']) -> typing.Literal['parent_location_id'] | None: + ... +global___CreateLocationRequest = CreateLocationRequest + +@typing.final +class CreateLocationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_FIELD_NUMBER: builtins.int + + @property + def location(self) -> global___Location: + """Location object is returned.""" + + def __init__(self, *, location: global___Location | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['location', b'location']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['location', b'location']) -> None: + ... +global___CreateLocationResponse = CreateLocationResponse + +@typing.final +class GetLocationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + location_id: builtins.str + 'Location ID of location to get.' + + def __init__(self, *, location_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id']) -> None: + ... +global___GetLocationRequest = GetLocationRequest + +@typing.final +class GetLocationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_FIELD_NUMBER: builtins.int + + @property + def location(self) -> global___Location: + """Location object is returned.""" + + def __init__(self, *, location: global___Location | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['location', b'location']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['location', b'location']) -> None: + ... +global___GetLocationResponse = GetLocationResponse + +@typing.final +class UpdateLocationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + PARENT_LOCATION_ID_FIELD_NUMBER: builtins.int + REGION_FIELD_NUMBER: builtins.int + location_id: builtins.str + 'Location ID of location to update.' + name: builtins.str + 'The new name to be updated on location.' + parent_location_id: builtins.str + 'The new parent location to move the location under.' + region: builtins.str + 'The new GCS region to associate the location with.' + + def __init__(self, *, location_id: builtins.str=..., name: builtins.str | None=..., parent_location_id: builtins.str | None=..., region: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_name', b'_name', '_parent_location_id', b'_parent_location_id', '_region', b'_region', 'name', b'name', 'parent_location_id', b'parent_location_id', 'region', b'region']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_name', b'_name', '_parent_location_id', b'_parent_location_id', '_region', b'_region', 'location_id', b'location_id', 'name', b'name', 'parent_location_id', b'parent_location_id', 'region', b'region']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_name', b'_name']) -> typing.Literal['name'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_parent_location_id', b'_parent_location_id']) -> typing.Literal['parent_location_id'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_region', b'_region']) -> typing.Literal['region'] | None: + ... +global___UpdateLocationRequest = UpdateLocationRequest + +@typing.final +class UpdateLocationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_FIELD_NUMBER: builtins.int + + @property + def location(self) -> global___Location: + """Location object is returned.""" + + def __init__(self, *, location: global___Location | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['location', b'location']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['location', b'location']) -> None: + ... +global___UpdateLocationResponse = UpdateLocationResponse + +@typing.final +class DeleteLocationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + location_id: builtins.str + 'Location ID of location to delete.' + + def __init__(self, *, location_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id']) -> None: + ... +global___DeleteLocationRequest = DeleteLocationRequest + +@typing.final +class DeleteLocationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteLocationResponse = DeleteLocationResponse + +@typing.final +class GetLocationMetadataRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + location_id: builtins.str + + def __init__(self, *, location_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id']) -> None: + ... +global___GetLocationMetadataRequest = GetLocationMetadataRequest + +@typing.final +class GetLocationMetadataResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, data: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['data', b'data']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___GetLocationMetadataResponse = GetLocationMetadataResponse + +@typing.final +class UpdateLocationMetadataRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + location_id: builtins.str + + @property + def data(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, location_id: builtins.str=..., data: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['data', b'data']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data', 'location_id', b'location_id']) -> None: + ... +global___UpdateLocationMetadataRequest = UpdateLocationMetadataRequest + +@typing.final +class UpdateLocationMetadataResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___UpdateLocationMetadataResponse = UpdateLocationMetadataResponse + +@typing.final +class GetOrganizationsWithAccessToLocationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + location_id: builtins.str + + def __init__(self, *, location_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id']) -> None: + ... +global___GetOrganizationsWithAccessToLocationRequest = GetOrganizationsWithAccessToLocationRequest + +@typing.final +class GetOrganizationsWithAccessToLocationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_IDENTITIES_FIELD_NUMBER: builtins.int + + @property + def organization_identities(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___OrganizationIdentity]: + ... + + def __init__(self, *, organization_identities: collections.abc.Iterable[global___OrganizationIdentity] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_identities', b'organization_identities']) -> None: + ... +global___GetOrganizationsWithAccessToLocationResponse = GetOrganizationsWithAccessToLocationResponse + +@typing.final +class ListLocationsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + 'Organization ID under which to list all locations.' + + def __init__(self, *, organization_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id']) -> None: + ... +global___ListLocationsRequest = ListLocationsRequest + +@typing.final +class ShareLocationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + location_id: builtins.str + 'Location ID to be shared.' + organization_id: builtins.str + 'Organization ID to share the location with.' + + def __init__(self, *, location_id: builtins.str=..., organization_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id', 'organization_id', b'organization_id']) -> None: + ... +global___ShareLocationRequest = ShareLocationRequest + +@typing.final +class ShareLocationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___ShareLocationResponse = ShareLocationResponse + +@typing.final +class UnshareLocationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + location_id: builtins.str + 'Location ID to be unshared.' + organization_id: builtins.str + 'Organization ID to unshare the location with.' + + def __init__(self, *, location_id: builtins.str=..., organization_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id', 'organization_id', b'organization_id']) -> None: + ... +global___UnshareLocationRequest = UnshareLocationRequest + +@typing.final +class UnshareLocationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___UnshareLocationResponse = UnshareLocationResponse + +@typing.final +class ListLocationsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATIONS_FIELD_NUMBER: builtins.int + + @property + def locations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Location]: + ... + + def __init__(self, *, locations: collections.abc.Iterable[global___Location] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['locations', b'locations']) -> None: + ... +global___ListLocationsResponse = ListLocationsResponse + +@typing.final +class CreateLocationSecretRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + location_id: builtins.str + 'Location ID to create the secret in.' + + def __init__(self, *, location_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id']) -> None: + ... +global___CreateLocationSecretRequest = CreateLocationSecretRequest + +@typing.final +class CreateLocationSecretResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + AUTH_FIELD_NUMBER: builtins.int + + @property + def auth(self) -> global___LocationAuth: + """Location's auth after updates.""" + + def __init__(self, *, auth: global___LocationAuth | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['auth', b'auth']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['auth', b'auth']) -> None: + ... +global___CreateLocationSecretResponse = CreateLocationSecretResponse + +@typing.final +class DeleteLocationSecretRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + SECRET_ID_FIELD_NUMBER: builtins.int + location_id: builtins.str + secret_id: builtins.str + + def __init__(self, *, location_id: builtins.str=..., secret_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id', 'secret_id', b'secret_id']) -> None: + ... +global___DeleteLocationSecretRequest = DeleteLocationSecretRequest + +@typing.final +class DeleteLocationSecretResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteLocationSecretResponse = DeleteLocationSecretResponse + +@typing.final +class LocationAuthRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + location_id: builtins.str + + def __init__(self, *, location_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id']) -> None: + ... +global___LocationAuthRequest = LocationAuthRequest + +@typing.final +class LocationAuthResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + AUTH_FIELD_NUMBER: builtins.int + + @property + def auth(self) -> global___LocationAuth: + ... + + def __init__(self, *, auth: global___LocationAuth | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['auth', b'auth']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['auth', b'auth']) -> None: + ... +global___LocationAuthResponse = LocationAuthResponse + +@typing.final +class GetRobotRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___GetRobotRequest = GetRobotRequest + +@typing.final +class GetRoverRentalRobotsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___GetRoverRentalRobotsRequest = GetRoverRentalRobotsRequest + +@typing.final +class RoverRentalRobot(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ROBOT_ID_FIELD_NUMBER: builtins.int + LOCATION_ID_FIELD_NUMBER: builtins.int + ROBOT_NAME_FIELD_NUMBER: builtins.int + ROBOT_MAIN_PART_ID_FIELD_NUMBER: builtins.int + robot_id: builtins.str + location_id: builtins.str + robot_name: builtins.str + robot_main_part_id: builtins.str + + def __init__(self, *, robot_id: builtins.str=..., location_id: builtins.str=..., robot_name: builtins.str=..., robot_main_part_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id', 'robot_id', b'robot_id', 'robot_main_part_id', b'robot_main_part_id', 'robot_name', b'robot_name']) -> None: + ... +global___RoverRentalRobot = RoverRentalRobot + +@typing.final +class GetRoverRentalRobotsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ROBOTS_FIELD_NUMBER: builtins.int + + @property + def robots(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___RoverRentalRobot]: + ... + + def __init__(self, *, robots: collections.abc.Iterable[global___RoverRentalRobot] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['robots', b'robots']) -> None: + ... +global___GetRoverRentalRobotsResponse = GetRoverRentalRobotsResponse + +@typing.final +class GetRobotResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ROBOT_FIELD_NUMBER: builtins.int + + @property + def robot(self) -> global___Robot: + ... + + def __init__(self, *, robot: global___Robot | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['robot', b'robot']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['robot', b'robot']) -> None: + ... +global___GetRobotResponse = GetRobotResponse + +@typing.final +class GetRobotPartsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ROBOT_ID_FIELD_NUMBER: builtins.int + robot_id: builtins.str + + def __init__(self, *, robot_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['robot_id', b'robot_id']) -> None: + ... +global___GetRobotPartsRequest = GetRobotPartsRequest + +@typing.final +class GetRobotPartsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PARTS_FIELD_NUMBER: builtins.int + + @property + def parts(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___RobotPart]: + ... + + def __init__(self, *, parts: collections.abc.Iterable[global___RobotPart] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['parts', b'parts']) -> None: + ... +global___GetRobotPartsResponse = GetRobotPartsResponse + +@typing.final +class GetRobotPartRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___GetRobotPartRequest = GetRobotPartRequest + +@typing.final +class GetRobotPartResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_FIELD_NUMBER: builtins.int + CONFIG_JSON_FIELD_NUMBER: builtins.int + config_json: builtins.str + + @property + def part(self) -> global___RobotPart: + ... + + def __init__(self, *, part: global___RobotPart | None=..., config_json: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['part', b'part']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['config_json', b'config_json', 'part', b'part']) -> None: + ... +global___GetRobotPartResponse = GetRobotPartResponse + +@typing.final +class GetRobotPartLogsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + ERRORS_ONLY_FIELD_NUMBER: builtins.int + FILTER_FIELD_NUMBER: builtins.int + PAGE_TOKEN_FIELD_NUMBER: builtins.int + LEVELS_FIELD_NUMBER: builtins.int + START_FIELD_NUMBER: builtins.int + END_FIELD_NUMBER: builtins.int + LIMIT_FIELD_NUMBER: builtins.int + SOURCE_FIELD_NUMBER: builtins.int + id: builtins.str + errors_only: builtins.bool + 'TODO(https://viam.atlassian.net/browse/APP-3877): Remove this field' + filter: builtins.str + page_token: builtins.str + limit: builtins.int + source: builtins.str + + @property + def levels(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """logs of all levels are returned when the levels field is empty""" + + @property + def start(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def end(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, id: builtins.str=..., errors_only: builtins.bool=..., filter: builtins.str | None=..., page_token: builtins.str | None=..., levels: collections.abc.Iterable[builtins.str] | None=..., start: google.protobuf.timestamp_pb2.Timestamp | None=..., end: google.protobuf.timestamp_pb2.Timestamp | None=..., limit: builtins.int | None=..., source: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_end', b'_end', '_filter', b'_filter', '_limit', b'_limit', '_page_token', b'_page_token', '_source', b'_source', '_start', b'_start', 'end', b'end', 'filter', b'filter', 'limit', b'limit', 'page_token', b'page_token', 'source', b'source', 'start', b'start']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_end', b'_end', '_filter', b'_filter', '_limit', b'_limit', '_page_token', b'_page_token', '_source', b'_source', '_start', b'_start', 'end', b'end', 'errors_only', b'errors_only', 'filter', b'filter', 'id', b'id', 'levels', b'levels', 'limit', b'limit', 'page_token', b'page_token', 'source', b'source', 'start', b'start']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_end', b'_end']) -> typing.Literal['end'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_filter', b'_filter']) -> typing.Literal['filter'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_limit', b'_limit']) -> typing.Literal['limit'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_page_token', b'_page_token']) -> typing.Literal['page_token'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_source', b'_source']) -> typing.Literal['source'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_start', b'_start']) -> typing.Literal['start'] | None: + ... +global___GetRobotPartLogsRequest = GetRobotPartLogsRequest + +@typing.final +class GetRobotPartLogsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOGS_FIELD_NUMBER: builtins.int + NEXT_PAGE_TOKEN_FIELD_NUMBER: builtins.int + next_page_token: builtins.str + + @property + def logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.LogEntry]: + ... + + def __init__(self, *, logs: collections.abc.Iterable[common.v1.common_pb2.LogEntry] | None=..., next_page_token: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['logs', b'logs', 'next_page_token', b'next_page_token']) -> None: + ... +global___GetRobotPartLogsResponse = GetRobotPartLogsResponse + +@typing.final +class TailRobotPartLogsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + ERRORS_ONLY_FIELD_NUMBER: builtins.int + FILTER_FIELD_NUMBER: builtins.int + id: builtins.str + errors_only: builtins.bool + filter: builtins.str + + def __init__(self, *, id: builtins.str=..., errors_only: builtins.bool=..., filter: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_filter', b'_filter', 'filter', b'filter']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_filter', b'_filter', 'errors_only', b'errors_only', 'filter', b'filter', 'id', b'id']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_filter', b'_filter']) -> typing.Literal['filter'] | None: + ... +global___TailRobotPartLogsRequest = TailRobotPartLogsRequest + +@typing.final +class TailRobotPartLogsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOGS_FIELD_NUMBER: builtins.int + + @property + def logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.LogEntry]: + ... + + def __init__(self, *, logs: collections.abc.Iterable[common.v1.common_pb2.LogEntry] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['logs', b'logs']) -> None: + ... +global___TailRobotPartLogsResponse = TailRobotPartLogsResponse + +@typing.final +class GetRobotPartHistoryRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___GetRobotPartHistoryRequest = GetRobotPartHistoryRequest + +@typing.final +class GetRobotPartHistoryResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + HISTORY_FIELD_NUMBER: builtins.int + + @property + def history(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___RobotPartHistoryEntry]: + ... + + def __init__(self, *, history: collections.abc.Iterable[global___RobotPartHistoryEntry] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['history', b'history']) -> None: + ... +global___GetRobotPartHistoryResponse = GetRobotPartHistoryResponse + +@typing.final +class UpdateRobotPartRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + ROBOT_CONFIG_FIELD_NUMBER: builtins.int + LAST_KNOWN_UPDATE_FIELD_NUMBER: builtins.int + id: builtins.str + name: builtins.str + + @property + def robot_config(self) -> google.protobuf.struct_pb2.Struct: + ... + + @property + def last_known_update(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., robot_config: google.protobuf.struct_pb2.Struct | None=..., last_known_update: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_last_known_update', b'_last_known_update', 'last_known_update', b'last_known_update', 'robot_config', b'robot_config']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_last_known_update', b'_last_known_update', 'id', b'id', 'last_known_update', b'last_known_update', 'name', b'name', 'robot_config', b'robot_config']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_last_known_update', b'_last_known_update']) -> typing.Literal['last_known_update'] | None: + ... +global___UpdateRobotPartRequest = UpdateRobotPartRequest + +@typing.final +class UpdateRobotPartResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_FIELD_NUMBER: builtins.int + + @property + def part(self) -> global___RobotPart: + ... + + def __init__(self, *, part: global___RobotPart | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['part', b'part']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['part', b'part']) -> None: + ... +global___UpdateRobotPartResponse = UpdateRobotPartResponse + +@typing.final +class NewRobotPartRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ROBOT_ID_FIELD_NUMBER: builtins.int + PART_NAME_FIELD_NUMBER: builtins.int + robot_id: builtins.str + part_name: builtins.str + + def __init__(self, *, robot_id: builtins.str=..., part_name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['part_name', b'part_name', 'robot_id', b'robot_id']) -> None: + ... +global___NewRobotPartRequest = NewRobotPartRequest + +@typing.final +class NewRobotPartResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_ID_FIELD_NUMBER: builtins.int + part_id: builtins.str + + def __init__(self, *, part_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['part_id', b'part_id']) -> None: + ... +global___NewRobotPartResponse = NewRobotPartResponse + +@typing.final +class DeleteRobotPartRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_ID_FIELD_NUMBER: builtins.int + part_id: builtins.str + + def __init__(self, *, part_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['part_id', b'part_id']) -> None: + ... +global___DeleteRobotPartRequest = DeleteRobotPartRequest + +@typing.final +class GetRobotPartMetadataRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___GetRobotPartMetadataRequest = GetRobotPartMetadataRequest + +@typing.final +class GetRobotPartMetadataResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, data: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['data', b'data']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___GetRobotPartMetadataResponse = GetRobotPartMetadataResponse + +@typing.final +class UpdateRobotPartMetadataRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + id: builtins.str + + @property + def data(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, id: builtins.str=..., data: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['data', b'data']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data', 'id', b'id']) -> None: + ... +global___UpdateRobotPartMetadataRequest = UpdateRobotPartMetadataRequest + +@typing.final +class UpdateRobotPartMetadataResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___UpdateRobotPartMetadataResponse = UpdateRobotPartMetadataResponse + +@typing.final +class GetRobotAPIKeysRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ROBOT_ID_FIELD_NUMBER: builtins.int + robot_id: builtins.str + + def __init__(self, *, robot_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['robot_id', b'robot_id']) -> None: + ... +global___GetRobotAPIKeysRequest = GetRobotAPIKeysRequest + +@typing.final +class APIKey(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + KEY_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + id: builtins.str + key: builtins.str + name: builtins.str + + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, id: builtins.str=..., key: builtins.str=..., name: builtins.str=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['created_on', b'created_on']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['created_on', b'created_on', 'id', b'id', 'key', b'key', 'name', b'name']) -> None: + ... +global___APIKey = APIKey + +@typing.final +class GetRobotAPIKeysResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + API_KEYS_FIELD_NUMBER: builtins.int + + @property + def api_keys(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___APIKeyWithAuthorizations]: + ... + + def __init__(self, *, api_keys: collections.abc.Iterable[global___APIKeyWithAuthorizations] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['api_keys', b'api_keys']) -> None: + ... +global___GetRobotAPIKeysResponse = GetRobotAPIKeysResponse + +@typing.final +class DeleteRobotPartResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteRobotPartResponse = DeleteRobotPartResponse + +@typing.final +class Fragment(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + FRAGMENT_FIELD_NUMBER: builtins.int + ORGANIZATION_OWNER_FIELD_NUMBER: builtins.int + PUBLIC_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + ORGANIZATION_NAME_FIELD_NUMBER: builtins.int + ROBOT_PART_COUNT_FIELD_NUMBER: builtins.int + ORGANIZATION_COUNT_FIELD_NUMBER: builtins.int + ONLY_USED_BY_OWNER_FIELD_NUMBER: builtins.int + VISIBILITY_FIELD_NUMBER: builtins.int + LAST_UPDATED_FIELD_NUMBER: builtins.int + REVISION_FIELD_NUMBER: builtins.int + id: builtins.str + name: builtins.str + organization_owner: builtins.str + public: builtins.bool + organization_name: builtins.str + robot_part_count: builtins.int + 'number of robot parts using this fragment' + organization_count: builtins.int + 'number of organizations using this fragment' + only_used_by_owner: builtins.bool + 'whether the organization(s) using this fragment is the same as the fragment org' + visibility: global___FragmentVisibility.ValueType + 'the visibility of a fragment; public, private or unlisted' + revision: builtins.str + + @property + def fragment(self) -> google.protobuf.struct_pb2.Struct: + ... + + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def last_updated(self) -> google.protobuf.timestamp_pb2.Timestamp: + """latest timestamp when fragment was updated""" + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., fragment: google.protobuf.struct_pb2.Struct | None=..., organization_owner: builtins.str=..., public: builtins.bool=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=..., organization_name: builtins.str=..., robot_part_count: builtins.int=..., organization_count: builtins.int=..., only_used_by_owner: builtins.bool=..., visibility: global___FragmentVisibility.ValueType=..., last_updated: google.protobuf.timestamp_pb2.Timestamp | None=..., revision: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['created_on', b'created_on', 'fragment', b'fragment', 'last_updated', b'last_updated']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['created_on', b'created_on', 'fragment', b'fragment', 'id', b'id', 'last_updated', b'last_updated', 'name', b'name', 'only_used_by_owner', b'only_used_by_owner', 'organization_count', b'organization_count', 'organization_name', b'organization_name', 'organization_owner', b'organization_owner', 'public', b'public', 'revision', b'revision', 'robot_part_count', b'robot_part_count', 'visibility', b'visibility']) -> None: + ... +global___Fragment = Fragment + +@typing.final +class FragmentHistoryEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_FIELD_NUMBER: builtins.int + EDITED_ON_FIELD_NUMBER: builtins.int + OLD_FIELD_NUMBER: builtins.int + EDITED_BY_FIELD_NUMBER: builtins.int + REVISION_FIELD_NUMBER: builtins.int + CONFIG_FIELD_NUMBER: builtins.int + fragment: builtins.str + revision: builtins.str + + @property + def edited_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def old(self) -> global___Fragment: + ... + + @property + def edited_by(self) -> global___AuthenticatorInfo: + ... + + @property + def config(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, fragment: builtins.str=..., edited_on: google.protobuf.timestamp_pb2.Timestamp | None=..., old: global___Fragment | None=..., edited_by: global___AuthenticatorInfo | None=..., revision: builtins.str=..., config: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['config', b'config', 'edited_by', b'edited_by', 'edited_on', b'edited_on', 'old', b'old']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['config', b'config', 'edited_by', b'edited_by', 'edited_on', b'edited_on', 'fragment', b'fragment', 'old', b'old', 'revision', b'revision']) -> None: + ... +global___FragmentHistoryEntry = FragmentHistoryEntry + +@typing.final +class FragmentRevision(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + REVISION_FIELD_NUMBER: builtins.int + CREATED_AT_FIELD_NUMBER: builtins.int + revision: builtins.str + + @property + def created_at(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, revision: builtins.str=..., created_at: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['created_at', b'created_at']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['created_at', b'created_at', 'revision', b'revision']) -> None: + ... +global___FragmentRevision = FragmentRevision + +@typing.final +class FragmentTag(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TAG_FIELD_NUMBER: builtins.int + REVISION_FIELD_NUMBER: builtins.int + tag: builtins.str + revision: builtins.str + + def __init__(self, *, tag: builtins.str=..., revision: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['revision', b'revision', 'tag', b'tag']) -> None: + ... +global___FragmentTag = FragmentTag + +@typing.final +class FragmentError(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ERROR_TYPE_FIELD_NUMBER: builtins.int + FRAGMENT_ID_FIELD_NUMBER: builtins.int + DETAIL_FIELD_NUMBER: builtins.int + error_type: global___FragmentErrorType.ValueType + fragment_id: builtins.str + detail: builtins.str + + def __init__(self, *, error_type: global___FragmentErrorType.ValueType=..., fragment_id: builtins.str=..., detail: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['detail', b'detail', 'error_type', b'error_type', 'fragment_id', b'fragment_id']) -> None: + ... +global___FragmentError = FragmentError + +@typing.final +class FragmentUsage(google.protobuf.message.Message): + """Cached fragment usage statistics""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_ID_FIELD_NUMBER: builtins.int + ORGANIZATIONS_FIELD_NUMBER: builtins.int + MACHINES_FIELD_NUMBER: builtins.int + MACHINES_IN_CURRENT_ORG_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + fragment_id: builtins.str + organizations: builtins.int + machines: builtins.int + machines_in_current_org: builtins.int + version: builtins.str + 'revision or tag' + + def __init__(self, *, fragment_id: builtins.str=..., organizations: builtins.int=..., machines: builtins.int=..., machines_in_current_org: builtins.int=..., version: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_version', b'_version', 'version', b'version']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_version', b'_version', 'fragment_id', b'fragment_id', 'machines', b'machines', 'machines_in_current_org', b'machines_in_current_org', 'organizations', b'organizations', 'version', b'version']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_version', b'_version']) -> typing.Literal['version'] | None: + ... +global___FragmentUsage = FragmentUsage + +@typing.final +class ResolvedFragment(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_ID_FIELD_NUMBER: builtins.int + RESOLVED_CONFIG_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + REVISION_FIELD_NUMBER: builtins.int + fragment_id: builtins.str + revision: builtins.str + + @property + def resolved_config(self) -> google.protobuf.struct_pb2.Struct: + ... + + @property + def error(self) -> global___FragmentError: + ... + + def __init__(self, *, fragment_id: builtins.str=..., resolved_config: google.protobuf.struct_pb2.Struct | None=..., error: global___FragmentError | None=..., revision: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['error', b'error', 'resolved_config', b'resolved_config']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['error', b'error', 'fragment_id', b'fragment_id', 'resolved_config', b'resolved_config', 'revision', b'revision']) -> None: + ... +global___ResolvedFragment = ResolvedFragment + +@typing.final +class ListFragmentsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + SHOW_PUBLIC_FIELD_NUMBER: builtins.int + FRAGMENT_VISIBILITY_FIELD_NUMBER: builtins.int + organization_id: builtins.str + show_public: builtins.bool + + @property + def fragment_visibility(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___FragmentVisibility.ValueType]: + ... + + def __init__(self, *, organization_id: builtins.str=..., show_public: builtins.bool=..., fragment_visibility: collections.abc.Iterable[global___FragmentVisibility.ValueType] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['fragment_visibility', b'fragment_visibility', 'organization_id', b'organization_id', 'show_public', b'show_public']) -> None: + ... +global___ListFragmentsRequest = ListFragmentsRequest + +@typing.final +class ListFragmentsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENTS_FIELD_NUMBER: builtins.int + FRAGMENT_USAGES_FIELD_NUMBER: builtins.int + + @property + def fragments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Fragment]: + ... + + @property + def fragment_usages(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___FragmentUsage]: + ... + + def __init__(self, *, fragments: collections.abc.Iterable[global___Fragment] | None=..., fragment_usages: collections.abc.Iterable[global___FragmentUsage] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['fragment_usages', b'fragment_usages', 'fragments', b'fragments']) -> None: + ... +global___ListFragmentsResponse = ListFragmentsResponse + +@typing.final +class GetFragmentRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + CURRENT_ORGANIZATION_ID_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + id: builtins.str + current_organization_id: builtins.str + version: builtins.str + 'revision or tag' + + def __init__(self, *, id: builtins.str=..., current_organization_id: builtins.str=..., version: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_version', b'_version', 'version', b'version']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_version', b'_version', 'current_organization_id', b'current_organization_id', 'id', b'id', 'version', b'version']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_version', b'_version']) -> typing.Literal['version'] | None: + ... +global___GetFragmentRequest = GetFragmentRequest + +@typing.final +class GetFragmentResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_FIELD_NUMBER: builtins.int + FRAGMENT_USAGE_FIELD_NUMBER: builtins.int + REVISIONS_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + + @property + def fragment(self) -> global___Fragment: + ... + + @property + def fragment_usage(self) -> global___FragmentUsage: + ... + + @property + def revisions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___FragmentRevision]: + ... + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___FragmentTag]: + ... + + def __init__(self, *, fragment: global___Fragment | None=..., fragment_usage: global___FragmentUsage | None=..., revisions: collections.abc.Iterable[global___FragmentRevision] | None=..., tags: collections.abc.Iterable[global___FragmentTag] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['fragment', b'fragment', 'fragment_usage', b'fragment_usage']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['fragment', b'fragment', 'fragment_usage', b'fragment_usage', 'revisions', b'revisions', 'tags', b'tags']) -> None: + ... +global___GetFragmentResponse = GetFragmentResponse + +@typing.final +class CreateFragmentRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + CONFIG_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + VISIBILITY_FIELD_NUMBER: builtins.int + name: builtins.str + organization_id: builtins.str + visibility: global___FragmentVisibility.ValueType + + @property + def config(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., config: google.protobuf.struct_pb2.Struct | None=..., organization_id: builtins.str=..., visibility: global___FragmentVisibility.ValueType | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_visibility', b'_visibility', 'config', b'config', 'visibility', b'visibility']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_visibility', b'_visibility', 'config', b'config', 'name', b'name', 'organization_id', b'organization_id', 'visibility', b'visibility']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_visibility', b'_visibility']) -> typing.Literal['visibility'] | None: + ... +global___CreateFragmentRequest = CreateFragmentRequest + +@typing.final +class CreateFragmentResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_FIELD_NUMBER: builtins.int + + @property + def fragment(self) -> global___Fragment: + ... + + def __init__(self, *, fragment: global___Fragment | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['fragment', b'fragment']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['fragment', b'fragment']) -> None: + ... +global___CreateFragmentResponse = CreateFragmentResponse + +@typing.final +class UpdateFragmentRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + CONFIG_FIELD_NUMBER: builtins.int + PUBLIC_FIELD_NUMBER: builtins.int + VISIBILITY_FIELD_NUMBER: builtins.int + LAST_KNOWN_UPDATE_FIELD_NUMBER: builtins.int + id: builtins.str + name: builtins.str + public: builtins.bool + visibility: global___FragmentVisibility.ValueType + + @property + def config(self) -> google.protobuf.struct_pb2.Struct: + ... + + @property + def last_known_update(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., config: google.protobuf.struct_pb2.Struct | None=..., public: builtins.bool | None=..., visibility: global___FragmentVisibility.ValueType | None=..., last_known_update: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_last_known_update', b'_last_known_update', '_public', b'_public', '_visibility', b'_visibility', 'config', b'config', 'last_known_update', b'last_known_update', 'public', b'public', 'visibility', b'visibility']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_last_known_update', b'_last_known_update', '_public', b'_public', '_visibility', b'_visibility', 'config', b'config', 'id', b'id', 'last_known_update', b'last_known_update', 'name', b'name', 'public', b'public', 'visibility', b'visibility']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_last_known_update', b'_last_known_update']) -> typing.Literal['last_known_update'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_public', b'_public']) -> typing.Literal['public'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_visibility', b'_visibility']) -> typing.Literal['visibility'] | None: + ... +global___UpdateFragmentRequest = UpdateFragmentRequest + +@typing.final +class UpdateFragmentResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_FIELD_NUMBER: builtins.int + + @property + def fragment(self) -> global___Fragment: + ... + + def __init__(self, *, fragment: global___Fragment | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['fragment', b'fragment']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['fragment', b'fragment']) -> None: + ... +global___UpdateFragmentResponse = UpdateFragmentResponse + +@typing.final +class DeleteFragmentRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___DeleteFragmentRequest = DeleteFragmentRequest + +@typing.final +class DeleteFragmentResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteFragmentResponse = DeleteFragmentResponse + +@typing.final +class GetFragmentHistoryRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + PAGE_TOKEN_FIELD_NUMBER: builtins.int + PAGE_LIMIT_FIELD_NUMBER: builtins.int + id: builtins.str + page_token: builtins.str + page_limit: builtins.int + + def __init__(self, *, id: builtins.str=..., page_token: builtins.str | None=..., page_limit: builtins.int | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_page_limit', b'_page_limit', '_page_token', b'_page_token', 'page_limit', b'page_limit', 'page_token', b'page_token']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_page_limit', b'_page_limit', '_page_token', b'_page_token', 'id', b'id', 'page_limit', b'page_limit', 'page_token', b'page_token']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_page_limit', b'_page_limit']) -> typing.Literal['page_limit'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_page_token', b'_page_token']) -> typing.Literal['page_token'] | None: + ... +global___GetFragmentHistoryRequest = GetFragmentHistoryRequest + +@typing.final +class GetFragmentHistoryResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + HISTORY_FIELD_NUMBER: builtins.int + NEXT_PAGE_TOKEN_FIELD_NUMBER: builtins.int + next_page_token: builtins.str + + @property + def history(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___FragmentHistoryEntry]: + ... + + def __init__(self, *, history: collections.abc.Iterable[global___FragmentHistoryEntry] | None=..., next_page_token: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['history', b'history', 'next_page_token', b'next_page_token']) -> None: + ... +global___GetFragmentHistoryResponse = GetFragmentHistoryResponse + +@typing.final +class GetFragmentUsageRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_ID_FIELD_NUMBER: builtins.int + fragment_id: builtins.str + + def __init__(self, *, fragment_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['fragment_id', b'fragment_id']) -> None: + ... +global___GetFragmentUsageRequest = GetFragmentUsageRequest + +@typing.final +class GetFragmentUsageResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + VERSION_USAGES_FIELD_NUMBER: builtins.int + + @property + def version_usages(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___FragmentUsage]: + ... + + def __init__(self, *, version_usages: collections.abc.Iterable[global___FragmentUsage] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['version_usages', b'version_usages']) -> None: + ... +global___GetFragmentUsageResponse = GetFragmentUsageResponse + +@typing.final +class SetFragmentTagRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_ID_FIELD_NUMBER: builtins.int + TAG_FIELD_NUMBER: builtins.int + REVISION_FIELD_NUMBER: builtins.int + fragment_id: builtins.str + tag: builtins.str + revision: builtins.str + + def __init__(self, *, fragment_id: builtins.str=..., tag: builtins.str=..., revision: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['fragment_id', b'fragment_id', 'revision', b'revision', 'tag', b'tag']) -> None: + ... +global___SetFragmentTagRequest = SetFragmentTagRequest + +@typing.final +class SetFragmentTagResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TAGS_FIELD_NUMBER: builtins.int + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___FragmentTag]: + ... + + def __init__(self, *, tags: collections.abc.Iterable[global___FragmentTag] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['tags', b'tags']) -> None: + ... +global___SetFragmentTagResponse = SetFragmentTagResponse + +@typing.final +class DeleteFragmentTagRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_ID_FIELD_NUMBER: builtins.int + TAG_FIELD_NUMBER: builtins.int + fragment_id: builtins.str + tag: builtins.str + + def __init__(self, *, fragment_id: builtins.str=..., tag: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['fragment_id', b'fragment_id', 'tag', b'tag']) -> None: + ... +global___DeleteFragmentTagRequest = DeleteFragmentTagRequest + +@typing.final +class DeleteFragmentTagResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TAGS_FIELD_NUMBER: builtins.int + + @property + def tags(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___FragmentTag]: + ... + + def __init__(self, *, tags: collections.abc.Iterable[global___FragmentTag] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['tags', b'tags']) -> None: + ... +global___DeleteFragmentTagResponse = DeleteFragmentTagResponse + +@typing.final +class ListRobotsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + location_id: builtins.str + + def __init__(self, *, location_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id']) -> None: + ... +global___ListRobotsRequest = ListRobotsRequest + +@typing.final +class AdditionalFragment(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_ID_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + fragment_id: builtins.str + version: builtins.str + + def __init__(self, *, fragment_id: builtins.str=..., version: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_version', b'_version', 'version', b'version']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_version', b'_version', 'fragment_id', b'fragment_id', 'version', b'version']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_version', b'_version']) -> typing.Literal['version'] | None: + ... +global___AdditionalFragment = AdditionalFragment + +@typing.final +class ListNestedFragmentsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_ID_FIELD_NUMBER: builtins.int + ADDITIONAL_FRAGMENTS_FIELD_NUMBER: builtins.int + fragment_id: builtins.str + + @property + def additional_fragments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AdditionalFragment]: + ... + + def __init__(self, *, fragment_id: builtins.str | None=..., additional_fragments: collections.abc.Iterable[global___AdditionalFragment] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_fragment_id', b'_fragment_id', 'fragment_id', b'fragment_id']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_fragment_id', b'_fragment_id', 'additional_fragments', b'additional_fragments', 'fragment_id', b'fragment_id']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_fragment_id', b'_fragment_id']) -> typing.Literal['fragment_id'] | None: + ... +global___ListNestedFragmentsRequest = ListNestedFragmentsRequest + +@typing.final +class ListNestedFragmentsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENTS_FIELD_NUMBER: builtins.int + RESOLVED_FRAGMENTS_FIELD_NUMBER: builtins.int + + @property + def fragments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Fragment]: + ... + + @property + def resolved_fragments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResolvedFragment]: + ... + + def __init__(self, *, fragments: collections.abc.Iterable[global___Fragment] | None=..., resolved_fragments: collections.abc.Iterable[global___ResolvedFragment] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['fragments', b'fragments', 'resolved_fragments', b'resolved_fragments']) -> None: + ... +global___ListNestedFragmentsResponse = ListNestedFragmentsResponse + +@typing.final +class ListMachineFragmentsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MACHINE_ID_FIELD_NUMBER: builtins.int + ADDITIONAL_FRAGMENT_IDS_FIELD_NUMBER: builtins.int + ADDITIONAL_FRAGMENTS_FIELD_NUMBER: builtins.int + machine_id: builtins.str + "the machine_id used to filter fragments defined in a machine's parts.\n Also returns any fragments nested within the fragments defined in parts.\n " + + @property + def additional_fragment_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """TODO(APP-7642): Mark this field as deprecated""" + + @property + def additional_fragments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AdditionalFragment]: + """additional fragments to append to the response. useful when needing to view fragments that will be + provisionally added to the machine alongside existing fragments. + """ + + def __init__(self, *, machine_id: builtins.str=..., additional_fragment_ids: collections.abc.Iterable[builtins.str] | None=..., additional_fragments: collections.abc.Iterable[global___AdditionalFragment] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['additional_fragment_ids', b'additional_fragment_ids', 'additional_fragments', b'additional_fragments', 'machine_id', b'machine_id']) -> None: + ... +global___ListMachineFragmentsRequest = ListMachineFragmentsRequest + +@typing.final +class ListMachineFragmentsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENTS_FIELD_NUMBER: builtins.int + RESOLVED_FRAGMENTS_FIELD_NUMBER: builtins.int + + @property + def fragments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Fragment]: + ... + + @property + def resolved_fragments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResolvedFragment]: + ... + + def __init__(self, *, fragments: collections.abc.Iterable[global___Fragment] | None=..., resolved_fragments: collections.abc.Iterable[global___ResolvedFragment] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['fragments', b'fragments', 'resolved_fragments', b'resolved_fragments']) -> None: + ... +global___ListMachineFragmentsResponse = ListMachineFragmentsResponse + +@typing.final +class ListMachineSummariesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + organization_id: builtins.str + + def __init__(self, *, organization_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id']) -> None: + ... +global___ListMachineSummariesRequest = ListMachineSummariesRequest + +@typing.final +class ListMachineSummariesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_SUMMARIES_FIELD_NUMBER: builtins.int + + @property + def location_summaries(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___LocationSummary]: + ... + + def __init__(self, *, location_summaries: collections.abc.Iterable[global___LocationSummary] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_summaries', b'location_summaries']) -> None: + ... +global___ListMachineSummariesResponse = ListMachineSummariesResponse + +@typing.final +class LocationSummary(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_ID_FIELD_NUMBER: builtins.int + LOCATION_NAME_FIELD_NUMBER: builtins.int + MACHINE_SUMMARIES_FIELD_NUMBER: builtins.int + location_id: builtins.str + location_name: builtins.str + + @property + def machine_summaries(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MachineSummary]: + ... + + def __init__(self, *, location_id: builtins.str=..., location_name: builtins.str=..., machine_summaries: collections.abc.Iterable[global___MachineSummary] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id', 'location_name', b'location_name', 'machine_summaries', b'machine_summaries']) -> None: + ... +global___LocationSummary = LocationSummary + +@typing.final +class MachineSummary(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MACHINE_ID_FIELD_NUMBER: builtins.int + MACHINE_NAME_FIELD_NUMBER: builtins.int + PART_SUMMARIES_FIELD_NUMBER: builtins.int + machine_id: builtins.str + machine_name: builtins.str + + @property + def part_summaries(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PartSummary]: + ... + + def __init__(self, *, machine_id: builtins.str=..., machine_name: builtins.str=..., part_summaries: collections.abc.Iterable[global___PartSummary] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['machine_id', b'machine_id', 'machine_name', b'machine_name', 'part_summaries', b'part_summaries']) -> None: + ... +global___MachineSummary = MachineSummary + +@typing.final +class FragmentSummary(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + id: builtins.str + name: builtins.str + + def __init__(self, *, id: builtins.str=..., name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'name', b'name']) -> None: + ... +global___FragmentSummary = FragmentSummary + +@typing.final +class ViamServerVersion(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MAJOR_FIELD_NUMBER: builtins.int + MINOR_FIELD_NUMBER: builtins.int + major: builtins.str + minor: builtins.str + + def __init__(self, *, major: builtins.str=..., minor: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['major', b'major', 'minor', b'minor', 'version', b'version']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['major', b'major', 'minor', b'minor', 'version', b'version']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['version', b'version']) -> typing.Literal['major', 'minor'] | None: + ... +global___ViamServerVersion = ViamServerVersion + +@typing.final +class ViamAgentVersion(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MAJOR_FIELD_NUMBER: builtins.int + MINOR_FIELD_NUMBER: builtins.int + major: builtins.str + minor: builtins.str + + def __init__(self, *, major: builtins.str=..., minor: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['major', b'major', 'minor', b'minor', 'version', b'version']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['major', b'major', 'minor', b'minor', 'version', b'version']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['version', b'version']) -> typing.Literal['major', 'minor'] | None: + ... +global___ViamAgentVersion = ViamAgentVersion + +@typing.final +class PartSummary(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_ID_FIELD_NUMBER: builtins.int + PART_NAME_FIELD_NUMBER: builtins.int + LAST_ONLINE_FIELD_NUMBER: builtins.int + VIAM_SERVER_VERSION_FIELD_NUMBER: builtins.int + VIAM_AGENT_VERSION_FIELD_NUMBER: builtins.int + OS_FIELD_NUMBER: builtins.int + PLATFORM_FIELD_NUMBER: builtins.int + PUBLIC_IP_ADDRESS_FIELD_NUMBER: builtins.int + FRAGMENTS_FIELD_NUMBER: builtins.int + part_id: builtins.str + part_name: builtins.str + os: builtins.str + platform: builtins.str + public_ip_address: builtins.str + + @property + def last_online(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def viam_server_version(self) -> global___ViamServerVersion: + ... + + @property + def viam_agent_version(self) -> global___ViamAgentVersion: + ... + + @property + def fragments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___FragmentSummary]: + ... + + def __init__(self, *, part_id: builtins.str=..., part_name: builtins.str=..., last_online: google.protobuf.timestamp_pb2.Timestamp | None=..., viam_server_version: global___ViamServerVersion | None=..., viam_agent_version: global___ViamAgentVersion | None=..., os: builtins.str | None=..., platform: builtins.str | None=..., public_ip_address: builtins.str | None=..., fragments: collections.abc.Iterable[global___FragmentSummary] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_last_online', b'_last_online', '_os', b'_os', '_platform', b'_platform', '_public_ip_address', b'_public_ip_address', '_viam_agent_version', b'_viam_agent_version', '_viam_server_version', b'_viam_server_version', 'last_online', b'last_online', 'os', b'os', 'platform', b'platform', 'public_ip_address', b'public_ip_address', 'viam_agent_version', b'viam_agent_version', 'viam_server_version', b'viam_server_version']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_last_online', b'_last_online', '_os', b'_os', '_platform', b'_platform', '_public_ip_address', b'_public_ip_address', '_viam_agent_version', b'_viam_agent_version', '_viam_server_version', b'_viam_server_version', 'fragments', b'fragments', 'last_online', b'last_online', 'os', b'os', 'part_id', b'part_id', 'part_name', b'part_name', 'platform', b'platform', 'public_ip_address', b'public_ip_address', 'viam_agent_version', b'viam_agent_version', 'viam_server_version', b'viam_server_version']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_last_online', b'_last_online']) -> typing.Literal['last_online'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_os', b'_os']) -> typing.Literal['os'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_platform', b'_platform']) -> typing.Literal['platform'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_public_ip_address', b'_public_ip_address']) -> typing.Literal['public_ip_address'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_viam_agent_version', b'_viam_agent_version']) -> typing.Literal['viam_agent_version'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_viam_server_version', b'_viam_server_version']) -> typing.Literal['viam_server_version'] | None: + ... +global___PartSummary = PartSummary + +@typing.final +class ListRobotsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ROBOTS_FIELD_NUMBER: builtins.int + + @property + def robots(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Robot]: + ... + + def __init__(self, *, robots: collections.abc.Iterable[global___Robot] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['robots', b'robots']) -> None: + ... +global___ListRobotsResponse = ListRobotsResponse + +@typing.final +class NewRobotRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + LOCATION_FIELD_NUMBER: builtins.int + name: builtins.str + location: builtins.str + + def __init__(self, *, name: builtins.str=..., location: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location', b'location', 'name', b'name']) -> None: + ... +global___NewRobotRequest = NewRobotRequest + +@typing.final +class NewRobotResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___NewRobotResponse = NewRobotResponse + +@typing.final +class UpdateRobotRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + LOCATION_FIELD_NUMBER: builtins.int + id: builtins.str + name: builtins.str + location: builtins.str + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., location: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'location', b'location', 'name', b'name']) -> None: + ... +global___UpdateRobotRequest = UpdateRobotRequest + +@typing.final +class UpdateRobotResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ROBOT_FIELD_NUMBER: builtins.int + + @property + def robot(self) -> global___Robot: + ... + + def __init__(self, *, robot: global___Robot | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['robot', b'robot']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['robot', b'robot']) -> None: + ... +global___UpdateRobotResponse = UpdateRobotResponse + +@typing.final +class DeleteRobotRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___DeleteRobotRequest = DeleteRobotRequest + +@typing.final +class DeleteRobotResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteRobotResponse = DeleteRobotResponse + +@typing.final +class GetRobotMetadataRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___GetRobotMetadataRequest = GetRobotMetadataRequest + +@typing.final +class GetRobotMetadataResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, data: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['data', b'data']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___GetRobotMetadataResponse = GetRobotMetadataResponse + +@typing.final +class UpdateRobotMetadataRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + id: builtins.str + + @property + def data(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, id: builtins.str=..., data: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['data', b'data']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data', 'id', b'id']) -> None: + ... +global___UpdateRobotMetadataRequest = UpdateRobotMetadataRequest + +@typing.final +class UpdateRobotMetadataResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___UpdateRobotMetadataResponse = UpdateRobotMetadataResponse + +@typing.final +class MarkPartAsMainRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_ID_FIELD_NUMBER: builtins.int + part_id: builtins.str + + def __init__(self, *, part_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['part_id', b'part_id']) -> None: + ... +global___MarkPartAsMainRequest = MarkPartAsMainRequest + +@typing.final +class MarkPartAsMainResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___MarkPartAsMainResponse = MarkPartAsMainResponse + +@typing.final +class MarkPartForRestartRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_ID_FIELD_NUMBER: builtins.int + part_id: builtins.str + + def __init__(self, *, part_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['part_id', b'part_id']) -> None: + ... +global___MarkPartForRestartRequest = MarkPartForRestartRequest + +@typing.final +class MarkPartForRestartResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___MarkPartForRestartResponse = MarkPartForRestartResponse + +@typing.final +class CreateRobotPartSecretRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_ID_FIELD_NUMBER: builtins.int + part_id: builtins.str + 'Robot Part ID to create the secret in.' + + def __init__(self, *, part_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['part_id', b'part_id']) -> None: + ... +global___CreateRobotPartSecretRequest = CreateRobotPartSecretRequest + +@typing.final +class CreateRobotPartSecretResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_FIELD_NUMBER: builtins.int + + @property + def part(self) -> global___RobotPart: + """Location's auth after updates.""" + + def __init__(self, *, part: global___RobotPart | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['part', b'part']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['part', b'part']) -> None: + ... +global___CreateRobotPartSecretResponse = CreateRobotPartSecretResponse + +@typing.final +class DeleteRobotPartSecretRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PART_ID_FIELD_NUMBER: builtins.int + SECRET_ID_FIELD_NUMBER: builtins.int + part_id: builtins.str + secret_id: builtins.str + + def __init__(self, *, part_id: builtins.str=..., secret_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['part_id', b'part_id', 'secret_id', b'secret_id']) -> None: + ... +global___DeleteRobotPartSecretRequest = DeleteRobotPartSecretRequest + +@typing.final +class DeleteRobotPartSecretResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteRobotPartSecretResponse = DeleteRobotPartSecretResponse + +@typing.final +class Authorization(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + AUTHORIZATION_TYPE_FIELD_NUMBER: builtins.int + AUTHORIZATION_ID_FIELD_NUMBER: builtins.int + RESOURCE_TYPE_FIELD_NUMBER: builtins.int + RESOURCE_ID_FIELD_NUMBER: builtins.int + IDENTITY_ID_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + IDENTITY_TYPE_FIELD_NUMBER: builtins.int + authorization_type: builtins.str + authorization_id: builtins.str + resource_type: builtins.str + resource_id: builtins.str + identity_id: builtins.str + organization_id: builtins.str + identity_type: builtins.str + + def __init__(self, *, authorization_type: builtins.str=..., authorization_id: builtins.str=..., resource_type: builtins.str=..., resource_id: builtins.str=..., identity_id: builtins.str=..., organization_id: builtins.str=..., identity_type: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['authorization_id', b'authorization_id', 'authorization_type', b'authorization_type', 'identity_id', b'identity_id', 'identity_type', b'identity_type', 'organization_id', b'organization_id', 'resource_id', b'resource_id', 'resource_type', b'resource_type']) -> None: + ... +global___Authorization = Authorization + +@typing.final +class AddRoleRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + AUTHORIZATION_FIELD_NUMBER: builtins.int + + @property + def authorization(self) -> global___Authorization: + ... + + def __init__(self, *, authorization: global___Authorization | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['authorization', b'authorization']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['authorization', b'authorization']) -> None: + ... +global___AddRoleRequest = AddRoleRequest + +@typing.final +class AddRoleResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___AddRoleResponse = AddRoleResponse + +@typing.final +class RemoveRoleRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + AUTHORIZATION_FIELD_NUMBER: builtins.int + + @property + def authorization(self) -> global___Authorization: + ... + + def __init__(self, *, authorization: global___Authorization | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['authorization', b'authorization']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['authorization', b'authorization']) -> None: + ... +global___RemoveRoleRequest = RemoveRoleRequest + +@typing.final +class RemoveRoleResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___RemoveRoleResponse = RemoveRoleResponse + +@typing.final +class ChangeRoleRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + OLD_AUTHORIZATION_FIELD_NUMBER: builtins.int + NEW_AUTHORIZATION_FIELD_NUMBER: builtins.int + + @property + def old_authorization(self) -> global___Authorization: + ... + + @property + def new_authorization(self) -> global___Authorization: + ... + + def __init__(self, *, old_authorization: global___Authorization | None=..., new_authorization: global___Authorization | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['new_authorization', b'new_authorization', 'old_authorization', b'old_authorization']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['new_authorization', b'new_authorization', 'old_authorization', b'old_authorization']) -> None: + ... +global___ChangeRoleRequest = ChangeRoleRequest + +@typing.final +class ChangeRoleResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___ChangeRoleResponse = ChangeRoleResponse + +@typing.final +class ListAuthorizationsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + RESOURCE_IDS_FIELD_NUMBER: builtins.int + organization_id: builtins.str + + @property + def resource_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """optional filter""" + + def __init__(self, *, organization_id: builtins.str=..., resource_ids: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['organization_id', b'organization_id', 'resource_ids', b'resource_ids']) -> None: + ... +global___ListAuthorizationsRequest = ListAuthorizationsRequest + +@typing.final +class ListAuthorizationsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + AUTHORIZATIONS_FIELD_NUMBER: builtins.int + + @property + def authorizations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Authorization]: + ... + + def __init__(self, *, authorizations: collections.abc.Iterable[global___Authorization] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['authorizations', b'authorizations']) -> None: + ... +global___ListAuthorizationsResponse = ListAuthorizationsResponse + +@typing.final +class CheckPermissionsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PERMISSIONS_FIELD_NUMBER: builtins.int + + @property + def permissions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AuthorizedPermissions]: + ... + + def __init__(self, *, permissions: collections.abc.Iterable[global___AuthorizedPermissions] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['permissions', b'permissions']) -> None: + ... +global___CheckPermissionsRequest = CheckPermissionsRequest + +@typing.final +class AuthorizedPermissions(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESOURCE_TYPE_FIELD_NUMBER: builtins.int + RESOURCE_ID_FIELD_NUMBER: builtins.int + PERMISSIONS_FIELD_NUMBER: builtins.int + resource_type: builtins.str + resource_id: builtins.str + + @property + def permissions(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, resource_type: builtins.str=..., resource_id: builtins.str=..., permissions: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['permissions', b'permissions', 'resource_id', b'resource_id', 'resource_type', b'resource_type']) -> None: + ... +global___AuthorizedPermissions = AuthorizedPermissions + +@typing.final +class CheckPermissionsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + AUTHORIZED_PERMISSIONS_FIELD_NUMBER: builtins.int + + @property + def authorized_permissions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AuthorizedPermissions]: + ... + + def __init__(self, *, authorized_permissions: collections.abc.Iterable[global___AuthorizedPermissions] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['authorized_permissions', b'authorized_permissions']) -> None: + ... +global___CheckPermissionsResponse = CheckPermissionsResponse + +@typing.final +class ModuleVersion(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + VERSION_FIELD_NUMBER: builtins.int + FILES_FIELD_NUMBER: builtins.int + MODELS_FIELD_NUMBER: builtins.int + ENTRYPOINT_FIELD_NUMBER: builtins.int + FIRST_RUN_FIELD_NUMBER: builtins.int + MARKDOWN_DESCRIPTION_FIELD_NUMBER: builtins.int + version: builtins.str + 'The semver string that represents the major/minor/patch version of the module' + entrypoint: builtins.str + 'The entrypoint for this version of the module' + first_run: builtins.str + 'The path to a setup script that is run before a newly downloaded module starts.' + markdown_description: builtins.str + 'The markdown documentation for this version of the module' + + @property + def files(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Uploads]: + """The uploads that are available for this module version""" + + @property + def models(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Model]: + """The models that this verion of the module provides""" + + def __init__(self, *, version: builtins.str=..., files: collections.abc.Iterable[global___Uploads] | None=..., models: collections.abc.Iterable[global___Model] | None=..., entrypoint: builtins.str=..., first_run: builtins.str | None=..., markdown_description: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_first_run', b'_first_run', '_markdown_description', b'_markdown_description', 'first_run', b'first_run', 'markdown_description', b'markdown_description']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_first_run', b'_first_run', '_markdown_description', b'_markdown_description', 'entrypoint', b'entrypoint', 'files', b'files', 'first_run', b'first_run', 'markdown_description', b'markdown_description', 'models', b'models', 'version', b'version']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_first_run', b'_first_run']) -> typing.Literal['first_run'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_markdown_description', b'_markdown_description']) -> typing.Literal['markdown_description'] | None: + ... +global___ModuleVersion = ModuleVersion + +@typing.final +class ModuleMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODELS_FIELD_NUMBER: builtins.int + VERSIONS_FIELD_NUMBER: builtins.int + ENTRYPOINT_FIELD_NUMBER: builtins.int + FIRST_RUN_FIELD_NUMBER: builtins.int + MARKDOWN_DESCRIPTION_FIELD_NUMBER: builtins.int + entrypoint: builtins.str + 'The executable to run to start the module program' + first_run: builtins.str + 'The path to a setup script that is run before a newly downloaded module starts.' + markdown_description: builtins.str + 'markdown content for the entire module' + + @property + def models(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Model]: + """A list of models that are available in the module""" + + @property + def versions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ModuleVersion]: + """A list of versions of the module that are available + When this is returned from the backend, the versions are sorted in ascending order by the semver version + """ + + def __init__(self, *, models: collections.abc.Iterable[global___Model] | None=..., versions: collections.abc.Iterable[global___ModuleVersion] | None=..., entrypoint: builtins.str=..., first_run: builtins.str | None=..., markdown_description: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_first_run', b'_first_run', '_markdown_description', b'_markdown_description', 'first_run', b'first_run', 'markdown_description', b'markdown_description']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_first_run', b'_first_run', '_markdown_description', b'_markdown_description', 'entrypoint', b'entrypoint', 'first_run', b'first_run', 'markdown_description', b'markdown_description', 'models', b'models', 'versions', b'versions']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_first_run', b'_first_run']) -> typing.Literal['first_run'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_markdown_description', b'_markdown_description']) -> typing.Literal['markdown_description'] | None: + ... +global___ModuleMetadata = ModuleMetadata + +@typing.final +class MLModelMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + VERSIONS_FIELD_NUMBER: builtins.int + MODEL_TYPE_FIELD_NUMBER: builtins.int + MODEL_FRAMEWORK_FIELD_NUMBER: builtins.int + model_type: app.mltraining.v1.ml_training_pb2.ModelType.ValueType + model_framework: app.mltraining.v1.ml_training_pb2.ModelFramework.ValueType + + @property + def versions(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """A list of package versions for a ML model""" + + def __init__(self, *, versions: collections.abc.Iterable[builtins.str] | None=..., model_type: app.mltraining.v1.ml_training_pb2.ModelType.ValueType=..., model_framework: app.mltraining.v1.ml_training_pb2.ModelFramework.ValueType=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['model_framework', b'model_framework', 'model_type', b'model_type', 'versions', b'versions']) -> None: + ... +global___MLModelMetadata = MLModelMetadata + +@typing.final +class MLTrainingVersion(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + VERSION_FIELD_NUMBER: builtins.int + CREATED_ON_FIELD_NUMBER: builtins.int + version: builtins.str + + @property + def created_on(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, version: builtins.str=..., created_on: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['created_on', b'created_on']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['created_on', b'created_on', 'version', b'version']) -> None: + ... +global___MLTrainingVersion = MLTrainingVersion + +@typing.final +class MLTrainingMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + VERSIONS_FIELD_NUMBER: builtins.int + MODEL_TYPE_FIELD_NUMBER: builtins.int + MODEL_FRAMEWORK_FIELD_NUMBER: builtins.int + DRAFT_FIELD_NUMBER: builtins.int + model_type: app.mltraining.v1.ml_training_pb2.ModelType.ValueType + model_framework: app.mltraining.v1.ml_training_pb2.ModelFramework.ValueType + draft: builtins.bool + + @property + def versions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___MLTrainingVersion]: + """A list of package versions for ML training source distribution""" + + def __init__(self, *, versions: collections.abc.Iterable[global___MLTrainingVersion] | None=..., model_type: app.mltraining.v1.ml_training_pb2.ModelType.ValueType=..., model_framework: app.mltraining.v1.ml_training_pb2.ModelFramework.ValueType=..., draft: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['draft', b'draft', 'model_framework', b'model_framework', 'model_type', b'model_type', 'versions', b'versions']) -> None: + ... +global___MLTrainingMetadata = MLTrainingMetadata + +@typing.final +class RegistryItem(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ITEM_ID_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + PUBLIC_NAMESPACE_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + VISIBILITY_FIELD_NUMBER: builtins.int + URL_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + TOTAL_ROBOT_USAGE_FIELD_NUMBER: builtins.int + TOTAL_EXTERNAL_ROBOT_USAGE_FIELD_NUMBER: builtins.int + TOTAL_ORGANIZATION_USAGE_FIELD_NUMBER: builtins.int + TOTAL_EXTERNAL_ORGANIZATION_USAGE_FIELD_NUMBER: builtins.int + MODULE_METADATA_FIELD_NUMBER: builtins.int + ML_MODEL_METADATA_FIELD_NUMBER: builtins.int + ML_TRAINING_METADATA_FIELD_NUMBER: builtins.int + CREATED_AT_FIELD_NUMBER: builtins.int + UPDATED_AT_FIELD_NUMBER: builtins.int + item_id: builtins.str + 'The id of the item, containing either:\n namespace:item_name when a namespace exists on the org.\n org_id:item_name when a namespace does not exist.\n ' + organization_id: builtins.str + 'The id of the organization that owns the item' + public_namespace: builtins.str + 'The public namespace of the organization that owns the module\n This is empty if no public namespace is set\n ' + name: builtins.str + 'The name of the registry item' + type: app.packages.v1.packages_pb2.PackageType.ValueType + 'The type of the item in the registry' + visibility: global___Visibility.ValueType + 'The visibility of the registry item' + url: builtins.str + 'The url to reference for documentation, code, etc.' + description: builtins.str + 'A short description of the item that explains its purpose' + total_robot_usage: builtins.int + 'The total number of robots using this item' + total_external_robot_usage: builtins.int + 'The total number of robots using this item outside of the owning org' + total_organization_usage: builtins.int + 'The total number of organizations using this item' + total_external_organization_usage: builtins.int + 'The total number of organizations using this item outside of the owning org' + + @property + def module_metadata(self) -> global___ModuleMetadata: + ... + + @property + def ml_model_metadata(self) -> global___MLModelMetadata: + ... + + @property + def ml_training_metadata(self) -> global___MLTrainingMetadata: + ... + + @property + def created_at(self) -> google.protobuf.timestamp_pb2.Timestamp: + """When the item was created""" + + @property + def updated_at(self) -> google.protobuf.timestamp_pb2.Timestamp: + """When the item was last updated, either through an update or upload.""" + + def __init__(self, *, item_id: builtins.str=..., organization_id: builtins.str=..., public_namespace: builtins.str=..., name: builtins.str=..., type: app.packages.v1.packages_pb2.PackageType.ValueType=..., visibility: global___Visibility.ValueType=..., url: builtins.str=..., description: builtins.str=..., total_robot_usage: builtins.int=..., total_external_robot_usage: builtins.int=..., total_organization_usage: builtins.int=..., total_external_organization_usage: builtins.int=..., module_metadata: global___ModuleMetadata | None=..., ml_model_metadata: global___MLModelMetadata | None=..., ml_training_metadata: global___MLTrainingMetadata | None=..., created_at: google.protobuf.timestamp_pb2.Timestamp | None=..., updated_at: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['created_at', b'created_at', 'metadata', b'metadata', 'ml_model_metadata', b'ml_model_metadata', 'ml_training_metadata', b'ml_training_metadata', 'module_metadata', b'module_metadata', 'updated_at', b'updated_at']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['created_at', b'created_at', 'description', b'description', 'item_id', b'item_id', 'metadata', b'metadata', 'ml_model_metadata', b'ml_model_metadata', 'ml_training_metadata', b'ml_training_metadata', 'module_metadata', b'module_metadata', 'name', b'name', 'organization_id', b'organization_id', 'public_namespace', b'public_namespace', 'total_external_organization_usage', b'total_external_organization_usage', 'total_external_robot_usage', b'total_external_robot_usage', 'total_organization_usage', b'total_organization_usage', 'total_robot_usage', b'total_robot_usage', 'type', b'type', 'updated_at', b'updated_at', 'url', b'url', 'visibility', b'visibility']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['metadata', b'metadata']) -> typing.Literal['module_metadata', 'ml_model_metadata', 'ml_training_metadata'] | None: + ... +global___RegistryItem = RegistryItem + +@typing.final +class GetRegistryItemRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ITEM_ID_FIELD_NUMBER: builtins.int + INCLUDE_MARKDOWN_DOCUMENTATION_FIELD_NUMBER: builtins.int + item_id: builtins.str + include_markdown_documentation: builtins.bool + + def __init__(self, *, item_id: builtins.str=..., include_markdown_documentation: builtins.bool | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation', 'include_markdown_documentation', b'include_markdown_documentation']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation', 'include_markdown_documentation', b'include_markdown_documentation', 'item_id', b'item_id']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation']) -> typing.Literal['include_markdown_documentation'] | None: + ... +global___GetRegistryItemRequest = GetRegistryItemRequest + +@typing.final +class GetRegistryItemResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ITEM_FIELD_NUMBER: builtins.int + + @property + def item(self) -> global___RegistryItem: + ... + + def __init__(self, *, item: global___RegistryItem | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['item', b'item']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['item', b'item']) -> None: + ... +global___GetRegistryItemResponse = GetRegistryItemResponse + +@typing.final +class CreateRegistryItemRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + organization_id: builtins.str + 'The organization to create the registry item under' + name: builtins.str + 'The name of the registry item, which must be unique within your org' + type: app.packages.v1.packages_pb2.PackageType.ValueType + 'The type of the item in the registry' + + def __init__(self, *, organization_id: builtins.str=..., name: builtins.str=..., type: app.packages.v1.packages_pb2.PackageType.ValueType=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name', 'organization_id', b'organization_id', 'type', b'type']) -> None: + ... +global___CreateRegistryItemRequest = CreateRegistryItemRequest + +@typing.final +class CreateRegistryItemResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___CreateRegistryItemResponse = CreateRegistryItemResponse + +@typing.final +class UpdateRegistryItemRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ITEM_ID_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + VISIBILITY_FIELD_NUMBER: builtins.int + URL_FIELD_NUMBER: builtins.int + UPDATE_MODULE_METADATA_FIELD_NUMBER: builtins.int + UPDATE_ML_MODEL_METADATA_FIELD_NUMBER: builtins.int + UPDATE_ML_TRAINING_METADATA_FIELD_NUMBER: builtins.int + MARKDOWN_DESCRIPTION_FIELD_NUMBER: builtins.int + item_id: builtins.str + type: app.packages.v1.packages_pb2.PackageType.ValueType + description: builtins.str + visibility: global___Visibility.ValueType + url: builtins.str + markdown_description: builtins.str + + @property + def update_module_metadata(self) -> global___UpdateModuleMetadata: + ... + + @property + def update_ml_model_metadata(self) -> global___UpdateMLModelMetadata: + ... + + @property + def update_ml_training_metadata(self) -> global___UpdateMLTrainingMetadata: + ... + + def __init__(self, *, item_id: builtins.str=..., type: app.packages.v1.packages_pb2.PackageType.ValueType=..., description: builtins.str=..., visibility: global___Visibility.ValueType=..., url: builtins.str | None=..., update_module_metadata: global___UpdateModuleMetadata | None=..., update_ml_model_metadata: global___UpdateMLModelMetadata | None=..., update_ml_training_metadata: global___UpdateMLTrainingMetadata | None=..., markdown_description: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_markdown_description', b'_markdown_description', '_url', b'_url', 'markdown_description', b'markdown_description', 'metadata', b'metadata', 'update_ml_model_metadata', b'update_ml_model_metadata', 'update_ml_training_metadata', b'update_ml_training_metadata', 'update_module_metadata', b'update_module_metadata', 'url', b'url']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_markdown_description', b'_markdown_description', '_url', b'_url', 'description', b'description', 'item_id', b'item_id', 'markdown_description', b'markdown_description', 'metadata', b'metadata', 'type', b'type', 'update_ml_model_metadata', b'update_ml_model_metadata', 'update_ml_training_metadata', b'update_ml_training_metadata', 'update_module_metadata', b'update_module_metadata', 'url', b'url', 'visibility', b'visibility']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_markdown_description', b'_markdown_description']) -> typing.Literal['markdown_description'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_url', b'_url']) -> typing.Literal['url'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['metadata', b'metadata']) -> typing.Literal['update_module_metadata', 'update_ml_model_metadata', 'update_ml_training_metadata'] | None: + ... +global___UpdateRegistryItemRequest = UpdateRegistryItemRequest + +@typing.final +class UpdateRegistryItemResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___UpdateRegistryItemResponse = UpdateRegistryItemResponse + +@typing.final +class ListRegistryItemsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + TYPES_FIELD_NUMBER: builtins.int + VISIBILITIES_FIELD_NUMBER: builtins.int + PLATFORMS_FIELD_NUMBER: builtins.int + STATUSES_FIELD_NUMBER: builtins.int + SEARCH_TERM_FIELD_NUMBER: builtins.int + PAGE_TOKEN_FIELD_NUMBER: builtins.int + PUBLIC_NAMESPACES_FIELD_NUMBER: builtins.int + INCLUDE_MARKDOWN_DOCUMENTATION_FIELD_NUMBER: builtins.int + organization_id: builtins.str + 'The id of the organization to return registry items for.' + search_term: builtins.str + page_token: builtins.str + include_markdown_documentation: builtins.bool + + @property + def types(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[app.packages.v1.packages_pb2.PackageType.ValueType]: + ... + + @property + def visibilities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___Visibility.ValueType]: + ... + + @property + def platforms(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def statuses(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___RegistryItemStatus.ValueType]: + ... + + @property + def public_namespaces(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """One or more public namespaces to return results for.""" + + def __init__(self, *, organization_id: builtins.str | None=..., types: collections.abc.Iterable[app.packages.v1.packages_pb2.PackageType.ValueType] | None=..., visibilities: collections.abc.Iterable[global___Visibility.ValueType] | None=..., platforms: collections.abc.Iterable[builtins.str] | None=..., statuses: collections.abc.Iterable[global___RegistryItemStatus.ValueType] | None=..., search_term: builtins.str | None=..., page_token: builtins.str | None=..., public_namespaces: collections.abc.Iterable[builtins.str] | None=..., include_markdown_documentation: builtins.bool | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation', '_organization_id', b'_organization_id', '_page_token', b'_page_token', '_search_term', b'_search_term', 'include_markdown_documentation', b'include_markdown_documentation', 'organization_id', b'organization_id', 'page_token', b'page_token', 'search_term', b'search_term']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation', '_organization_id', b'_organization_id', '_page_token', b'_page_token', '_search_term', b'_search_term', 'include_markdown_documentation', b'include_markdown_documentation', 'organization_id', b'organization_id', 'page_token', b'page_token', 'platforms', b'platforms', 'public_namespaces', b'public_namespaces', 'search_term', b'search_term', 'statuses', b'statuses', 'types', b'types', 'visibilities', b'visibilities']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation']) -> typing.Literal['include_markdown_documentation'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_organization_id', b'_organization_id']) -> typing.Literal['organization_id'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_page_token', b'_page_token']) -> typing.Literal['page_token'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_search_term', b'_search_term']) -> typing.Literal['search_term'] | None: + ... +global___ListRegistryItemsRequest = ListRegistryItemsRequest + +@typing.final +class ListRegistryItemsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ITEMS_FIELD_NUMBER: builtins.int + + @property + def items(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___RegistryItem]: + ... + + def __init__(self, *, items: collections.abc.Iterable[global___RegistryItem] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['items', b'items']) -> None: + ... +global___ListRegistryItemsResponse = ListRegistryItemsResponse + +@typing.final +class DeleteRegistryItemRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ITEM_ID_FIELD_NUMBER: builtins.int + item_id: builtins.str + "The id of the item (formatted as prefix:name where prefix is the owner's orgid or namespace)" + + def __init__(self, *, item_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['item_id', b'item_id']) -> None: + ... +global___DeleteRegistryItemRequest = DeleteRegistryItemRequest + +@typing.final +class DeleteRegistryItemResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteRegistryItemResponse = DeleteRegistryItemResponse + +@typing.final +class RenameRegistryItemRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ITEM_ID_FIELD_NUMBER: builtins.int + NEW_NAME_FIELD_NUMBER: builtins.int + item_id: builtins.str + new_name: builtins.str + + def __init__(self, *, item_id: builtins.str=..., new_name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['item_id', b'item_id', 'new_name', b'new_name']) -> None: + ... +global___RenameRegistryItemRequest = RenameRegistryItemRequest + +@typing.final +class RenameRegistryItemResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ITEM_FIELD_NUMBER: builtins.int + + @property + def item(self) -> global___RegistryItem: + ... + + def __init__(self, *, item: global___RegistryItem | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['item', b'item']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['item', b'item']) -> None: + ... +global___RenameRegistryItemResponse = RenameRegistryItemResponse + +@typing.final +class TransferRegistryItemRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ITEM_ID_FIELD_NUMBER: builtins.int + NEW_PUBLIC_NAMESPACE_FIELD_NUMBER: builtins.int + item_id: builtins.str + new_public_namespace: builtins.str + + def __init__(self, *, item_id: builtins.str=..., new_public_namespace: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['item_id', b'item_id', 'new_public_namespace', b'new_public_namespace']) -> None: + ... +global___TransferRegistryItemRequest = TransferRegistryItemRequest + +@typing.final +class TransferRegistryItemResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___TransferRegistryItemResponse = TransferRegistryItemResponse + +@typing.final +class CreateModuleRequest(google.protobuf.message.Message): + """Modules""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + organization_id: builtins.str + 'The organization to create the module under' + name: builtins.str + 'The name of the module, which must be unique within your org' + + def __init__(self, *, organization_id: builtins.str=..., name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name', 'organization_id', b'organization_id']) -> None: + ... +global___CreateModuleRequest = CreateModuleRequest + +@typing.final +class CreateModuleResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODULE_ID_FIELD_NUMBER: builtins.int + URL_FIELD_NUMBER: builtins.int + module_id: builtins.str + "The id of the module (formatted as prefix:name where prefix is the module owner's orgid or namespace)" + url: builtins.str + 'The detail page of the module' + + def __init__(self, *, module_id: builtins.str=..., url: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['module_id', b'module_id', 'url', b'url']) -> None: + ... +global___CreateModuleResponse = CreateModuleResponse + +@typing.final +class UpdateModuleRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODULE_ID_FIELD_NUMBER: builtins.int + VISIBILITY_FIELD_NUMBER: builtins.int + URL_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + MODELS_FIELD_NUMBER: builtins.int + ENTRYPOINT_FIELD_NUMBER: builtins.int + FIRST_RUN_FIELD_NUMBER: builtins.int + APPS_FIELD_NUMBER: builtins.int + MARKDOWN_DESCRIPTION_FIELD_NUMBER: builtins.int + module_id: builtins.str + "The id of the module (formatted as prefix:name where prefix is the module owner's orgid or namespace)" + visibility: global___Visibility.ValueType + 'The visibility that should be set for the module' + url: builtins.str + 'The url to reference for documentation, code, etc.' + description: builtins.str + 'A short description of the module that explains its purpose' + entrypoint: builtins.str + 'The executable to run to start the module program' + first_run: builtins.str + 'The path to a setup script that is run before a newly downloaded module starts.' + markdown_description: builtins.str + 'longer documentation provided in markdown format' + + @property + def models(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Model]: + """A list of models that are available in the module""" + + @property + def apps(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___App]: + """A list of applications associated with the module""" + + def __init__(self, *, module_id: builtins.str=..., visibility: global___Visibility.ValueType=..., url: builtins.str=..., description: builtins.str=..., models: collections.abc.Iterable[global___Model] | None=..., entrypoint: builtins.str=..., first_run: builtins.str | None=..., apps: collections.abc.Iterable[global___App] | None=..., markdown_description: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_first_run', b'_first_run', '_markdown_description', b'_markdown_description', 'first_run', b'first_run', 'markdown_description', b'markdown_description']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_first_run', b'_first_run', '_markdown_description', b'_markdown_description', 'apps', b'apps', 'description', b'description', 'entrypoint', b'entrypoint', 'first_run', b'first_run', 'markdown_description', b'markdown_description', 'models', b'models', 'module_id', b'module_id', 'url', b'url', 'visibility', b'visibility']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_first_run', b'_first_run']) -> typing.Literal['first_run'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_markdown_description', b'_markdown_description']) -> typing.Literal['markdown_description'] | None: + ... +global___UpdateModuleRequest = UpdateModuleRequest + +@typing.final +class App(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + ENTRYPOINT_FIELD_NUMBER: builtins.int + name: builtins.str + 'The name of the application' + type: builtins.str + 'The type of the application' + entrypoint: builtins.str + 'The entrypoint of the application' + + def __init__(self, *, name: builtins.str=..., type: builtins.str=..., entrypoint: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['entrypoint', b'entrypoint', 'name', b'name', 'type', b'type']) -> None: + ... +global___App = App + +@typing.final +class UpdateModuleResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + URL_FIELD_NUMBER: builtins.int + url: builtins.str + 'The detail page of the module' + + def __init__(self, *, url: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['url', b'url']) -> None: + ... +global___UpdateModuleResponse = UpdateModuleResponse + +@typing.final +class UpdateModuleMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODELS_FIELD_NUMBER: builtins.int + ENTRYPOINT_FIELD_NUMBER: builtins.int + entrypoint: builtins.str + 'The executable to run to start the module program' + + @property + def models(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Model]: + """A list of models that are available in the module""" + + def __init__(self, *, models: collections.abc.Iterable[global___Model] | None=..., entrypoint: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['entrypoint', b'entrypoint', 'models', b'models']) -> None: + ... +global___UpdateModuleMetadata = UpdateModuleMetadata + +@typing.final +class UpdateMLModelMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODEL_TYPE_FIELD_NUMBER: builtins.int + MODEL_FRAMEWORK_FIELD_NUMBER: builtins.int + model_type: app.mltraining.v1.ml_training_pb2.ModelType.ValueType + model_framework: app.mltraining.v1.ml_training_pb2.ModelFramework.ValueType + + def __init__(self, *, model_type: app.mltraining.v1.ml_training_pb2.ModelType.ValueType=..., model_framework: app.mltraining.v1.ml_training_pb2.ModelFramework.ValueType=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['model_framework', b'model_framework', 'model_type', b'model_type']) -> None: + ... +global___UpdateMLModelMetadata = UpdateMLModelMetadata + +@typing.final +class UpdateMLTrainingMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODEL_TYPE_FIELD_NUMBER: builtins.int + MODEL_FRAMEWORK_FIELD_NUMBER: builtins.int + DRAFT_FIELD_NUMBER: builtins.int + model_type: app.mltraining.v1.ml_training_pb2.ModelType.ValueType + model_framework: app.mltraining.v1.ml_training_pb2.ModelFramework.ValueType + draft: builtins.bool + + def __init__(self, *, model_type: app.mltraining.v1.ml_training_pb2.ModelType.ValueType=..., model_framework: app.mltraining.v1.ml_training_pb2.ModelFramework.ValueType=..., draft: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['draft', b'draft', 'model_framework', b'model_framework', 'model_type', b'model_type']) -> None: + ... +global___UpdateMLTrainingMetadata = UpdateMLTrainingMetadata + +@typing.final +class Model(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + API_FIELD_NUMBER: builtins.int + MODEL_FIELD_NUMBER: builtins.int + MARKDOWN_DOCUMENTATION_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + SUPPORTED_HARDWARE_FIELD_NUMBER: builtins.int + api: builtins.str + 'The colon-delimited-triplet of the api implemented by the model' + model: builtins.str + 'The colon-delimited-triplet of the model' + markdown_documentation: builtins.str + 'The markdown content describing the usage of the model' + description: builtins.str + 'A short description of the model that explains its purpose' + + @property + def supported_hardware(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """A list of supported hardware names""" + + def __init__(self, *, api: builtins.str=..., model: builtins.str=..., markdown_documentation: builtins.str | None=..., description: builtins.str | None=..., supported_hardware: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_description', b'_description', '_markdown_documentation', b'_markdown_documentation', 'description', b'description', 'markdown_documentation', b'markdown_documentation']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_description', b'_description', '_markdown_documentation', b'_markdown_documentation', 'api', b'api', 'description', b'description', 'markdown_documentation', b'markdown_documentation', 'model', b'model', 'supported_hardware', b'supported_hardware']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_description', b'_description']) -> typing.Literal['description'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_markdown_documentation', b'_markdown_documentation']) -> typing.Literal['markdown_documentation'] | None: + ... +global___Model = Model + +@typing.final +class ModuleFileInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODULE_ID_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + PLATFORM_FIELD_NUMBER: builtins.int + PLATFORM_TAGS_FIELD_NUMBER: builtins.int + module_id: builtins.str + "The id of the module (formatted as prefix:name where prefix is the module owner's orgid or namespace)" + version: builtins.str + 'The semver string that represents the new major/minor/patch version of the module' + platform: builtins.str + 'The platform that the file is built to run on' + + @property + def platform_tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Platform tag constraints. When a robot requests its config, it uploads a platform and a list of + platform tags. The platform is checked against `platform` above, and the tags are checked against + this list. + """ + + def __init__(self, *, module_id: builtins.str=..., version: builtins.str=..., platform: builtins.str=..., platform_tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['module_id', b'module_id', 'platform', b'platform', 'platform_tags', b'platform_tags', 'version', b'version']) -> None: + ... +global___ModuleFileInfo = ModuleFileInfo + +@typing.final +class UploadModuleFileRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODULE_FILE_INFO_FIELD_NUMBER: builtins.int + FILE_FIELD_NUMBER: builtins.int + file: builtins.bytes + 'The file contents to be uploaded' + + @property + def module_file_info(self) -> global___ModuleFileInfo: + """The information about the module file being uploaded""" + + def __init__(self, *, module_file_info: global___ModuleFileInfo | None=..., file: builtins.bytes=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['file', b'file', 'module_file', b'module_file', 'module_file_info', b'module_file_info']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['file', b'file', 'module_file', b'module_file', 'module_file_info', b'module_file_info']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['module_file', b'module_file']) -> typing.Literal['module_file_info', 'file'] | None: + ... +global___UploadModuleFileRequest = UploadModuleFileRequest + +@typing.final +class UploadModuleFileResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + URL_FIELD_NUMBER: builtins.int + url: builtins.str + 'The detail page of the module' + + def __init__(self, *, url: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['url', b'url']) -> None: + ... +global___UploadModuleFileResponse = UploadModuleFileResponse + +@typing.final +class GetModuleRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODULE_ID_FIELD_NUMBER: builtins.int + INCLUDE_MARKDOWN_DOCUMENTATION_FIELD_NUMBER: builtins.int + module_id: builtins.str + "The id of the module (formatted as prefix:name where prefix is the module owner's orgid or namespace)" + include_markdown_documentation: builtins.bool + + def __init__(self, *, module_id: builtins.str=..., include_markdown_documentation: builtins.bool | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation', 'include_markdown_documentation', b'include_markdown_documentation']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation', 'include_markdown_documentation', b'include_markdown_documentation', 'module_id', b'module_id']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation']) -> typing.Literal['include_markdown_documentation'] | None: + ... +global___GetModuleRequest = GetModuleRequest + +@typing.final +class GetModuleResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODULE_FIELD_NUMBER: builtins.int + + @property + def module(self) -> global___Module: + """The module object""" + + def __init__(self, *, module: global___Module | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['module', b'module']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['module', b'module']) -> None: + ... +global___GetModuleResponse = GetModuleResponse + +@typing.final +class Module(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODULE_ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + VISIBILITY_FIELD_NUMBER: builtins.int + VERSIONS_FIELD_NUMBER: builtins.int + URL_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + MODELS_FIELD_NUMBER: builtins.int + TOTAL_ROBOT_USAGE_FIELD_NUMBER: builtins.int + TOTAL_ORGANIZATION_USAGE_FIELD_NUMBER: builtins.int + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + ENTRYPOINT_FIELD_NUMBER: builtins.int + PUBLIC_NAMESPACE_FIELD_NUMBER: builtins.int + FIRST_RUN_FIELD_NUMBER: builtins.int + MARKDOWN_DESCRIPTION_FIELD_NUMBER: builtins.int + module_id: builtins.str + "The id of the module (formatted as prefix:name where prefix is the module owner's orgid or namespace)" + name: builtins.str + 'The name of the module' + visibility: global___Visibility.ValueType + 'The visibility of the module' + url: builtins.str + 'The url to reference for documentation, code, etc.' + description: builtins.str + 'A short description of the module that explains its purpose' + total_robot_usage: builtins.int + 'The total number of robots using this module' + total_organization_usage: builtins.int + 'The total number of organizations using this module' + organization_id: builtins.str + 'The id of the organization that owns the module' + entrypoint: builtins.str + 'The executable to run to start the module program' + public_namespace: builtins.str + 'The public namespace of the organization that owns the module\n This is empty if no public namespace is set\n ' + first_run: builtins.str + 'The path to a setup script that is run before a newly downloaded module starts.' + markdown_description: builtins.str + 'Longer documentation provided in markdown format' + + @property + def versions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___VersionHistory]: + """The versions of the module that are available + When this is returned from the backend, the versions are sorted in ascending order by the semver version + """ + + @property + def models(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Model]: + """A list of models that are available in the module""" + + def __init__(self, *, module_id: builtins.str=..., name: builtins.str=..., visibility: global___Visibility.ValueType=..., versions: collections.abc.Iterable[global___VersionHistory] | None=..., url: builtins.str=..., description: builtins.str=..., models: collections.abc.Iterable[global___Model] | None=..., total_robot_usage: builtins.int=..., total_organization_usage: builtins.int=..., organization_id: builtins.str=..., entrypoint: builtins.str=..., public_namespace: builtins.str=..., first_run: builtins.str | None=..., markdown_description: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_first_run', b'_first_run', '_markdown_description', b'_markdown_description', 'first_run', b'first_run', 'markdown_description', b'markdown_description']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_first_run', b'_first_run', '_markdown_description', b'_markdown_description', 'description', b'description', 'entrypoint', b'entrypoint', 'first_run', b'first_run', 'markdown_description', b'markdown_description', 'models', b'models', 'module_id', b'module_id', 'name', b'name', 'organization_id', b'organization_id', 'public_namespace', b'public_namespace', 'total_organization_usage', b'total_organization_usage', 'total_robot_usage', b'total_robot_usage', 'url', b'url', 'versions', b'versions', 'visibility', b'visibility']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_first_run', b'_first_run']) -> typing.Literal['first_run'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_markdown_description', b'_markdown_description']) -> typing.Literal['markdown_description'] | None: + ... +global___Module = Module + +@typing.final +class VersionHistory(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + VERSION_FIELD_NUMBER: builtins.int + FILES_FIELD_NUMBER: builtins.int + MODELS_FIELD_NUMBER: builtins.int + ENTRYPOINT_FIELD_NUMBER: builtins.int + FIRST_RUN_FIELD_NUMBER: builtins.int + MARKDOWN_DESCRIPTION_FIELD_NUMBER: builtins.int + version: builtins.str + 'The semver string that represents the major/minor/patch version of the module' + entrypoint: builtins.str + 'The entrypoint for this version of the module' + first_run: builtins.str + 'The path to a setup script that is run before a newly downloaded module starts.' + markdown_description: builtins.str + 'The markdown documentation for this version of the module' + + @property + def files(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Uploads]: + """The uploads that are available for this module version""" + + @property + def models(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Model]: + """The models that this verion of the module provides""" + + def __init__(self, *, version: builtins.str=..., files: collections.abc.Iterable[global___Uploads] | None=..., models: collections.abc.Iterable[global___Model] | None=..., entrypoint: builtins.str=..., first_run: builtins.str | None=..., markdown_description: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_first_run', b'_first_run', '_markdown_description', b'_markdown_description', 'first_run', b'first_run', 'markdown_description', b'markdown_description']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_first_run', b'_first_run', '_markdown_description', b'_markdown_description', 'entrypoint', b'entrypoint', 'files', b'files', 'first_run', b'first_run', 'markdown_description', b'markdown_description', 'models', b'models', 'version', b'version']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_first_run', b'_first_run']) -> typing.Literal['first_run'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_markdown_description', b'_markdown_description']) -> typing.Literal['markdown_description'] | None: + ... +global___VersionHistory = VersionHistory + +@typing.final +class Uploads(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PLATFORM_FIELD_NUMBER: builtins.int + UPLOADED_AT_FIELD_NUMBER: builtins.int + platform: builtins.str + 'The OS and architecture the module is built to run on' + + @property + def uploaded_at(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The time when the file was uploaded""" + + def __init__(self, *, platform: builtins.str=..., uploaded_at: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['uploaded_at', b'uploaded_at']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['platform', b'platform', 'uploaded_at', b'uploaded_at']) -> None: + ... +global___Uploads = Uploads + +@typing.final +class ListModulesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + INCLUDE_MARKDOWN_DOCUMENTATION_FIELD_NUMBER: builtins.int + organization_id: builtins.str + 'The id of the organization to return private modules for.' + include_markdown_documentation: builtins.bool + + def __init__(self, *, organization_id: builtins.str | None=..., include_markdown_documentation: builtins.bool | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation', '_organization_id', b'_organization_id', 'include_markdown_documentation', b'include_markdown_documentation', 'organization_id', b'organization_id']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation', '_organization_id', b'_organization_id', 'include_markdown_documentation', b'include_markdown_documentation', 'organization_id', b'organization_id']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_include_markdown_documentation', b'_include_markdown_documentation']) -> typing.Literal['include_markdown_documentation'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_organization_id', b'_organization_id']) -> typing.Literal['organization_id'] | None: + ... +global___ListModulesRequest = ListModulesRequest + +@typing.final +class ListModulesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODULES_FIELD_NUMBER: builtins.int + + @property + def modules(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Module]: + """A listed of modules. When authenticated, this API will return modules that are private for this org. Public modules are always returned.""" + + def __init__(self, *, modules: collections.abc.Iterable[global___Module] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['modules', b'modules']) -> None: + ... +global___ListModulesResponse = ListModulesResponse + +@typing.final +class GetUserIDByEmailRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + EMAIL_FIELD_NUMBER: builtins.int + email: builtins.str + + def __init__(self, *, email: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['email', b'email']) -> None: + ... +global___GetUserIDByEmailRequest = GetUserIDByEmailRequest + +@typing.final +class GetUserIDByEmailResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + USER_ID_FIELD_NUMBER: builtins.int + user_id: builtins.str + + def __init__(self, *, user_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['user_id', b'user_id']) -> None: + ... +global___GetUserIDByEmailResponse = GetUserIDByEmailResponse + +@typing.final +class ListOrganizationsByUserRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + USER_ID_FIELD_NUMBER: builtins.int + user_id: builtins.str + + def __init__(self, *, user_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['user_id', b'user_id']) -> None: + ... +global___ListOrganizationsByUserRequest = ListOrganizationsByUserRequest + +@typing.final +class OrgDetails(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + ORG_NAME_FIELD_NUMBER: builtins.int + ORG_CID_FIELD_NUMBER: builtins.int + PUBLIC_NAMESPACE_FIELD_NUMBER: builtins.int + BILLING_TIER_FIELD_NUMBER: builtins.int + org_id: builtins.str + org_name: builtins.str + org_cid: builtins.str + public_namespace: builtins.str + billing_tier: builtins.str + + def __init__(self, *, org_id: builtins.str=..., org_name: builtins.str=..., org_cid: builtins.str | None=..., public_namespace: builtins.str | None=..., billing_tier: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_billing_tier', b'_billing_tier', '_org_cid', b'_org_cid', '_public_namespace', b'_public_namespace', 'billing_tier', b'billing_tier', 'org_cid', b'org_cid', 'public_namespace', b'public_namespace']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_billing_tier', b'_billing_tier', '_org_cid', b'_org_cid', '_public_namespace', b'_public_namespace', 'billing_tier', b'billing_tier', 'org_cid', b'org_cid', 'org_id', b'org_id', 'org_name', b'org_name', 'public_namespace', b'public_namespace']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_billing_tier', b'_billing_tier']) -> typing.Literal['billing_tier'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_org_cid', b'_org_cid']) -> typing.Literal['org_cid'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_public_namespace', b'_public_namespace']) -> typing.Literal['public_namespace'] | None: + ... +global___OrgDetails = OrgDetails + +@typing.final +class ListOrganizationsByUserResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGS_FIELD_NUMBER: builtins.int + + @property + def orgs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___OrgDetails]: + ... - @property - def fragment(self) -> google.protobuf.struct_pb2.Struct: + def __init__(self, *, orgs: collections.abc.Iterable[global___OrgDetails] | None=...) -> None: ... - organization_owner: builtins.str - public: builtins.bool - def __init__(self, *, id: builtins.str=..., name: builtins.str=..., fragment: google.protobuf.struct_pb2.Struct | None=..., organization_owner: builtins.str=..., public: builtins.bool=...) -> None: + def ClearField(self, field_name: typing.Literal['orgs', b'orgs']) -> None: ... +global___ListOrganizationsByUserResponse = ListOrganizationsByUserResponse - def HasField(self, field_name: typing_extensions.Literal['fragment', b'fragment']) -> builtins.bool: +@typing.final +class SearchOrganizationsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + ORG_NAME_FIELD_NUMBER: builtins.int + CID_FIELD_NUMBER: builtins.int + PUBLIC_NAMESPACE_FIELD_NUMBER: builtins.int + org_id: builtins.str + org_name: builtins.str + cid: builtins.str + public_namespace: builtins.str + + def __init__(self, *, org_id: builtins.str | None=..., org_name: builtins.str | None=..., cid: builtins.str | None=..., public_namespace: builtins.str | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['fragment', b'fragment', 'id', b'id', 'name', b'name', 'organization_owner', b'organization_owner', 'public', b'public']) -> None: + def HasField(self, field_name: typing.Literal['_cid', b'_cid', '_org_id', b'_org_id', '_org_name', b'_org_name', '_public_namespace', b'_public_namespace', 'cid', b'cid', 'org_id', b'org_id', 'org_name', b'org_name', 'public_namespace', b'public_namespace']) -> builtins.bool: ... -global___Fragment = Fragment -class FindRobotsRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - LOCATION_ID_FIELD_NUMBER: builtins.int - location_id: builtins.str + def ClearField(self, field_name: typing.Literal['_cid', b'_cid', '_org_id', b'_org_id', '_org_name', b'_org_name', '_public_namespace', b'_public_namespace', 'cid', b'cid', 'org_id', b'org_id', 'org_name', b'org_name', 'public_namespace', b'public_namespace']) -> None: + ... - def __init__(self, *, location_id: builtins.str=...) -> None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_cid', b'_cid']) -> typing.Literal['cid'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_org_id', b'_org_id']) -> typing.Literal['org_id'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_org_name', b'_org_name']) -> typing.Literal['org_name'] | None: ... - def ClearField(self, field_name: typing_extensions.Literal['location_id', b'location_id']) -> None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_public_namespace', b'_public_namespace']) -> typing.Literal['public_namespace'] | None: ... -global___FindRobotsRequest = FindRobotsRequest +global___SearchOrganizationsRequest = SearchOrganizationsRequest -class FindRobotsResponse(google.protobuf.message.Message): +@typing.final +class SearchOrganizationsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ROBOTS_FIELD_NUMBER: builtins.int + ORGANIZATIONS_FIELD_NUMBER: builtins.int @property - def robots(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Robot]: + def organizations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___OrgDetails]: ... - def __init__(self, *, robots: collections.abc.Iterable[global___Robot] | None=...) -> None: + def __init__(self, *, organizations: collections.abc.Iterable[global___OrgDetails] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['robots', b'robots']) -> None: + def ClearField(self, field_name: typing.Literal['organizations', b'organizations']) -> None: ... -global___FindRobotsResponse = FindRobotsResponse +global___SearchOrganizationsResponse = SearchOrganizationsResponse -class NewRobotRequest(google.protobuf.message.Message): +@typing.final +class CreateKeyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor + AUTHORIZATIONS_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int - LOCATION_FIELD_NUMBER: builtins.int name: builtins.str - location: builtins.str - def __init__(self, *, name: builtins.str=..., location: builtins.str=...) -> None: + @property + def authorizations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Authorization]: ... - def ClearField(self, field_name: typing_extensions.Literal['location', b'location', 'name', b'name']) -> None: + def __init__(self, *, authorizations: collections.abc.Iterable[global___Authorization] | None=..., name: builtins.str=...) -> None: ... -global___NewRobotRequest = NewRobotRequest -class NewRobotResponse(google.protobuf.message.Message): + def ClearField(self, field_name: typing.Literal['authorizations', b'authorizations', 'name', b'name']) -> None: + ... +global___CreateKeyRequest = CreateKeyRequest + +@typing.final +class CreateKeyResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + ID_FIELD_NUMBER: builtins.int + key: builtins.str + id: builtins.str + + def __init__(self, *, key: builtins.str=..., id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'key', b'key']) -> None: + ... +global___CreateKeyResponse = CreateKeyResponse + +@typing.final +class DeleteKeyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int id: builtins.str @@ -606,45 +4784,124 @@ class NewRobotResponse(google.protobuf.message.Message): def __init__(self, *, id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: ... -global___NewRobotResponse = NewRobotResponse +global___DeleteKeyRequest = DeleteKeyRequest -class UpdateRobotRequest(google.protobuf.message.Message): +@typing.final +class DeleteKeyResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DeleteKeyResponse = DeleteKeyResponse + +@typing.final +class RenameKeyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int - LOCATION_FIELD_NUMBER: builtins.int id: builtins.str name: builtins.str - location: builtins.str - def __init__(self, *, id: builtins.str=..., name: builtins.str=..., location: builtins.str=...) -> None: + def __init__(self, *, id: builtins.str=..., name: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id', 'location', b'location', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id', 'name', b'name']) -> None: ... -global___UpdateRobotRequest = UpdateRobotRequest +global___RenameKeyRequest = RenameKeyRequest -class UpdateRobotResponse(google.protobuf.message.Message): +@typing.final +class RenameKeyResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - ROBOT_FIELD_NUMBER: builtins.int + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + id: builtins.str + name: builtins.str + + def __init__(self, *, id: builtins.str=..., name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'name', b'name']) -> None: + ... +global___RenameKeyResponse = RenameKeyResponse + +@typing.final +class AuthorizationDetails(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + AUTHORIZATION_TYPE_FIELD_NUMBER: builtins.int + AUTHORIZATION_ID_FIELD_NUMBER: builtins.int + RESOURCE_TYPE_FIELD_NUMBER: builtins.int + RESOURCE_ID_FIELD_NUMBER: builtins.int + ORG_ID_FIELD_NUMBER: builtins.int + authorization_type: builtins.str + authorization_id: builtins.str + resource_type: builtins.str + resource_id: builtins.str + org_id: builtins.str + + def __init__(self, *, authorization_type: builtins.str=..., authorization_id: builtins.str=..., resource_type: builtins.str=..., resource_id: builtins.str=..., org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['authorization_id', b'authorization_id', 'authorization_type', b'authorization_type', 'org_id', b'org_id', 'resource_id', b'resource_id', 'resource_type', b'resource_type']) -> None: + ... +global___AuthorizationDetails = AuthorizationDetails + +@typing.final +class APIKeyWithAuthorizations(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + API_KEY_FIELD_NUMBER: builtins.int + AUTHORIZATIONS_FIELD_NUMBER: builtins.int @property - def robot(self) -> global___Robot: + def api_key(self) -> global___APIKey: ... - def __init__(self, *, robot: global___Robot | None=...) -> None: + @property + def authorizations(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AuthorizationDetails]: ... - def HasField(self, field_name: typing_extensions.Literal['robot', b'robot']) -> builtins.bool: + def __init__(self, *, api_key: global___APIKey | None=..., authorizations: collections.abc.Iterable[global___AuthorizationDetails] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['robot', b'robot']) -> None: + def HasField(self, field_name: typing.Literal['api_key', b'api_key']) -> builtins.bool: ... -global___UpdateRobotResponse = UpdateRobotResponse -class DeleteRobotRequest(google.protobuf.message.Message): + def ClearField(self, field_name: typing.Literal['api_key', b'api_key', 'authorizations', b'authorizations']) -> None: + ... +global___APIKeyWithAuthorizations = APIKeyWithAuthorizations + +@typing.final +class ListKeysRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___ListKeysRequest = ListKeysRequest + +@typing.final +class ListKeysResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + API_KEYS_FIELD_NUMBER: builtins.int + + @property + def api_keys(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___APIKeyWithAuthorizations]: + ... + + def __init__(self, *, api_keys: collections.abc.Iterable[global___APIKeyWithAuthorizations] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['api_keys', b'api_keys']) -> None: + ... +global___ListKeysResponse = ListKeysResponse + +@typing.final +class RotateKeyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int id: builtins.str @@ -652,32 +4909,365 @@ class DeleteRobotRequest(google.protobuf.message.Message): def __init__(self, *, id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: ... -global___DeleteRobotRequest = DeleteRobotRequest +global___RotateKeyRequest = RotateKeyRequest -class DeleteRobotResponse(google.protobuf.message.Message): +@typing.final +class RotateKeyResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + KEY_FIELD_NUMBER: builtins.int + id: builtins.str + key: builtins.str + + def __init__(self, *, id: builtins.str=..., key: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'key', b'key']) -> None: + ... +global___RotateKeyResponse = RotateKeyResponse + +@typing.final +class CreateKeyFromExistingKeyAuthorizationsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___CreateKeyFromExistingKeyAuthorizationsRequest = CreateKeyFromExistingKeyAuthorizationsRequest + +@typing.final +class CreateKeyFromExistingKeyAuthorizationsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + KEY_FIELD_NUMBER: builtins.int + id: builtins.str + key: builtins.str + + def __init__(self, *, id: builtins.str=..., key: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'key', b'key']) -> None: + ... +global___CreateKeyFromExistingKeyAuthorizationsResponse = CreateKeyFromExistingKeyAuthorizationsResponse + +@typing.final +class GetAppContentRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PUBLIC_NAMESPACE_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + public_namespace: builtins.str + name: builtins.str + + def __init__(self, *, public_namespace: builtins.str=..., name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name', 'public_namespace', b'public_namespace']) -> None: + ... +global___GetAppContentRequest = GetAppContentRequest + +@typing.final +class GetAppContentResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BLOB_PATH_FIELD_NUMBER: builtins.int + ENTRYPOINT_FIELD_NUMBER: builtins.int + blob_path: builtins.str + entrypoint: builtins.str + + def __init__(self, *, blob_path: builtins.str=..., entrypoint: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['blob_path', b'blob_path', 'entrypoint', b'entrypoint']) -> None: + ... +global___GetAppContentResponse = GetAppContentResponse + +@typing.final +class OrganizationSetLogoRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + LOGO_FIELD_NUMBER: builtins.int + org_id: builtins.str + logo: builtins.bytes + + def __init__(self, *, org_id: builtins.str=..., logo: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['logo', b'logo', 'org_id', b'org_id']) -> None: + ... +global___OrganizationSetLogoRequest = OrganizationSetLogoRequest + +@typing.final +class OrganizationSetLogoResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor def __init__(self) -> None: ... -global___DeleteRobotResponse = DeleteRobotResponse +global___OrganizationSetLogoResponse = OrganizationSetLogoResponse -class MarkPartAsMainRequest(google.protobuf.message.Message): +@typing.final +class OrganizationGetLogoRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PART_ID_FIELD_NUMBER: builtins.int - part_id: builtins.str + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str - def __init__(self, *, part_id: builtins.str=...) -> None: + def __init__(self, *, org_id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['part_id', b'part_id']) -> None: + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: ... -global___MarkPartAsMainRequest = MarkPartAsMainRequest +global___OrganizationGetLogoRequest = OrganizationGetLogoRequest -class MarkPartAsMainResponse(google.protobuf.message.Message): +@typing.final +class OrganizationGetLogoResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + URL_FIELD_NUMBER: builtins.int + url: builtins.str + + def __init__(self, *, url: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['url', b'url']) -> None: + ... +global___OrganizationGetLogoResponse = OrganizationGetLogoResponse + +@typing.final +class EnableAuthServiceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___EnableAuthServiceRequest = EnableAuthServiceRequest + +@typing.final +class EnableAuthServiceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___EnableAuthServiceResponse = EnableAuthServiceResponse + +@typing.final +class DisableAuthServiceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___DisableAuthServiceRequest = DisableAuthServiceRequest + +@typing.final +class DisableAuthServiceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___DisableAuthServiceResponse = DisableAuthServiceResponse + +@typing.final +class CreateOAuthAppRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + CLIENT_NAME_FIELD_NUMBER: builtins.int + OAUTH_CONFIG_FIELD_NUMBER: builtins.int + org_id: builtins.str + client_name: builtins.str + + @property + def oauth_config(self) -> global___OAuthConfig: + ... + + def __init__(self, *, org_id: builtins.str=..., client_name: builtins.str=..., oauth_config: global___OAuthConfig | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['oauth_config', b'oauth_config']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['client_name', b'client_name', 'oauth_config', b'oauth_config', 'org_id', b'org_id']) -> None: + ... +global___CreateOAuthAppRequest = CreateOAuthAppRequest + +@typing.final +class CreateOAuthAppResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CLIENT_ID_FIELD_NUMBER: builtins.int + CLIENT_SECRET_FIELD_NUMBER: builtins.int + client_id: builtins.str + client_secret: builtins.str + + def __init__(self, *, client_id: builtins.str=..., client_secret: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['client_id', b'client_id', 'client_secret', b'client_secret']) -> None: + ... +global___CreateOAuthAppResponse = CreateOAuthAppResponse + +@typing.final +class ReadOAuthAppRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + CLIENT_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + client_id: builtins.str + + def __init__(self, *, org_id: builtins.str=..., client_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['client_id', b'client_id', 'org_id', b'org_id']) -> None: + ... +global___ReadOAuthAppRequest = ReadOAuthAppRequest + +@typing.final +class ReadOAuthAppResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CLIENT_NAME_FIELD_NUMBER: builtins.int + CLIENT_SECRET_FIELD_NUMBER: builtins.int + OAUTH_CONFIG_FIELD_NUMBER: builtins.int + client_name: builtins.str + client_secret: builtins.str + + @property + def oauth_config(self) -> global___OAuthConfig: + ... + + def __init__(self, *, client_name: builtins.str=..., client_secret: builtins.str=..., oauth_config: global___OAuthConfig | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['oauth_config', b'oauth_config']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['client_name', b'client_name', 'client_secret', b'client_secret', 'oauth_config', b'oauth_config']) -> None: + ... +global___ReadOAuthAppResponse = ReadOAuthAppResponse + +@typing.final +class UpdateOAuthAppRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + CLIENT_ID_FIELD_NUMBER: builtins.int + CLIENT_NAME_FIELD_NUMBER: builtins.int + OAUTH_CONFIG_FIELD_NUMBER: builtins.int + org_id: builtins.str + client_id: builtins.str + client_name: builtins.str + + @property + def oauth_config(self) -> global___OAuthConfig: + ... + + def __init__(self, *, org_id: builtins.str=..., client_id: builtins.str=..., client_name: builtins.str=..., oauth_config: global___OAuthConfig | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['oauth_config', b'oauth_config']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['client_id', b'client_id', 'client_name', b'client_name', 'oauth_config', b'oauth_config', 'org_id', b'org_id']) -> None: + ... +global___UpdateOAuthAppRequest = UpdateOAuthAppRequest + +@typing.final +class UpdateOAuthAppResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___UpdateOAuthAppResponse = UpdateOAuthAppResponse + +@typing.final +class DeleteOAuthAppRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + CLIENT_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + client_id: builtins.str + + def __init__(self, *, org_id: builtins.str=..., client_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['client_id', b'client_id', 'org_id', b'org_id']) -> None: + ... +global___DeleteOAuthAppRequest = DeleteOAuthAppRequest + +@typing.final +class DeleteOAuthAppResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor def __init__(self) -> None: ... -global___MarkPartAsMainResponse = MarkPartAsMainResponse \ No newline at end of file +global___DeleteOAuthAppResponse = DeleteOAuthAppResponse + +@typing.final +class ListOAuthAppsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___ListOAuthAppsRequest = ListOAuthAppsRequest + +@typing.final +class ListOAuthAppsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CLIENT_IDS_FIELD_NUMBER: builtins.int + + @property + def client_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, client_ids: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['client_ids', b'client_ids']) -> None: + ... +global___ListOAuthAppsResponse = ListOAuthAppsResponse + +@typing.final +class OAuthConfig(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CLIENT_AUTHENTICATION_FIELD_NUMBER: builtins.int + PKCE_FIELD_NUMBER: builtins.int + URL_VALIDATION_FIELD_NUMBER: builtins.int + ORIGIN_URIS_FIELD_NUMBER: builtins.int + REDIRECT_URIS_FIELD_NUMBER: builtins.int + LOGOUT_URI_FIELD_NUMBER: builtins.int + ENABLED_GRANTS_FIELD_NUMBER: builtins.int + client_authentication: global___ClientAuthentication.ValueType + pkce: global___PKCE.ValueType + url_validation: global___URLValidation.ValueType + logout_uri: builtins.str + + @property + def origin_uris(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def redirect_uris(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def enabled_grants(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___EnabledGrant.ValueType]: + ... + + def __init__(self, *, client_authentication: global___ClientAuthentication.ValueType=..., pkce: global___PKCE.ValueType=..., url_validation: global___URLValidation.ValueType=..., origin_uris: collections.abc.Iterable[builtins.str] | None=..., redirect_uris: collections.abc.Iterable[builtins.str] | None=..., logout_uri: builtins.str=..., enabled_grants: collections.abc.Iterable[global___EnabledGrant.ValueType] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['client_authentication', b'client_authentication', 'enabled_grants', b'enabled_grants', 'logout_uri', b'logout_uri', 'origin_uris', b'origin_uris', 'pkce', b'pkce', 'redirect_uris', b'redirect_uris', 'url_validation', b'url_validation']) -> None: + ... +global___OAuthConfig = OAuthConfig \ No newline at end of file diff --git a/src/viam/gen/app/v1/billing_grpc.py b/src/viam/gen/app/v1/billing_grpc.py new file mode 100644 index 000000000..542ac0ffe --- /dev/null +++ b/src/viam/gen/app/v1/billing_grpc.py @@ -0,0 +1,76 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +import google.protobuf.timestamp_pb2 +from ... import app + +class BillingServiceBase(abc.ABC): + + @abc.abstractmethod + async def GetCurrentMonthUsage(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.GetCurrentMonthUsageRequest, app.v1.billing_pb2.GetCurrentMonthUsageResponse]') -> None: + pass + + @abc.abstractmethod + async def GetOrgBillingInformation(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.GetOrgBillingInformationRequest, app.v1.billing_pb2.GetOrgBillingInformationResponse]') -> None: + pass + + @abc.abstractmethod + async def GetInvoicesSummary(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.GetInvoicesSummaryRequest, app.v1.billing_pb2.GetInvoicesSummaryResponse]') -> None: + pass + + @abc.abstractmethod + async def GetInvoicePdf(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.GetInvoicePdfRequest, app.v1.billing_pb2.GetInvoicePdfResponse]') -> None: + pass + + @abc.abstractmethod + async def SendPaymentRequiredEmail(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.SendPaymentRequiredEmailRequest, app.v1.billing_pb2.SendPaymentRequiredEmailResponse]') -> None: + pass + + @abc.abstractmethod + async def GetAvailableBillingTiers(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.GetAvailableBillingTiersRequest, app.v1.billing_pb2.GetAvailableBillingTiersResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateOrganizationBillingTier(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.UpdateOrganizationBillingTierRequest, app.v1.billing_pb2.UpdateOrganizationBillingTierResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.app.v1.BillingService/GetCurrentMonthUsage': grpclib.const.Handler(self.GetCurrentMonthUsage, grpclib.const.Cardinality.UNARY_UNARY, app.v1.billing_pb2.GetCurrentMonthUsageRequest, app.v1.billing_pb2.GetCurrentMonthUsageResponse), '/viam.app.v1.BillingService/GetOrgBillingInformation': grpclib.const.Handler(self.GetOrgBillingInformation, grpclib.const.Cardinality.UNARY_UNARY, app.v1.billing_pb2.GetOrgBillingInformationRequest, app.v1.billing_pb2.GetOrgBillingInformationResponse), '/viam.app.v1.BillingService/GetInvoicesSummary': grpclib.const.Handler(self.GetInvoicesSummary, grpclib.const.Cardinality.UNARY_UNARY, app.v1.billing_pb2.GetInvoicesSummaryRequest, app.v1.billing_pb2.GetInvoicesSummaryResponse), '/viam.app.v1.BillingService/GetInvoicePdf': grpclib.const.Handler(self.GetInvoicePdf, grpclib.const.Cardinality.UNARY_STREAM, app.v1.billing_pb2.GetInvoicePdfRequest, app.v1.billing_pb2.GetInvoicePdfResponse), '/viam.app.v1.BillingService/SendPaymentRequiredEmail': grpclib.const.Handler(self.SendPaymentRequiredEmail, grpclib.const.Cardinality.UNARY_UNARY, app.v1.billing_pb2.SendPaymentRequiredEmailRequest, app.v1.billing_pb2.SendPaymentRequiredEmailResponse), '/viam.app.v1.BillingService/GetAvailableBillingTiers': grpclib.const.Handler(self.GetAvailableBillingTiers, grpclib.const.Cardinality.UNARY_UNARY, app.v1.billing_pb2.GetAvailableBillingTiersRequest, app.v1.billing_pb2.GetAvailableBillingTiersResponse), '/viam.app.v1.BillingService/UpdateOrganizationBillingTier': grpclib.const.Handler(self.UpdateOrganizationBillingTier, grpclib.const.Cardinality.UNARY_UNARY, app.v1.billing_pb2.UpdateOrganizationBillingTierRequest, app.v1.billing_pb2.UpdateOrganizationBillingTierResponse)} + +class UnimplementedBillingServiceBase(BillingServiceBase): + + async def GetCurrentMonthUsage(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.GetCurrentMonthUsageRequest, app.v1.billing_pb2.GetCurrentMonthUsageResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetOrgBillingInformation(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.GetOrgBillingInformationRequest, app.v1.billing_pb2.GetOrgBillingInformationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetInvoicesSummary(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.GetInvoicesSummaryRequest, app.v1.billing_pb2.GetInvoicesSummaryResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetInvoicePdf(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.GetInvoicePdfRequest, app.v1.billing_pb2.GetInvoicePdfResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SendPaymentRequiredEmail(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.SendPaymentRequiredEmailRequest, app.v1.billing_pb2.SendPaymentRequiredEmailResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetAvailableBillingTiers(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.GetAvailableBillingTiersRequest, app.v1.billing_pb2.GetAvailableBillingTiersResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateOrganizationBillingTier(self, stream: 'grpclib.server.Stream[app.v1.billing_pb2.UpdateOrganizationBillingTierRequest, app.v1.billing_pb2.UpdateOrganizationBillingTierResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class BillingServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.GetCurrentMonthUsage = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.BillingService/GetCurrentMonthUsage', app.v1.billing_pb2.GetCurrentMonthUsageRequest, app.v1.billing_pb2.GetCurrentMonthUsageResponse) + self.GetOrgBillingInformation = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.BillingService/GetOrgBillingInformation', app.v1.billing_pb2.GetOrgBillingInformationRequest, app.v1.billing_pb2.GetOrgBillingInformationResponse) + self.GetInvoicesSummary = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.BillingService/GetInvoicesSummary', app.v1.billing_pb2.GetInvoicesSummaryRequest, app.v1.billing_pb2.GetInvoicesSummaryResponse) + self.GetInvoicePdf = grpclib.client.UnaryStreamMethod(channel, '/viam.app.v1.BillingService/GetInvoicePdf', app.v1.billing_pb2.GetInvoicePdfRequest, app.v1.billing_pb2.GetInvoicePdfResponse) + self.SendPaymentRequiredEmail = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.BillingService/SendPaymentRequiredEmail', app.v1.billing_pb2.SendPaymentRequiredEmailRequest, app.v1.billing_pb2.SendPaymentRequiredEmailResponse) + self.GetAvailableBillingTiers = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.BillingService/GetAvailableBillingTiers', app.v1.billing_pb2.GetAvailableBillingTiersRequest, app.v1.billing_pb2.GetAvailableBillingTiersResponse) + self.UpdateOrganizationBillingTier = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.BillingService/UpdateOrganizationBillingTier', app.v1.billing_pb2.UpdateOrganizationBillingTierRequest, app.v1.billing_pb2.UpdateOrganizationBillingTierResponse) \ No newline at end of file diff --git a/src/viam/gen/app/v1/billing_pb2.py b/src/viam/gen/app/v1/billing_pb2.py new file mode 100644 index 000000000..580fc115b --- /dev/null +++ b/src/viam/gen/app/v1/billing_pb2.py @@ -0,0 +1,92 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/v1/billing.proto') +_sym_db = _symbol_database.Default() +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14app/v1/billing.proto\x12\x0bviam.app.v1\x1a\x1fgoogle/protobuf/timestamp.proto"\x8e\x02\n\x0eInvoiceSummary\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12=\n\x0cinvoice_date\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0binvoiceDate\x12%\n\x0einvoice_amount\x18\x03 \x01(\x01R\rinvoiceAmount\x12\x16\n\x06status\x18\x04 \x01(\tR\x06status\x125\n\x08due_date\x18\x05 \x01(\x0b2\x1a.google.protobuf.TimestampR\x07dueDate\x127\n\tpaid_date\x18\x06 \x01(\x0b2\x1a.google.protobuf.TimestampR\x08paidDate"S\n\x11PaymentMethodCard\x12\x14\n\x05brand\x18\x01 \x01(\tR\x05brand\x12(\n\x10last_four_digits\x18\x02 \x01(\tR\x0elastFourDigits"4\n\x1bGetCurrentMonthUsageRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"`\n\tUsageCost\x12?\n\rresource_type\x18\x01 \x01(\x0e2\x1a.viam.app.v1.UsageCostTypeR\x0cresourceType\x12\x12\n\x04cost\x18\x02 \x01(\x01R\x04cost"\xc6\x01\n\x1aResourceUsageCostsBySource\x128\n\x0bsource_type\x18\x01 \x01(\x0e2\x17.viam.app.v1.SourceTypeR\nsourceType\x12Q\n\x14resource_usage_costs\x18\x02 \x01(\x0b2\x1f.viam.app.v1.ResourceUsageCostsR\x12resourceUsageCosts\x12\x1b\n\ttier_name\x18\x03 \x01(\tR\x08tierName"\xcf\x01\n\x12ResourceUsageCosts\x127\n\x0busage_costs\x18\x01 \x03(\x0b2\x16.viam.app.v1.UsageCostR\nusageCosts\x12\x1a\n\x08discount\x18\x02 \x01(\x01R\x08discount\x12.\n\x13total_with_discount\x18\x03 \x01(\x01R\x11totalWithDiscount\x124\n\x16total_without_discount\x18\x04 \x01(\x01R\x14totalWithoutDiscount"\xcd\x07\n\x1cGetCurrentMonthUsageResponse\x129\n\nstart_date\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\tstartDate\x125\n\x08end_date\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x07endDate\x12k\n\x1eresource_usage_costs_by_source\x18\x0e \x03(\x0b2\'.viam.app.v1.ResourceUsageCostsBySourceR\x1aresourceUsageCostsBySource\x12\x1a\n\x08subtotal\x18\x0f \x01(\x01R\x08subtotal\x12;\n\x18cloud_storage_usage_cost\x18\x03 \x01(\x01B\x02\x18\x01R\x15cloudStorageUsageCost\x127\n\x16data_upload_usage_cost\x18\x04 \x01(\x01B\x02\x18\x01R\x13dataUploadUsageCost\x125\n\x15data_egres_usage_cost\x18\x05 \x01(\x01B\x02\x18\x01R\x12dataEgresUsageCost\x12=\n\x19remote_control_usage_cost\x18\x06 \x01(\x01B\x02\x18\x01R\x16remoteControlUsageCost\x12A\n\x1bstandard_compute_usage_cost\x18\x07 \x01(\x01B\x02\x18\x01R\x18standardComputeUsageCost\x12+\n\x0fdiscount_amount\x18\x08 \x01(\x01B\x02\x18\x01R\x0ediscountAmount\x12=\n\x19total_usage_with_discount\x18\t \x01(\x01B\x02\x18\x01R\x16totalUsageWithDiscount\x12C\n\x1ctotal_usage_without_discount\x18\n \x01(\x01B\x02\x18\x01R\x19totalUsageWithoutDiscount\x127\n\x16per_machine_usage_cost\x18\x0b \x01(\x01B\x02\x18\x01R\x13perMachineUsageCost\x12Q\n$binary_data_cloud_storage_usage_cost\x18\x0c \x01(\x01B\x02\x18\x01R\x1fbinaryDataCloudStorageUsageCost\x12F\n\x1eother_cloud_storage_usage_cost\x18\r \x01(\x01B\x02\x18\x01R\x1aotherCloudStorageUsageCost"8\n\x1fGetOrgBillingInformationRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"\xfc\x01\n GetOrgBillingInformationResponse\x122\n\x04type\x18\x01 \x01(\x0e2\x1e.viam.app.v1.PaymentMethodTypeR\x04type\x12#\n\rbilling_email\x18\x02 \x01(\tR\x0cbillingEmail\x12;\n\x06method\x18\x03 \x01(\x0b2\x1e.viam.app.v1.PaymentMethodCardH\x00R\x06method\x88\x01\x01\x12&\n\x0cbilling_tier\x18\x04 \x01(\tH\x01R\x0bbillingTier\x88\x01\x01B\t\n\x07_methodB\x0f\n\r_billing_tier"2\n\x19GetInvoicesSummaryRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId"\x86\x01\n\x1aGetInvoicesSummaryResponse\x12/\n\x13outstanding_balance\x18\x01 \x01(\x01R\x12outstandingBalance\x127\n\x08invoices\x18\x02 \x03(\x0b2\x1b.viam.app.v1.InvoiceSummaryR\x08invoices"=\n\x14GetInvoicePdfRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x15\n\x06org_id\x18\x02 \x01(\tR\x05orgId"-\n\x15GetInvoicePdfResponse\x12\x14\n\x05chunk\x18\x01 \x01(\x0cR\x05chunk"z\n\x1fSendPaymentRequiredEmailRequest\x12&\n\x0fcustomer_org_id\x18\x01 \x01(\tR\rcustomerOrgId\x12/\n\x14billing_owner_org_id\x18\x02 \x01(\tR\x11billingOwnerOrgId""\n SendPaymentRequiredEmailResponse"!\n\x1fGetAvailableBillingTiersRequest"8\n GetAvailableBillingTiersResponse\x12\x14\n\x05tiers\x18\x01 \x03(\tR\x05tiers"r\n$UpdateOrganizationBillingTierRequest\x12\'\n\x0forganization_id\x18\x01 \x01(\tR\x0eorganizationId\x12!\n\x0cbilling_tier\x18\x02 \x01(\tR\x0bbillingTier"\'\n%UpdateOrganizationBillingTierResponse*V\n\x11PaymentMethodType\x12#\n\x1fPAYMENT_METHOD_TYPE_UNSPECIFIED\x10\x00\x12\x1c\n\x18PAYMENT_METHOD_TYPE_CARD\x10\x01*\xf3\x07\n\rUsageCostType\x12\x1f\n\x1bUSAGE_COST_TYPE_UNSPECIFIED\x10\x00\x12#\n\x1bUSAGE_COST_TYPE_DATA_UPLOAD\x10\x01\x1a\x02\x08\x01\x12#\n\x1bUSAGE_COST_TYPE_DATA_EGRESS\x10\x02\x1a\x02\x08\x01\x12"\n\x1eUSAGE_COST_TYPE_REMOTE_CONTROL\x10\x03\x12$\n USAGE_COST_TYPE_STANDARD_COMPUTE\x10\x04\x12%\n\x1dUSAGE_COST_TYPE_CLOUD_STORAGE\x10\x05\x1a\x02\x08\x01\x12-\n)USAGE_COST_TYPE_BINARY_DATA_CLOUD_STORAGE\x10\x06\x12+\n#USAGE_COST_TYPE_OTHER_CLOUD_STORAGE\x10\x07\x1a\x02\x08\x01\x12\x1f\n\x1bUSAGE_COST_TYPE_PER_MACHINE\x10\x08\x12(\n$USAGE_COST_TYPE_TRIGGER_NOTIFICATION\x10\t\x12.\n*USAGE_COST_TYPE_TABULAR_DATA_CLOUD_STORAGE\x10\n\x120\n,USAGE_COST_TYPE_CONFIG_HISTORY_CLOUD_STORAGE\x10\x0b\x12&\n"USAGE_COST_TYPE_LOGS_CLOUD_STORAGE\x10\x0c\x12/\n+USAGE_COST_TYPE_TRAINING_LOGS_CLOUD_STORAGE\x10\r\x12*\n&USAGE_COST_TYPE_PACKAGES_CLOUD_STORAGE\x10\x0e\x12&\n"USAGE_COST_TYPE_BINARY_DATA_UPLOAD\x10\x0f\x12\'\n#USAGE_COST_TYPE_TABULAR_DATA_UPLOAD\x10\x10\x12\x1f\n\x1bUSAGE_COST_TYPE_LOGS_UPLOAD\x10\x11\x12&\n"USAGE_COST_TYPE_BINARY_DATA_EGRESS\x10\x12\x12\'\n#USAGE_COST_TYPE_TABULAR_DATA_EGRESS\x10\x13\x12\x1f\n\x1bUSAGE_COST_TYPE_LOGS_EGRESS\x10\x14\x12(\n$USAGE_COST_TYPE_TRAINING_LOGS_EGRESS\x10\x15\x127\n3USAGE_COST_TYPE_TABULAR_DATA_DATABASE_CLOUD_STORAGE\x10\x16\x121\n-USAGE_COST_TYPE_TABULAR_DATA_DATABASE_COMPUTE\x10\x17*X\n\nSourceType\x12\x1b\n\x17SOURCE_TYPE_UNSPECIFIED\x10\x00\x12\x13\n\x0fSOURCE_TYPE_ORG\x10\x01\x12\x18\n\x14SOURCE_TYPE_FRAGMENT\x10\x022\xb2\x06\n\x0eBillingService\x12k\n\x14GetCurrentMonthUsage\x12(.viam.app.v1.GetCurrentMonthUsageRequest\x1a).viam.app.v1.GetCurrentMonthUsageResponse\x12w\n\x18GetOrgBillingInformation\x12,.viam.app.v1.GetOrgBillingInformationRequest\x1a-.viam.app.v1.GetOrgBillingInformationResponse\x12e\n\x12GetInvoicesSummary\x12&.viam.app.v1.GetInvoicesSummaryRequest\x1a\'.viam.app.v1.GetInvoicesSummaryResponse\x12X\n\rGetInvoicePdf\x12!.viam.app.v1.GetInvoicePdfRequest\x1a".viam.app.v1.GetInvoicePdfResponse0\x01\x12w\n\x18SendPaymentRequiredEmail\x12,.viam.app.v1.SendPaymentRequiredEmailRequest\x1a-.viam.app.v1.SendPaymentRequiredEmailResponse\x12w\n\x18GetAvailableBillingTiers\x12,.viam.app.v1.GetAvailableBillingTiersRequest\x1a-.viam.app.v1.GetAvailableBillingTiersResponse\x12\x86\x01\n\x1dUpdateOrganizationBillingTier\x121.viam.app.v1.UpdateOrganizationBillingTierRequest\x1a2.viam.app.v1.UpdateOrganizationBillingTierResponseB\x18Z\x16go.viam.com/api/app/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.v1.billing_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x16go.viam.com/api/app/v1' + _globals['_USAGECOSTTYPE'].values_by_name['USAGE_COST_TYPE_DATA_UPLOAD']._loaded_options = None + _globals['_USAGECOSTTYPE'].values_by_name['USAGE_COST_TYPE_DATA_UPLOAD']._serialized_options = b'\x08\x01' + _globals['_USAGECOSTTYPE'].values_by_name['USAGE_COST_TYPE_DATA_EGRESS']._loaded_options = None + _globals['_USAGECOSTTYPE'].values_by_name['USAGE_COST_TYPE_DATA_EGRESS']._serialized_options = b'\x08\x01' + _globals['_USAGECOSTTYPE'].values_by_name['USAGE_COST_TYPE_CLOUD_STORAGE']._loaded_options = None + _globals['_USAGECOSTTYPE'].values_by_name['USAGE_COST_TYPE_CLOUD_STORAGE']._serialized_options = b'\x08\x01' + _globals['_USAGECOSTTYPE'].values_by_name['USAGE_COST_TYPE_OTHER_CLOUD_STORAGE']._loaded_options = None + _globals['_USAGECOSTTYPE'].values_by_name['USAGE_COST_TYPE_OTHER_CLOUD_STORAGE']._serialized_options = b'\x08\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['cloud_storage_usage_cost']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['cloud_storage_usage_cost']._serialized_options = b'\x18\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['data_upload_usage_cost']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['data_upload_usage_cost']._serialized_options = b'\x18\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['data_egres_usage_cost']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['data_egres_usage_cost']._serialized_options = b'\x18\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['remote_control_usage_cost']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['remote_control_usage_cost']._serialized_options = b'\x18\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['standard_compute_usage_cost']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['standard_compute_usage_cost']._serialized_options = b'\x18\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['discount_amount']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['discount_amount']._serialized_options = b'\x18\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['total_usage_with_discount']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['total_usage_with_discount']._serialized_options = b'\x18\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['total_usage_without_discount']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['total_usage_without_discount']._serialized_options = b'\x18\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['per_machine_usage_cost']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['per_machine_usage_cost']._serialized_options = b'\x18\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['binary_data_cloud_storage_usage_cost']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['binary_data_cloud_storage_usage_cost']._serialized_options = b'\x18\x01' + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['other_cloud_storage_usage_cost']._loaded_options = None + _globals['_GETCURRENTMONTHUSAGERESPONSE'].fields_by_name['other_cloud_storage_usage_cost']._serialized_options = b'\x18\x01' + _globals['_PAYMENTMETHODTYPE']._serialized_start = 2989 + _globals['_PAYMENTMETHODTYPE']._serialized_end = 3075 + _globals['_USAGECOSTTYPE']._serialized_start = 3078 + _globals['_USAGECOSTTYPE']._serialized_end = 4089 + _globals['_SOURCETYPE']._serialized_start = 4091 + _globals['_SOURCETYPE']._serialized_end = 4179 + _globals['_INVOICESUMMARY']._serialized_start = 71 + _globals['_INVOICESUMMARY']._serialized_end = 341 + _globals['_PAYMENTMETHODCARD']._serialized_start = 343 + _globals['_PAYMENTMETHODCARD']._serialized_end = 426 + _globals['_GETCURRENTMONTHUSAGEREQUEST']._serialized_start = 428 + _globals['_GETCURRENTMONTHUSAGEREQUEST']._serialized_end = 480 + _globals['_USAGECOST']._serialized_start = 482 + _globals['_USAGECOST']._serialized_end = 578 + _globals['_RESOURCEUSAGECOSTSBYSOURCE']._serialized_start = 581 + _globals['_RESOURCEUSAGECOSTSBYSOURCE']._serialized_end = 779 + _globals['_RESOURCEUSAGECOSTS']._serialized_start = 782 + _globals['_RESOURCEUSAGECOSTS']._serialized_end = 989 + _globals['_GETCURRENTMONTHUSAGERESPONSE']._serialized_start = 992 + _globals['_GETCURRENTMONTHUSAGERESPONSE']._serialized_end = 1965 + _globals['_GETORGBILLINGINFORMATIONREQUEST']._serialized_start = 1967 + _globals['_GETORGBILLINGINFORMATIONREQUEST']._serialized_end = 2023 + _globals['_GETORGBILLINGINFORMATIONRESPONSE']._serialized_start = 2026 + _globals['_GETORGBILLINGINFORMATIONRESPONSE']._serialized_end = 2278 + _globals['_GETINVOICESSUMMARYREQUEST']._serialized_start = 2280 + _globals['_GETINVOICESSUMMARYREQUEST']._serialized_end = 2330 + _globals['_GETINVOICESSUMMARYRESPONSE']._serialized_start = 2333 + _globals['_GETINVOICESSUMMARYRESPONSE']._serialized_end = 2467 + _globals['_GETINVOICEPDFREQUEST']._serialized_start = 2469 + _globals['_GETINVOICEPDFREQUEST']._serialized_end = 2530 + _globals['_GETINVOICEPDFRESPONSE']._serialized_start = 2532 + _globals['_GETINVOICEPDFRESPONSE']._serialized_end = 2577 + _globals['_SENDPAYMENTREQUIREDEMAILREQUEST']._serialized_start = 2579 + _globals['_SENDPAYMENTREQUIREDEMAILREQUEST']._serialized_end = 2701 + _globals['_SENDPAYMENTREQUIREDEMAILRESPONSE']._serialized_start = 2703 + _globals['_SENDPAYMENTREQUIREDEMAILRESPONSE']._serialized_end = 2737 + _globals['_GETAVAILABLEBILLINGTIERSREQUEST']._serialized_start = 2739 + _globals['_GETAVAILABLEBILLINGTIERSREQUEST']._serialized_end = 2772 + _globals['_GETAVAILABLEBILLINGTIERSRESPONSE']._serialized_start = 2774 + _globals['_GETAVAILABLEBILLINGTIERSRESPONSE']._serialized_end = 2830 + _globals['_UPDATEORGANIZATIONBILLINGTIERREQUEST']._serialized_start = 2832 + _globals['_UPDATEORGANIZATIONBILLINGTIERREQUEST']._serialized_end = 2946 + _globals['_UPDATEORGANIZATIONBILLINGTIERRESPONSE']._serialized_start = 2948 + _globals['_UPDATEORGANIZATIONBILLINGTIERRESPONSE']._serialized_end = 2987 + _globals['_BILLINGSERVICE']._serialized_start = 4182 + _globals['_BILLINGSERVICE']._serialized_end = 5000 \ No newline at end of file diff --git a/src/viam/gen/app/v1/billing_pb2.pyi b/src/viam/gen/app/v1/billing_pb2.pyi new file mode 100644 index 000000000..5ddde4be5 --- /dev/null +++ b/src/viam/gen/app/v1/billing_pb2.pyi @@ -0,0 +1,463 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import google.protobuf.timestamp_pb2 +import sys +import typing +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _PaymentMethodType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PaymentMethodTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PaymentMethodType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PAYMENT_METHOD_TYPE_UNSPECIFIED: _PaymentMethodType.ValueType + PAYMENT_METHOD_TYPE_CARD: _PaymentMethodType.ValueType + +class PaymentMethodType(_PaymentMethodType, metaclass=_PaymentMethodTypeEnumTypeWrapper): + ... +PAYMENT_METHOD_TYPE_UNSPECIFIED: PaymentMethodType.ValueType +PAYMENT_METHOD_TYPE_CARD: PaymentMethodType.ValueType +global___PaymentMethodType = PaymentMethodType + +class _UsageCostType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _UsageCostTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_UsageCostType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + USAGE_COST_TYPE_UNSPECIFIED: _UsageCostType.ValueType + USAGE_COST_TYPE_DATA_UPLOAD: _UsageCostType.ValueType + USAGE_COST_TYPE_DATA_EGRESS: _UsageCostType.ValueType + USAGE_COST_TYPE_REMOTE_CONTROL: _UsageCostType.ValueType + USAGE_COST_TYPE_STANDARD_COMPUTE: _UsageCostType.ValueType + USAGE_COST_TYPE_CLOUD_STORAGE: _UsageCostType.ValueType + USAGE_COST_TYPE_BINARY_DATA_CLOUD_STORAGE: _UsageCostType.ValueType + USAGE_COST_TYPE_OTHER_CLOUD_STORAGE: _UsageCostType.ValueType + USAGE_COST_TYPE_PER_MACHINE: _UsageCostType.ValueType + USAGE_COST_TYPE_TRIGGER_NOTIFICATION: _UsageCostType.ValueType + USAGE_COST_TYPE_TABULAR_DATA_CLOUD_STORAGE: _UsageCostType.ValueType + USAGE_COST_TYPE_CONFIG_HISTORY_CLOUD_STORAGE: _UsageCostType.ValueType + USAGE_COST_TYPE_LOGS_CLOUD_STORAGE: _UsageCostType.ValueType + USAGE_COST_TYPE_TRAINING_LOGS_CLOUD_STORAGE: _UsageCostType.ValueType + USAGE_COST_TYPE_PACKAGES_CLOUD_STORAGE: _UsageCostType.ValueType + USAGE_COST_TYPE_BINARY_DATA_UPLOAD: _UsageCostType.ValueType + USAGE_COST_TYPE_TABULAR_DATA_UPLOAD: _UsageCostType.ValueType + USAGE_COST_TYPE_LOGS_UPLOAD: _UsageCostType.ValueType + USAGE_COST_TYPE_BINARY_DATA_EGRESS: _UsageCostType.ValueType + USAGE_COST_TYPE_TABULAR_DATA_EGRESS: _UsageCostType.ValueType + USAGE_COST_TYPE_LOGS_EGRESS: _UsageCostType.ValueType + USAGE_COST_TYPE_TRAINING_LOGS_EGRESS: _UsageCostType.ValueType + USAGE_COST_TYPE_TABULAR_DATA_DATABASE_CLOUD_STORAGE: _UsageCostType.ValueType + USAGE_COST_TYPE_TABULAR_DATA_DATABASE_COMPUTE: _UsageCostType.ValueType + +class UsageCostType(_UsageCostType, metaclass=_UsageCostTypeEnumTypeWrapper): + ... +USAGE_COST_TYPE_UNSPECIFIED: UsageCostType.ValueType +USAGE_COST_TYPE_DATA_UPLOAD: UsageCostType.ValueType +USAGE_COST_TYPE_DATA_EGRESS: UsageCostType.ValueType +USAGE_COST_TYPE_REMOTE_CONTROL: UsageCostType.ValueType +USAGE_COST_TYPE_STANDARD_COMPUTE: UsageCostType.ValueType +USAGE_COST_TYPE_CLOUD_STORAGE: UsageCostType.ValueType +USAGE_COST_TYPE_BINARY_DATA_CLOUD_STORAGE: UsageCostType.ValueType +USAGE_COST_TYPE_OTHER_CLOUD_STORAGE: UsageCostType.ValueType +USAGE_COST_TYPE_PER_MACHINE: UsageCostType.ValueType +USAGE_COST_TYPE_TRIGGER_NOTIFICATION: UsageCostType.ValueType +USAGE_COST_TYPE_TABULAR_DATA_CLOUD_STORAGE: UsageCostType.ValueType +USAGE_COST_TYPE_CONFIG_HISTORY_CLOUD_STORAGE: UsageCostType.ValueType +USAGE_COST_TYPE_LOGS_CLOUD_STORAGE: UsageCostType.ValueType +USAGE_COST_TYPE_TRAINING_LOGS_CLOUD_STORAGE: UsageCostType.ValueType +USAGE_COST_TYPE_PACKAGES_CLOUD_STORAGE: UsageCostType.ValueType +USAGE_COST_TYPE_BINARY_DATA_UPLOAD: UsageCostType.ValueType +USAGE_COST_TYPE_TABULAR_DATA_UPLOAD: UsageCostType.ValueType +USAGE_COST_TYPE_LOGS_UPLOAD: UsageCostType.ValueType +USAGE_COST_TYPE_BINARY_DATA_EGRESS: UsageCostType.ValueType +USAGE_COST_TYPE_TABULAR_DATA_EGRESS: UsageCostType.ValueType +USAGE_COST_TYPE_LOGS_EGRESS: UsageCostType.ValueType +USAGE_COST_TYPE_TRAINING_LOGS_EGRESS: UsageCostType.ValueType +USAGE_COST_TYPE_TABULAR_DATA_DATABASE_CLOUD_STORAGE: UsageCostType.ValueType +USAGE_COST_TYPE_TABULAR_DATA_DATABASE_COMPUTE: UsageCostType.ValueType +global___UsageCostType = UsageCostType + +class _SourceType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _SourceTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_SourceType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SOURCE_TYPE_UNSPECIFIED: _SourceType.ValueType + SOURCE_TYPE_ORG: _SourceType.ValueType + SOURCE_TYPE_FRAGMENT: _SourceType.ValueType + +class SourceType(_SourceType, metaclass=_SourceTypeEnumTypeWrapper): + ... +SOURCE_TYPE_UNSPECIFIED: SourceType.ValueType +SOURCE_TYPE_ORG: SourceType.ValueType +SOURCE_TYPE_FRAGMENT: SourceType.ValueType +global___SourceType = SourceType + +@typing.final +class InvoiceSummary(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + INVOICE_DATE_FIELD_NUMBER: builtins.int + INVOICE_AMOUNT_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + DUE_DATE_FIELD_NUMBER: builtins.int + PAID_DATE_FIELD_NUMBER: builtins.int + id: builtins.str + invoice_amount: builtins.float + status: builtins.str + + @property + def invoice_date(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def due_date(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def paid_date(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, id: builtins.str=..., invoice_date: google.protobuf.timestamp_pb2.Timestamp | None=..., invoice_amount: builtins.float=..., status: builtins.str=..., due_date: google.protobuf.timestamp_pb2.Timestamp | None=..., paid_date: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['due_date', b'due_date', 'invoice_date', b'invoice_date', 'paid_date', b'paid_date']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['due_date', b'due_date', 'id', b'id', 'invoice_amount', b'invoice_amount', 'invoice_date', b'invoice_date', 'paid_date', b'paid_date', 'status', b'status']) -> None: + ... +global___InvoiceSummary = InvoiceSummary + +@typing.final +class PaymentMethodCard(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + BRAND_FIELD_NUMBER: builtins.int + LAST_FOUR_DIGITS_FIELD_NUMBER: builtins.int + brand: builtins.str + last_four_digits: builtins.str + + def __init__(self, *, brand: builtins.str=..., last_four_digits: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['brand', b'brand', 'last_four_digits', b'last_four_digits']) -> None: + ... +global___PaymentMethodCard = PaymentMethodCard + +@typing.final +class GetCurrentMonthUsageRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___GetCurrentMonthUsageRequest = GetCurrentMonthUsageRequest + +@typing.final +class UsageCost(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESOURCE_TYPE_FIELD_NUMBER: builtins.int + COST_FIELD_NUMBER: builtins.int + resource_type: global___UsageCostType.ValueType + cost: builtins.float + + def __init__(self, *, resource_type: global___UsageCostType.ValueType=..., cost: builtins.float=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['cost', b'cost', 'resource_type', b'resource_type']) -> None: + ... +global___UsageCost = UsageCost + +@typing.final +class ResourceUsageCostsBySource(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SOURCE_TYPE_FIELD_NUMBER: builtins.int + RESOURCE_USAGE_COSTS_FIELD_NUMBER: builtins.int + TIER_NAME_FIELD_NUMBER: builtins.int + source_type: global___SourceType.ValueType + tier_name: builtins.str + + @property + def resource_usage_costs(self) -> global___ResourceUsageCosts: + ... + + def __init__(self, *, source_type: global___SourceType.ValueType=..., resource_usage_costs: global___ResourceUsageCosts | None=..., tier_name: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['resource_usage_costs', b'resource_usage_costs']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['resource_usage_costs', b'resource_usage_costs', 'source_type', b'source_type', 'tier_name', b'tier_name']) -> None: + ... +global___ResourceUsageCostsBySource = ResourceUsageCostsBySource + +@typing.final +class ResourceUsageCosts(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + USAGE_COSTS_FIELD_NUMBER: builtins.int + DISCOUNT_FIELD_NUMBER: builtins.int + TOTAL_WITH_DISCOUNT_FIELD_NUMBER: builtins.int + TOTAL_WITHOUT_DISCOUNT_FIELD_NUMBER: builtins.int + discount: builtins.float + total_with_discount: builtins.float + total_without_discount: builtins.float + + @property + def usage_costs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___UsageCost]: + ... + + def __init__(self, *, usage_costs: collections.abc.Iterable[global___UsageCost] | None=..., discount: builtins.float=..., total_with_discount: builtins.float=..., total_without_discount: builtins.float=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['discount', b'discount', 'total_with_discount', b'total_with_discount', 'total_without_discount', b'total_without_discount', 'usage_costs', b'usage_costs']) -> None: + ... +global___ResourceUsageCosts = ResourceUsageCosts + +@typing.final +class GetCurrentMonthUsageResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + START_DATE_FIELD_NUMBER: builtins.int + END_DATE_FIELD_NUMBER: builtins.int + RESOURCE_USAGE_COSTS_BY_SOURCE_FIELD_NUMBER: builtins.int + SUBTOTAL_FIELD_NUMBER: builtins.int + CLOUD_STORAGE_USAGE_COST_FIELD_NUMBER: builtins.int + DATA_UPLOAD_USAGE_COST_FIELD_NUMBER: builtins.int + DATA_EGRES_USAGE_COST_FIELD_NUMBER: builtins.int + REMOTE_CONTROL_USAGE_COST_FIELD_NUMBER: builtins.int + STANDARD_COMPUTE_USAGE_COST_FIELD_NUMBER: builtins.int + DISCOUNT_AMOUNT_FIELD_NUMBER: builtins.int + TOTAL_USAGE_WITH_DISCOUNT_FIELD_NUMBER: builtins.int + TOTAL_USAGE_WITHOUT_DISCOUNT_FIELD_NUMBER: builtins.int + PER_MACHINE_USAGE_COST_FIELD_NUMBER: builtins.int + BINARY_DATA_CLOUD_STORAGE_USAGE_COST_FIELD_NUMBER: builtins.int + OTHER_CLOUD_STORAGE_USAGE_COST_FIELD_NUMBER: builtins.int + subtotal: builtins.float + cloud_storage_usage_cost: builtins.float + 'all fields below are deprecated' + data_upload_usage_cost: builtins.float + data_egres_usage_cost: builtins.float + remote_control_usage_cost: builtins.float + standard_compute_usage_cost: builtins.float + discount_amount: builtins.float + total_usage_with_discount: builtins.float + total_usage_without_discount: builtins.float + per_machine_usage_cost: builtins.float + binary_data_cloud_storage_usage_cost: builtins.float + other_cloud_storage_usage_cost: builtins.float + + @property + def start_date(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def end_date(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def resource_usage_costs_by_source(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResourceUsageCostsBySource]: + ... + + def __init__(self, *, start_date: google.protobuf.timestamp_pb2.Timestamp | None=..., end_date: google.protobuf.timestamp_pb2.Timestamp | None=..., resource_usage_costs_by_source: collections.abc.Iterable[global___ResourceUsageCostsBySource] | None=..., subtotal: builtins.float=..., cloud_storage_usage_cost: builtins.float=..., data_upload_usage_cost: builtins.float=..., data_egres_usage_cost: builtins.float=..., remote_control_usage_cost: builtins.float=..., standard_compute_usage_cost: builtins.float=..., discount_amount: builtins.float=..., total_usage_with_discount: builtins.float=..., total_usage_without_discount: builtins.float=..., per_machine_usage_cost: builtins.float=..., binary_data_cloud_storage_usage_cost: builtins.float=..., other_cloud_storage_usage_cost: builtins.float=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['end_date', b'end_date', 'start_date', b'start_date']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['binary_data_cloud_storage_usage_cost', b'binary_data_cloud_storage_usage_cost', 'cloud_storage_usage_cost', b'cloud_storage_usage_cost', 'data_egres_usage_cost', b'data_egres_usage_cost', 'data_upload_usage_cost', b'data_upload_usage_cost', 'discount_amount', b'discount_amount', 'end_date', b'end_date', 'other_cloud_storage_usage_cost', b'other_cloud_storage_usage_cost', 'per_machine_usage_cost', b'per_machine_usage_cost', 'remote_control_usage_cost', b'remote_control_usage_cost', 'resource_usage_costs_by_source', b'resource_usage_costs_by_source', 'standard_compute_usage_cost', b'standard_compute_usage_cost', 'start_date', b'start_date', 'subtotal', b'subtotal', 'total_usage_with_discount', b'total_usage_with_discount', 'total_usage_without_discount', b'total_usage_without_discount']) -> None: + ... +global___GetCurrentMonthUsageResponse = GetCurrentMonthUsageResponse + +@typing.final +class GetOrgBillingInformationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___GetOrgBillingInformationRequest = GetOrgBillingInformationRequest + +@typing.final +class GetOrgBillingInformationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TYPE_FIELD_NUMBER: builtins.int + BILLING_EMAIL_FIELD_NUMBER: builtins.int + METHOD_FIELD_NUMBER: builtins.int + BILLING_TIER_FIELD_NUMBER: builtins.int + type: global___PaymentMethodType.ValueType + billing_email: builtins.str + billing_tier: builtins.str + 'Only return billing_tier for billing dashboard admin users' + + @property + def method(self) -> global___PaymentMethodCard: + """defined if type is PAYMENT_METHOD_TYPE_CARD""" + + def __init__(self, *, type: global___PaymentMethodType.ValueType=..., billing_email: builtins.str=..., method: global___PaymentMethodCard | None=..., billing_tier: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_billing_tier', b'_billing_tier', '_method', b'_method', 'billing_tier', b'billing_tier', 'method', b'method']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_billing_tier', b'_billing_tier', '_method', b'_method', 'billing_email', b'billing_email', 'billing_tier', b'billing_tier', 'method', b'method', 'type', b'type']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_billing_tier', b'_billing_tier']) -> typing.Literal['billing_tier'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_method', b'_method']) -> typing.Literal['method'] | None: + ... +global___GetOrgBillingInformationResponse = GetOrgBillingInformationResponse + +@typing.final +class GetInvoicesSummaryRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + + def __init__(self, *, org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['org_id', b'org_id']) -> None: + ... +global___GetInvoicesSummaryRequest = GetInvoicesSummaryRequest + +@typing.final +class GetInvoicesSummaryResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + OUTSTANDING_BALANCE_FIELD_NUMBER: builtins.int + INVOICES_FIELD_NUMBER: builtins.int + outstanding_balance: builtins.float + 'all unpaid balances at the end of the last billing cycle' + + @property + def invoices(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___InvoiceSummary]: + """all previous invoices""" + + def __init__(self, *, outstanding_balance: builtins.float=..., invoices: collections.abc.Iterable[global___InvoiceSummary] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['invoices', b'invoices', 'outstanding_balance', b'outstanding_balance']) -> None: + ... +global___GetInvoicesSummaryResponse = GetInvoicesSummaryResponse + +@typing.final +class GetInvoicePdfRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + ORG_ID_FIELD_NUMBER: builtins.int + id: builtins.str + org_id: builtins.str + + def __init__(self, *, id: builtins.str=..., org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'org_id', b'org_id']) -> None: + ... +global___GetInvoicePdfRequest = GetInvoicePdfRequest + +@typing.final +class GetInvoicePdfResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CHUNK_FIELD_NUMBER: builtins.int + chunk: builtins.bytes + + def __init__(self, *, chunk: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['chunk', b'chunk']) -> None: + ... +global___GetInvoicePdfResponse = GetInvoicePdfResponse + +@typing.final +class SendPaymentRequiredEmailRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CUSTOMER_ORG_ID_FIELD_NUMBER: builtins.int + BILLING_OWNER_ORG_ID_FIELD_NUMBER: builtins.int + customer_org_id: builtins.str + billing_owner_org_id: builtins.str + + def __init__(self, *, customer_org_id: builtins.str=..., billing_owner_org_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['billing_owner_org_id', b'billing_owner_org_id', 'customer_org_id', b'customer_org_id']) -> None: + ... +global___SendPaymentRequiredEmailRequest = SendPaymentRequiredEmailRequest + +@typing.final +class SendPaymentRequiredEmailResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___SendPaymentRequiredEmailResponse = SendPaymentRequiredEmailResponse + +@typing.final +class GetAvailableBillingTiersRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___GetAvailableBillingTiersRequest = GetAvailableBillingTiersRequest + +@typing.final +class GetAvailableBillingTiersResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TIERS_FIELD_NUMBER: builtins.int + + @property + def tiers(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, tiers: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['tiers', b'tiers']) -> None: + ... +global___GetAvailableBillingTiersResponse = GetAvailableBillingTiersResponse + +@typing.final +class UpdateOrganizationBillingTierRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORGANIZATION_ID_FIELD_NUMBER: builtins.int + BILLING_TIER_FIELD_NUMBER: builtins.int + organization_id: builtins.str + billing_tier: builtins.str + + def __init__(self, *, organization_id: builtins.str=..., billing_tier: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['billing_tier', b'billing_tier', 'organization_id', b'organization_id']) -> None: + ... +global___UpdateOrganizationBillingTierRequest = UpdateOrganizationBillingTierRequest + +@typing.final +class UpdateOrganizationBillingTierResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___UpdateOrganizationBillingTierResponse = UpdateOrganizationBillingTierResponse \ No newline at end of file diff --git a/src/viam/gen/app/v1/end_user_grpc.py b/src/viam/gen/app/v1/end_user_grpc.py new file mode 100644 index 000000000..ba326b644 --- /dev/null +++ b/src/viam/gen/app/v1/end_user_grpc.py @@ -0,0 +1,59 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from ... import app + +class EndUserServiceBase(abc.ABC): + + @abc.abstractmethod + async def IsLegalAccepted(self, stream: 'grpclib.server.Stream[app.v1.end_user_pb2.IsLegalAcceptedRequest, app.v1.end_user_pb2.IsLegalAcceptedResponse]') -> None: + pass + + @abc.abstractmethod + async def AcceptLegal(self, stream: 'grpclib.server.Stream[app.v1.end_user_pb2.AcceptLegalRequest, app.v1.end_user_pb2.AcceptLegalResponse]') -> None: + pass + + @abc.abstractmethod + async def RegisterAuthApplication(self, stream: 'grpclib.server.Stream[app.v1.end_user_pb2.RegisterAuthApplicationRequest, app.v1.end_user_pb2.RegisterAuthApplicationResponse]') -> None: + pass + + @abc.abstractmethod + async def UpdateAuthApplication(self, stream: 'grpclib.server.Stream[app.v1.end_user_pb2.UpdateAuthApplicationRequest, app.v1.end_user_pb2.UpdateAuthApplicationResponse]') -> None: + pass + + @abc.abstractmethod + async def GetAuthApplication(self, stream: 'grpclib.server.Stream[app.v1.end_user_pb2.GetAuthApplicationRequest, app.v1.end_user_pb2.GetAuthApplicationResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.app.v1.EndUserService/IsLegalAccepted': grpclib.const.Handler(self.IsLegalAccepted, grpclib.const.Cardinality.UNARY_UNARY, app.v1.end_user_pb2.IsLegalAcceptedRequest, app.v1.end_user_pb2.IsLegalAcceptedResponse), '/viam.app.v1.EndUserService/AcceptLegal': grpclib.const.Handler(self.AcceptLegal, grpclib.const.Cardinality.UNARY_UNARY, app.v1.end_user_pb2.AcceptLegalRequest, app.v1.end_user_pb2.AcceptLegalResponse), '/viam.app.v1.EndUserService/RegisterAuthApplication': grpclib.const.Handler(self.RegisterAuthApplication, grpclib.const.Cardinality.UNARY_UNARY, app.v1.end_user_pb2.RegisterAuthApplicationRequest, app.v1.end_user_pb2.RegisterAuthApplicationResponse), '/viam.app.v1.EndUserService/UpdateAuthApplication': grpclib.const.Handler(self.UpdateAuthApplication, grpclib.const.Cardinality.UNARY_UNARY, app.v1.end_user_pb2.UpdateAuthApplicationRequest, app.v1.end_user_pb2.UpdateAuthApplicationResponse), '/viam.app.v1.EndUserService/GetAuthApplication': grpclib.const.Handler(self.GetAuthApplication, grpclib.const.Cardinality.UNARY_UNARY, app.v1.end_user_pb2.GetAuthApplicationRequest, app.v1.end_user_pb2.GetAuthApplicationResponse)} + +class UnimplementedEndUserServiceBase(EndUserServiceBase): + + async def IsLegalAccepted(self, stream: 'grpclib.server.Stream[app.v1.end_user_pb2.IsLegalAcceptedRequest, app.v1.end_user_pb2.IsLegalAcceptedResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def AcceptLegal(self, stream: 'grpclib.server.Stream[app.v1.end_user_pb2.AcceptLegalRequest, app.v1.end_user_pb2.AcceptLegalResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RegisterAuthApplication(self, stream: 'grpclib.server.Stream[app.v1.end_user_pb2.RegisterAuthApplicationRequest, app.v1.end_user_pb2.RegisterAuthApplicationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def UpdateAuthApplication(self, stream: 'grpclib.server.Stream[app.v1.end_user_pb2.UpdateAuthApplicationRequest, app.v1.end_user_pb2.UpdateAuthApplicationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetAuthApplication(self, stream: 'grpclib.server.Stream[app.v1.end_user_pb2.GetAuthApplicationRequest, app.v1.end_user_pb2.GetAuthApplicationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class EndUserServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.IsLegalAccepted = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.EndUserService/IsLegalAccepted', app.v1.end_user_pb2.IsLegalAcceptedRequest, app.v1.end_user_pb2.IsLegalAcceptedResponse) + self.AcceptLegal = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.EndUserService/AcceptLegal', app.v1.end_user_pb2.AcceptLegalRequest, app.v1.end_user_pb2.AcceptLegalResponse) + self.RegisterAuthApplication = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.EndUserService/RegisterAuthApplication', app.v1.end_user_pb2.RegisterAuthApplicationRequest, app.v1.end_user_pb2.RegisterAuthApplicationResponse) + self.UpdateAuthApplication = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.EndUserService/UpdateAuthApplication', app.v1.end_user_pb2.UpdateAuthApplicationRequest, app.v1.end_user_pb2.UpdateAuthApplicationResponse) + self.GetAuthApplication = grpclib.client.UnaryUnaryMethod(channel, '/viam.app.v1.EndUserService/GetAuthApplication', app.v1.end_user_pb2.GetAuthApplicationRequest, app.v1.end_user_pb2.GetAuthApplicationResponse) \ No newline at end of file diff --git a/src/viam/gen/app/v1/end_user_pb2.py b/src/viam/gen/app/v1/end_user_pb2.py new file mode 100644 index 000000000..5ee6dd8a5 --- /dev/null +++ b/src/viam/gen/app/v1/end_user_pb2.py @@ -0,0 +1,55 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/v1/end_user.proto') +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15app/v1/end_user.proto\x12\x0bviam.app.v1"\x18\n\x16IsLegalAcceptedRequest"@\n\x17IsLegalAcceptedResponse\x12%\n\x0eaccepted_legal\x18\x01 \x01(\x08R\racceptedLegal"\x14\n\x12AcceptLegalRequest"\x15\n\x13AcceptLegalResponse"\xcb\x01\n\x1eRegisterAuthApplicationRequest\x12)\n\x10application_name\x18\x01 \x01(\tR\x0fapplicationName\x12\x15\n\x06org_id\x18\x02 \x01(\tR\x05orgId\x12\x1f\n\x0borigin_uris\x18\x03 \x03(\tR\noriginUris\x12#\n\rredirect_uris\x18\x04 \x03(\tR\x0credirectUris\x12\x1d\n\nlogout_uri\x18\x05 \x01(\tR\tlogoutUri:\x02\x18\x01"\x9c\x01\n\x1fRegisterAuthApplicationResponse\x12%\n\x0eapplication_id\x18\x01 \x01(\tR\rapplicationId\x12)\n\x10application_name\x18\x02 \x01(\tR\x0fapplicationName\x12#\n\rclient_secret\x18\x03 \x01(\tR\x0cclientSecret:\x02\x18\x01"\xf0\x01\n\x1cUpdateAuthApplicationRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12%\n\x0eapplication_id\x18\x02 \x01(\tR\rapplicationId\x12)\n\x10application_name\x18\x03 \x01(\tR\x0fapplicationName\x12\x1f\n\x0borigin_uris\x18\x04 \x03(\tR\noriginUris\x12#\n\rredirect_uris\x18\x05 \x03(\tR\x0credirectUris\x12\x1d\n\nlogout_uri\x18\x06 \x01(\tR\tlogoutUri:\x02\x18\x01"u\n\x1dUpdateAuthApplicationResponse\x12%\n\x0eapplication_id\x18\x01 \x01(\tR\rapplicationId\x12)\n\x10application_name\x18\x02 \x01(\tR\x0fapplicationName:\x02\x18\x01"]\n\x19GetAuthApplicationRequest\x12\x15\n\x06org_id\x18\x01 \x01(\tR\x05orgId\x12%\n\x0eapplication_id\x18\x02 \x01(\tR\rapplicationId:\x02\x18\x01"\xfc\x01\n\x1aGetAuthApplicationResponse\x12%\n\x0eapplication_id\x18\x01 \x01(\tR\rapplicationId\x12)\n\x10application_name\x18\x02 \x01(\tR\x0fapplicationName\x12#\n\rclient_secret\x18\x03 \x01(\tR\x0cclientSecret\x12\x1f\n\x0borigin_uris\x18\x04 \x03(\tR\noriginUris\x12#\n\rredirect_uris\x18\x05 \x03(\tR\x0credirectUris\x12\x1d\n\nlogout_uri\x18\x06 \x01(\tR\tlogoutUri:\x02\x18\x012\x9c\x04\n\x0eEndUserService\x12\\\n\x0fIsLegalAccepted\x12#.viam.app.v1.IsLegalAcceptedRequest\x1a$.viam.app.v1.IsLegalAcceptedResponse\x12P\n\x0bAcceptLegal\x12\x1f.viam.app.v1.AcceptLegalRequest\x1a .viam.app.v1.AcceptLegalResponse\x12y\n\x17RegisterAuthApplication\x12+.viam.app.v1.RegisterAuthApplicationRequest\x1a,.viam.app.v1.RegisterAuthApplicationResponse"\x03\x88\x02\x01\x12s\n\x15UpdateAuthApplication\x12).viam.app.v1.UpdateAuthApplicationRequest\x1a*.viam.app.v1.UpdateAuthApplicationResponse"\x03\x88\x02\x01\x12j\n\x12GetAuthApplication\x12&.viam.app.v1.GetAuthApplicationRequest\x1a\'.viam.app.v1.GetAuthApplicationResponse"\x03\x88\x02\x01B\x18Z\x16go.viam.com/api/app/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.v1.end_user_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x16go.viam.com/api/app/v1' + _globals['_REGISTERAUTHAPPLICATIONREQUEST']._loaded_options = None + _globals['_REGISTERAUTHAPPLICATIONREQUEST']._serialized_options = b'\x18\x01' + _globals['_REGISTERAUTHAPPLICATIONRESPONSE']._loaded_options = None + _globals['_REGISTERAUTHAPPLICATIONRESPONSE']._serialized_options = b'\x18\x01' + _globals['_UPDATEAUTHAPPLICATIONREQUEST']._loaded_options = None + _globals['_UPDATEAUTHAPPLICATIONREQUEST']._serialized_options = b'\x18\x01' + _globals['_UPDATEAUTHAPPLICATIONRESPONSE']._loaded_options = None + _globals['_UPDATEAUTHAPPLICATIONRESPONSE']._serialized_options = b'\x18\x01' + _globals['_GETAUTHAPPLICATIONREQUEST']._loaded_options = None + _globals['_GETAUTHAPPLICATIONREQUEST']._serialized_options = b'\x18\x01' + _globals['_GETAUTHAPPLICATIONRESPONSE']._loaded_options = None + _globals['_GETAUTHAPPLICATIONRESPONSE']._serialized_options = b'\x18\x01' + _globals['_ENDUSERSERVICE'].methods_by_name['RegisterAuthApplication']._loaded_options = None + _globals['_ENDUSERSERVICE'].methods_by_name['RegisterAuthApplication']._serialized_options = b'\x88\x02\x01' + _globals['_ENDUSERSERVICE'].methods_by_name['UpdateAuthApplication']._loaded_options = None + _globals['_ENDUSERSERVICE'].methods_by_name['UpdateAuthApplication']._serialized_options = b'\x88\x02\x01' + _globals['_ENDUSERSERVICE'].methods_by_name['GetAuthApplication']._loaded_options = None + _globals['_ENDUSERSERVICE'].methods_by_name['GetAuthApplication']._serialized_options = b'\x88\x02\x01' + _globals['_ISLEGALACCEPTEDREQUEST']._serialized_start = 38 + _globals['_ISLEGALACCEPTEDREQUEST']._serialized_end = 62 + _globals['_ISLEGALACCEPTEDRESPONSE']._serialized_start = 64 + _globals['_ISLEGALACCEPTEDRESPONSE']._serialized_end = 128 + _globals['_ACCEPTLEGALREQUEST']._serialized_start = 130 + _globals['_ACCEPTLEGALREQUEST']._serialized_end = 150 + _globals['_ACCEPTLEGALRESPONSE']._serialized_start = 152 + _globals['_ACCEPTLEGALRESPONSE']._serialized_end = 173 + _globals['_REGISTERAUTHAPPLICATIONREQUEST']._serialized_start = 176 + _globals['_REGISTERAUTHAPPLICATIONREQUEST']._serialized_end = 379 + _globals['_REGISTERAUTHAPPLICATIONRESPONSE']._serialized_start = 382 + _globals['_REGISTERAUTHAPPLICATIONRESPONSE']._serialized_end = 538 + _globals['_UPDATEAUTHAPPLICATIONREQUEST']._serialized_start = 541 + _globals['_UPDATEAUTHAPPLICATIONREQUEST']._serialized_end = 781 + _globals['_UPDATEAUTHAPPLICATIONRESPONSE']._serialized_start = 783 + _globals['_UPDATEAUTHAPPLICATIONRESPONSE']._serialized_end = 900 + _globals['_GETAUTHAPPLICATIONREQUEST']._serialized_start = 902 + _globals['_GETAUTHAPPLICATIONREQUEST']._serialized_end = 995 + _globals['_GETAUTHAPPLICATIONRESPONSE']._serialized_start = 998 + _globals['_GETAUTHAPPLICATIONRESPONSE']._serialized_end = 1250 + _globals['_ENDUSERSERVICE']._serialized_start = 1253 + _globals['_ENDUSERSERVICE']._serialized_end = 1793 \ No newline at end of file diff --git a/src/viam/gen/app/v1/end_user_pb2.pyi b/src/viam/gen/app/v1/end_user_pb2.pyi new file mode 100644 index 000000000..350e74d23 --- /dev/null +++ b/src/viam/gen/app/v1/end_user_pb2.pyi @@ -0,0 +1,181 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class IsLegalAcceptedRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___IsLegalAcceptedRequest = IsLegalAcceptedRequest + +@typing.final +class IsLegalAcceptedResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ACCEPTED_LEGAL_FIELD_NUMBER: builtins.int + accepted_legal: builtins.bool + 'If false, the user should not be able to use the application.' + + def __init__(self, *, accepted_legal: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['accepted_legal', b'accepted_legal']) -> None: + ... +global___IsLegalAcceptedResponse = IsLegalAcceptedResponse + +@typing.final +class AcceptLegalRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___AcceptLegalRequest = AcceptLegalRequest + +@typing.final +class AcceptLegalResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___AcceptLegalResponse = AcceptLegalResponse + +@typing.final +class RegisterAuthApplicationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + APPLICATION_NAME_FIELD_NUMBER: builtins.int + ORG_ID_FIELD_NUMBER: builtins.int + ORIGIN_URIS_FIELD_NUMBER: builtins.int + REDIRECT_URIS_FIELD_NUMBER: builtins.int + LOGOUT_URI_FIELD_NUMBER: builtins.int + application_name: builtins.str + org_id: builtins.str + logout_uri: builtins.str + + @property + def origin_uris(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def redirect_uris(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, application_name: builtins.str=..., org_id: builtins.str=..., origin_uris: collections.abc.Iterable[builtins.str] | None=..., redirect_uris: collections.abc.Iterable[builtins.str] | None=..., logout_uri: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['application_name', b'application_name', 'logout_uri', b'logout_uri', 'org_id', b'org_id', 'origin_uris', b'origin_uris', 'redirect_uris', b'redirect_uris']) -> None: + ... +global___RegisterAuthApplicationRequest = RegisterAuthApplicationRequest + +@typing.final +class RegisterAuthApplicationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + APPLICATION_ID_FIELD_NUMBER: builtins.int + APPLICATION_NAME_FIELD_NUMBER: builtins.int + CLIENT_SECRET_FIELD_NUMBER: builtins.int + application_id: builtins.str + application_name: builtins.str + client_secret: builtins.str + + def __init__(self, *, application_id: builtins.str=..., application_name: builtins.str=..., client_secret: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['application_id', b'application_id', 'application_name', b'application_name', 'client_secret', b'client_secret']) -> None: + ... +global___RegisterAuthApplicationResponse = RegisterAuthApplicationResponse + +@typing.final +class UpdateAuthApplicationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + APPLICATION_ID_FIELD_NUMBER: builtins.int + APPLICATION_NAME_FIELD_NUMBER: builtins.int + ORIGIN_URIS_FIELD_NUMBER: builtins.int + REDIRECT_URIS_FIELD_NUMBER: builtins.int + LOGOUT_URI_FIELD_NUMBER: builtins.int + org_id: builtins.str + application_id: builtins.str + application_name: builtins.str + logout_uri: builtins.str + + @property + def origin_uris(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def redirect_uris(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, org_id: builtins.str=..., application_id: builtins.str=..., application_name: builtins.str=..., origin_uris: collections.abc.Iterable[builtins.str] | None=..., redirect_uris: collections.abc.Iterable[builtins.str] | None=..., logout_uri: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['application_id', b'application_id', 'application_name', b'application_name', 'logout_uri', b'logout_uri', 'org_id', b'org_id', 'origin_uris', b'origin_uris', 'redirect_uris', b'redirect_uris']) -> None: + ... +global___UpdateAuthApplicationRequest = UpdateAuthApplicationRequest + +@typing.final +class UpdateAuthApplicationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + APPLICATION_ID_FIELD_NUMBER: builtins.int + APPLICATION_NAME_FIELD_NUMBER: builtins.int + application_id: builtins.str + application_name: builtins.str + + def __init__(self, *, application_id: builtins.str=..., application_name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['application_id', b'application_id', 'application_name', b'application_name']) -> None: + ... +global___UpdateAuthApplicationResponse = UpdateAuthApplicationResponse + +@typing.final +class GetAuthApplicationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORG_ID_FIELD_NUMBER: builtins.int + APPLICATION_ID_FIELD_NUMBER: builtins.int + org_id: builtins.str + application_id: builtins.str + + def __init__(self, *, org_id: builtins.str=..., application_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['application_id', b'application_id', 'org_id', b'org_id']) -> None: + ... +global___GetAuthApplicationRequest = GetAuthApplicationRequest + +@typing.final +class GetAuthApplicationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + APPLICATION_ID_FIELD_NUMBER: builtins.int + APPLICATION_NAME_FIELD_NUMBER: builtins.int + CLIENT_SECRET_FIELD_NUMBER: builtins.int + ORIGIN_URIS_FIELD_NUMBER: builtins.int + REDIRECT_URIS_FIELD_NUMBER: builtins.int + LOGOUT_URI_FIELD_NUMBER: builtins.int + application_id: builtins.str + application_name: builtins.str + client_secret: builtins.str + logout_uri: builtins.str + + @property + def origin_uris(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def redirect_uris(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, application_id: builtins.str=..., application_name: builtins.str=..., client_secret: builtins.str=..., origin_uris: collections.abc.Iterable[builtins.str] | None=..., redirect_uris: collections.abc.Iterable[builtins.str] | None=..., logout_uri: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['application_id', b'application_id', 'application_name', b'application_name', 'client_secret', b'client_secret', 'logout_uri', b'logout_uri', 'origin_uris', b'origin_uris', 'redirect_uris', b'redirect_uris']) -> None: + ... +global___GetAuthApplicationResponse = GetAuthApplicationResponse \ No newline at end of file diff --git a/src/viam/gen/app/v1/robot_grpc.py b/src/viam/gen/app/v1/robot_grpc.py index 3608ade05..62cc13f4e 100644 --- a/src/viam/gen/app/v1/robot_grpc.py +++ b/src/viam/gen/app/v1/robot_grpc.py @@ -2,12 +2,14 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server -from ... import app +from ... import common import google.protobuf.duration_pb2 import google.protobuf.struct_pb2 from ... import tagger +from ... import app class RobotServiceBase(abc.ABC): @@ -30,6 +32,20 @@ async def NeedsRestart(self, stream: 'grpclib.server.Stream[app.v1.robot_pb2.Nee def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: return {'/viam.app.v1.RobotService/Config': grpclib.const.Handler(self.Config, grpclib.const.Cardinality.UNARY_UNARY, app.v1.robot_pb2.ConfigRequest, app.v1.robot_pb2.ConfigResponse), '/viam.app.v1.RobotService/Certificate': grpclib.const.Handler(self.Certificate, grpclib.const.Cardinality.UNARY_UNARY, app.v1.robot_pb2.CertificateRequest, app.v1.robot_pb2.CertificateResponse), '/viam.app.v1.RobotService/Log': grpclib.const.Handler(self.Log, grpclib.const.Cardinality.UNARY_UNARY, app.v1.robot_pb2.LogRequest, app.v1.robot_pb2.LogResponse), '/viam.app.v1.RobotService/NeedsRestart': grpclib.const.Handler(self.NeedsRestart, grpclib.const.Cardinality.UNARY_UNARY, app.v1.robot_pb2.NeedsRestartRequest, app.v1.robot_pb2.NeedsRestartResponse)} +class UnimplementedRobotServiceBase(RobotServiceBase): + + async def Config(self, stream: 'grpclib.server.Stream[app.v1.robot_pb2.ConfigRequest, app.v1.robot_pb2.ConfigResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Certificate(self, stream: 'grpclib.server.Stream[app.v1.robot_pb2.CertificateRequest, app.v1.robot_pb2.CertificateResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Log(self, stream: 'grpclib.server.Stream[app.v1.robot_pb2.LogRequest, app.v1.robot_pb2.LogResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def NeedsRestart(self, stream: 'grpclib.server.Stream[app.v1.robot_pb2.NeedsRestartRequest, app.v1.robot_pb2.NeedsRestartResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + class RobotServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: diff --git a/src/viam/gen/app/v1/robot_pb2.py b/src/viam/gen/app/v1/robot_pb2.py index 186ddfd2a..a26f56ea5 100644 --- a/src/viam/gen/app/v1/robot_pb2.py +++ b/src/viam/gen/app/v1/robot_pb2.py @@ -1,90 +1,127 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'app/v1/robot.proto') _sym_db = _symbol_database.Default() -from ...app.v1 import app_pb2 as app_dot_v1_dot_app__pb2 +from ...common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from ...tagger.v1 import tagger_pb2 as tagger_dot_v1_dot_tagger__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12app/v1/robot.proto\x12\x0bviam.app.v1\x1a\x10app/v1/app.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x16tagger/v1/tagger.proto"\xc9\x03\n\x0bRobotConfig\x12.\n\x05cloud\x18\x01 \x01(\x0b2\x18.viam.app.v1.CloudConfigR\x05cloud\x123\n\x07remotes\x18\x02 \x03(\x0b2\x19.viam.app.v1.RemoteConfigR\x07remotes\x12<\n\ncomponents\x18\x03 \x03(\x0b2\x1c.viam.app.v1.ComponentConfigR\ncomponents\x128\n\tprocesses\x18\x04 \x03(\x0b2\x1a.viam.app.v1.ProcessConfigR\tprocesses\x126\n\x08services\x18\x05 \x03(\x0b2\x1a.viam.app.v1.ServiceConfigR\x08services\x129\n\x07network\x18\x06 \x01(\x0b2\x1a.viam.app.v1.NetworkConfigH\x00R\x07network\x88\x01\x01\x120\n\x04auth\x18\x07 \x01(\x0b2\x17.viam.app.v1.AuthConfigH\x01R\x04auth\x88\x01\x01\x12\x19\n\x05debug\x18\x08 \x01(\x08H\x02R\x05debug\x88\x01\x01B\n\n\x08_networkB\x07\n\x05_authB\x08\n\x06_debug"\x8c\x02\n\x0bCloudConfig\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04fqdn\x18\x02 \x01(\tR\x04fqdn\x12\x1d\n\nlocal_fqdn\x18\x03 \x01(\tR\tlocalFqdn\x12\x1d\n\nmanaged_by\x18\x04 \x01(\tR\tmanagedBy\x12+\n\x11signaling_address\x18\x05 \x01(\tR\x10signalingAddress\x12-\n\x12signaling_insecure\x18\x06 \x01(\x08R\x11signalingInsecure\x12\'\n\x0flocation_secret\x18\x07 \x01(\tR\x0elocationSecret\x12\x16\n\x06secret\x18\x08 \x01(\tR\x06secret"\xdd\x02\n\x0fComponentConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1c\n\tnamespace\x18\x02 \x01(\tR\tnamespace\x12\x12\n\x04type\x18\x03 \x01(\tR\x04type\x12\x14\n\x05model\x18\x04 \x01(\tR\x05model\x12(\n\x05frame\x18\x05 \x01(\x0b2\x12.viam.app.v1.FrameR\x05frame\x12\x1d\n\ndepends_on\x18\x06 \x03(\tR\tdependsOn\x12l\n\x0fservice_configs\x18\x07 \x03(\x0b2\'.viam.app.v1.ResourceLevelServiceConfigB\x1a\x9a\x84\x9e\x03\x15json:"service_config"R\x0eserviceConfigs\x127\n\nattributes\x18\x08 \x01(\x0b2\x17.google.protobuf.StructR\nattributes"i\n\x1aResourceLevelServiceConfig\x12\x12\n\x04type\x18\x01 \x01(\tR\x04type\x127\n\nattributes\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\nattributes"\x86\x01\n\rProcessConfig\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n\x04args\x18\x03 \x03(\tR\x04args\x12\x10\n\x03cwd\x18\x04 \x01(\tR\x03cwd\x12\x19\n\x08one_shot\x18\x05 \x01(\x08R\x07oneShot\x12\x10\n\x03log\x18\x06 \x01(\x08R\x03log"\x8e\x01\n\rServiceConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1c\n\tnamespace\x18\x02 \x01(\tR\tnamespace\x12\x12\n\x04type\x18\x03 \x01(\tR\x04type\x127\n\nattributes\x18\x04 \x01(\x0b2\x17.google.protobuf.StructR\nattributes"\x8c\x01\n\rNetworkConfig\x12\x12\n\x04fqdn\x18\x01 \x01(\tR\x04fqdn\x12!\n\x0cbind_address\x18\x02 \x01(\tR\x0bbindAddress\x12"\n\rtls_cert_file\x18\x03 \x01(\tR\x0btlsCertFile\x12 \n\x0ctls_key_file\x18\x04 \x01(\tR\ntlsKeyFile"t\n\nAuthConfig\x12:\n\x08handlers\x18\x01 \x03(\x0b2\x1e.viam.app.v1.AuthHandlerConfigR\x08handlers\x12*\n\x11tls_auth_entities\x18\x02 \x03(\tR\x0ftlsAuthEntities"v\n\x11AuthHandlerConfig\x120\n\x04type\x18\x01 \x01(\x0e2\x1c.viam.app.v1.CredentialsTypeR\x04type\x12/\n\x06config\x18\x05 \x01(\x0b2\x17.google.protobuf.StructR\x06config"\x97\x01\n\x05Frame\x12\x16\n\x06parent\x18\x01 \x01(\tR\x06parent\x12:\n\x0btranslation\x18\x02 \x01(\x0b2\x18.viam.app.v1.TranslationR\x0btranslation\x12:\n\x0borientation\x18\x03 \x01(\x0b2\x18.viam.app.v1.OrientationR\x0borientation"7\n\x0bTranslation\x12\x0c\n\x01x\x18\x01 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x02 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x03 \x01(\x01R\x01z"\xd0\x07\n\x0bOrientation\x12O\n\x0eno_orientation\x18\x01 \x01(\x0b2&.viam.app.v1.Orientation.NoOrientationH\x00R\rnoOrientation\x12Z\n\x0evector_radians\x18\x02 \x01(\x0b21.viam.app.v1.Orientation.OrientationVectorRadiansH\x00R\rvectorRadians\x12Z\n\x0evector_degrees\x18\x03 \x01(\x0b21.viam.app.v1.Orientation.OrientationVectorDegreesH\x00R\rvectorDegrees\x12I\n\x0ceuler_angles\x18\x04 \x01(\x0b2$.viam.app.v1.Orientation.EulerAnglesH\x00R\x0beulerAngles\x12F\n\x0baxis_angles\x18\x05 \x01(\x0b2#.viam.app.v1.Orientation.AxisAnglesH\x00R\naxisAngles\x12E\n\nquaternion\x18\x06 \x01(\x0b2#.viam.app.v1.Orientation.QuaternionH\x00R\nquaternion\x1a\x0f\n\rNoOrientation\x1aj\n\x18OrientationVectorRadians\x12$\n\x05theta\x18\x01 \x01(\x01B\x0e\x9a\x84\x9e\x03\tjson:"th"R\x05theta\x12\x0c\n\x01x\x18\x02 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x03 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x04 \x01(\x01R\x01z\x1aj\n\x18OrientationVectorDegrees\x12$\n\x05theta\x18\x01 \x01(\x01B\x0e\x9a\x84\x9e\x03\tjson:"th"R\x05theta\x12\x0c\n\x01x\x18\x02 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x03 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x04 \x01(\x01R\x01z\x1aI\n\x0bEulerAngles\x12\x12\n\x04roll\x18\x01 \x01(\x01R\x04roll\x12\x14\n\x05pitch\x18\x02 \x01(\x01R\x05pitch\x12\x10\n\x03yaw\x18\x03 \x01(\x01R\x03yaw\x1a\\\n\nAxisAngles\x12$\n\x05theta\x18\x01 \x01(\x01B\x0e\x9a\x84\x9e\x03\tjson:"th"R\x05theta\x12\x0c\n\x01x\x18\x02 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x03 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x04 \x01(\x01R\x01z\x1aD\n\nQuaternion\x12\x0c\n\x01w\x18\x01 \x01(\x01R\x01w\x12\x0c\n\x01x\x18\x02 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x03 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x04 \x01(\x01R\x01zB\x06\n\x04type"\xf5\x03\n\x0cRemoteConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07address\x18\x02 \x01(\tR\x07address\x12(\n\x05frame\x18\x03 \x01(\x0b2\x12.viam.app.v1.FrameR\x05frame\x12+\n\x04auth\x18\x04 \x01(\x0b2\x17.viam.app.v1.RemoteAuthR\x04auth\x12\x1d\n\nmanaged_by\x18\x05 \x01(\tR\tmanagedBy\x12\x1a\n\x08insecure\x18\x06 \x01(\x08R\x08insecure\x12U\n\x19connection_check_interval\x18\x07 \x01(\x0b2\x19.google.protobuf.DurationR\x17connectionCheckInterval\x12H\n\x12reconnect_interval\x18\x08 \x01(\x0b2\x19.google.protobuf.DurationR\x11reconnectInterval\x12l\n\x0fservice_configs\x18\t \x03(\x0b2\'.viam.app.v1.ResourceLevelServiceConfigB\x1a\x9a\x84\x9e\x03\x15json:"service_config"R\x0eserviceConfigs\x12\x16\n\x06secret\x18\n \x01(\tR\x06secret"\xc6\x01\n\nRemoteAuth\x12E\n\x0bcredentials\x18\x01 \x01(\x0b2#.viam.app.v1.RemoteAuth.CredentialsR\x0bcredentials\x12\x16\n\x06entity\x18\x02 \x01(\tR\x06entity\x1aY\n\x0bCredentials\x120\n\x04type\x18\x01 \x01(\x0e2\x1c.viam.app.v1.CredentialsTypeR\x04type\x12\x18\n\x07payload\x18\x02 \x01(\tR\x07payload"~\n\tAgentInfo\x12\x12\n\x04host\x18\x01 \x01(\tR\x04host\x12\x0e\n\x02os\x18\x02 \x01(\tR\x02os\x12\x10\n\x03ips\x18\x03 \x03(\tR\x03ips\x12\x18\n\x07version\x18\x04 \x01(\tR\x07version\x12!\n\x0cgit_revision\x18\x05 \x01(\tR\x0bgitRevision"j\n\rConfigRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12:\n\nagent_info\x18\x02 \x01(\x0b2\x16.viam.app.v1.AgentInfoH\x00R\tagentInfo\x88\x01\x01B\r\n\x0b_agent_info"B\n\x0eConfigResponse\x120\n\x06config\x18\x01 \x01(\x0b2\x18.viam.app.v1.RobotConfigR\x06config"$\n\x12CertificateRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"v\n\x13CertificateResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\'\n\x0ftls_certificate\x18\x02 \x01(\tR\x0etlsCertificate\x12&\n\x0ftls_private_key\x18\x03 \x01(\tR\rtlsPrivateKey"G\n\nLogRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12)\n\x04logs\x18\x02 \x03(\x0b2\x15.viam.app.v1.LogEntryR\x04logs"\r\n\x0bLogResponse"%\n\x13NeedsRestartRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x9a\x01\n\x14NeedsRestartResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12!\n\x0cmust_restart\x18\x02 \x01(\x08R\x0bmustRestart\x12O\n\x16restart_check_interval\x18\x03 \x01(\x0b2\x19.google.protobuf.DurationR\x14restartCheckInterval*\xbf\x01\n\x0fCredentialsType\x12 \n\x1cCREDENTIALS_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19CREDENTIALS_TYPE_INTERNAL\x10\x01\x12\x1c\n\x18CREDENTIALS_TYPE_API_KEY\x10\x02\x12!\n\x1dCREDENTIALS_TYPE_ROBOT_SECRET\x10\x03\x12*\n&CREDENTIALS_TYPE_ROBOT_LOCATION_SECRET\x10\x042\xb2\x02\n\x0cRobotService\x12A\n\x06Config\x12\x1a.viam.app.v1.ConfigRequest\x1a\x1b.viam.app.v1.ConfigResponse\x12P\n\x0bCertificate\x12\x1f.viam.app.v1.CertificateRequest\x1a .viam.app.v1.CertificateResponse\x128\n\x03Log\x12\x17.viam.app.v1.LogRequest\x1a\x18.viam.app.v1.LogResponse\x12S\n\x0cNeedsRestart\x12 .viam.app.v1.NeedsRestartRequest\x1a!.viam.app.v1.NeedsRestartResponseB\x18Z\x16go.viam.com/api/app/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.v1.robot_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z\x16go.viam.com/api/app/v1' - _COMPONENTCONFIG.fields_by_name['service_configs']._options = None - _COMPONENTCONFIG.fields_by_name['service_configs']._serialized_options = b'\x9a\x84\x9e\x03\x15json:"service_config"' - _ORIENTATION_ORIENTATIONVECTORRADIANS.fields_by_name['theta']._options = None - _ORIENTATION_ORIENTATIONVECTORRADIANS.fields_by_name['theta']._serialized_options = b'\x9a\x84\x9e\x03\tjson:"th"' - _ORIENTATION_ORIENTATIONVECTORDEGREES.fields_by_name['theta']._options = None - _ORIENTATION_ORIENTATIONVECTORDEGREES.fields_by_name['theta']._serialized_options = b'\x9a\x84\x9e\x03\tjson:"th"' - _ORIENTATION_AXISANGLES.fields_by_name['theta']._options = None - _ORIENTATION_AXISANGLES.fields_by_name['theta']._serialized_options = b'\x9a\x84\x9e\x03\tjson:"th"' - _REMOTECONFIG.fields_by_name['service_configs']._options = None - _REMOTECONFIG.fields_by_name['service_configs']._serialized_options = b'\x9a\x84\x9e\x03\x15json:"service_config"' - _CREDENTIALSTYPE._serialized_start = 4634 - _CREDENTIALSTYPE._serialized_end = 4825 - _ROBOTCONFIG._serialized_start = 140 - _ROBOTCONFIG._serialized_end = 597 - _CLOUDCONFIG._serialized_start = 600 - _CLOUDCONFIG._serialized_end = 868 - _COMPONENTCONFIG._serialized_start = 871 - _COMPONENTCONFIG._serialized_end = 1220 - _RESOURCELEVELSERVICECONFIG._serialized_start = 1222 - _RESOURCELEVELSERVICECONFIG._serialized_end = 1327 - _PROCESSCONFIG._serialized_start = 1330 - _PROCESSCONFIG._serialized_end = 1464 - _SERVICECONFIG._serialized_start = 1467 - _SERVICECONFIG._serialized_end = 1609 - _NETWORKCONFIG._serialized_start = 1612 - _NETWORKCONFIG._serialized_end = 1752 - _AUTHCONFIG._serialized_start = 1754 - _AUTHCONFIG._serialized_end = 1870 - _AUTHHANDLERCONFIG._serialized_start = 1872 - _AUTHHANDLERCONFIG._serialized_end = 1990 - _FRAME._serialized_start = 1993 - _FRAME._serialized_end = 2144 - _TRANSLATION._serialized_start = 2146 - _TRANSLATION._serialized_end = 2201 - _ORIENTATION._serialized_start = 2204 - _ORIENTATION._serialized_end = 3180 - _ORIENTATION_NOORIENTATION._serialized_start = 2702 - _ORIENTATION_NOORIENTATION._serialized_end = 2717 - _ORIENTATION_ORIENTATIONVECTORRADIANS._serialized_start = 2719 - _ORIENTATION_ORIENTATIONVECTORRADIANS._serialized_end = 2825 - _ORIENTATION_ORIENTATIONVECTORDEGREES._serialized_start = 2827 - _ORIENTATION_ORIENTATIONVECTORDEGREES._serialized_end = 2933 - _ORIENTATION_EULERANGLES._serialized_start = 2935 - _ORIENTATION_EULERANGLES._serialized_end = 3008 - _ORIENTATION_AXISANGLES._serialized_start = 3010 - _ORIENTATION_AXISANGLES._serialized_end = 3102 - _ORIENTATION_QUATERNION._serialized_start = 3104 - _ORIENTATION_QUATERNION._serialized_end = 3172 - _REMOTECONFIG._serialized_start = 3183 - _REMOTECONFIG._serialized_end = 3684 - _REMOTEAUTH._serialized_start = 3687 - _REMOTEAUTH._serialized_end = 3885 - _REMOTEAUTH_CREDENTIALS._serialized_start = 3796 - _REMOTEAUTH_CREDENTIALS._serialized_end = 3885 - _AGENTINFO._serialized_start = 3887 - _AGENTINFO._serialized_end = 4013 - _CONFIGREQUEST._serialized_start = 4015 - _CONFIGREQUEST._serialized_end = 4121 - _CONFIGRESPONSE._serialized_start = 4123 - _CONFIGRESPONSE._serialized_end = 4189 - _CERTIFICATEREQUEST._serialized_start = 4191 - _CERTIFICATEREQUEST._serialized_end = 4227 - _CERTIFICATERESPONSE._serialized_start = 4229 - _CERTIFICATERESPONSE._serialized_end = 4347 - _LOGREQUEST._serialized_start = 4349 - _LOGREQUEST._serialized_end = 4420 - _LOGRESPONSE._serialized_start = 4422 - _LOGRESPONSE._serialized_end = 4435 - _NEEDSRESTARTREQUEST._serialized_start = 4437 - _NEEDSRESTARTREQUEST._serialized_end = 4474 - _NEEDSRESTARTRESPONSE._serialized_start = 4477 - _NEEDSRESTARTRESPONSE._serialized_end = 4631 - _ROBOTSERVICE._serialized_start = 4828 - _ROBOTSERVICE._serialized_end = 5134 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12app/v1/robot.proto\x12\x0bviam.app.v1\x1a\x16common/v1/common.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x16tagger/v1/tagger.proto"\xf5\x07\n\x0bRobotConfig\x12.\n\x05cloud\x18\x01 \x01(\x0b2\x18.viam.app.v1.CloudConfigR\x05cloud\x123\n\x07remotes\x18\x02 \x03(\x0b2\x19.viam.app.v1.RemoteConfigR\x07remotes\x12<\n\ncomponents\x18\x03 \x03(\x0b2\x1c.viam.app.v1.ComponentConfigR\ncomponents\x128\n\tprocesses\x18\x04 \x03(\x0b2\x1a.viam.app.v1.ProcessConfigR\tprocesses\x126\n\x08services\x18\x05 \x03(\x0b2\x1a.viam.app.v1.ServiceConfigR\x08services\x129\n\x07network\x18\x06 \x01(\x0b2\x1a.viam.app.v1.NetworkConfigH\x00R\x07network\x88\x01\x01\x120\n\x04auth\x18\x07 \x01(\x0b2\x17.viam.app.v1.AuthConfigH\x01R\x04auth\x88\x01\x01\x12\x19\n\x05debug\x18\x08 \x01(\x08H\x02R\x05debug\x88\x01\x01\x123\n\x07modules\x18\t \x03(\x0b2\x19.viam.app.v1.ModuleConfigR\x07modules\x127\n\x15disable_partial_start\x18\n \x01(\x08H\x03R\x13disablePartialStart\x88\x01\x01\x126\n\x08packages\x18\x0b \x03(\x0b2\x1a.viam.app.v1.PackageConfigR\x08packages\x12\\\n\x19overwrite_fragment_status\x18\x0c \x03(\x0b2 .viam.app.v1.AppValidationStatusR\x17overwriteFragmentStatus\x12,\n\x12enable_web_profile\x18\r \x01(\x08R\x10enableWebProfile\x12/\n\x03log\x18\x0e \x03(\x0b2\x1d.viam.app.v1.LogPatternConfigR\x03log\x12\x1a\n\x08revision\x18\x0f \x01(\tR\x08revision\x12E\n\x0bmaintenance\x18\x10 \x01(\x0b2\x1e.viam.app.v1.MaintenanceConfigH\x04R\x0bmaintenance\x88\x01\x01\x12:\n\x19disable_log_deduplication\x18\x11 \x01(\x08R\x17disableLogDeduplicationB\n\n\x08_networkB\x07\n\x05_authB\x08\n\x06_debugB\x18\n\x16_disable_partial_startB\x0e\n\x0c_maintenance"B\n\x10LogPatternConfig\x12\x18\n\x07pattern\x18\x01 \x01(\tR\x07pattern\x12\x14\n\x05level\x18\x02 \x01(\tR\x05level"8\n\x0eLocationSecret\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n\x06secret\x18\x02 \x01(\tR\x06secret"+\n\x13AppValidationStatus\x12\x14\n\x05error\x18\x01 \x01(\tR\x05error"\xbe\x03\n\x0bCloudConfig\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04fqdn\x18\x02 \x01(\tR\x04fqdn\x12\x1d\n\nlocal_fqdn\x18\x03 \x01(\tR\tlocalFqdn\x12\x1d\n\nmanaged_by\x18\x04 \x01(\tR\tmanagedBy\x12+\n\x11signaling_address\x18\x05 \x01(\tR\x10signalingAddress\x12-\n\x12signaling_insecure\x18\x06 \x01(\x08R\x11signalingInsecure\x12+\n\x0flocation_secret\x18\x07 \x01(\tB\x02\x18\x01R\x0elocationSecret\x12\x16\n\x06secret\x18\x08 \x01(\tR\x06secret\x12F\n\x10location_secrets\x18\t \x03(\x0b2\x1b.viam.app.v1.LocationSecretR\x0flocationSecrets\x12$\n\x0eprimary_org_id\x18\n \x01(\tR\x0cprimaryOrgId\x12\x1f\n\x0blocation_id\x18\x0b \x01(\tR\nlocationId\x12\x1d\n\nmachine_id\x18\x0c \x01(\tR\tmachineId"\xbb\x03\n\x0fComponentConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1c\n\tnamespace\x18\x02 \x01(\tR\tnamespace\x12\x12\n\x04type\x18\x03 \x01(\tR\x04type\x12\x14\n\x05model\x18\x04 \x01(\tR\x05model\x12(\n\x05frame\x18\x05 \x01(\x0b2\x12.viam.app.v1.FrameR\x05frame\x12\x1d\n\ndepends_on\x18\x06 \x03(\tR\tdependsOn\x12l\n\x0fservice_configs\x18\x07 \x03(\x0b2\'.viam.app.v1.ResourceLevelServiceConfigB\x1a\x9a\x84\x9e\x03\x15json:"service_config"R\x0eserviceConfigs\x127\n\nattributes\x18\x08 \x01(\x0b2\x17.google.protobuf.StructR\nattributes\x12\x10\n\x03api\x18\t \x01(\tR\x03api\x12J\n\x11log_configuration\x18\n \x01(\x0b2\x1d.viam.app.v1.LogConfigurationR\x10logConfiguration"i\n\x1aResourceLevelServiceConfig\x12\x12\n\x04type\x18\x01 \x01(\tR\x04type\x127\n\nattributes\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\nattributes"\xf0\x02\n\rProcessConfig\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x12\n\x04name\x18\x02 \x01(\tR\x04name\x12\x12\n\x04args\x18\x03 \x03(\tR\x04args\x12\x10\n\x03cwd\x18\x04 \x01(\tR\x03cwd\x12\x19\n\x08one_shot\x18\x05 \x01(\x08R\x07oneShot\x12\x10\n\x03log\x18\x06 \x01(\x08R\x03log\x12\x1f\n\x0bstop_signal\x18\x07 \x01(\x05R\nstopSignal\x12<\n\x0cstop_timeout\x18\x08 \x01(\x0b2\x19.google.protobuf.DurationR\x0bstopTimeout\x125\n\x03env\x18\t \x03(\x0b2#.viam.app.v1.ProcessConfig.EnvEntryR\x03env\x12\x1a\n\x08username\x18\n \x01(\tR\x08username\x1a6\n\x08EnvEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x028\x01"\x8f\x03\n\rServiceConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1c\n\tnamespace\x18\x02 \x01(\tR\tnamespace\x12\x12\n\x04type\x18\x03 \x01(\tR\x04type\x127\n\nattributes\x18\x04 \x01(\x0b2\x17.google.protobuf.StructR\nattributes\x12\x1d\n\ndepends_on\x18\x05 \x03(\tR\tdependsOn\x12\x14\n\x05model\x18\x06 \x01(\tR\x05model\x12\x10\n\x03api\x18\t \x01(\tR\x03api\x12l\n\x0fservice_configs\x18\n \x03(\x0b2\'.viam.app.v1.ResourceLevelServiceConfigB\x1a\x9a\x84\x9e\x03\x15json:"service_config"R\x0eserviceConfigs\x12J\n\x11log_configuration\x18\x0b \x01(\x0b2\x1d.viam.app.v1.LogConfigurationR\x10logConfiguration"\xba\x02\n\rNetworkConfig\x12\x12\n\x04fqdn\x18\x01 \x01(\tR\x04fqdn\x12!\n\x0cbind_address\x18\x02 \x01(\tR\x0bbindAddress\x12"\n\rtls_cert_file\x18\x03 \x01(\tR\x0btlsCertFile\x12 \n\x0ctls_key_file\x18\x04 \x01(\tR\ntlsKeyFile\x127\n\x08sessions\x18\x05 \x01(\x0b2\x1b.viam.app.v1.SessionsConfigR\x08sessions\x12\\\n\x18traffic_tunnel_endpoints\x18\x06 \x03(\x0b2".viam.app.v1.TrafficTunnelEndpointR\x16trafficTunnelEndpoints\x12\x15\n\x06no_tls\x18\x07 \x01(\x08R\x05noTls"V\n\x0eSessionsConfig\x12D\n\x10heartbeat_window\x18\x01 \x01(\x0b2\x19.google.protobuf.DurationR\x0fheartbeatWindow"u\n\x15TrafficTunnelEndpoint\x12\x12\n\x04port\x18\x01 \x01(\x05R\x04port\x12H\n\x12connection_timeout\x18\x02 \x01(\x0b2\x19.google.protobuf.DurationR\x11connectionTimeout"\xe5\x01\n\nAuthConfig\x12:\n\x08handlers\x18\x01 \x03(\x0b2\x1e.viam.app.v1.AuthHandlerConfigR\x08handlers\x12*\n\x11tls_auth_entities\x18\x02 \x03(\tR\x0ftlsAuthEntities\x12V\n\x14external_auth_config\x18\x03 \x01(\x0b2\x1f.viam.app.v1.ExternalAuthConfigH\x00R\x12externalAuthConfig\x88\x01\x01B\x17\n\x15_external_auth_config"7\n\x08JWKSFile\x12+\n\x04json\x18\x01 \x01(\x0b2\x17.google.protobuf.StructR\x04json"?\n\x12ExternalAuthConfig\x12)\n\x04jwks\x18\x01 \x01(\x0b2\x15.viam.app.v1.JWKSFileR\x04jwks"v\n\x11AuthHandlerConfig\x120\n\x04type\x18\x01 \x01(\x0e2\x1c.viam.app.v1.CredentialsTypeR\x04type\x12/\n\x06config\x18\x05 \x01(\x0b2\x17.google.protobuf.StructR\x06config"\xcd\x01\n\x05Frame\x12\x16\n\x06parent\x18\x01 \x01(\tR\x06parent\x12:\n\x0btranslation\x18\x02 \x01(\x0b2\x18.viam.app.v1.TranslationR\x0btranslation\x12:\n\x0borientation\x18\x03 \x01(\x0b2\x18.viam.app.v1.OrientationR\x0borientation\x124\n\x08geometry\x18\x04 \x01(\x0b2\x18.viam.common.v1.GeometryR\x08geometry"(\n\x10LogConfiguration\x12\x14\n\x05level\x18\x01 \x01(\tR\x05level"7\n\x0bTranslation\x12\x0c\n\x01x\x18\x01 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x02 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x03 \x01(\x01R\x01z"\xd0\x07\n\x0bOrientation\x12O\n\x0eno_orientation\x18\x01 \x01(\x0b2&.viam.app.v1.Orientation.NoOrientationH\x00R\rnoOrientation\x12Z\n\x0evector_radians\x18\x02 \x01(\x0b21.viam.app.v1.Orientation.OrientationVectorRadiansH\x00R\rvectorRadians\x12Z\n\x0evector_degrees\x18\x03 \x01(\x0b21.viam.app.v1.Orientation.OrientationVectorDegreesH\x00R\rvectorDegrees\x12I\n\x0ceuler_angles\x18\x04 \x01(\x0b2$.viam.app.v1.Orientation.EulerAnglesH\x00R\x0beulerAngles\x12F\n\x0baxis_angles\x18\x05 \x01(\x0b2#.viam.app.v1.Orientation.AxisAnglesH\x00R\naxisAngles\x12E\n\nquaternion\x18\x06 \x01(\x0b2#.viam.app.v1.Orientation.QuaternionH\x00R\nquaternion\x1a\x0f\n\rNoOrientation\x1aj\n\x18OrientationVectorRadians\x12$\n\x05theta\x18\x01 \x01(\x01B\x0e\x9a\x84\x9e\x03\tjson:"th"R\x05theta\x12\x0c\n\x01x\x18\x02 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x03 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x04 \x01(\x01R\x01z\x1aj\n\x18OrientationVectorDegrees\x12$\n\x05theta\x18\x01 \x01(\x01B\x0e\x9a\x84\x9e\x03\tjson:"th"R\x05theta\x12\x0c\n\x01x\x18\x02 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x03 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x04 \x01(\x01R\x01z\x1aI\n\x0bEulerAngles\x12\x12\n\x04roll\x18\x01 \x01(\x01R\x04roll\x12\x14\n\x05pitch\x18\x02 \x01(\x01R\x05pitch\x12\x10\n\x03yaw\x18\x03 \x01(\x01R\x03yaw\x1a\\\n\nAxisAngles\x12$\n\x05theta\x18\x01 \x01(\x01B\x0e\x9a\x84\x9e\x03\tjson:"th"R\x05theta\x12\x0c\n\x01x\x18\x02 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x03 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x04 \x01(\x01R\x01z\x1aD\n\nQuaternion\x12\x0c\n\x01w\x18\x01 \x01(\x01R\x01w\x12\x0c\n\x01x\x18\x02 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x03 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x04 \x01(\x01R\x01zB\x06\n\x04type"\xf5\x03\n\x0cRemoteConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07address\x18\x02 \x01(\tR\x07address\x12(\n\x05frame\x18\x03 \x01(\x0b2\x12.viam.app.v1.FrameR\x05frame\x12+\n\x04auth\x18\x04 \x01(\x0b2\x17.viam.app.v1.RemoteAuthR\x04auth\x12\x1d\n\nmanaged_by\x18\x05 \x01(\tR\tmanagedBy\x12\x1a\n\x08insecure\x18\x06 \x01(\x08R\x08insecure\x12U\n\x19connection_check_interval\x18\x07 \x01(\x0b2\x19.google.protobuf.DurationR\x17connectionCheckInterval\x12H\n\x12reconnect_interval\x18\x08 \x01(\x0b2\x19.google.protobuf.DurationR\x11reconnectInterval\x12l\n\x0fservice_configs\x18\t \x03(\x0b2\'.viam.app.v1.ResourceLevelServiceConfigB\x1a\x9a\x84\x9e\x03\x15json:"service_config"R\x0eserviceConfigs\x12\x16\n\x06secret\x18\n \x01(\tR\x06secret"\xc6\x01\n\nRemoteAuth\x12E\n\x0bcredentials\x18\x01 \x01(\x0b2#.viam.app.v1.RemoteAuth.CredentialsR\x0bcredentials\x12\x16\n\x06entity\x18\x02 \x01(\tR\x06entity\x1aY\n\x0bCredentials\x120\n\x04type\x18\x01 \x01(\x0e2\x1c.viam.app.v1.CredentialsTypeR\x04type\x12\x18\n\x07payload\x18\x02 \x01(\tR\x07payload"\xd1\x01\n\tAgentInfo\x12\x12\n\x04host\x18\x01 \x01(\tR\x04host\x12\x0e\n\x02os\x18\x02 \x01(\tR\x02os\x12\x10\n\x03ips\x18\x03 \x03(\tR\x03ips\x12\x18\n\x07version\x18\x04 \x01(\tR\x07version\x12!\n\x0cgit_revision\x18\x05 \x01(\tR\x0bgitRevision\x12\x1f\n\x08platform\x18\x06 \x01(\tH\x00R\x08platform\x88\x01\x01\x12#\n\rplatform_tags\x18\x07 \x03(\tR\x0cplatformTagsB\x0b\n\t_platform"j\n\rConfigRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12:\n\nagent_info\x18\x02 \x01(\x0b2\x16.viam.app.v1.AgentInfoH\x00R\tagentInfo\x88\x01\x01B\r\n\x0b_agent_info"B\n\x0eConfigResponse\x120\n\x06config\x18\x01 \x01(\x0b2\x18.viam.app.v1.RobotConfigR\x06config"$\n\x12CertificateRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"v\n\x13CertificateResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\'\n\x0ftls_certificate\x18\x02 \x01(\tR\x0etlsCertificate\x12&\n\x0ftls_private_key\x18\x03 \x01(\tR\rtlsPrivateKey"J\n\nLogRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12,\n\x04logs\x18\x02 \x03(\x0b2\x18.viam.common.v1.LogEntryR\x04logs"\r\n\x0bLogResponse"%\n\x13NeedsRestartRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x9a\x01\n\x14NeedsRestartResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12!\n\x0cmust_restart\x18\x02 \x01(\x08R\x0bmustRestart\x12O\n\x16restart_check_interval\x18\x03 \x01(\x0b2\x19.google.protobuf.DurationR\x14restartCheckInterval"\xf3\x02\n\x0cModuleConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04path\x18\x02 \x01(\tR\x04path\x12\x1b\n\tlog_level\x18\x03 \x01(\tR\x08logLevel\x12\x12\n\x04type\x18\x04 \x01(\tR\x04type\x12\x1b\n\tmodule_id\x18\x05 \x01(\tR\x08moduleId\x124\n\x03env\x18\x06 \x03(\x0b2".viam.app.v1.ModuleConfig.EnvEntryR\x03env\x128\n\x06status\x18\x07 \x01(\x0b2 .viam.app.v1.AppValidationStatusR\x06status\x12E\n\x11first_run_timeout\x18\x08 \x01(\x0b2\x19.google.protobuf.DurationR\x0ffirstRunTimeout\x1a6\n\x08EnvEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\tR\x05value:\x028\x01"\xa5\x01\n\rPackageConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07package\x18\x02 \x01(\tR\x07package\x12\x18\n\x07version\x18\x03 \x01(\tR\x07version\x12\x12\n\x04type\x18\x04 \x01(\tR\x04type\x128\n\x06status\x18\x05 \x01(\x0b2 .viam.app.v1.AppValidationStatusR\x06status"\x8a\x01\n\x11MaintenanceConfig\x12=\n\x0bsensor_name\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\nsensorName\x126\n\x17maintenance_allowed_key\x18\x02 \x01(\tR\x15maintenanceAllowedKey*\xbf\x01\n\x0fCredentialsType\x12 \n\x1cCREDENTIALS_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19CREDENTIALS_TYPE_INTERNAL\x10\x01\x12\x1c\n\x18CREDENTIALS_TYPE_API_KEY\x10\x02\x12!\n\x1dCREDENTIALS_TYPE_ROBOT_SECRET\x10\x03\x12*\n&CREDENTIALS_TYPE_ROBOT_LOCATION_SECRET\x10\x042\xb2\x02\n\x0cRobotService\x12A\n\x06Config\x12\x1a.viam.app.v1.ConfigRequest\x1a\x1b.viam.app.v1.ConfigResponse\x12P\n\x0bCertificate\x12\x1f.viam.app.v1.CertificateRequest\x1a .viam.app.v1.CertificateResponse\x128\n\x03Log\x12\x17.viam.app.v1.LogRequest\x1a\x18.viam.app.v1.LogResponse\x12S\n\x0cNeedsRestart\x12 .viam.app.v1.NeedsRestartRequest\x1a!.viam.app.v1.NeedsRestartResponseB\x18Z\x16go.viam.com/api/app/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.v1.robot_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x16go.viam.com/api/app/v1' + _globals['_CLOUDCONFIG'].fields_by_name['location_secret']._loaded_options = None + _globals['_CLOUDCONFIG'].fields_by_name['location_secret']._serialized_options = b'\x18\x01' + _globals['_COMPONENTCONFIG'].fields_by_name['service_configs']._loaded_options = None + _globals['_COMPONENTCONFIG'].fields_by_name['service_configs']._serialized_options = b'\x9a\x84\x9e\x03\x15json:"service_config"' + _globals['_PROCESSCONFIG_ENVENTRY']._loaded_options = None + _globals['_PROCESSCONFIG_ENVENTRY']._serialized_options = b'8\x01' + _globals['_SERVICECONFIG'].fields_by_name['service_configs']._loaded_options = None + _globals['_SERVICECONFIG'].fields_by_name['service_configs']._serialized_options = b'\x9a\x84\x9e\x03\x15json:"service_config"' + _globals['_ORIENTATION_ORIENTATIONVECTORRADIANS'].fields_by_name['theta']._loaded_options = None + _globals['_ORIENTATION_ORIENTATIONVECTORRADIANS'].fields_by_name['theta']._serialized_options = b'\x9a\x84\x9e\x03\tjson:"th"' + _globals['_ORIENTATION_ORIENTATIONVECTORDEGREES'].fields_by_name['theta']._loaded_options = None + _globals['_ORIENTATION_ORIENTATIONVECTORDEGREES'].fields_by_name['theta']._serialized_options = b'\x9a\x84\x9e\x03\tjson:"th"' + _globals['_ORIENTATION_AXISANGLES'].fields_by_name['theta']._loaded_options = None + _globals['_ORIENTATION_AXISANGLES'].fields_by_name['theta']._serialized_options = b'\x9a\x84\x9e\x03\tjson:"th"' + _globals['_REMOTECONFIG'].fields_by_name['service_configs']._loaded_options = None + _globals['_REMOTECONFIG'].fields_by_name['service_configs']._serialized_options = b'\x9a\x84\x9e\x03\x15json:"service_config"' + _globals['_MODULECONFIG_ENVENTRY']._loaded_options = None + _globals['_MODULECONFIG_ENVENTRY']._serialized_options = b'8\x01' + _globals['_CREDENTIALSTYPE']._serialized_start = 7613 + _globals['_CREDENTIALSTYPE']._serialized_end = 7804 + _globals['_ROBOTCONFIG']._serialized_start = 146 + _globals['_ROBOTCONFIG']._serialized_end = 1159 + _globals['_LOGPATTERNCONFIG']._serialized_start = 1161 + _globals['_LOGPATTERNCONFIG']._serialized_end = 1227 + _globals['_LOCATIONSECRET']._serialized_start = 1229 + _globals['_LOCATIONSECRET']._serialized_end = 1285 + _globals['_APPVALIDATIONSTATUS']._serialized_start = 1287 + _globals['_APPVALIDATIONSTATUS']._serialized_end = 1330 + _globals['_CLOUDCONFIG']._serialized_start = 1333 + _globals['_CLOUDCONFIG']._serialized_end = 1779 + _globals['_COMPONENTCONFIG']._serialized_start = 1782 + _globals['_COMPONENTCONFIG']._serialized_end = 2225 + _globals['_RESOURCELEVELSERVICECONFIG']._serialized_start = 2227 + _globals['_RESOURCELEVELSERVICECONFIG']._serialized_end = 2332 + _globals['_PROCESSCONFIG']._serialized_start = 2335 + _globals['_PROCESSCONFIG']._serialized_end = 2703 + _globals['_PROCESSCONFIG_ENVENTRY']._serialized_start = 2649 + _globals['_PROCESSCONFIG_ENVENTRY']._serialized_end = 2703 + _globals['_SERVICECONFIG']._serialized_start = 2706 + _globals['_SERVICECONFIG']._serialized_end = 3105 + _globals['_NETWORKCONFIG']._serialized_start = 3108 + _globals['_NETWORKCONFIG']._serialized_end = 3422 + _globals['_SESSIONSCONFIG']._serialized_start = 3424 + _globals['_SESSIONSCONFIG']._serialized_end = 3510 + _globals['_TRAFFICTUNNELENDPOINT']._serialized_start = 3512 + _globals['_TRAFFICTUNNELENDPOINT']._serialized_end = 3629 + _globals['_AUTHCONFIG']._serialized_start = 3632 + _globals['_AUTHCONFIG']._serialized_end = 3861 + _globals['_JWKSFILE']._serialized_start = 3863 + _globals['_JWKSFILE']._serialized_end = 3918 + _globals['_EXTERNALAUTHCONFIG']._serialized_start = 3920 + _globals['_EXTERNALAUTHCONFIG']._serialized_end = 3983 + _globals['_AUTHHANDLERCONFIG']._serialized_start = 3985 + _globals['_AUTHHANDLERCONFIG']._serialized_end = 4103 + _globals['_FRAME']._serialized_start = 4106 + _globals['_FRAME']._serialized_end = 4311 + _globals['_LOGCONFIGURATION']._serialized_start = 4313 + _globals['_LOGCONFIGURATION']._serialized_end = 4353 + _globals['_TRANSLATION']._serialized_start = 4355 + _globals['_TRANSLATION']._serialized_end = 4410 + _globals['_ORIENTATION']._serialized_start = 4413 + _globals['_ORIENTATION']._serialized_end = 5389 + _globals['_ORIENTATION_NOORIENTATION']._serialized_start = 4911 + _globals['_ORIENTATION_NOORIENTATION']._serialized_end = 4926 + _globals['_ORIENTATION_ORIENTATIONVECTORRADIANS']._serialized_start = 4928 + _globals['_ORIENTATION_ORIENTATIONVECTORRADIANS']._serialized_end = 5034 + _globals['_ORIENTATION_ORIENTATIONVECTORDEGREES']._serialized_start = 5036 + _globals['_ORIENTATION_ORIENTATIONVECTORDEGREES']._serialized_end = 5142 + _globals['_ORIENTATION_EULERANGLES']._serialized_start = 5144 + _globals['_ORIENTATION_EULERANGLES']._serialized_end = 5217 + _globals['_ORIENTATION_AXISANGLES']._serialized_start = 5219 + _globals['_ORIENTATION_AXISANGLES']._serialized_end = 5311 + _globals['_ORIENTATION_QUATERNION']._serialized_start = 5313 + _globals['_ORIENTATION_QUATERNION']._serialized_end = 5381 + _globals['_REMOTECONFIG']._serialized_start = 5392 + _globals['_REMOTECONFIG']._serialized_end = 5893 + _globals['_REMOTEAUTH']._serialized_start = 5896 + _globals['_REMOTEAUTH']._serialized_end = 6094 + _globals['_REMOTEAUTH_CREDENTIALS']._serialized_start = 6005 + _globals['_REMOTEAUTH_CREDENTIALS']._serialized_end = 6094 + _globals['_AGENTINFO']._serialized_start = 6097 + _globals['_AGENTINFO']._serialized_end = 6306 + _globals['_CONFIGREQUEST']._serialized_start = 6308 + _globals['_CONFIGREQUEST']._serialized_end = 6414 + _globals['_CONFIGRESPONSE']._serialized_start = 6416 + _globals['_CONFIGRESPONSE']._serialized_end = 6482 + _globals['_CERTIFICATEREQUEST']._serialized_start = 6484 + _globals['_CERTIFICATEREQUEST']._serialized_end = 6520 + _globals['_CERTIFICATERESPONSE']._serialized_start = 6522 + _globals['_CERTIFICATERESPONSE']._serialized_end = 6640 + _globals['_LOGREQUEST']._serialized_start = 6642 + _globals['_LOGREQUEST']._serialized_end = 6716 + _globals['_LOGRESPONSE']._serialized_start = 6718 + _globals['_LOGRESPONSE']._serialized_end = 6731 + _globals['_NEEDSRESTARTREQUEST']._serialized_start = 6733 + _globals['_NEEDSRESTARTREQUEST']._serialized_end = 6770 + _globals['_NEEDSRESTARTRESPONSE']._serialized_start = 6773 + _globals['_NEEDSRESTARTRESPONSE']._serialized_end = 6927 + _globals['_MODULECONFIG']._serialized_start = 6930 + _globals['_MODULECONFIG']._serialized_end = 7301 + _globals['_MODULECONFIG_ENVENTRY']._serialized_start = 2649 + _globals['_MODULECONFIG_ENVENTRY']._serialized_end = 2703 + _globals['_PACKAGECONFIG']._serialized_start = 7304 + _globals['_PACKAGECONFIG']._serialized_end = 7469 + _globals['_MAINTENANCECONFIG']._serialized_start = 7472 + _globals['_MAINTENANCECONFIG']._serialized_end = 7610 + _globals['_ROBOTSERVICE']._serialized_start = 7807 + _globals['_ROBOTSERVICE']._serialized_end = 8113 \ No newline at end of file diff --git a/src/viam/gen/app/v1/robot_pb2.pyi b/src/viam/gen/app/v1/robot_pb2.pyi index f25082cb6..84608e8fc 100644 --- a/src/viam/gen/app/v1/robot_pb2.pyi +++ b/src/viam/gen/app/v1/robot_pb2.pyi @@ -2,9 +2,9 @@ @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ -from ... import app import builtins import collections.abc +from ... import common import google.protobuf.descriptor import google.protobuf.duration_pb2 import google.protobuf.internal.containers @@ -40,6 +40,7 @@ CREDENTIALS_TYPE_ROBOT_SECRET: CredentialsType.ValueType CREDENTIALS_TYPE_ROBOT_LOCATION_SECRET: CredentialsType.ValueType global___CredentialsType = CredentialsType +@typing.final class RobotConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor CLOUD_FIELD_NUMBER: builtins.int @@ -50,6 +51,24 @@ class RobotConfig(google.protobuf.message.Message): NETWORK_FIELD_NUMBER: builtins.int AUTH_FIELD_NUMBER: builtins.int DEBUG_FIELD_NUMBER: builtins.int + MODULES_FIELD_NUMBER: builtins.int + DISABLE_PARTIAL_START_FIELD_NUMBER: builtins.int + PACKAGES_FIELD_NUMBER: builtins.int + OVERWRITE_FRAGMENT_STATUS_FIELD_NUMBER: builtins.int + ENABLE_WEB_PROFILE_FIELD_NUMBER: builtins.int + LOG_FIELD_NUMBER: builtins.int + REVISION_FIELD_NUMBER: builtins.int + MAINTENANCE_FIELD_NUMBER: builtins.int + DISABLE_LOG_DEDUPLICATION_FIELD_NUMBER: builtins.int + debug: builtins.bool + 'Turns on debug mode for robot, adding an echo server and more logging and tracing. Only works after restart' + disable_partial_start: builtins.bool + enable_web_profile: builtins.bool + 'Turns on pprof http server on localhost. By default false.' + revision: builtins.str + 'Attributes a particular revision to the config.' + disable_log_deduplication: builtins.bool + "Disables the robot's log deduplication (identical, noisy logs will still\n be output individually instead of aggregated.)\n " @property def cloud(self) -> global___CloudConfig: @@ -78,31 +97,106 @@ class RobotConfig(google.protobuf.message.Message): @property def auth(self) -> global___AuthConfig: ... - debug: builtins.bool - 'Turns on debug mode for robot, adding an echo server and more logging and tracing. Only works after restart' - def __init__(self, *, cloud: global___CloudConfig | None=..., remotes: collections.abc.Iterable[global___RemoteConfig] | None=..., components: collections.abc.Iterable[global___ComponentConfig] | None=..., processes: collections.abc.Iterable[global___ProcessConfig] | None=..., services: collections.abc.Iterable[global___ServiceConfig] | None=..., network: global___NetworkConfig | None=..., auth: global___AuthConfig | None=..., debug: builtins.bool | None=...) -> None: + @property + def modules(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ModuleConfig]: ... - def HasField(self, field_name: typing_extensions.Literal['_auth', b'_auth', '_debug', b'_debug', '_network', b'_network', 'auth', b'auth', 'cloud', b'cloud', 'debug', b'debug', 'network', b'network']) -> builtins.bool: + @property + def packages(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PackageConfig]: ... - def ClearField(self, field_name: typing_extensions.Literal['_auth', b'_auth', '_debug', b'_debug', '_network', b'_network', 'auth', b'auth', 'cloud', b'cloud', 'components', b'components', 'debug', b'debug', 'network', b'network', 'processes', b'processes', 'remotes', b'remotes', 'services', b'services']) -> None: + @property + def overwrite_fragment_status(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AppValidationStatus]: + ... + + @property + def log(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___LogPatternConfig]: + ... + + @property + def maintenance(self) -> global___MaintenanceConfig: + ... + + def __init__(self, *, cloud: global___CloudConfig | None=..., remotes: collections.abc.Iterable[global___RemoteConfig] | None=..., components: collections.abc.Iterable[global___ComponentConfig] | None=..., processes: collections.abc.Iterable[global___ProcessConfig] | None=..., services: collections.abc.Iterable[global___ServiceConfig] | None=..., network: global___NetworkConfig | None=..., auth: global___AuthConfig | None=..., debug: builtins.bool | None=..., modules: collections.abc.Iterable[global___ModuleConfig] | None=..., disable_partial_start: builtins.bool | None=..., packages: collections.abc.Iterable[global___PackageConfig] | None=..., overwrite_fragment_status: collections.abc.Iterable[global___AppValidationStatus] | None=..., enable_web_profile: builtins.bool=..., log: collections.abc.Iterable[global___LogPatternConfig] | None=..., revision: builtins.str=..., maintenance: global___MaintenanceConfig | None=..., disable_log_deduplication: builtins.bool=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_auth', b'_auth', '_debug', b'_debug', '_disable_partial_start', b'_disable_partial_start', '_maintenance', b'_maintenance', '_network', b'_network', 'auth', b'auth', 'cloud', b'cloud', 'debug', b'debug', 'disable_partial_start', b'disable_partial_start', 'maintenance', b'maintenance', 'network', b'network']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_auth', b'_auth', '_debug', b'_debug', '_disable_partial_start', b'_disable_partial_start', '_maintenance', b'_maintenance', '_network', b'_network', 'auth', b'auth', 'cloud', b'cloud', 'components', b'components', 'debug', b'debug', 'disable_log_deduplication', b'disable_log_deduplication', 'disable_partial_start', b'disable_partial_start', 'enable_web_profile', b'enable_web_profile', 'log', b'log', 'maintenance', b'maintenance', 'modules', b'modules', 'network', b'network', 'overwrite_fragment_status', b'overwrite_fragment_status', 'packages', b'packages', 'processes', b'processes', 'remotes', b'remotes', 'revision', b'revision', 'services', b'services']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_auth', b'_auth']) -> typing.Literal['auth'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_debug', b'_debug']) -> typing.Literal['debug'] | None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal['_auth', b'_auth']) -> typing_extensions.Literal['auth'] | None: + def WhichOneof(self, oneof_group: typing.Literal['_disable_partial_start', b'_disable_partial_start']) -> typing.Literal['disable_partial_start'] | None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal['_debug', b'_debug']) -> typing_extensions.Literal['debug'] | None: + def WhichOneof(self, oneof_group: typing.Literal['_maintenance', b'_maintenance']) -> typing.Literal['maintenance'] | None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal['_network', b'_network']) -> typing_extensions.Literal['network'] | None: + def WhichOneof(self, oneof_group: typing.Literal['_network', b'_network']) -> typing.Literal['network'] | None: ... global___RobotConfig = RobotConfig +@typing.final +class LogPatternConfig(google.protobuf.message.Message): + """LogPatternConfig allows you to specify a 2-tuple consisting + of a logger name and its corresponding log level. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PATTERN_FIELD_NUMBER: builtins.int + LEVEL_FIELD_NUMBER: builtins.int + pattern: builtins.str + level: builtins.str + + def __init__(self, *, pattern: builtins.str=..., level: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['level', b'level', 'pattern', b'pattern']) -> None: + ... +global___LogPatternConfig = LogPatternConfig + +@typing.final +class LocationSecret(google.protobuf.message.Message): + """Valid location secret that can be used for authentication to the robot.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + SECRET_FIELD_NUMBER: builtins.int + id: builtins.str + secret: builtins.str + 'secret payload' + + def __init__(self, *, id: builtins.str=..., secret: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'secret', b'secret']) -> None: + ... +global___LocationSecret = LocationSecret + +@typing.final +class AppValidationStatus(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + + def __init__(self, *, error: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['error', b'error']) -> None: + ... +global___AppValidationStatus = AppValidationStatus + +@typing.final class CloudConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -113,6 +207,10 @@ class CloudConfig(google.protobuf.message.Message): SIGNALING_INSECURE_FIELD_NUMBER: builtins.int LOCATION_SECRET_FIELD_NUMBER: builtins.int SECRET_FIELD_NUMBER: builtins.int + LOCATION_SECRETS_FIELD_NUMBER: builtins.int + PRIMARY_ORG_ID_FIELD_NUMBER: builtins.int + LOCATION_ID_FIELD_NUMBER: builtins.int + MACHINE_ID_FIELD_NUMBER: builtins.int id: builtins.str 'Robot part id.' fqdn: builtins.str @@ -121,15 +219,25 @@ class CloudConfig(google.protobuf.message.Message): signaling_address: builtins.str signaling_insecure: builtins.bool location_secret: builtins.str + 'Deprecated use location_secrets' secret: builtins.str + 'Robot part secret' + primary_org_id: builtins.str + location_id: builtins.str + machine_id: builtins.str + + @property + def location_secrets(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___LocationSecret]: + """All valid location secrets.""" - def __init__(self, *, id: builtins.str=..., fqdn: builtins.str=..., local_fqdn: builtins.str=..., managed_by: builtins.str=..., signaling_address: builtins.str=..., signaling_insecure: builtins.bool=..., location_secret: builtins.str=..., secret: builtins.str=...) -> None: + def __init__(self, *, id: builtins.str=..., fqdn: builtins.str=..., local_fqdn: builtins.str=..., managed_by: builtins.str=..., signaling_address: builtins.str=..., signaling_insecure: builtins.bool=..., location_secret: builtins.str=..., secret: builtins.str=..., location_secrets: collections.abc.Iterable[global___LocationSecret] | None=..., primary_org_id: builtins.str=..., location_id: builtins.str=..., machine_id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['fqdn', b'fqdn', 'id', b'id', 'local_fqdn', b'local_fqdn', 'location_secret', b'location_secret', 'managed_by', b'managed_by', 'secret', b'secret', 'signaling_address', b'signaling_address', 'signaling_insecure', b'signaling_insecure']) -> None: + def ClearField(self, field_name: typing.Literal['fqdn', b'fqdn', 'id', b'id', 'local_fqdn', b'local_fqdn', 'location_id', b'location_id', 'location_secret', b'location_secret', 'location_secrets', b'location_secrets', 'machine_id', b'machine_id', 'managed_by', b'managed_by', 'primary_org_id', b'primary_org_id', 'secret', b'secret', 'signaling_address', b'signaling_address', 'signaling_insecure', b'signaling_insecure']) -> None: ... global___CloudConfig = CloudConfig +@typing.final class ComponentConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -140,10 +248,15 @@ class ComponentConfig(google.protobuf.message.Message): DEPENDS_ON_FIELD_NUMBER: builtins.int SERVICE_CONFIGS_FIELD_NUMBER: builtins.int ATTRIBUTES_FIELD_NUMBER: builtins.int + API_FIELD_NUMBER: builtins.int + LOG_CONFIGURATION_FIELD_NUMBER: builtins.int name: builtins.str namespace: builtins.str + 'deprecated; use api' type: builtins.str + 'deprecated; use api' model: builtins.str + api: builtins.str @property def frame(self) -> global___Frame: @@ -161,16 +274,21 @@ class ComponentConfig(google.protobuf.message.Message): def attributes(self) -> google.protobuf.struct_pb2.Struct: ... - def __init__(self, *, name: builtins.str=..., namespace: builtins.str=..., type: builtins.str=..., model: builtins.str=..., frame: global___Frame | None=..., depends_on: collections.abc.Iterable[builtins.str] | None=..., service_configs: collections.abc.Iterable[global___ResourceLevelServiceConfig] | None=..., attributes: google.protobuf.struct_pb2.Struct | None=...) -> None: + @property + def log_configuration(self) -> global___LogConfiguration: + ... + + def __init__(self, *, name: builtins.str=..., namespace: builtins.str=..., type: builtins.str=..., model: builtins.str=..., frame: global___Frame | None=..., depends_on: collections.abc.Iterable[builtins.str] | None=..., service_configs: collections.abc.Iterable[global___ResourceLevelServiceConfig] | None=..., attributes: google.protobuf.struct_pb2.Struct | None=..., api: builtins.str=..., log_configuration: global___LogConfiguration | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['attributes', b'attributes', 'frame', b'frame']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['attributes', b'attributes', 'frame', b'frame', 'log_configuration', b'log_configuration']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['attributes', b'attributes', 'depends_on', b'depends_on', 'frame', b'frame', 'model', b'model', 'name', b'name', 'namespace', b'namespace', 'service_configs', b'service_configs', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['api', b'api', 'attributes', b'attributes', 'depends_on', b'depends_on', 'frame', b'frame', 'log_configuration', b'log_configuration', 'model', b'model', 'name', b'name', 'namespace', b'namespace', 'service_configs', b'service_configs', 'type', b'type']) -> None: ... global___ComponentConfig = ComponentConfig +@typing.final class ResourceLevelServiceConfig(google.protobuf.message.Message): """A ResourceLevelServiceConfig describes component or remote configuration for a service.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -185,85 +303,197 @@ class ResourceLevelServiceConfig(google.protobuf.message.Message): def __init__(self, *, type: builtins.str=..., attributes: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['attributes', b'attributes']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['attributes', b'attributes']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['attributes', b'attributes', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['attributes', b'attributes', 'type', b'type']) -> None: ... global___ResourceLevelServiceConfig = ResourceLevelServiceConfig +@typing.final class ProcessConfig(google.protobuf.message.Message): """A ProcessConfig describes how to manage a system process.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class EnvEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + + def __init__(self, *, key: builtins.str=..., value: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... ID_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int ARGS_FIELD_NUMBER: builtins.int CWD_FIELD_NUMBER: builtins.int ONE_SHOT_FIELD_NUMBER: builtins.int LOG_FIELD_NUMBER: builtins.int + STOP_SIGNAL_FIELD_NUMBER: builtins.int + STOP_TIMEOUT_FIELD_NUMBER: builtins.int + ENV_FIELD_NUMBER: builtins.int + USERNAME_FIELD_NUMBER: builtins.int id: builtins.str name: builtins.str + cwd: builtins.str + one_shot: builtins.bool + log: builtins.bool + stop_signal: builtins.int + username: builtins.str @property def args(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... - cwd: builtins.str - one_shot: builtins.bool - log: builtins.bool - def __init__(self, *, id: builtins.str=..., name: builtins.str=..., args: collections.abc.Iterable[builtins.str] | None=..., cwd: builtins.str=..., one_shot: builtins.bool=..., log: builtins.bool=...) -> None: + @property + def stop_timeout(self) -> google.protobuf.duration_pb2.Duration: + ... + + @property + def env(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """additional environment variables passed to the process""" + + def __init__(self, *, id: builtins.str=..., name: builtins.str=..., args: collections.abc.Iterable[builtins.str] | None=..., cwd: builtins.str=..., one_shot: builtins.bool=..., log: builtins.bool=..., stop_signal: builtins.int=..., stop_timeout: google.protobuf.duration_pb2.Duration | None=..., env: collections.abc.Mapping[builtins.str, builtins.str] | None=..., username: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['stop_timeout', b'stop_timeout']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['args', b'args', 'cwd', b'cwd', 'id', b'id', 'log', b'log', 'name', b'name', 'one_shot', b'one_shot']) -> None: + def ClearField(self, field_name: typing.Literal['args', b'args', 'cwd', b'cwd', 'env', b'env', 'id', b'id', 'log', b'log', 'name', b'name', 'one_shot', b'one_shot', 'stop_signal', b'stop_signal', 'stop_timeout', b'stop_timeout', 'username', b'username']) -> None: ... global___ProcessConfig = ProcessConfig +@typing.final class ServiceConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int NAMESPACE_FIELD_NUMBER: builtins.int TYPE_FIELD_NUMBER: builtins.int ATTRIBUTES_FIELD_NUMBER: builtins.int + DEPENDS_ON_FIELD_NUMBER: builtins.int + MODEL_FIELD_NUMBER: builtins.int + API_FIELD_NUMBER: builtins.int + SERVICE_CONFIGS_FIELD_NUMBER: builtins.int + LOG_CONFIGURATION_FIELD_NUMBER: builtins.int name: builtins.str namespace: builtins.str + 'deprecated; use api' type: builtins.str + 'deprecated; use api' + model: builtins.str + api: builtins.str @property def attributes(self) -> google.protobuf.struct_pb2.Struct: ... - def __init__(self, *, name: builtins.str=..., namespace: builtins.str=..., type: builtins.str=..., attributes: google.protobuf.struct_pb2.Struct | None=...) -> None: + @property + def depends_on(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + @property + def service_configs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResourceLevelServiceConfig]: ... - def HasField(self, field_name: typing_extensions.Literal['attributes', b'attributes']) -> builtins.bool: + @property + def log_configuration(self) -> global___LogConfiguration: ... - def ClearField(self, field_name: typing_extensions.Literal['attributes', b'attributes', 'name', b'name', 'namespace', b'namespace', 'type', b'type']) -> None: + def __init__(self, *, name: builtins.str=..., namespace: builtins.str=..., type: builtins.str=..., attributes: google.protobuf.struct_pb2.Struct | None=..., depends_on: collections.abc.Iterable[builtins.str] | None=..., model: builtins.str=..., api: builtins.str=..., service_configs: collections.abc.Iterable[global___ResourceLevelServiceConfig] | None=..., log_configuration: global___LogConfiguration | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['attributes', b'attributes', 'log_configuration', b'log_configuration']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['api', b'api', 'attributes', b'attributes', 'depends_on', b'depends_on', 'log_configuration', b'log_configuration', 'model', b'model', 'name', b'name', 'namespace', b'namespace', 'service_configs', b'service_configs', 'type', b'type']) -> None: ... global___ServiceConfig = ServiceConfig +@typing.final class NetworkConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor FQDN_FIELD_NUMBER: builtins.int BIND_ADDRESS_FIELD_NUMBER: builtins.int TLS_CERT_FILE_FIELD_NUMBER: builtins.int TLS_KEY_FILE_FIELD_NUMBER: builtins.int + SESSIONS_FIELD_NUMBER: builtins.int + TRAFFIC_TUNNEL_ENDPOINTS_FIELD_NUMBER: builtins.int + NO_TLS_FIELD_NUMBER: builtins.int fqdn: builtins.str bind_address: builtins.str tls_cert_file: builtins.str tls_key_file: builtins.str + no_tls: builtins.bool + + @property + def sessions(self) -> global___SessionsConfig: + ... + + @property + def traffic_tunnel_endpoints(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TrafficTunnelEndpoint]: + ... - def __init__(self, *, fqdn: builtins.str=..., bind_address: builtins.str=..., tls_cert_file: builtins.str=..., tls_key_file: builtins.str=...) -> None: + def __init__(self, *, fqdn: builtins.str=..., bind_address: builtins.str=..., tls_cert_file: builtins.str=..., tls_key_file: builtins.str=..., sessions: global___SessionsConfig | None=..., traffic_tunnel_endpoints: collections.abc.Iterable[global___TrafficTunnelEndpoint] | None=..., no_tls: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['bind_address', b'bind_address', 'fqdn', b'fqdn', 'tls_cert_file', b'tls_cert_file', 'tls_key_file', b'tls_key_file']) -> None: + def HasField(self, field_name: typing.Literal['sessions', b'sessions']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['bind_address', b'bind_address', 'fqdn', b'fqdn', 'no_tls', b'no_tls', 'sessions', b'sessions', 'tls_cert_file', b'tls_cert_file', 'tls_key_file', b'tls_key_file', 'traffic_tunnel_endpoints', b'traffic_tunnel_endpoints']) -> None: ... global___NetworkConfig = NetworkConfig +@typing.final +class SessionsConfig(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + HEARTBEAT_WINDOW_FIELD_NUMBER: builtins.int + + @property + def heartbeat_window(self) -> google.protobuf.duration_pb2.Duration: + ... + + def __init__(self, *, heartbeat_window: google.protobuf.duration_pb2.Duration | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['heartbeat_window', b'heartbeat_window']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['heartbeat_window', b'heartbeat_window']) -> None: + ... +global___SessionsConfig = SessionsConfig + +@typing.final +class TrafficTunnelEndpoint(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PORT_FIELD_NUMBER: builtins.int + CONNECTION_TIMEOUT_FIELD_NUMBER: builtins.int + port: builtins.int + + @property + def connection_timeout(self) -> google.protobuf.duration_pb2.Duration: + ... + + def __init__(self, *, port: builtins.int=..., connection_timeout: google.protobuf.duration_pb2.Duration | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['connection_timeout', b'connection_timeout']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['connection_timeout', b'connection_timeout', 'port', b'port']) -> None: + ... +global___TrafficTunnelEndpoint = TrafficTunnelEndpoint + +@typing.final class AuthConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor HANDLERS_FIELD_NUMBER: builtins.int TLS_AUTH_ENTITIES_FIELD_NUMBER: builtins.int + EXTERNAL_AUTH_CONFIG_FIELD_NUMBER: builtins.int @property def handlers(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AuthHandlerConfig]: @@ -273,13 +503,67 @@ class AuthConfig(google.protobuf.message.Message): def tls_auth_entities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... - def __init__(self, *, handlers: collections.abc.Iterable[global___AuthHandlerConfig] | None=..., tls_auth_entities: collections.abc.Iterable[builtins.str] | None=...) -> None: + @property + def external_auth_config(self) -> global___ExternalAuthConfig: + ... + + def __init__(self, *, handlers: collections.abc.Iterable[global___AuthHandlerConfig] | None=..., tls_auth_entities: collections.abc.Iterable[builtins.str] | None=..., external_auth_config: global___ExternalAuthConfig | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_external_auth_config', b'_external_auth_config', 'external_auth_config', b'external_auth_config']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['handlers', b'handlers', 'tls_auth_entities', b'tls_auth_entities']) -> None: + def ClearField(self, field_name: typing.Literal['_external_auth_config', b'_external_auth_config', 'external_auth_config', b'external_auth_config', 'handlers', b'handlers', 'tls_auth_entities', b'tls_auth_entities']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_external_auth_config', b'_external_auth_config']) -> typing.Literal['external_auth_config'] | None: ... global___AuthConfig = AuthConfig +@typing.final +class JWKSFile(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + JSON_FIELD_NUMBER: builtins.int + + @property + def json(self) -> google.protobuf.struct_pb2.Struct: + """JSON Web Keys (JWKS) file as arbitary json. + See https://www.rfc-editor.org/rfc/rfc7517 + """ + + def __init__(self, *, json: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['json', b'json']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['json', b'json']) -> None: + ... +global___JWKSFile = JWKSFile + +@typing.final +class ExternalAuthConfig(google.protobuf.message.Message): + """ExternalAuthConfig describes how a viam managed robot can accept + credentials signed by the cloud app. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + JWKS_FIELD_NUMBER: builtins.int + + @property + def jwks(self) -> global___JWKSFile: + ... + + def __init__(self, *, jwks: global___JWKSFile | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['jwks', b'jwks']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['jwks', b'jwks']) -> None: + ... +global___ExternalAuthConfig = ExternalAuthConfig + +@typing.final class AuthHandlerConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor TYPE_FIELD_NUMBER: builtins.int @@ -293,18 +577,20 @@ class AuthHandlerConfig(google.protobuf.message.Message): def __init__(self, *, type: global___CredentialsType.ValueType=..., config: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['config', b'config']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['config', b'config']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['config', b'config', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['config', b'config', 'type', b'type']) -> None: ... global___AuthHandlerConfig = AuthHandlerConfig +@typing.final class Frame(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor PARENT_FIELD_NUMBER: builtins.int TRANSLATION_FIELD_NUMBER: builtins.int ORIENTATION_FIELD_NUMBER: builtins.int + GEOMETRY_FIELD_NUMBER: builtins.int parent: builtins.str @property @@ -315,16 +601,34 @@ class Frame(google.protobuf.message.Message): def orientation(self) -> global___Orientation: ... - def __init__(self, *, parent: builtins.str=..., translation: global___Translation | None=..., orientation: global___Orientation | None=...) -> None: + @property + def geometry(self) -> common.v1.common_pb2.Geometry: + ... + + def __init__(self, *, parent: builtins.str=..., translation: global___Translation | None=..., orientation: global___Orientation | None=..., geometry: common.v1.common_pb2.Geometry | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['orientation', b'orientation', 'translation', b'translation']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['geometry', b'geometry', 'orientation', b'orientation', 'translation', b'translation']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['orientation', b'orientation', 'parent', b'parent', 'translation', b'translation']) -> None: + def ClearField(self, field_name: typing.Literal['geometry', b'geometry', 'orientation', b'orientation', 'parent', b'parent', 'translation', b'translation']) -> None: ... global___Frame = Frame +@typing.final +class LogConfiguration(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LEVEL_FIELD_NUMBER: builtins.int + level: builtins.str + + def __init__(self, *, level: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['level', b'level']) -> None: + ... +global___LogConfiguration = LogConfiguration + +@typing.final class Translation(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor X_FIELD_NUMBER: builtins.int @@ -337,19 +641,22 @@ class Translation(google.protobuf.message.Message): def __init__(self, *, x: builtins.float=..., y: builtins.float=..., z: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['x', b'x', 'y', b'y', 'z', b'z']) -> None: + def ClearField(self, field_name: typing.Literal['x', b'x', 'y', b'y', 'z', b'z']) -> None: ... global___Translation = Translation +@typing.final class Orientation(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor + @typing.final class NoOrientation(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor def __init__(self) -> None: ... + @typing.final class OrientationVectorRadians(google.protobuf.message.Message): """OrientationVector containing ox, oy, oz, theta represents an orientation vector Structured similarly to an angle axis, an orientation vector works differently. Rather than representing an orientation @@ -374,9 +681,10 @@ class Orientation(google.protobuf.message.Message): def __init__(self, *, theta: builtins.float=..., x: builtins.float=..., y: builtins.float=..., z: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['theta', b'theta', 'x', b'x', 'y', b'y', 'z', b'z']) -> None: + def ClearField(self, field_name: typing.Literal['theta', b'theta', 'x', b'x', 'y', b'y', 'z', b'z']) -> None: ... + @typing.final class OrientationVectorDegrees(google.protobuf.message.Message): """OrientationVectorDegrees is the orientation vector between two objects, but expressed in degrees rather than radians. Because protobuf Pose is in degrees, this is necessary. @@ -394,9 +702,10 @@ class Orientation(google.protobuf.message.Message): def __init__(self, *, theta: builtins.float=..., x: builtins.float=..., y: builtins.float=..., z: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['theta', b'theta', 'x', b'x', 'y', b'y', 'z', b'z']) -> None: + def ClearField(self, field_name: typing.Literal['theta', b'theta', 'x', b'x', 'y', b'y', 'z', b'z']) -> None: ... + @typing.final class EulerAngles(google.protobuf.message.Message): """EulerAngles are three angles (in radians) used to represent the rotation of an object in 3D Euclidean space The Tait–Bryan angle formalism is used, with rotations around three distinct axes in the z-y′-x″ sequence. @@ -412,9 +721,10 @@ class Orientation(google.protobuf.message.Message): def __init__(self, *, roll: builtins.float=..., pitch: builtins.float=..., yaw: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['pitch', b'pitch', 'roll', b'roll', 'yaw', b'yaw']) -> None: + def ClearField(self, field_name: typing.Literal['pitch', b'pitch', 'roll', b'roll', 'yaw', b'yaw']) -> None: ... + @typing.final class AxisAngles(google.protobuf.message.Message): """See here for a thorough explanation: https://en.wikipedia.org/wiki/Axis%E2%80%93angle_representation Basic explanation: Imagine a 3d cartesian grid centered at 0,0,0, and a sphere of radius 1 centered at @@ -437,9 +747,10 @@ class Orientation(google.protobuf.message.Message): def __init__(self, *, theta: builtins.float=..., x: builtins.float=..., y: builtins.float=..., z: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['theta', b'theta', 'x', b'x', 'y', b'y', 'z', b'z']) -> None: + def ClearField(self, field_name: typing.Literal['theta', b'theta', 'x', b'x', 'y', b'y', 'z', b'z']) -> None: ... + @typing.final class Quaternion(google.protobuf.message.Message): """Quaternion is a float64 precision quaternion.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -455,7 +766,7 @@ class Orientation(google.protobuf.message.Message): def __init__(self, *, w: builtins.float=..., x: builtins.float=..., y: builtins.float=..., z: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['w', b'w', 'x', b'x', 'y', b'y', 'z', b'z']) -> None: + def ClearField(self, field_name: typing.Literal['w', b'w', 'x', b'x', 'y', b'y', 'z', b'z']) -> None: ... NO_ORIENTATION_FIELD_NUMBER: builtins.int VECTOR_RADIANS_FIELD_NUMBER: builtins.int @@ -491,16 +802,17 @@ class Orientation(google.protobuf.message.Message): def __init__(self, *, no_orientation: global___Orientation.NoOrientation | None=..., vector_radians: global___Orientation.OrientationVectorRadians | None=..., vector_degrees: global___Orientation.OrientationVectorDegrees | None=..., euler_angles: global___Orientation.EulerAngles | None=..., axis_angles: global___Orientation.AxisAngles | None=..., quaternion: global___Orientation.Quaternion | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['axis_angles', b'axis_angles', 'euler_angles', b'euler_angles', 'no_orientation', b'no_orientation', 'quaternion', b'quaternion', 'type', b'type', 'vector_degrees', b'vector_degrees', 'vector_radians', b'vector_radians']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['axis_angles', b'axis_angles', 'euler_angles', b'euler_angles', 'no_orientation', b'no_orientation', 'quaternion', b'quaternion', 'type', b'type', 'vector_degrees', b'vector_degrees', 'vector_radians', b'vector_radians']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['axis_angles', b'axis_angles', 'euler_angles', b'euler_angles', 'no_orientation', b'no_orientation', 'quaternion', b'quaternion', 'type', b'type', 'vector_degrees', b'vector_degrees', 'vector_radians', b'vector_radians']) -> None: + def ClearField(self, field_name: typing.Literal['axis_angles', b'axis_angles', 'euler_angles', b'euler_angles', 'no_orientation', b'no_orientation', 'quaternion', b'quaternion', 'type', b'type', 'vector_degrees', b'vector_degrees', 'vector_radians', b'vector_radians']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['type', b'type']) -> typing_extensions.Literal['no_orientation', 'vector_radians', 'vector_degrees', 'euler_angles', 'axis_angles', 'quaternion'] | None: + def WhichOneof(self, oneof_group: typing.Literal['type', b'type']) -> typing.Literal['no_orientation', 'vector_radians', 'vector_degrees', 'euler_angles', 'axis_angles', 'quaternion'] | None: ... global___Orientation = Orientation +@typing.final class RemoteConfig(google.protobuf.message.Message): """A RemoteConfig describes a remote robot that should be integrated. The Frame field defines how the "world" node of the remote robot should be reconciled with the "world" node of the @@ -520,6 +832,10 @@ class RemoteConfig(google.protobuf.message.Message): SECRET_FIELD_NUMBER: builtins.int name: builtins.str address: builtins.str + managed_by: builtins.str + insecure: builtins.bool + secret: builtins.str + 'Secret is a helper for a robot location secret.' @property def frame(self) -> global___Frame: @@ -528,8 +844,6 @@ class RemoteConfig(google.protobuf.message.Message): @property def auth(self) -> global___RemoteAuth: ... - managed_by: builtins.str - insecure: builtins.bool @property def connection_check_interval(self) -> google.protobuf.duration_pb2.Duration: @@ -542,19 +856,18 @@ class RemoteConfig(google.protobuf.message.Message): @property def service_configs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResourceLevelServiceConfig]: ... - secret: builtins.str - 'Secret is a helper for a robot location secret.' def __init__(self, *, name: builtins.str=..., address: builtins.str=..., frame: global___Frame | None=..., auth: global___RemoteAuth | None=..., managed_by: builtins.str=..., insecure: builtins.bool=..., connection_check_interval: google.protobuf.duration_pb2.Duration | None=..., reconnect_interval: google.protobuf.duration_pb2.Duration | None=..., service_configs: collections.abc.Iterable[global___ResourceLevelServiceConfig] | None=..., secret: builtins.str=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['auth', b'auth', 'connection_check_interval', b'connection_check_interval', 'frame', b'frame', 'reconnect_interval', b'reconnect_interval']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['auth', b'auth', 'connection_check_interval', b'connection_check_interval', 'frame', b'frame', 'reconnect_interval', b'reconnect_interval']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['address', b'address', 'auth', b'auth', 'connection_check_interval', b'connection_check_interval', 'frame', b'frame', 'insecure', b'insecure', 'managed_by', b'managed_by', 'name', b'name', 'reconnect_interval', b'reconnect_interval', 'secret', b'secret', 'service_configs', b'service_configs']) -> None: + def ClearField(self, field_name: typing.Literal['address', b'address', 'auth', b'auth', 'connection_check_interval', b'connection_check_interval', 'frame', b'frame', 'insecure', b'insecure', 'managed_by', b'managed_by', 'name', b'name', 'reconnect_interval', b'reconnect_interval', 'secret', b'secret', 'service_configs', b'service_configs']) -> None: ... global___RemoteConfig = RemoteConfig +@typing.final class RemoteAuth(google.protobuf.message.Message): """RemoteAuth specifies how to authenticate against a remote. If no credentials are specified, authentication does not happen. If an entity is specified, the @@ -562,6 +875,7 @@ class RemoteAuth(google.protobuf.message.Message): """ DESCRIPTOR: google.protobuf.descriptor.Descriptor + @typing.final class Credentials(google.protobuf.message.Message): """Credentials packages up both a type of credential along with its payload which is formatted specific to the type. @@ -575,26 +889,27 @@ class RemoteAuth(google.protobuf.message.Message): def __init__(self, *, type: global___CredentialsType.ValueType=..., payload: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['payload', b'payload', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['payload', b'payload', 'type', b'type']) -> None: ... CREDENTIALS_FIELD_NUMBER: builtins.int ENTITY_FIELD_NUMBER: builtins.int + entity: builtins.str @property def credentials(self) -> global___RemoteAuth.Credentials: ... - entity: builtins.str def __init__(self, *, credentials: global___RemoteAuth.Credentials | None=..., entity: builtins.str=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['credentials', b'credentials']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['credentials', b'credentials']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['credentials', b'credentials', 'entity', b'entity']) -> None: + def ClearField(self, field_name: typing.Literal['credentials', b'credentials', 'entity', b'entity']) -> None: ... global___RemoteAuth = RemoteAuth +@typing.final class AgentInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor HOST_FIELD_NUMBER: builtins.int @@ -602,23 +917,39 @@ class AgentInfo(google.protobuf.message.Message): IPS_FIELD_NUMBER: builtins.int VERSION_FIELD_NUMBER: builtins.int GIT_REVISION_FIELD_NUMBER: builtins.int + PLATFORM_FIELD_NUMBER: builtins.int + PLATFORM_TAGS_FIELD_NUMBER: builtins.int host: builtins.str os: builtins.str + 'Will soon be deprecated, use platform instead' + version: builtins.str + 'RDK version' + git_revision: builtins.str + platform: builtins.str + 'The platform the RDK is running on. For example linux/amd64' @property def ips(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: """list of all ipv4 ips.""" - version: builtins.str - 'RDK version' - git_revision: builtins.str - def __init__(self, *, host: builtins.str=..., os: builtins.str=..., ips: collections.abc.Iterable[builtins.str] | None=..., version: builtins.str=..., git_revision: builtins.str=...) -> None: + @property + def platform_tags(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Optional tags to further constrain which artifact is returned for modules.""" + + def __init__(self, *, host: builtins.str=..., os: builtins.str=..., ips: collections.abc.Iterable[builtins.str] | None=..., version: builtins.str=..., git_revision: builtins.str=..., platform: builtins.str | None=..., platform_tags: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_platform', b'_platform', 'platform', b'platform']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['git_revision', b'git_revision', 'host', b'host', 'ips', b'ips', 'os', b'os', 'version', b'version']) -> None: + def ClearField(self, field_name: typing.Literal['_platform', b'_platform', 'git_revision', b'git_revision', 'host', b'host', 'ips', b'ips', 'os', b'os', 'platform', b'platform', 'platform_tags', b'platform_tags', 'version', b'version']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_platform', b'_platform']) -> typing.Literal['platform'] | None: ... global___AgentInfo = AgentInfo +@typing.final class ConfigRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -633,16 +964,17 @@ class ConfigRequest(google.protobuf.message.Message): def __init__(self, *, id: builtins.str=..., agent_info: global___AgentInfo | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['_agent_info', b'_agent_info', 'agent_info', b'agent_info']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['_agent_info', b'_agent_info', 'agent_info', b'agent_info']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['_agent_info', b'_agent_info', 'agent_info', b'agent_info', 'id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['_agent_info', b'_agent_info', 'agent_info', b'agent_info', 'id', b'id']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['_agent_info', b'_agent_info']) -> typing_extensions.Literal['agent_info'] | None: + def WhichOneof(self, oneof_group: typing.Literal['_agent_info', b'_agent_info']) -> typing.Literal['agent_info'] | None: ... global___ConfigRequest = ConfigRequest +@typing.final class ConfigResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor CONFIG_FIELD_NUMBER: builtins.int @@ -654,13 +986,14 @@ class ConfigResponse(google.protobuf.message.Message): def __init__(self, *, config: global___RobotConfig | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['config', b'config']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['config', b'config']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['config', b'config']) -> None: + def ClearField(self, field_name: typing.Literal['config', b'config']) -> None: ... global___ConfigResponse = ConfigResponse +@typing.final class CertificateRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -670,10 +1003,11 @@ class CertificateRequest(google.protobuf.message.Message): def __init__(self, *, id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: ... global___CertificateRequest = CertificateRequest +@typing.final class CertificateResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -687,10 +1021,11 @@ class CertificateResponse(google.protobuf.message.Message): def __init__(self, *, id: builtins.str=..., tls_certificate: builtins.str=..., tls_private_key: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id', 'tls_certificate', b'tls_certificate', 'tls_private_key', b'tls_private_key']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id', 'tls_certificate', b'tls_certificate', 'tls_private_key', b'tls_private_key']) -> None: ... global___CertificateResponse = CertificateResponse +@typing.final class LogRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -699,16 +1034,17 @@ class LogRequest(google.protobuf.message.Message): 'Robot part id.' @property - def logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[app.v1.app_pb2.LogEntry]: + def logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.LogEntry]: ... - def __init__(self, *, id: builtins.str=..., logs: collections.abc.Iterable[app.v1.app_pb2.LogEntry] | None=...) -> None: + def __init__(self, *, id: builtins.str=..., logs: collections.abc.Iterable[common.v1.common_pb2.LogEntry] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id', 'logs', b'logs']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id', 'logs', b'logs']) -> None: ... global___LogRequest = LogRequest +@typing.final class LogResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -716,6 +1052,7 @@ class LogResponse(google.protobuf.message.Message): ... global___LogResponse = LogResponse +@typing.final class NeedsRestartRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -725,10 +1062,11 @@ class NeedsRestartRequest(google.protobuf.message.Message): def __init__(self, *, id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: ... global___NeedsRestartRequest = NeedsRestartRequest +@typing.final class NeedsRestartResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -745,9 +1083,120 @@ class NeedsRestartResponse(google.protobuf.message.Message): def __init__(self, *, id: builtins.str=..., must_restart: builtins.bool=..., restart_check_interval: google.protobuf.duration_pb2.Duration | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['restart_check_interval', b'restart_check_interval']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['restart_check_interval', b'restart_check_interval']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id', 'must_restart', b'must_restart', 'restart_check_interval', b'restart_check_interval']) -> None: + ... +global___NeedsRestartResponse = NeedsRestartResponse + +@typing.final +class ModuleConfig(google.protobuf.message.Message): + """ModuleConfig is the configuration for a module.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class EnvEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + + def __init__(self, *, key: builtins.str=..., value: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... + NAME_FIELD_NUMBER: builtins.int + PATH_FIELD_NUMBER: builtins.int + LOG_LEVEL_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + MODULE_ID_FIELD_NUMBER: builtins.int + ENV_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + FIRST_RUN_TIMEOUT_FIELD_NUMBER: builtins.int + name: builtins.str + path: builtins.str + 'path to the executable' + log_level: builtins.str + 'log level for module' + type: builtins.str + 'type of the module ("local" or "registry")' + module_id: builtins.str + 'the id of the module if it is a registry module' + + @property + def env(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """additional environment variables passed to the module process""" + + @property + def status(self) -> global___AppValidationStatus: + """info about the validity of the module""" + + @property + def first_run_timeout(self) -> google.protobuf.duration_pb2.Duration: + """timeout for first_run script""" + + def __init__(self, *, name: builtins.str=..., path: builtins.str=..., log_level: builtins.str=..., type: builtins.str=..., module_id: builtins.str=..., env: collections.abc.Mapping[builtins.str, builtins.str] | None=..., status: global___AppValidationStatus | None=..., first_run_timeout: google.protobuf.duration_pb2.Duration | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['first_run_timeout', b'first_run_timeout', 'status', b'status']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['env', b'env', 'first_run_timeout', b'first_run_timeout', 'log_level', b'log_level', 'module_id', b'module_id', 'name', b'name', 'path', b'path', 'status', b'status', 'type', b'type']) -> None: + ... +global___ModuleConfig = ModuleConfig + +@typing.final +class PackageConfig(google.protobuf.message.Message): + """PackageConfig is the configration for deployed Packages.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + PACKAGE_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name is the local name of the package on the RDK. Must be unique across Packages. Must not be empty.' + package: builtins.str + 'Package is the unique package name hosted by Viam. Must not be empty.' + version: builtins.str + 'version of the package ID hosted by Viam. If not specified "latest" is assumed.' + type: builtins.str + 'type of the package' + + @property + def status(self) -> global___AppValidationStatus: + """info about the validity of the package""" + + def __init__(self, *, name: builtins.str=..., package: builtins.str=..., version: builtins.str=..., type: builtins.str=..., status: global___AppValidationStatus | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['status', b'status']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name', 'package', b'package', 'status', b'status', 'type', b'type', 'version', b'version']) -> None: + ... +global___PackageConfig = PackageConfig + +@typing.final +class MaintenanceConfig(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SENSOR_NAME_FIELD_NUMBER: builtins.int + MAINTENANCE_ALLOWED_KEY_FIELD_NUMBER: builtins.int + maintenance_allowed_key: builtins.str + + @property + def sensor_name(self) -> common.v1.common_pb2.ResourceName: + ... + + def __init__(self, *, sensor_name: common.v1.common_pb2.ResourceName | None=..., maintenance_allowed_key: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['sensor_name', b'sensor_name']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id', 'must_restart', b'must_restart', 'restart_check_interval', b'restart_check_interval']) -> None: + def ClearField(self, field_name: typing.Literal['maintenance_allowed_key', b'maintenance_allowed_key', 'sensor_name', b'sensor_name']) -> None: ... -global___NeedsRestartResponse = NeedsRestartResponse \ No newline at end of file +global___MaintenanceConfig = MaintenanceConfig \ No newline at end of file diff --git a/src/viam/gen/common/v1/common_pb2.py b/src/viam/gen/common/v1/common_pb2.py index f3639a916..a287926c6 100644 --- a/src/viam/gen/common/v1/common_pb2.py +++ b/src/viam/gen/common/v1/common_pb2.py @@ -1,54 +1,78 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'common/v1/common.proto') _sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16common/v1/common.proto\x12\x0eviam.common.v1"n\n\x0cResourceName\x12\x1c\n\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x12\n\x04type\x18\x02 \x01(\tR\x04type\x12\x18\n\x07subtype\x18\x03 \x01(\tR\x07subtype\x12\x12\n\x04name\x18\x04 \x01(\tR\x04name"\xfc\x02\n\x0bBoardStatus\x12B\n\x07analogs\x18\x01 \x03(\x0b2(.viam.common.v1.BoardStatus.AnalogsEntryR\x07analogs\x12a\n\x12digital_interrupts\x18\x02 \x03(\x0b22.viam.common.v1.BoardStatus.DigitalInterruptsEntryR\x11digitalInterrupts\x1aX\n\x0cAnalogsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x122\n\x05value\x18\x02 \x01(\x0b2\x1c.viam.common.v1.AnalogStatusR\x05value:\x028\x01\x1al\n\x16DigitalInterruptsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12<\n\x05value\x18\x02 \x01(\x0b2&.viam.common.v1.DigitalInterruptStatusR\x05value:\x028\x01"$\n\x0cAnalogStatus\x12\x14\n\x05value\x18\x01 \x01(\x05R\x05value".\n\x16DigitalInterruptStatus\x12\x14\n\x05value\x18\x01 \x01(\x03R\x05value"y\n\x04Pose\x12\x0c\n\x01x\x18\x01 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x02 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x03 \x01(\x01R\x01z\x12\x0f\n\x03o_x\x18\x04 \x01(\x01R\x02oX\x12\x0f\n\x03o_y\x18\x05 \x01(\x01R\x02oY\x12\x0f\n\x03o_z\x18\x06 \x01(\x01R\x02oZ\x12\x14\n\x05theta\x18\x07 \x01(\x01R\x05theta"V\n\x0bOrientation\x12\x0f\n\x03o_x\x18\x01 \x01(\x01R\x02oX\x12\x0f\n\x03o_y\x18\x02 \x01(\x01R\x02oY\x12\x0f\n\x03o_z\x18\x03 \x01(\x01R\x02oZ\x12\x14\n\x05theta\x18\x04 \x01(\x01R\x05theta"`\n\x0bPoseInFrame\x12\'\n\x0freference_frame\x18\x01 \x01(\tR\x0ereferenceFrame\x12(\n\x04pose\x18\x02 \x01(\x0b2\x14.viam.common.v1.PoseR\x04pose"3\n\x07Vector3\x12\x0c\n\x01x\x18\x01 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x02 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x03 \x01(\x01R\x01z"%\n\x06Sphere\x12\x1b\n\tradius_mm\x18\x01 \x01(\x01R\x08radiusMm"D\n\x10RectangularPrism\x120\n\x07dims_mm\x18\x01 \x01(\x0b2\x17.viam.common.v1.Vector3R\x06dimsMm"\xb1\x01\n\x08Geometry\x12,\n\x06center\x18\x01 \x01(\x0b2\x14.viam.common.v1.PoseR\x06center\x120\n\x06sphere\x18\x02 \x01(\x0b2\x16.viam.common.v1.SphereH\x00R\x06sphere\x124\n\x03box\x18\x03 \x01(\x0b2 .viam.common.v1.RectangularPrismH\x00R\x03boxB\x0f\n\rgeometry_type"v\n\x11GeometriesInFrame\x12\'\n\x0freference_frame\x18\x01 \x01(\tR\x0ereferenceFrame\x128\n\ngeometries\x18\x02 \x03(\x0b2\x18.viam.common.v1.GeometryR\ngeometries"v\n\x10PointCloudObject\x12\x1f\n\x0bpoint_cloud\x18\x01 \x01(\x0cR\npointCloud\x12A\n\ngeometries\x18\x02 \x01(\x0b2!.viam.common.v1.GeometriesInFrameR\ngeometries"D\n\x08GeoPoint\x12\x1a\n\x08latitude\x18\x01 \x01(\x01R\x08latitude\x12\x1c\n\tlongitude\x18\x02 \x01(\x01R\tlongitude"\x86\x01\n\tTransform\x12\'\n\x0freference_frame\x18\x01 \x01(\tR\x0ereferenceFrame\x12P\n\x16pose_in_observer_frame\x18\x02 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x13poseInObserverFrame"\xda\x01\n\nWorldState\x12?\n\tobstacles\x18\x01 \x03(\x0b2!.viam.common.v1.GeometriesInFrameR\tobstacles\x12P\n\x12interaction_spaces\x18\x02 \x03(\x0b2!.viam.common.v1.GeometriesInFrameR\x11interactionSpaces\x129\n\ntransforms\x18\x03 \x03(\x0b2\x19.viam.common.v1.TransformR\ntransforms"-\n\x0eActuatorStatus\x12\x1b\n\tis_moving\x18\x01 \x01(\x08R\x08isMovingB/\n\x12com.viam.common.v1Z\x19go.viam.com/api/common/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'common.v1.common_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x12com.viam.common.v1Z\x19go.viam.com/api/common/v1' - _BOARDSTATUS_ANALOGSENTRY._options = None - _BOARDSTATUS_ANALOGSENTRY._serialized_options = b'8\x01' - _BOARDSTATUS_DIGITALINTERRUPTSENTRY._options = None - _BOARDSTATUS_DIGITALINTERRUPTSENTRY._serialized_options = b'8\x01' - _RESOURCENAME._serialized_start = 42 - _RESOURCENAME._serialized_end = 152 - _BOARDSTATUS._serialized_start = 155 - _BOARDSTATUS._serialized_end = 535 - _BOARDSTATUS_ANALOGSENTRY._serialized_start = 337 - _BOARDSTATUS_ANALOGSENTRY._serialized_end = 425 - _BOARDSTATUS_DIGITALINTERRUPTSENTRY._serialized_start = 427 - _BOARDSTATUS_DIGITALINTERRUPTSENTRY._serialized_end = 535 - _ANALOGSTATUS._serialized_start = 537 - _ANALOGSTATUS._serialized_end = 573 - _DIGITALINTERRUPTSTATUS._serialized_start = 575 - _DIGITALINTERRUPTSTATUS._serialized_end = 621 - _POSE._serialized_start = 623 - _POSE._serialized_end = 744 - _ORIENTATION._serialized_start = 746 - _ORIENTATION._serialized_end = 832 - _POSEINFRAME._serialized_start = 834 - _POSEINFRAME._serialized_end = 930 - _VECTOR3._serialized_start = 932 - _VECTOR3._serialized_end = 983 - _SPHERE._serialized_start = 985 - _SPHERE._serialized_end = 1022 - _RECTANGULARPRISM._serialized_start = 1024 - _RECTANGULARPRISM._serialized_end = 1092 - _GEOMETRY._serialized_start = 1095 - _GEOMETRY._serialized_end = 1272 - _GEOMETRIESINFRAME._serialized_start = 1274 - _GEOMETRIESINFRAME._serialized_end = 1392 - _POINTCLOUDOBJECT._serialized_start = 1394 - _POINTCLOUDOBJECT._serialized_end = 1512 - _GEOPOINT._serialized_start = 1514 - _GEOPOINT._serialized_end = 1582 - _TRANSFORM._serialized_start = 1585 - _TRANSFORM._serialized_end = 1719 - _WORLDSTATE._serialized_start = 1722 - _WORLDSTATE._serialized_end = 1940 - _ACTUATORSTATUS._serialized_start = 1942 - _ACTUATORSTATUS._serialized_end = 1987 \ No newline at end of file +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16common/v1/common.proto\x12\x0eviam.common.v1\x1a google/protobuf/descriptor.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\xae\x01\n\x0cResourceName\x12\x1c\n\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x12\n\x04type\x18\x02 \x01(\tR\x04type\x12\x18\n\x07subtype\x18\x03 \x01(\tR\x07subtype\x12\x12\n\x04name\x18\x04 \x01(\tR\x04name\x12\x1f\n\x0bremote_path\x18\x05 \x03(\tR\nremotePath\x12\x1d\n\nlocal_name\x18\x06 \x01(\tR\tlocalName"y\n\x04Pose\x12\x0c\n\x01x\x18\x01 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x02 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x03 \x01(\x01R\x01z\x12\x0f\n\x03o_x\x18\x04 \x01(\x01R\x02oX\x12\x0f\n\x03o_y\x18\x05 \x01(\x01R\x02oY\x12\x0f\n\x03o_z\x18\x06 \x01(\x01R\x02oZ\x12\x14\n\x05theta\x18\x07 \x01(\x01R\x05theta"V\n\x0bOrientation\x12\x0f\n\x03o_x\x18\x01 \x01(\x01R\x02oX\x12\x0f\n\x03o_y\x18\x02 \x01(\x01R\x02oY\x12\x0f\n\x03o_z\x18\x03 \x01(\x01R\x02oZ\x12\x14\n\x05theta\x18\x04 \x01(\x01R\x05theta"`\n\x0bPoseInFrame\x12\'\n\x0freference_frame\x18\x01 \x01(\tR\x0ereferenceFrame\x12(\n\x04pose\x18\x02 \x01(\x0b2\x14.viam.common.v1.PoseR\x04pose"3\n\x07Vector3\x12\x0c\n\x01x\x18\x01 \x01(\x01R\x01x\x12\x0c\n\x01y\x18\x02 \x01(\x01R\x01y\x12\x0c\n\x01z\x18\x03 \x01(\x01R\x01z"%\n\x06Sphere\x12\x1b\n\tradius_mm\x18\x01 \x01(\x01R\x08radiusMm"C\n\x07Capsule\x12\x1b\n\tradius_mm\x18\x01 \x01(\x01R\x08radiusMm\x12\x1b\n\tlength_mm\x18\x02 \x01(\x01R\x08lengthMm"D\n\x10RectangularPrism\x120\n\x07dims_mm\x18\x01 \x01(\x0b2\x17.viam.common.v1.Vector3R\x06dimsMm"=\n\x04Mesh\x12!\n\x0ccontent_type\x18\x01 \x01(\tR\x0bcontentType\x12\x12\n\x04mesh\x18\x02 \x01(\x0cR\x04mesh"\xa8\x02\n\x08Geometry\x12,\n\x06center\x18\x01 \x01(\x0b2\x14.viam.common.v1.PoseR\x06center\x120\n\x06sphere\x18\x02 \x01(\x0b2\x16.viam.common.v1.SphereH\x00R\x06sphere\x124\n\x03box\x18\x03 \x01(\x0b2 .viam.common.v1.RectangularPrismH\x00R\x03box\x123\n\x07capsule\x18\x05 \x01(\x0b2\x17.viam.common.v1.CapsuleH\x00R\x07capsule\x12*\n\x04mesh\x18\x06 \x01(\x0b2\x14.viam.common.v1.MeshH\x00R\x04mesh\x12\x14\n\x05label\x18\x04 \x01(\tR\x05labelB\x0f\n\rgeometry_type"v\n\x11GeometriesInFrame\x12\'\n\x0freference_frame\x18\x01 \x01(\tR\x0ereferenceFrame\x128\n\ngeometries\x18\x02 \x03(\x0b2\x18.viam.common.v1.GeometryR\ngeometries"v\n\x10PointCloudObject\x12\x1f\n\x0bpoint_cloud\x18\x01 \x01(\x0cR\npointCloud\x12A\n\ngeometries\x18\x02 \x01(\x0b2!.viam.common.v1.GeometriesInFrameR\ngeometries"D\n\x08GeoPoint\x12\x1a\n\x08latitude\x18\x01 \x01(\x01R\x08latitude\x12\x1c\n\tlongitude\x18\x02 \x01(\x01R\tlongitude"}\n\x0bGeoGeometry\x124\n\x08location\x18\x01 \x01(\x0b2\x18.viam.common.v1.GeoPointR\x08location\x128\n\ngeometries\x18\x02 \x03(\x0b2\x18.viam.common.v1.GeometryR\ngeometries"\xe2\x01\n\tTransform\x12\'\n\x0freference_frame\x18\x01 \x01(\tR\x0ereferenceFrame\x12P\n\x16pose_in_observer_frame\x18\x02 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x13poseInObserverFrame\x12F\n\x0fphysical_object\x18\x03 \x01(\x0b2\x18.viam.common.v1.GeometryH\x00R\x0ephysicalObject\x88\x01\x01B\x12\n\x10_physical_object"\x88\x01\n\nWorldState\x12?\n\tobstacles\x18\x01 \x03(\x0b2!.viam.common.v1.GeometriesInFrameR\tobstacles\x129\n\ntransforms\x18\x03 \x03(\x0b2\x19.viam.common.v1.TransformR\ntransforms"-\n\x0eActuatorStatus\x12\x1b\n\tis_moving\x18\x01 \x01(\x08R\x08isMoving"d\n\x10ResponseMetadata\x12@\n\x0bcaptured_at\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampH\x00R\ncapturedAt\x88\x01\x01B\x0e\n\x0c_captured_at"Y\n\x10DoCommandRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x121\n\x07command\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x07command"D\n\x11DoCommandResponse\x12/\n\x06result\x18\x01 \x01(\x0b2\x17.google.protobuf.StructR\x06result"Y\n\x14GetKinematicsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"~\n\x15GetKinematicsResponse\x12<\n\x06format\x18\x01 \x01(\x0e2$.viam.common.v1.KinematicsFileFormatR\x06format\x12\'\n\x0fkinematics_data\x18\x02 \x01(\x0cR\x0ekinematicsData"Y\n\x14GetGeometriesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"Q\n\x15GetGeometriesResponse\x128\n\ngeometries\x18\x01 \x03(\x0b2\x18.viam.common.v1.GeometryR\ngeometries"W\n\x12GetReadingsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\xb9\x01\n\x13GetReadingsResponse\x12M\n\x08readings\x18\x01 \x03(\x0b21.viam.common.v1.GetReadingsResponse.ReadingsEntryR\x08readings\x1aS\n\rReadingsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12,\n\x05value\x18\x02 \x01(\x0b2\x16.google.protobuf.ValueR\x05value:\x028\x01"\x97\x02\n\x08LogEntry\x12\x12\n\x04host\x18\x01 \x01(\tR\x04host\x12\x14\n\x05level\x18\x02 \x01(\tR\x05level\x12.\n\x04time\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\x04time\x12\x1f\n\x0blogger_name\x18\x04 \x01(\tR\nloggerName\x12\x18\n\x07message\x18\x05 \x01(\tR\x07message\x12/\n\x06caller\x18\x06 \x01(\x0b2\x17.google.protobuf.StructR\x06caller\x12\x14\n\x05stack\x18\x07 \x01(\tR\x05stack\x12/\n\x06fields\x18\x08 \x03(\x0b2\x17.google.protobuf.StructR\x06fields*\x7f\n\x14KinematicsFileFormat\x12&\n"KINEMATICS_FILE_FORMAT_UNSPECIFIED\x10\x00\x12\x1e\n\x1aKINEMATICS_FILE_FORMAT_SVA\x10\x01\x12\x1f\n\x1bKINEMATICS_FILE_FORMAT_URDF\x10\x02:a\n\x1asafety_heartbeat_monitored\x12\x1e.google.protobuf.MethodOptions\x18\xa4\x92\x05 \x01(\x08R\x18safetyHeartbeatMonitored\x88\x01\x01B/\n\x12com.viam.common.v1Z\x19go.viam.com/api/common/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'common.v1.common_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x12com.viam.common.v1Z\x19go.viam.com/api/common/v1' + _globals['_GETREADINGSRESPONSE_READINGSENTRY']._loaded_options = None + _globals['_GETREADINGSRESPONSE_READINGSENTRY']._serialized_options = b'8\x01' + _globals['_KINEMATICSFILEFORMAT']._serialized_start = 3285 + _globals['_KINEMATICSFILEFORMAT']._serialized_end = 3412 + _globals['_RESOURCENAME']._serialized_start = 140 + _globals['_RESOURCENAME']._serialized_end = 314 + _globals['_POSE']._serialized_start = 316 + _globals['_POSE']._serialized_end = 437 + _globals['_ORIENTATION']._serialized_start = 439 + _globals['_ORIENTATION']._serialized_end = 525 + _globals['_POSEINFRAME']._serialized_start = 527 + _globals['_POSEINFRAME']._serialized_end = 623 + _globals['_VECTOR3']._serialized_start = 625 + _globals['_VECTOR3']._serialized_end = 676 + _globals['_SPHERE']._serialized_start = 678 + _globals['_SPHERE']._serialized_end = 715 + _globals['_CAPSULE']._serialized_start = 717 + _globals['_CAPSULE']._serialized_end = 784 + _globals['_RECTANGULARPRISM']._serialized_start = 786 + _globals['_RECTANGULARPRISM']._serialized_end = 854 + _globals['_MESH']._serialized_start = 856 + _globals['_MESH']._serialized_end = 917 + _globals['_GEOMETRY']._serialized_start = 920 + _globals['_GEOMETRY']._serialized_end = 1216 + _globals['_GEOMETRIESINFRAME']._serialized_start = 1218 + _globals['_GEOMETRIESINFRAME']._serialized_end = 1336 + _globals['_POINTCLOUDOBJECT']._serialized_start = 1338 + _globals['_POINTCLOUDOBJECT']._serialized_end = 1456 + _globals['_GEOPOINT']._serialized_start = 1458 + _globals['_GEOPOINT']._serialized_end = 1526 + _globals['_GEOGEOMETRY']._serialized_start = 1528 + _globals['_GEOGEOMETRY']._serialized_end = 1653 + _globals['_TRANSFORM']._serialized_start = 1656 + _globals['_TRANSFORM']._serialized_end = 1882 + _globals['_WORLDSTATE']._serialized_start = 1885 + _globals['_WORLDSTATE']._serialized_end = 2021 + _globals['_ACTUATORSTATUS']._serialized_start = 2023 + _globals['_ACTUATORSTATUS']._serialized_end = 2068 + _globals['_RESPONSEMETADATA']._serialized_start = 2070 + _globals['_RESPONSEMETADATA']._serialized_end = 2170 + _globals['_DOCOMMANDREQUEST']._serialized_start = 2172 + _globals['_DOCOMMANDREQUEST']._serialized_end = 2261 + _globals['_DOCOMMANDRESPONSE']._serialized_start = 2263 + _globals['_DOCOMMANDRESPONSE']._serialized_end = 2331 + _globals['_GETKINEMATICSREQUEST']._serialized_start = 2333 + _globals['_GETKINEMATICSREQUEST']._serialized_end = 2422 + _globals['_GETKINEMATICSRESPONSE']._serialized_start = 2424 + _globals['_GETKINEMATICSRESPONSE']._serialized_end = 2550 + _globals['_GETGEOMETRIESREQUEST']._serialized_start = 2552 + _globals['_GETGEOMETRIESREQUEST']._serialized_end = 2641 + _globals['_GETGEOMETRIESRESPONSE']._serialized_start = 2643 + _globals['_GETGEOMETRIESRESPONSE']._serialized_end = 2724 + _globals['_GETREADINGSREQUEST']._serialized_start = 2726 + _globals['_GETREADINGSREQUEST']._serialized_end = 2813 + _globals['_GETREADINGSRESPONSE']._serialized_start = 2816 + _globals['_GETREADINGSRESPONSE']._serialized_end = 3001 + _globals['_GETREADINGSRESPONSE_READINGSENTRY']._serialized_start = 2918 + _globals['_GETREADINGSRESPONSE_READINGSENTRY']._serialized_end = 3001 + _globals['_LOGENTRY']._serialized_start = 3004 + _globals['_LOGENTRY']._serialized_end = 3283 \ No newline at end of file diff --git a/src/viam/gen/common/v1/common_pb2.pyi b/src/viam/gen/common/v1/common_pb2.pyi index 56dad5987..be939d793 100644 --- a/src/viam/gen/common/v1/common_pb2.pyi +++ b/src/viam/gen/common/v1/common_pb2.pyi @@ -5,117 +5,65 @@ The following is a list of messages that are used across multiple resource subty import builtins import collections.abc import google.protobuf.descriptor +import google.protobuf.descriptor_pb2 import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.internal.extension_dict import google.protobuf.message +import google.protobuf.struct_pb2 +import google.protobuf.timestamp_pb2 import sys -if sys.version_info >= (3, 8): +import typing +if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _KinematicsFileFormat: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _KinematicsFileFormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_KinematicsFileFormat.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + KINEMATICS_FILE_FORMAT_UNSPECIFIED: _KinematicsFileFormat.ValueType + KINEMATICS_FILE_FORMAT_SVA: _KinematicsFileFormat.ValueType + KINEMATICS_FILE_FORMAT_URDF: _KinematicsFileFormat.ValueType + +class KinematicsFileFormat(_KinematicsFileFormat, metaclass=_KinematicsFileFormatEnumTypeWrapper): + ... +KINEMATICS_FILE_FORMAT_UNSPECIFIED: KinematicsFileFormat.ValueType +KINEMATICS_FILE_FORMAT_SVA: KinematicsFileFormat.ValueType +KINEMATICS_FILE_FORMAT_URDF: KinematicsFileFormat.ValueType +global___KinematicsFileFormat = KinematicsFileFormat + +@typing.final class ResourceName(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAMESPACE_FIELD_NUMBER: builtins.int TYPE_FIELD_NUMBER: builtins.int SUBTYPE_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int + REMOTE_PATH_FIELD_NUMBER: builtins.int + LOCAL_NAME_FIELD_NUMBER: builtins.int namespace: builtins.str type: builtins.str subtype: builtins.str name: builtins.str - - def __init__(self, *, namespace: builtins.str=..., type: builtins.str=..., subtype: builtins.str=..., name: builtins.str=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['name', b'name', 'namespace', b'namespace', 'subtype', b'subtype', 'type', b'type']) -> None: - ... -global___ResourceName = ResourceName - -class BoardStatus(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - class AnalogsEntry(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - KEY_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - key: builtins.str - - @property - def value(self) -> global___AnalogStatus: - ... - - def __init__(self, *, key: builtins.str=..., value: global___AnalogStatus | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: - ... - - class DigitalInterruptsEntry(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - KEY_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - key: builtins.str - - @property - def value(self) -> global___DigitalInterruptStatus: - ... - - def __init__(self, *, key: builtins.str=..., value: global___DigitalInterruptStatus | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: - ... - ANALOGS_FIELD_NUMBER: builtins.int - DIGITAL_INTERRUPTS_FIELD_NUMBER: builtins.int + local_name: builtins.str @property - def analogs(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___AnalogStatus]: - ... - - @property - def digital_interrupts(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___DigitalInterruptStatus]: - ... - - def __init__(self, *, analogs: collections.abc.Mapping[builtins.str, global___AnalogStatus] | None=..., digital_interrupts: collections.abc.Mapping[builtins.str, global___DigitalInterruptStatus] | None=...) -> None: + def remote_path(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... - def ClearField(self, field_name: typing_extensions.Literal['analogs', b'analogs', 'digital_interrupts', b'digital_interrupts']) -> None: + def __init__(self, *, namespace: builtins.str=..., type: builtins.str=..., subtype: builtins.str=..., name: builtins.str=..., remote_path: collections.abc.Iterable[builtins.str] | None=..., local_name: builtins.str=...) -> None: ... -global___BoardStatus = BoardStatus -class AnalogStatus(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - VALUE_FIELD_NUMBER: builtins.int - value: builtins.int - "Current value of the analog reader of a robot's board" - - def __init__(self, *, value: builtins.int=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['value', b'value']) -> None: - ... -global___AnalogStatus = AnalogStatus - -class DigitalInterruptStatus(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - VALUE_FIELD_NUMBER: builtins.int - value: builtins.int - "Current value of the digital interrupt of a robot's board" - - def __init__(self, *, value: builtins.int=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['local_name', b'local_name', 'name', b'name', 'namespace', b'namespace', 'remote_path', b'remote_path', 'subtype', b'subtype', 'type', b'type']) -> None: ... -global___DigitalInterruptStatus = DigitalInterruptStatus +global___ResourceName = ResourceName +@typing.final class Pose(google.protobuf.message.Message): """Pose is a combination of location and orientation. Location is expressed as distance which is represented by x , y, z coordinates. Orientation is expressed as an orientation vector which @@ -152,10 +100,11 @@ class Pose(google.protobuf.message.Message): def __init__(self, *, x: builtins.float=..., y: builtins.float=..., z: builtins.float=..., o_x: builtins.float=..., o_y: builtins.float=..., o_z: builtins.float=..., theta: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['o_x', b'o_x', 'o_y', b'o_y', 'o_z', b'o_z', 'theta', b'theta', 'x', b'x', 'y', b'y', 'z', b'z']) -> None: + def ClearField(self, field_name: typing.Literal['o_x', b'o_x', 'o_y', b'o_y', 'o_z', b'o_z', 'theta', b'theta', 'x', b'x', 'y', b'y', 'z', b'z']) -> None: ... global___Pose = Pose +@typing.final class Orientation(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor O_X_FIELD_NUMBER: builtins.int @@ -174,10 +123,11 @@ class Orientation(google.protobuf.message.Message): def __init__(self, *, o_x: builtins.float=..., o_y: builtins.float=..., o_z: builtins.float=..., theta: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['o_x', b'o_x', 'o_y', b'o_y', 'o_z', b'o_z', 'theta', b'theta']) -> None: + def ClearField(self, field_name: typing.Literal['o_x', b'o_x', 'o_y', b'o_y', 'o_z', b'o_z', 'theta', b'theta']) -> None: ... global___Orientation = Orientation +@typing.final class PoseInFrame(google.protobuf.message.Message): """PoseInFrame contains a pose and the and the reference frame in which it was observed""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -192,13 +142,14 @@ class PoseInFrame(google.protobuf.message.Message): def __init__(self, *, reference_frame: builtins.str=..., pose: global___Pose | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['pose', b'pose']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['pose', b'pose']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['pose', b'pose', 'reference_frame', b'reference_frame']) -> None: + def ClearField(self, field_name: typing.Literal['pose', b'pose', 'reference_frame', b'reference_frame']) -> None: ... global___PoseInFrame = PoseInFrame +@typing.final class Vector3(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor X_FIELD_NUMBER: builtins.int @@ -211,10 +162,11 @@ class Vector3(google.protobuf.message.Message): def __init__(self, *, x: builtins.float=..., y: builtins.float=..., z: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['x', b'x', 'y', b'y', 'z', b'z']) -> None: + def ClearField(self, field_name: typing.Literal['x', b'x', 'y', b'y', 'z', b'z']) -> None: ... global___Vector3 = Vector3 +@typing.final class Sphere(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor RADIUS_MM_FIELD_NUMBER: builtins.int @@ -223,10 +175,26 @@ class Sphere(google.protobuf.message.Message): def __init__(self, *, radius_mm: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['radius_mm', b'radius_mm']) -> None: + def ClearField(self, field_name: typing.Literal['radius_mm', b'radius_mm']) -> None: ... global___Sphere = Sphere +@typing.final +class Capsule(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RADIUS_MM_FIELD_NUMBER: builtins.int + LENGTH_MM_FIELD_NUMBER: builtins.int + radius_mm: builtins.float + length_mm: builtins.float + + def __init__(self, *, radius_mm: builtins.float=..., length_mm: builtins.float=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['length_mm', b'length_mm', 'radius_mm', b'radius_mm']) -> None: + ... +global___Capsule = Capsule + +@typing.final class RectangularPrism(google.protobuf.message.Message): """RectangularPrism contains a Vector3 field corresponding to the X, Y, Z dimensions of the prism in mms These dimensions are with respect to the referenceframe in which the RectangularPrism is defined @@ -241,23 +209,46 @@ class RectangularPrism(google.protobuf.message.Message): def __init__(self, *, dims_mm: global___Vector3 | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['dims_mm', b'dims_mm']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['dims_mm', b'dims_mm']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['dims_mm', b'dims_mm']) -> None: + def ClearField(self, field_name: typing.Literal['dims_mm', b'dims_mm']) -> None: ... global___RectangularPrism = RectangularPrism +@typing.final +class Mesh(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CONTENT_TYPE_FIELD_NUMBER: builtins.int + MESH_FIELD_NUMBER: builtins.int + content_type: builtins.str + 'Content type of mesh (e.g. ply)' + mesh: builtins.bytes + 'Contents of mesh data in binary form defined by content_type' + + def __init__(self, *, content_type: builtins.str=..., mesh: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['content_type', b'content_type', 'mesh', b'mesh']) -> None: + ... +global___Mesh = Mesh + +@typing.final class Geometry(google.protobuf.message.Message): """Geometry contains the dimensions of a given geometry and the pose of its center. The geometry is one of either a sphere or a box.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor CENTER_FIELD_NUMBER: builtins.int SPHERE_FIELD_NUMBER: builtins.int BOX_FIELD_NUMBER: builtins.int + CAPSULE_FIELD_NUMBER: builtins.int + MESH_FIELD_NUMBER: builtins.int + LABEL_FIELD_NUMBER: builtins.int + label: builtins.str + 'Label of the geometry. If none supplied, will be an empty string.' @property def center(self) -> global___Pose: - """Pose of a gemetries center point""" + """Pose of a geometries center point""" @property def sphere(self) -> global___Sphere: @@ -267,19 +258,28 @@ class Geometry(google.protobuf.message.Message): def box(self) -> global___RectangularPrism: ... - def __init__(self, *, center: global___Pose | None=..., sphere: global___Sphere | None=..., box: global___RectangularPrism | None=...) -> None: + @property + def capsule(self) -> global___Capsule: + ... + + @property + def mesh(self) -> global___Mesh: + ... + + def __init__(self, *, center: global___Pose | None=..., sphere: global___Sphere | None=..., box: global___RectangularPrism | None=..., capsule: global___Capsule | None=..., mesh: global___Mesh | None=..., label: builtins.str=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['box', b'box', 'center', b'center', 'geometry_type', b'geometry_type', 'sphere', b'sphere']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['box', b'box', 'capsule', b'capsule', 'center', b'center', 'geometry_type', b'geometry_type', 'mesh', b'mesh', 'sphere', b'sphere']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['box', b'box', 'center', b'center', 'geometry_type', b'geometry_type', 'sphere', b'sphere']) -> None: + def ClearField(self, field_name: typing.Literal['box', b'box', 'capsule', b'capsule', 'center', b'center', 'geometry_type', b'geometry_type', 'label', b'label', 'mesh', b'mesh', 'sphere', b'sphere']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['geometry_type', b'geometry_type']) -> typing_extensions.Literal['sphere', 'box'] | None: + def WhichOneof(self, oneof_group: typing.Literal['geometry_type', b'geometry_type']) -> typing.Literal['sphere', 'box', 'capsule', 'mesh'] | None: ... global___Geometry = Geometry +@typing.final class GeometriesInFrame(google.protobuf.message.Message): """GeometriesinFrame contains the dimensions of a given geometry, pose of its center point, and the reference frame by which it was observed. @@ -297,10 +297,11 @@ class GeometriesInFrame(google.protobuf.message.Message): def __init__(self, *, reference_frame: builtins.str=..., geometries: collections.abc.Iterable[global___Geometry] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['geometries', b'geometries', 'reference_frame', b'reference_frame']) -> None: + def ClearField(self, field_name: typing.Literal['geometries', b'geometries', 'reference_frame', b'reference_frame']) -> None: ... global___GeometriesInFrame = GeometriesInFrame +@typing.final class PointCloudObject(google.protobuf.message.Message): """PointCloudObject contains an image in bytes with point cloud data of all of the objects captured by a given observer as well as a repeated list of geometries which respresents the center point and geometry of each of the objects within the point cloud @@ -318,13 +319,14 @@ class PointCloudObject(google.protobuf.message.Message): def __init__(self, *, point_cloud: builtins.bytes=..., geometries: global___GeometriesInFrame | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['geometries', b'geometries']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['geometries', b'geometries']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['geometries', b'geometries', 'point_cloud', b'point_cloud']) -> None: + def ClearField(self, field_name: typing.Literal['geometries', b'geometries', 'point_cloud', b'point_cloud']) -> None: ... global___PointCloudObject = PointCloudObject +@typing.final class GeoPoint(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor LATITUDE_FIELD_NUMBER: builtins.int @@ -335,10 +337,36 @@ class GeoPoint(google.protobuf.message.Message): def __init__(self, *, latitude: builtins.float=..., longitude: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['latitude', b'latitude', 'longitude', b'longitude']) -> None: + def ClearField(self, field_name: typing.Literal['latitude', b'latitude', 'longitude', b'longitude']) -> None: ... global___GeoPoint = GeoPoint +@typing.final +class GeoGeometry(google.protobuf.message.Message): + """GeoGeometry contains information describing Geometry(s) that is located at a GeoPoint""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOCATION_FIELD_NUMBER: builtins.int + GEOMETRIES_FIELD_NUMBER: builtins.int + + @property + def location(self) -> global___GeoPoint: + """Location of the geometry""" + + @property + def geometries(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Geometry]: + """Geometries associated with the location, where embedded Pose data is with respect to the specified location""" + + def __init__(self, *, location: global___GeoPoint | None=..., geometries: collections.abc.Iterable[global___Geometry] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['location', b'location']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['geometries', b'geometries', 'location', b'location']) -> None: + ... +global___GeoGeometry = GeoGeometry + +@typing.final class Transform(google.protobuf.message.Message): """Transform contains a pose and two reference frames. The first reference frame is the starting reference frame, and the second reference frame is the observer reference frame. The second reference frame has a pose which represents the pose of an object in the first @@ -347,6 +375,7 @@ class Transform(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor REFERENCE_FRAME_FIELD_NUMBER: builtins.int POSE_IN_OBSERVER_FRAME_FIELD_NUMBER: builtins.int + PHYSICAL_OBJECT_FIELD_NUMBER: builtins.int reference_frame: builtins.str 'the name of a given reference frame' @@ -354,16 +383,24 @@ class Transform(google.protobuf.message.Message): def pose_in_observer_frame(self) -> global___PoseInFrame: """the pose of the above reference frame with respect to a different observer reference frame""" - def __init__(self, *, reference_frame: builtins.str=..., pose_in_observer_frame: global___PoseInFrame | None=...) -> None: + @property + def physical_object(self) -> global___Geometry: + ... + + def __init__(self, *, reference_frame: builtins.str=..., pose_in_observer_frame: global___PoseInFrame | None=..., physical_object: global___Geometry | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_physical_object', b'_physical_object', 'physical_object', b'physical_object', 'pose_in_observer_frame', b'pose_in_observer_frame']) -> builtins.bool: ... - def HasField(self, field_name: typing_extensions.Literal['pose_in_observer_frame', b'pose_in_observer_frame']) -> builtins.bool: + def ClearField(self, field_name: typing.Literal['_physical_object', b'_physical_object', 'physical_object', b'physical_object', 'pose_in_observer_frame', b'pose_in_observer_frame', 'reference_frame', b'reference_frame']) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['pose_in_observer_frame', b'pose_in_observer_frame', 'reference_frame', b'reference_frame']) -> None: + def WhichOneof(self, oneof_group: typing.Literal['_physical_object', b'_physical_object']) -> typing.Literal['physical_object'] | None: ... global___Transform = Transform +@typing.final class WorldState(google.protobuf.message.Message): """WorldState contains information about the physical environment around a given robot. All of the fields within this message are optional, they can include information about the physical dimensions of an obstacle, the freespace of a robot, and any desired transforms between a @@ -371,30 +408,26 @@ class WorldState(google.protobuf.message.Message): """ DESCRIPTOR: google.protobuf.descriptor.Descriptor OBSTACLES_FIELD_NUMBER: builtins.int - INTERACTION_SPACES_FIELD_NUMBER: builtins.int TRANSFORMS_FIELD_NUMBER: builtins.int @property def obstacles(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___GeometriesInFrame]: """a list of obstacles expressed as a geometry and the reference frame in which it was observed; this field is optional""" - @property - def interaction_spaces(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___GeometriesInFrame]: - """a list of spaces the robot is allowed to operate within expressed as a geometry and the reference frame it is measured fom; - this field is optional - """ - @property def transforms(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Transform]: - """a list of Transforms needed to transform a pose from one reference frame to another; this field is optional""" + """a list of Transforms, optionally with geometries. Used as supplemental transforms to transform a pose from one reference frame to + another, or to attach moving geometries to the frame system. This field is optional + """ - def __init__(self, *, obstacles: collections.abc.Iterable[global___GeometriesInFrame] | None=..., interaction_spaces: collections.abc.Iterable[global___GeometriesInFrame] | None=..., transforms: collections.abc.Iterable[global___Transform] | None=...) -> None: + def __init__(self, *, obstacles: collections.abc.Iterable[global___GeometriesInFrame] | None=..., transforms: collections.abc.Iterable[global___Transform] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['interaction_spaces', b'interaction_spaces', 'obstacles', b'obstacles', 'transforms', b'transforms']) -> None: + def ClearField(self, field_name: typing.Literal['obstacles', b'obstacles', 'transforms', b'transforms']) -> None: ... global___WorldState = WorldState +@typing.final class ActuatorStatus(google.protobuf.message.Message): """ActuatorStatus is a generic status for resources that only need to return actuator status.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -404,6 +437,251 @@ class ActuatorStatus(google.protobuf.message.Message): def __init__(self, *, is_moving: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['is_moving', b'is_moving']) -> None: + def ClearField(self, field_name: typing.Literal['is_moving', b'is_moving']) -> None: + ... +global___ActuatorStatus = ActuatorStatus + +@typing.final +class ResponseMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CAPTURED_AT_FIELD_NUMBER: builtins.int + + @property + def captured_at(self) -> google.protobuf.timestamp_pb2.Timestamp: + """captured_at is the time at which the resource as close as physically possible, captured + the data in the response. + Note: If correlating between other resources, be sure that the means + of measuring the capture are similar enough such that comparison can be made between them. + """ + + def __init__(self, *, captured_at: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_captured_at', b'_captured_at', 'captured_at', b'captured_at']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_captured_at', b'_captured_at', 'captured_at', b'captured_at']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_captured_at', b'_captured_at']) -> typing.Literal['captured_at'] | None: + ... +global___ResponseMetadata = ResponseMetadata + +@typing.final +class DoCommandRequest(google.protobuf.message.Message): + """DoCommandRequest represents a generic DoCommand input""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + COMMAND_FIELD_NUMBER: builtins.int + name: builtins.str + + @property + def command(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., command: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['command', b'command']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['command', b'command', 'name', b'name']) -> None: + ... +global___DoCommandRequest = DoCommandRequest + +@typing.final +class DoCommandResponse(google.protobuf.message.Message): + """DoCommandResponse represents a generic DoCommand output""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESULT_FIELD_NUMBER: builtins.int + + @property + def result(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, result: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['result', b'result']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['result', b'result']) -> None: + ... +global___DoCommandResponse = DoCommandResponse + +@typing.final +class GetKinematicsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'The component name' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetKinematicsRequest = GetKinematicsRequest + +@typing.final +class GetKinematicsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FORMAT_FIELD_NUMBER: builtins.int + KINEMATICS_DATA_FIELD_NUMBER: builtins.int + format: global___KinematicsFileFormat.ValueType + 'The kinematics of the component, in either URDF format or in Viam’s kinematic parameter format (spatial vector algebra)\n https://docs.viam.com/internals/kinematic-chain-config/#kinematic-parameters\n ' + kinematics_data: builtins.bytes + 'The byte contents of the file' + + def __init__(self, *, format: global___KinematicsFileFormat.ValueType=..., kinematics_data: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['format', b'format', 'kinematics_data', b'kinematics_data']) -> None: + ... +global___GetKinematicsResponse = GetKinematicsResponse + +@typing.final +class GetGeometriesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'The component name' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetGeometriesRequest = GetGeometriesRequest + +@typing.final +class GetGeometriesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + GEOMETRIES_FIELD_NUMBER: builtins.int + + @property + def geometries(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Geometry]: + """All geometries associated with the component, in their current configuration, in the frame of that component.""" + + def __init__(self, *, geometries: collections.abc.Iterable[global___Geometry] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['geometries', b'geometries']) -> None: + ... +global___GetGeometriesResponse = GetGeometriesResponse + +@typing.final +class GetReadingsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of a sensor' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetReadingsRequest = GetReadingsRequest + +@typing.final +class GetReadingsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class ReadingsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + + @property + def value(self) -> google.protobuf.struct_pb2.Value: + ... + + def __init__(self, *, key: builtins.str=..., value: google.protobuf.struct_pb2.Value | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['value', b'value']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... + READINGS_FIELD_NUMBER: builtins.int + + @property + def readings(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, google.protobuf.struct_pb2.Value]: + ... + + def __init__(self, *, readings: collections.abc.Mapping[builtins.str, google.protobuf.struct_pb2.Value] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['readings', b'readings']) -> None: + ... +global___GetReadingsResponse = GetReadingsResponse + +@typing.final +class LogEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + HOST_FIELD_NUMBER: builtins.int + LEVEL_FIELD_NUMBER: builtins.int + TIME_FIELD_NUMBER: builtins.int + LOGGER_NAME_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + CALLER_FIELD_NUMBER: builtins.int + STACK_FIELD_NUMBER: builtins.int + FIELDS_FIELD_NUMBER: builtins.int + host: builtins.str + level: builtins.str + logger_name: builtins.str + message: builtins.str + stack: builtins.str + + @property + def time(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + @property + def caller(self) -> google.protobuf.struct_pb2.Struct: + ... + + @property + def fields(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[google.protobuf.struct_pb2.Struct]: + ... + + def __init__(self, *, host: builtins.str=..., level: builtins.str=..., time: google.protobuf.timestamp_pb2.Timestamp | None=..., logger_name: builtins.str=..., message: builtins.str=..., caller: google.protobuf.struct_pb2.Struct | None=..., stack: builtins.str=..., fields: collections.abc.Iterable[google.protobuf.struct_pb2.Struct] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['caller', b'caller', 'time', b'time']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['caller', b'caller', 'fields', b'fields', 'host', b'host', 'level', b'level', 'logger_name', b'logger_name', 'message', b'message', 'stack', b'stack', 'time', b'time']) -> None: ... -global___ActuatorStatus = ActuatorStatus \ No newline at end of file +global___LogEntry = LogEntry +SAFETY_HEARTBEAT_MONITORED_FIELD_NUMBER: builtins.int +safety_heartbeat_monitored: google.protobuf.internal.extension_dict._ExtensionFieldDescriptor[google.protobuf.descriptor_pb2.MethodOptions, builtins.bool] +'safety_heartbeat_monitored is used on methods to signify that if a session is in use\nand the session was the last to call this method, the resource associated with the\nmethod will be stopped.\n' \ No newline at end of file diff --git a/src/viam/gen/component/arm/v1/arm_grpc.py b/src/viam/gen/component/arm/v1/arm_grpc.py index ee7e82abb..643de47c5 100644 --- a/src/viam/gen/component/arm/v1/arm_grpc.py +++ b/src/viam/gen/component/arm/v1/arm_grpc.py @@ -2,6 +2,7 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common @@ -27,12 +28,64 @@ async def GetJointPositions(self, stream: 'grpclib.server.Stream[component.arm.v async def MoveToJointPositions(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.MoveToJointPositionsRequest, component.arm.v1.arm_pb2.MoveToJointPositionsResponse]') -> None: pass + @abc.abstractmethod + async def MoveThroughJointPositions(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.MoveThroughJointPositionsRequest, component.arm.v1.arm_pb2.MoveThroughJointPositionsResponse]') -> None: + pass + @abc.abstractmethod async def Stop(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.StopRequest, component.arm.v1.arm_pb2.StopResponse]') -> None: pass + @abc.abstractmethod + async def IsMoving(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.IsMovingRequest, component.arm.v1.arm_pb2.IsMovingResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetKinematics(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetKinematicsRequest, common.v1.common_pb2.GetKinematicsResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.arm.v1.ArmService/GetEndPosition': grpclib.const.Handler(self.GetEndPosition, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.GetEndPositionRequest, component.arm.v1.arm_pb2.GetEndPositionResponse), '/viam.component.arm.v1.ArmService/MoveToPosition': grpclib.const.Handler(self.MoveToPosition, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.MoveToPositionRequest, component.arm.v1.arm_pb2.MoveToPositionResponse), '/viam.component.arm.v1.ArmService/GetJointPositions': grpclib.const.Handler(self.GetJointPositions, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.GetJointPositionsRequest, component.arm.v1.arm_pb2.GetJointPositionsResponse), '/viam.component.arm.v1.ArmService/MoveToJointPositions': grpclib.const.Handler(self.MoveToJointPositions, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.MoveToJointPositionsRequest, component.arm.v1.arm_pb2.MoveToJointPositionsResponse), '/viam.component.arm.v1.ArmService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.StopRequest, component.arm.v1.arm_pb2.StopResponse)} + return {'/viam.component.arm.v1.ArmService/GetEndPosition': grpclib.const.Handler(self.GetEndPosition, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.GetEndPositionRequest, component.arm.v1.arm_pb2.GetEndPositionResponse), '/viam.component.arm.v1.ArmService/MoveToPosition': grpclib.const.Handler(self.MoveToPosition, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.MoveToPositionRequest, component.arm.v1.arm_pb2.MoveToPositionResponse), '/viam.component.arm.v1.ArmService/GetJointPositions': grpclib.const.Handler(self.GetJointPositions, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.GetJointPositionsRequest, component.arm.v1.arm_pb2.GetJointPositionsResponse), '/viam.component.arm.v1.ArmService/MoveToJointPositions': grpclib.const.Handler(self.MoveToJointPositions, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.MoveToJointPositionsRequest, component.arm.v1.arm_pb2.MoveToJointPositionsResponse), '/viam.component.arm.v1.ArmService/MoveThroughJointPositions': grpclib.const.Handler(self.MoveThroughJointPositions, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.MoveThroughJointPositionsRequest, component.arm.v1.arm_pb2.MoveThroughJointPositionsResponse), '/viam.component.arm.v1.ArmService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.StopRequest, component.arm.v1.arm_pb2.StopResponse), '/viam.component.arm.v1.ArmService/IsMoving': grpclib.const.Handler(self.IsMoving, grpclib.const.Cardinality.UNARY_UNARY, component.arm.v1.arm_pb2.IsMovingRequest, component.arm.v1.arm_pb2.IsMovingResponse), '/viam.component.arm.v1.ArmService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.arm.v1.ArmService/GetKinematics': grpclib.const.Handler(self.GetKinematics, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetKinematicsRequest, common.v1.common_pb2.GetKinematicsResponse), '/viam.component.arm.v1.ArmService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedArmServiceBase(ArmServiceBase): + + async def GetEndPosition(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.GetEndPositionRequest, component.arm.v1.arm_pb2.GetEndPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def MoveToPosition(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.MoveToPositionRequest, component.arm.v1.arm_pb2.MoveToPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetJointPositions(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.GetJointPositionsRequest, component.arm.v1.arm_pb2.GetJointPositionsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def MoveToJointPositions(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.MoveToJointPositionsRequest, component.arm.v1.arm_pb2.MoveToJointPositionsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def MoveThroughJointPositions(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.MoveThroughJointPositionsRequest, component.arm.v1.arm_pb2.MoveThroughJointPositionsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Stop(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.StopRequest, component.arm.v1.arm_pb2.StopResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def IsMoving(self, stream: 'grpclib.server.Stream[component.arm.v1.arm_pb2.IsMovingRequest, component.arm.v1.arm_pb2.IsMovingResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetKinematics(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetKinematicsRequest, common.v1.common_pb2.GetKinematicsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class ArmServiceStub: @@ -41,4 +94,9 @@ def __init__(self, channel: grpclib.client.Channel) -> None: self.MoveToPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.arm.v1.ArmService/MoveToPosition', component.arm.v1.arm_pb2.MoveToPositionRequest, component.arm.v1.arm_pb2.MoveToPositionResponse) self.GetJointPositions = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.arm.v1.ArmService/GetJointPositions', component.arm.v1.arm_pb2.GetJointPositionsRequest, component.arm.v1.arm_pb2.GetJointPositionsResponse) self.MoveToJointPositions = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.arm.v1.ArmService/MoveToJointPositions', component.arm.v1.arm_pb2.MoveToJointPositionsRequest, component.arm.v1.arm_pb2.MoveToJointPositionsResponse) - self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.arm.v1.ArmService/Stop', component.arm.v1.arm_pb2.StopRequest, component.arm.v1.arm_pb2.StopResponse) \ No newline at end of file + self.MoveThroughJointPositions = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.arm.v1.ArmService/MoveThroughJointPositions', component.arm.v1.arm_pb2.MoveThroughJointPositionsRequest, component.arm.v1.arm_pb2.MoveThroughJointPositionsResponse) + self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.arm.v1.ArmService/Stop', component.arm.v1.arm_pb2.StopRequest, component.arm.v1.arm_pb2.StopResponse) + self.IsMoving = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.arm.v1.ArmService/IsMoving', component.arm.v1.arm_pb2.IsMovingRequest, component.arm.v1.arm_pb2.IsMovingResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.arm.v1.ArmService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetKinematics = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.arm.v1.ArmService/GetKinematics', common.v1.common_pb2.GetKinematicsRequest, common.v1.common_pb2.GetKinematicsResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.arm.v1.ArmService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/arm/v1/arm_pb2.py b/src/viam/gen/component/arm/v1/arm_pb2.py index 5862dd3d2..4c3a92368 100644 --- a/src/viam/gen/component/arm/v1/arm_pb2.py +++ b/src/viam/gen/component/arm/v1/arm_pb2.py @@ -1,51 +1,74 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/arm/v1/arm.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1acomponent/arm/v1/arm.proto\x12\x15viam.component.arm.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"Z\n\x15GetEndPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"B\n\x16GetEndPositionResponse\x12(\n\x04pose\x18\x01 \x01(\x0b2\x14.viam.common.v1.PoseR\x04pose"(\n\x0eJointPositions\x12\x16\n\x06values\x18\x01 \x03(\x01R\x06values"]\n\x18GetJointPositionsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"`\n\x19GetJointPositionsResponse\x12C\n\tpositions\x18\x01 \x01(\x0b2%.viam.component.arm.v1.JointPositionsR\tpositions"\xd2\x01\n\x15MoveToPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12$\n\x02to\x18\x02 \x01(\x0b2\x14.viam.common.v1.PoseR\x02to\x12@\n\x0bworld_state\x18\x03 \x01(\x0b2\x1a.viam.common.v1.WorldStateH\x00R\nworldState\x88\x01\x01\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\x0e\n\x0c_world_state"\x18\n\x16MoveToPositionResponse"\xa5\x01\n\x1bMoveToJointPositionsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12C\n\tpositions\x18\x02 \x01(\x0b2%.viam.component.arm.v1.JointPositionsR\tpositions\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x1e\n\x1cMoveToJointPositionsResponse"P\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cStopResponse"\xae\x01\n\x06Status\x127\n\x0cend_position\x18\x01 \x01(\x0b2\x14.viam.common.v1.PoseR\x0bendPosition\x12N\n\x0fjoint_positions\x18\x02 \x01(\x0b2%.viam.component.arm.v1.JointPositionsR\x0ejointPositions\x12\x1b\n\tis_moving\x18\x03 \x01(\x08R\x08isMoving2\xc6\x06\n\nArmService\x12\xa1\x01\n\x0eGetEndPosition\x12,.viam.component.arm.v1.GetEndPositionRequest\x1a-.viam.component.arm.v1.GetEndPositionResponse"2\x82\xd3\xe4\x93\x02,\x12*/viam/api/v1/component/arm/{name}/position\x12\xa1\x01\n\x0eMoveToPosition\x12,.viam.component.arm.v1.MoveToPositionRequest\x1a-.viam.component.arm.v1.MoveToPositionResponse"2\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/arm/{name}/position\x12\xb1\x01\n\x11GetJointPositions\x12/.viam.component.arm.v1.GetJointPositionsRequest\x1a0.viam.component.arm.v1.GetJointPositionsResponse"9\x82\xd3\xe4\x93\x023\x121/viam/api/v1/component/arm/{name}/joint_positions\x12\xba\x01\n\x14MoveToJointPositions\x122.viam.component.arm.v1.MoveToJointPositionsRequest\x1a3.viam.component.arm.v1.MoveToJointPositionsResponse"9\x82\xd3\xe4\x93\x023\x1a1/viam/api/v1/component/arm/{name}/joint_positions\x12\x7f\n\x04Stop\x12".viam.component.arm.v1.StopRequest\x1a#.viam.component.arm.v1.StopResponse".\x82\xd3\xe4\x93\x02("&/viam/api/v1/component/arm/{name}/stopB=\n\x19com.viam.component.arm.v1Z go.viam.com/api/component/arm/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.arm.v1.arm_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x19com.viam.component.arm.v1Z go.viam.com/api/component/arm/v1' - _ARMSERVICE.methods_by_name['GetEndPosition']._options = None - _ARMSERVICE.methods_by_name['GetEndPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02,\x12*/viam/api/v1/component/arm/{name}/position' - _ARMSERVICE.methods_by_name['MoveToPosition']._options = None - _ARMSERVICE.methods_by_name['MoveToPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/arm/{name}/position' - _ARMSERVICE.methods_by_name['GetJointPositions']._options = None - _ARMSERVICE.methods_by_name['GetJointPositions']._serialized_options = b'\x82\xd3\xe4\x93\x023\x121/viam/api/v1/component/arm/{name}/joint_positions' - _ARMSERVICE.methods_by_name['MoveToJointPositions']._options = None - _ARMSERVICE.methods_by_name['MoveToJointPositions']._serialized_options = b'\x82\xd3\xe4\x93\x023\x1a1/viam/api/v1/component/arm/{name}/joint_positions' - _ARMSERVICE.methods_by_name['Stop']._options = None - _ARMSERVICE.methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02("&/viam/api/v1/component/arm/{name}/stop' - _GETENDPOSITIONREQUEST._serialized_start = 137 - _GETENDPOSITIONREQUEST._serialized_end = 227 - _GETENDPOSITIONRESPONSE._serialized_start = 229 - _GETENDPOSITIONRESPONSE._serialized_end = 295 - _JOINTPOSITIONS._serialized_start = 297 - _JOINTPOSITIONS._serialized_end = 337 - _GETJOINTPOSITIONSREQUEST._serialized_start = 339 - _GETJOINTPOSITIONSREQUEST._serialized_end = 432 - _GETJOINTPOSITIONSRESPONSE._serialized_start = 434 - _GETJOINTPOSITIONSRESPONSE._serialized_end = 530 - _MOVETOPOSITIONREQUEST._serialized_start = 533 - _MOVETOPOSITIONREQUEST._serialized_end = 743 - _MOVETOPOSITIONRESPONSE._serialized_start = 745 - _MOVETOPOSITIONRESPONSE._serialized_end = 769 - _MOVETOJOINTPOSITIONSREQUEST._serialized_start = 772 - _MOVETOJOINTPOSITIONSREQUEST._serialized_end = 937 - _MOVETOJOINTPOSITIONSRESPONSE._serialized_start = 939 - _MOVETOJOINTPOSITIONSRESPONSE._serialized_end = 969 - _STOPREQUEST._serialized_start = 971 - _STOPREQUEST._serialized_end = 1051 - _STOPRESPONSE._serialized_start = 1053 - _STOPRESPONSE._serialized_end = 1067 - _STATUS._serialized_start = 1070 - _STATUS._serialized_end = 1244 - _ARMSERVICE._serialized_start = 1247 - _ARMSERVICE._serialized_end = 2085 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1acomponent/arm/v1/arm.proto\x12\x15viam.component.arm.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"Z\n\x15GetEndPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"B\n\x16GetEndPositionResponse\x12(\n\x04pose\x18\x01 \x01(\x0b2\x14.viam.common.v1.PoseR\x04pose"(\n\x0eJointPositions\x12\x16\n\x06values\x18\x01 \x03(\x01R\x06values"]\n\x18GetJointPositionsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"`\n\x19GetJointPositionsResponse\x12C\n\tpositions\x18\x01 \x01(\x0b2%.viam.component.arm.v1.JointPositionsR\tpositions"\x80\x01\n\x15MoveToPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12$\n\x02to\x18\x02 \x01(\x0b2\x14.viam.common.v1.PoseR\x02to\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x18\n\x16MoveToPositionResponse"\xa5\x01\n\x1bMoveToJointPositionsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12C\n\tpositions\x18\x02 \x01(\x0b2%.viam.component.arm.v1.JointPositionsR\tpositions\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x1e\n\x1cMoveToJointPositionsResponse"\xf9\x01\n MoveThroughJointPositionsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12C\n\tpositions\x18\x02 \x03(\x0b2%.viam.component.arm.v1.JointPositionsR\tpositions\x12A\n\x07options\x18\x03 \x01(\x0b2".viam.component.arm.v1.MoveOptionsH\x00R\x07options\x88\x01\x01\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\n\n\x08_options"#\n!MoveThroughJointPositionsResponse"P\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cStopResponse"\xae\x01\n\x06Status\x127\n\x0cend_position\x18\x01 \x01(\x0b2\x14.viam.common.v1.PoseR\x0bendPosition\x12N\n\x0fjoint_positions\x18\x02 \x01(\x0b2%.viam.component.arm.v1.JointPositionsR\x0ejointPositions\x12\x1b\n\tis_moving\x18\x03 \x01(\x08R\x08isMoving"%\n\x0fIsMovingRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"/\n\x10IsMovingResponse\x12\x1b\n\tis_moving\x18\x01 \x01(\x08R\x08isMoving"\xac\x01\n\x0bMoveOptions\x123\n\x14max_vel_degs_per_sec\x18\x01 \x01(\x01H\x00R\x10maxVelDegsPerSec\x88\x01\x01\x125\n\x15max_acc_degs_per_sec2\x18\x02 \x01(\x01H\x01R\x11maxAccDegsPerSec2\x88\x01\x01B\x17\n\x15_max_vel_degs_per_secB\x18\n\x16_max_acc_degs_per_sec22\xf1\x0c\n\nArmService\x12\xa1\x01\n\x0eGetEndPosition\x12,.viam.component.arm.v1.GetEndPositionRequest\x1a-.viam.component.arm.v1.GetEndPositionResponse"2\x82\xd3\xe4\x93\x02,\x12*/viam/api/v1/component/arm/{name}/position\x12\xa5\x01\n\x0eMoveToPosition\x12,.viam.component.arm.v1.MoveToPositionRequest\x1a-.viam.component.arm.v1.MoveToPositionResponse"6\xa0\x92)\x01\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/arm/{name}/position\x12\xb1\x01\n\x11GetJointPositions\x12/.viam.component.arm.v1.GetJointPositionsRequest\x1a0.viam.component.arm.v1.GetJointPositionsResponse"9\x82\xd3\xe4\x93\x023\x121/viam/api/v1/component/arm/{name}/joint_positions\x12\xbe\x01\n\x14MoveToJointPositions\x122.viam.component.arm.v1.MoveToJointPositionsRequest\x1a3.viam.component.arm.v1.MoveToJointPositionsResponse"=\xa0\x92)\x01\x82\xd3\xe4\x93\x023\x1a1/viam/api/v1/component/arm/{name}/joint_positions\x12\xda\x01\n\x19MoveThroughJointPositions\x127.viam.component.arm.v1.MoveThroughJointPositionsRequest\x1a8.viam.component.arm.v1.MoveThroughJointPositionsResponse"J\xa0\x92)\x01\x82\xd3\xe4\x93\x02@">/viam/api/v1/component/arm/{name}/move_through_joint_positions\x12\x7f\n\x04Stop\x12".viam.component.arm.v1.StopRequest\x1a#.viam.component.arm.v1.StopResponse".\x82\xd3\xe4\x93\x02("&/viam/api/v1/component/arm/{name}/stop\x12\x90\x01\n\x08IsMoving\x12&.viam.component.arm.v1.IsMovingRequest\x1a\'.viam.component.arm.v1.IsMovingResponse"3\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/component/arm/{name}/is_moving\x12\x86\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"4\x82\xd3\xe4\x93\x02.",/viam/api/v1/component/arm/{name}/do_command\x12\x92\x01\n\rGetKinematics\x12$.viam.common.v1.GetKinematicsRequest\x1a%.viam.common.v1.GetKinematicsResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/arm/{name}/kinematics\x12\x92\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/arm/{name}/geometriesB=\n\x19com.viam.component.arm.v1Z go.viam.com/api/component/arm/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.arm.v1.arm_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x19com.viam.component.arm.v1Z go.viam.com/api/component/arm/v1' + _globals['_ARMSERVICE'].methods_by_name['GetEndPosition']._loaded_options = None + _globals['_ARMSERVICE'].methods_by_name['GetEndPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02,\x12*/viam/api/v1/component/arm/{name}/position' + _globals['_ARMSERVICE'].methods_by_name['MoveToPosition']._loaded_options = None + _globals['_ARMSERVICE'].methods_by_name['MoveToPosition']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/arm/{name}/position' + _globals['_ARMSERVICE'].methods_by_name['GetJointPositions']._loaded_options = None + _globals['_ARMSERVICE'].methods_by_name['GetJointPositions']._serialized_options = b'\x82\xd3\xe4\x93\x023\x121/viam/api/v1/component/arm/{name}/joint_positions' + _globals['_ARMSERVICE'].methods_by_name['MoveToJointPositions']._loaded_options = None + _globals['_ARMSERVICE'].methods_by_name['MoveToJointPositions']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x023\x1a1/viam/api/v1/component/arm/{name}/joint_positions' + _globals['_ARMSERVICE'].methods_by_name['MoveThroughJointPositions']._loaded_options = None + _globals['_ARMSERVICE'].methods_by_name['MoveThroughJointPositions']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02@">/viam/api/v1/component/arm/{name}/move_through_joint_positions' + _globals['_ARMSERVICE'].methods_by_name['Stop']._loaded_options = None + _globals['_ARMSERVICE'].methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02("&/viam/api/v1/component/arm/{name}/stop' + _globals['_ARMSERVICE'].methods_by_name['IsMoving']._loaded_options = None + _globals['_ARMSERVICE'].methods_by_name['IsMoving']._serialized_options = b'\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/component/arm/{name}/is_moving' + _globals['_ARMSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_ARMSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x02.",/viam/api/v1/component/arm/{name}/do_command' + _globals['_ARMSERVICE'].methods_by_name['GetKinematics']._loaded_options = None + _globals['_ARMSERVICE'].methods_by_name['GetKinematics']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/arm/{name}/kinematics' + _globals['_ARMSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_ARMSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/arm/{name}/geometries' + _globals['_GETENDPOSITIONREQUEST']._serialized_start = 137 + _globals['_GETENDPOSITIONREQUEST']._serialized_end = 227 + _globals['_GETENDPOSITIONRESPONSE']._serialized_start = 229 + _globals['_GETENDPOSITIONRESPONSE']._serialized_end = 295 + _globals['_JOINTPOSITIONS']._serialized_start = 297 + _globals['_JOINTPOSITIONS']._serialized_end = 337 + _globals['_GETJOINTPOSITIONSREQUEST']._serialized_start = 339 + _globals['_GETJOINTPOSITIONSREQUEST']._serialized_end = 432 + _globals['_GETJOINTPOSITIONSRESPONSE']._serialized_start = 434 + _globals['_GETJOINTPOSITIONSRESPONSE']._serialized_end = 530 + _globals['_MOVETOPOSITIONREQUEST']._serialized_start = 533 + _globals['_MOVETOPOSITIONREQUEST']._serialized_end = 661 + _globals['_MOVETOPOSITIONRESPONSE']._serialized_start = 663 + _globals['_MOVETOPOSITIONRESPONSE']._serialized_end = 687 + _globals['_MOVETOJOINTPOSITIONSREQUEST']._serialized_start = 690 + _globals['_MOVETOJOINTPOSITIONSREQUEST']._serialized_end = 855 + _globals['_MOVETOJOINTPOSITIONSRESPONSE']._serialized_start = 857 + _globals['_MOVETOJOINTPOSITIONSRESPONSE']._serialized_end = 887 + _globals['_MOVETHROUGHJOINTPOSITIONSREQUEST']._serialized_start = 890 + _globals['_MOVETHROUGHJOINTPOSITIONSREQUEST']._serialized_end = 1139 + _globals['_MOVETHROUGHJOINTPOSITIONSRESPONSE']._serialized_start = 1141 + _globals['_MOVETHROUGHJOINTPOSITIONSRESPONSE']._serialized_end = 1176 + _globals['_STOPREQUEST']._serialized_start = 1178 + _globals['_STOPREQUEST']._serialized_end = 1258 + _globals['_STOPRESPONSE']._serialized_start = 1260 + _globals['_STOPRESPONSE']._serialized_end = 1274 + _globals['_STATUS']._serialized_start = 1277 + _globals['_STATUS']._serialized_end = 1451 + _globals['_ISMOVINGREQUEST']._serialized_start = 1453 + _globals['_ISMOVINGREQUEST']._serialized_end = 1490 + _globals['_ISMOVINGRESPONSE']._serialized_start = 1492 + _globals['_ISMOVINGRESPONSE']._serialized_end = 1539 + _globals['_MOVEOPTIONS']._serialized_start = 1542 + _globals['_MOVEOPTIONS']._serialized_end = 1714 + _globals['_ARMSERVICE']._serialized_start = 1717 + _globals['_ARMSERVICE']._serialized_end = 3366 \ No newline at end of file diff --git a/src/viam/gen/component/arm/v1/arm_pb2.pyi b/src/viam/gen/component/arm/v1/arm_pb2.pyi index beb4eff50..b955f5847 100644 --- a/src/viam/gen/component/arm/v1/arm_pb2.pyi +++ b/src/viam/gen/component/arm/v1/arm_pb2.pyi @@ -9,13 +9,10 @@ import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.message import google.protobuf.struct_pb2 -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class GetEndPositionRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -30,13 +27,14 @@ class GetEndPositionRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetEndPositionRequest = GetEndPositionRequest +@typing.final class GetEndPositionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSE_FIELD_NUMBER: builtins.int @@ -50,13 +48,14 @@ class GetEndPositionResponse(google.protobuf.message.Message): def __init__(self, *, pose: common.v1.common_pb2.Pose | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['pose', b'pose']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['pose', b'pose']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['pose', b'pose']) -> None: + def ClearField(self, field_name: typing.Literal['pose', b'pose']) -> None: ... global___GetEndPositionResponse = GetEndPositionResponse +@typing.final class JointPositions(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor VALUES_FIELD_NUMBER: builtins.int @@ -64,17 +63,17 @@ class JointPositions(google.protobuf.message.Message): @property def values(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: """A list of joint positions. Rotations values are in degrees, translational values in mm. - The numbers are ordered spatially from the base toward the end effector - This is used in GetJointPositionsResponse and MoveToJointPositionsRequest + There should be 1 entry in the list per joint DOF, ordered spatially from the base toward the end effector of the arm """ def __init__(self, *, values: collections.abc.Iterable[builtins.float] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['values', b'values']) -> None: + def ClearField(self, field_name: typing.Literal['values', b'values']) -> None: ... global___JointPositions = JointPositions +@typing.final class GetJointPositionsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -89,13 +88,14 @@ class GetJointPositionsRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetJointPositionsRequest = GetJointPositionsRequest +@typing.final class GetJointPositionsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSITIONS_FIELD_NUMBER: builtins.int @@ -107,47 +107,45 @@ class GetJointPositionsResponse(google.protobuf.message.Message): def __init__(self, *, positions: global___JointPositions | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['positions', b'positions']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['positions', b'positions']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['positions', b'positions']) -> None: + def ClearField(self, field_name: typing.Literal['positions', b'positions']) -> None: ... global___GetJointPositionsResponse = GetJointPositionsResponse +@typing.final class MoveToPositionRequest(google.protobuf.message.Message): + """Moves an arm to the specified pose that is within the reference frame of the arm. + Move request in Motion API has the same behavior except that it performs obstacle avoidance when a world_state + message is specified. + """ DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int TO_FIELD_NUMBER: builtins.int - WORLD_STATE_FIELD_NUMBER: builtins.int EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of an arm' @property def to(self) -> common.v1.common_pb2.Pose: - ... - - @property - def world_state(self) -> common.v1.common_pb2.WorldState: - ... + """The destination to move the arm to; this is from the reference frame of the arm.""" @property def extra(self) -> google.protobuf.struct_pb2.Struct: """Additional arguments to the method""" - def __init__(self, *, name: builtins.str=..., to: common.v1.common_pb2.Pose | None=..., world_state: common.v1.common_pb2.WorldState | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + def __init__(self, *, name: builtins.str=..., to: common.v1.common_pb2.Pose | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['_world_state', b'_world_state', 'extra', b'extra', 'to', b'to', 'world_state', b'world_state']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra', 'to', b'to']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['_world_state', b'_world_state', 'extra', b'extra', 'name', b'name', 'to', b'to', 'world_state', b'world_state']) -> None: - ... - - def WhichOneof(self, oneof_group: typing_extensions.Literal['_world_state', b'_world_state']) -> typing_extensions.Literal['world_state'] | None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'to', b'to']) -> None: ... global___MoveToPositionRequest = MoveToPositionRequest +@typing.final class MoveToPositionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -155,6 +153,7 @@ class MoveToPositionResponse(google.protobuf.message.Message): ... global___MoveToPositionResponse = MoveToPositionResponse +@typing.final class MoveToJointPositionsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -176,13 +175,14 @@ class MoveToJointPositionsRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., positions: global___JointPositions | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra', 'positions', b'positions']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra', 'positions', b'positions']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name', 'positions', b'positions']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'positions', b'positions']) -> None: ... global___MoveToJointPositionsRequest = MoveToJointPositionsRequest +@typing.final class MoveToJointPositionsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -190,6 +190,50 @@ class MoveToJointPositionsResponse(google.protobuf.message.Message): ... global___MoveToJointPositionsResponse = MoveToJointPositionsResponse +@typing.final +class MoveThroughJointPositionsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + POSITIONS_FIELD_NUMBER: builtins.int + OPTIONS_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of an arm' + + @property + def positions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___JointPositions]: + """A list of joint positions which will be moved to in the order they are specified""" + + @property + def options(self) -> global___MoveOptions: + """optional specifications to be obeyed during the motion""" + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., positions: collections.abc.Iterable[global___JointPositions] | None=..., options: global___MoveOptions | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_options', b'_options', 'extra', b'extra', 'options', b'options']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_options', b'_options', 'extra', b'extra', 'name', b'name', 'options', b'options', 'positions', b'positions']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_options', b'_options']) -> typing.Literal['options'] | None: + ... +global___MoveThroughJointPositionsRequest = MoveThroughJointPositionsRequest + +@typing.final +class MoveThroughJointPositionsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___MoveThroughJointPositionsResponse = MoveThroughJointPositionsResponse + +@typing.final class StopRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -204,13 +248,14 @@ class StopRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___StopRequest = StopRequest +@typing.final class StopResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -218,11 +263,13 @@ class StopResponse(google.protobuf.message.Message): ... global___StopResponse = StopResponse +@typing.final class Status(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor END_POSITION_FIELD_NUMBER: builtins.int JOINT_POSITIONS_FIELD_NUMBER: builtins.int IS_MOVING_FIELD_NUMBER: builtins.int + is_moving: builtins.bool @property def end_position(self) -> common.v1.common_pb2.Pose: @@ -231,14 +278,67 @@ class Status(google.protobuf.message.Message): @property def joint_positions(self) -> global___JointPositions: ... - is_moving: builtins.bool def __init__(self, *, end_position: common.v1.common_pb2.Pose | None=..., joint_positions: global___JointPositions | None=..., is_moving: builtins.bool=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['end_position', b'end_position', 'joint_positions', b'joint_positions']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['end_position', b'end_position', 'joint_positions', b'joint_positions']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['end_position', b'end_position', 'is_moving', b'is_moving', 'joint_positions', b'joint_positions']) -> None: + ... +global___Status = Status + +@typing.final +class IsMovingRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___IsMovingRequest = IsMovingRequest + +@typing.final +class IsMovingResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + IS_MOVING_FIELD_NUMBER: builtins.int + is_moving: builtins.bool + + def __init__(self, *, is_moving: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['is_moving', b'is_moving']) -> None: + ... +global___IsMovingResponse = IsMovingResponse + +@typing.final +class MoveOptions(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MAX_VEL_DEGS_PER_SEC_FIELD_NUMBER: builtins.int + MAX_ACC_DEGS_PER_SEC2_FIELD_NUMBER: builtins.int + max_vel_degs_per_sec: builtins.float + 'Maximum allowable velocity of an arm joint, in degrees per second' + max_acc_degs_per_sec2: builtins.float + 'Maximum allowable acceleration of an arm joint, in degrees per second squared' + + def __init__(self, *, max_vel_degs_per_sec: builtins.float | None=..., max_acc_degs_per_sec2: builtins.float | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_max_acc_degs_per_sec2', b'_max_acc_degs_per_sec2', '_max_vel_degs_per_sec', b'_max_vel_degs_per_sec', 'max_acc_degs_per_sec2', b'max_acc_degs_per_sec2', 'max_vel_degs_per_sec', b'max_vel_degs_per_sec']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_max_acc_degs_per_sec2', b'_max_acc_degs_per_sec2', '_max_vel_degs_per_sec', b'_max_vel_degs_per_sec', 'max_acc_degs_per_sec2', b'max_acc_degs_per_sec2', 'max_vel_degs_per_sec', b'max_vel_degs_per_sec']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_max_acc_degs_per_sec2', b'_max_acc_degs_per_sec2']) -> typing.Literal['max_acc_degs_per_sec2'] | None: ... - def ClearField(self, field_name: typing_extensions.Literal['end_position', b'end_position', 'is_moving', b'is_moving', 'joint_positions', b'joint_positions']) -> None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_max_vel_degs_per_sec', b'_max_vel_degs_per_sec']) -> typing.Literal['max_vel_degs_per_sec'] | None: ... -global___Status = Status \ No newline at end of file +global___MoveOptions = MoveOptions \ No newline at end of file diff --git a/src/viam/gen/component/audioinput/v1/audioinput_grpc.py b/src/viam/gen/component/audioinput/v1/audioinput_grpc.py index c184159e4..4d2a802b7 100644 --- a/src/viam/gen/component/audioinput/v1/audioinput_grpc.py +++ b/src/viam/gen/component/audioinput/v1/audioinput_grpc.py @@ -2,8 +2,10 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import common import google.api.annotations_pb2 import google.api.httpbody_pb2 import google.protobuf.duration_pb2 @@ -23,12 +25,39 @@ async def Properties(self, stream: 'grpclib.server.Stream[component.audioinput.v async def Record(self, stream: 'grpclib.server.Stream[component.audioinput.v1.audioinput_pb2.RecordRequest, google.api.httpbody_pb2.HttpBody]') -> None: pass + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.audioinput.v1.AudioInputService/Chunks': grpclib.const.Handler(self.Chunks, grpclib.const.Cardinality.UNARY_STREAM, component.audioinput.v1.audioinput_pb2.ChunksRequest, component.audioinput.v1.audioinput_pb2.ChunksResponse), '/viam.component.audioinput.v1.AudioInputService/Properties': grpclib.const.Handler(self.Properties, grpclib.const.Cardinality.UNARY_UNARY, component.audioinput.v1.audioinput_pb2.PropertiesRequest, component.audioinput.v1.audioinput_pb2.PropertiesResponse), '/viam.component.audioinput.v1.AudioInputService/Record': grpclib.const.Handler(self.Record, grpclib.const.Cardinality.UNARY_UNARY, component.audioinput.v1.audioinput_pb2.RecordRequest, google.api.httpbody_pb2.HttpBody)} + return {'/viam.component.audioinput.v1.AudioInputService/Chunks': grpclib.const.Handler(self.Chunks, grpclib.const.Cardinality.UNARY_STREAM, component.audioinput.v1.audioinput_pb2.ChunksRequest, component.audioinput.v1.audioinput_pb2.ChunksResponse), '/viam.component.audioinput.v1.AudioInputService/Properties': grpclib.const.Handler(self.Properties, grpclib.const.Cardinality.UNARY_UNARY, component.audioinput.v1.audioinput_pb2.PropertiesRequest, component.audioinput.v1.audioinput_pb2.PropertiesResponse), '/viam.component.audioinput.v1.AudioInputService/Record': grpclib.const.Handler(self.Record, grpclib.const.Cardinality.UNARY_UNARY, component.audioinput.v1.audioinput_pb2.RecordRequest, google.api.httpbody_pb2.HttpBody), '/viam.component.audioinput.v1.AudioInputService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.audioinput.v1.AudioInputService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedAudioInputServiceBase(AudioInputServiceBase): + + async def Chunks(self, stream: 'grpclib.server.Stream[component.audioinput.v1.audioinput_pb2.ChunksRequest, component.audioinput.v1.audioinput_pb2.ChunksResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Properties(self, stream: 'grpclib.server.Stream[component.audioinput.v1.audioinput_pb2.PropertiesRequest, component.audioinput.v1.audioinput_pb2.PropertiesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Record(self, stream: 'grpclib.server.Stream[component.audioinput.v1.audioinput_pb2.RecordRequest, google.api.httpbody_pb2.HttpBody]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class AudioInputServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: self.Chunks = grpclib.client.UnaryStreamMethod(channel, '/viam.component.audioinput.v1.AudioInputService/Chunks', component.audioinput.v1.audioinput_pb2.ChunksRequest, component.audioinput.v1.audioinput_pb2.ChunksResponse) self.Properties = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.audioinput.v1.AudioInputService/Properties', component.audioinput.v1.audioinput_pb2.PropertiesRequest, component.audioinput.v1.audioinput_pb2.PropertiesResponse) - self.Record = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.audioinput.v1.AudioInputService/Record', component.audioinput.v1.audioinput_pb2.RecordRequest, google.api.httpbody_pb2.HttpBody) \ No newline at end of file + self.Record = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.audioinput.v1.AudioInputService/Record', component.audioinput.v1.audioinput_pb2.RecordRequest, google.api.httpbody_pb2.HttpBody) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.audioinput.v1.AudioInputService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.audioinput.v1.AudioInputService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/audioinput/v1/audioinput_pb2.py b/src/viam/gen/component/audioinput/v1/audioinput_pb2.py index 223819e86..1d3d65e34 100644 --- a/src/viam/gen/component/audioinput/v1/audioinput_pb2.py +++ b/src/viam/gen/component/audioinput/v1/audioinput_pb2.py @@ -1,37 +1,45 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/audioinput/v1/audioinput.proto') _sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.api import httpbody_pb2 as google_dot_api_dot_httpbody__pb2 from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(component/audioinput/v1/audioinput.proto\x12\x1cviam.component.audioinput.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x19google/api/httpbody.proto\x1a\x1egoogle/protobuf/duration.proto"Z\n\rRecordRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x125\n\x08duration\x18\x02 \x01(\x0b2\x19.google.protobuf.DurationR\x08duration"\xa2\x01\n\x0eAudioChunkInfo\x12O\n\rsample_format\x18\x01 \x01(\x0e2*.viam.component.audioinput.v1.SampleFormatR\x0csampleFormat\x12\x1a\n\x08channels\x18\x02 \x01(\rR\x08channels\x12#\n\rsampling_rate\x18\x03 \x01(\x03R\x0csamplingRate"8\n\nAudioChunk\x12\x12\n\x04data\x18\x01 \x01(\x0cR\x04data\x12\x16\n\x06length\x18\x02 \x01(\rR\x06length"t\n\rChunksRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12O\n\rsample_format\x18\x02 \x01(\x0e2*.viam.component.audioinput.v1.SampleFormatR\x0csampleFormat"\x9e\x01\n\x0eChunksResponse\x12B\n\x04info\x18\x01 \x01(\x0b2,.viam.component.audioinput.v1.AudioChunkInfoH\x00R\x04info\x12@\n\x05chunk\x18\x02 \x01(\x0b2(.viam.component.audioinput.v1.AudioChunkH\x00R\x05chunkB\x06\n\x04type"\'\n\x11PropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x96\x02\n\x12PropertiesResponse\x12#\n\rchannel_count\x18\x01 \x01(\rR\x0cchannelCount\x123\n\x07latency\x18\x02 \x01(\x0b2\x19.google.protobuf.DurationR\x07latency\x12\x1f\n\x0bsample_rate\x18\x03 \x01(\rR\nsampleRate\x12\x1f\n\x0bsample_size\x18\x04 \x01(\rR\nsampleSize\x12"\n\ris_big_endian\x18\x05 \x01(\x08R\x0bisBigEndian\x12\x19\n\x08is_float\x18\x06 \x01(\x08R\x07isFloat\x12%\n\x0eis_interleaved\x18\x07 \x01(\x08R\risInterleaved*y\n\x0cSampleFormat\x12\x1d\n\x19SAMPLE_FORMAT_UNSPECIFIED\x10\x00\x12#\n\x1fSAMPLE_FORMAT_INT16_INTERLEAVED\x10\x01\x12%\n!SAMPLE_FORMAT_FLOAT32_INTERLEAVED\x10\x022\xb0\x03\n\x11AudioInputService\x12e\n\x06Chunks\x12+.viam.component.audioinput.v1.ChunksRequest\x1a,.viam.component.audioinput.v1.ChunksResponse0\x01\x12\xac\x01\n\nProperties\x12/.viam.component.audioinput.v1.PropertiesRequest\x1a0.viam.component.audioinput.v1.PropertiesResponse";\x82\xd3\xe4\x93\x025\x123/viam/api/v1/component/audioinput/{name}/properties\x12\x84\x01\n\x06Record\x12+.viam.component.audioinput.v1.RecordRequest\x1a\x14.google.api.HttpBody"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/audioinput/{name}/recordBK\n com.viam.component.audioinput.v1Z\'go.viam.com/api/component/audioinput/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.audioinput.v1.audioinput_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b"\n com.viam.component.audioinput.v1Z'go.viam.com/api/component/audioinput/v1" - _AUDIOINPUTSERVICE.methods_by_name['Properties']._options = None - _AUDIOINPUTSERVICE.methods_by_name['Properties']._serialized_options = b'\x82\xd3\xe4\x93\x025\x123/viam/api/v1/component/audioinput/{name}/properties' - _AUDIOINPUTSERVICE.methods_by_name['Record']._options = None - _AUDIOINPUTSERVICE.methods_by_name['Record']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/audioinput/{name}/record' - _SAMPLEFORMAT._serialized_start = 1079 - _SAMPLEFORMAT._serialized_end = 1200 - _RECORDREQUEST._serialized_start = 163 - _RECORDREQUEST._serialized_end = 253 - _AUDIOCHUNKINFO._serialized_start = 256 - _AUDIOCHUNKINFO._serialized_end = 418 - _AUDIOCHUNK._serialized_start = 420 - _AUDIOCHUNK._serialized_end = 476 - _CHUNKSREQUEST._serialized_start = 478 - _CHUNKSREQUEST._serialized_end = 594 - _CHUNKSRESPONSE._serialized_start = 597 - _CHUNKSRESPONSE._serialized_end = 755 - _PROPERTIESREQUEST._serialized_start = 757 - _PROPERTIESREQUEST._serialized_end = 796 - _PROPERTIESRESPONSE._serialized_start = 799 - _PROPERTIESRESPONSE._serialized_end = 1077 - _AUDIOINPUTSERVICE._serialized_start = 1203 - _AUDIOINPUTSERVICE._serialized_end = 1635 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n(component/audioinput/v1/audioinput.proto\x12\x1cviam.component.audioinput.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x19google/api/httpbody.proto\x1a\x1egoogle/protobuf/duration.proto"Z\n\rRecordRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x125\n\x08duration\x18\x02 \x01(\x0b2\x19.google.protobuf.DurationR\x08duration"\xa2\x01\n\x0eAudioChunkInfo\x12O\n\rsample_format\x18\x01 \x01(\x0e2*.viam.component.audioinput.v1.SampleFormatR\x0csampleFormat\x12\x1a\n\x08channels\x18\x02 \x01(\rR\x08channels\x12#\n\rsampling_rate\x18\x03 \x01(\x03R\x0csamplingRate"8\n\nAudioChunk\x12\x12\n\x04data\x18\x01 \x01(\x0cR\x04data\x12\x16\n\x06length\x18\x02 \x01(\rR\x06length"t\n\rChunksRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12O\n\rsample_format\x18\x02 \x01(\x0e2*.viam.component.audioinput.v1.SampleFormatR\x0csampleFormat"\x9e\x01\n\x0eChunksResponse\x12B\n\x04info\x18\x01 \x01(\x0b2,.viam.component.audioinput.v1.AudioChunkInfoH\x00R\x04info\x12@\n\x05chunk\x18\x02 \x01(\x0b2(.viam.component.audioinput.v1.AudioChunkH\x00R\x05chunkB\x06\n\x04type"\'\n\x11PropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x96\x02\n\x12PropertiesResponse\x12#\n\rchannel_count\x18\x01 \x01(\rR\x0cchannelCount\x123\n\x07latency\x18\x02 \x01(\x0b2\x19.google.protobuf.DurationR\x07latency\x12\x1f\n\x0bsample_rate\x18\x03 \x01(\rR\nsampleRate\x12\x1f\n\x0bsample_size\x18\x04 \x01(\rR\nsampleSize\x12"\n\ris_big_endian\x18\x05 \x01(\x08R\x0bisBigEndian\x12\x19\n\x08is_float\x18\x06 \x01(\x08R\x07isFloat\x12%\n\x0eis_interleaved\x18\x07 \x01(\x08R\risInterleaved*y\n\x0cSampleFormat\x12\x1d\n\x19SAMPLE_FORMAT_UNSPECIFIED\x10\x00\x12#\n\x1fSAMPLE_FORMAT_INT16_INTERLEAVED\x10\x01\x12%\n!SAMPLE_FORMAT_FLOAT32_INTERLEAVED\x10\x022\xdc\x05\n\x11AudioInputService\x12e\n\x06Chunks\x12+.viam.component.audioinput.v1.ChunksRequest\x1a,.viam.component.audioinput.v1.ChunksResponse0\x01\x12\xac\x01\n\nProperties\x12/.viam.component.audioinput.v1.PropertiesRequest\x1a0.viam.component.audioinput.v1.PropertiesResponse";\x82\xd3\xe4\x93\x025\x123/viam/api/v1/component/audioinput/{name}/properties\x12\x84\x01\n\x06Record\x12+.viam.component.audioinput.v1.RecordRequest\x1a\x14.google.api.HttpBody"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/audioinput/{name}/record\x12\x8d\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse";\x82\xd3\xe4\x93\x025"3/viam/api/v1/component/audioinput/{name}/do_command\x12\x99\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse";\x82\xd3\xe4\x93\x025\x123/viam/api/v1/component/audioinput/{name}/geometriesBK\n com.viam.component.audioinput.v1Z\'go.viam.com/api/component/audioinput/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.audioinput.v1.audioinput_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b"\n com.viam.component.audioinput.v1Z'go.viam.com/api/component/audioinput/v1" + _globals['_AUDIOINPUTSERVICE'].methods_by_name['Properties']._loaded_options = None + _globals['_AUDIOINPUTSERVICE'].methods_by_name['Properties']._serialized_options = b'\x82\xd3\xe4\x93\x025\x123/viam/api/v1/component/audioinput/{name}/properties' + _globals['_AUDIOINPUTSERVICE'].methods_by_name['Record']._loaded_options = None + _globals['_AUDIOINPUTSERVICE'].methods_by_name['Record']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/audioinput/{name}/record' + _globals['_AUDIOINPUTSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_AUDIOINPUTSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x025"3/viam/api/v1/component/audioinput/{name}/do_command' + _globals['_AUDIOINPUTSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_AUDIOINPUTSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x025\x123/viam/api/v1/component/audioinput/{name}/geometries' + _globals['_SAMPLEFORMAT']._serialized_start = 1103 + _globals['_SAMPLEFORMAT']._serialized_end = 1224 + _globals['_RECORDREQUEST']._serialized_start = 187 + _globals['_RECORDREQUEST']._serialized_end = 277 + _globals['_AUDIOCHUNKINFO']._serialized_start = 280 + _globals['_AUDIOCHUNKINFO']._serialized_end = 442 + _globals['_AUDIOCHUNK']._serialized_start = 444 + _globals['_AUDIOCHUNK']._serialized_end = 500 + _globals['_CHUNKSREQUEST']._serialized_start = 502 + _globals['_CHUNKSREQUEST']._serialized_end = 618 + _globals['_CHUNKSRESPONSE']._serialized_start = 621 + _globals['_CHUNKSRESPONSE']._serialized_end = 779 + _globals['_PROPERTIESREQUEST']._serialized_start = 781 + _globals['_PROPERTIESREQUEST']._serialized_end = 820 + _globals['_PROPERTIESRESPONSE']._serialized_start = 823 + _globals['_PROPERTIESRESPONSE']._serialized_end = 1101 + _globals['_AUDIOINPUTSERVICE']._serialized_start = 1227 + _globals['_AUDIOINPUTSERVICE']._serialized_end = 1959 \ No newline at end of file diff --git a/src/viam/gen/component/audioinput/v1/audioinput_pb2.pyi b/src/viam/gen/component/audioinput/v1/audioinput_pb2.pyi index d572a09a3..64d3667ae 100644 --- a/src/viam/gen/component/audioinput/v1/audioinput_pb2.pyi +++ b/src/viam/gen/component/audioinput/v1/audioinput_pb2.pyi @@ -32,6 +32,7 @@ SAMPLE_FORMAT_INT16_INTERLEAVED: SampleFormat.ValueType SAMPLE_FORMAT_FLOAT32_INTERLEAVED: SampleFormat.ValueType global___SampleFormat = SampleFormat +@typing.final class RecordRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -46,13 +47,14 @@ class RecordRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., duration: google.protobuf.duration_pb2.Duration | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['duration', b'duration']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['duration', b'duration']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['duration', b'duration', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['duration', b'duration', 'name', b'name']) -> None: ... global___RecordRequest = RecordRequest +@typing.final class AudioChunkInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor SAMPLE_FORMAT_FIELD_NUMBER: builtins.int @@ -66,10 +68,11 @@ class AudioChunkInfo(google.protobuf.message.Message): def __init__(self, *, sample_format: global___SampleFormat.ValueType=..., channels: builtins.int=..., sampling_rate: builtins.int=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['channels', b'channels', 'sample_format', b'sample_format', 'sampling_rate', b'sampling_rate']) -> None: + def ClearField(self, field_name: typing.Literal['channels', b'channels', 'sample_format', b'sample_format', 'sampling_rate', b'sampling_rate']) -> None: ... global___AudioChunkInfo = AudioChunkInfo +@typing.final class AudioChunk(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor DATA_FIELD_NUMBER: builtins.int @@ -82,10 +85,11 @@ class AudioChunk(google.protobuf.message.Message): def __init__(self, *, data: builtins.bytes=..., length: builtins.int=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['data', b'data', 'length', b'length']) -> None: + def ClearField(self, field_name: typing.Literal['data', b'data', 'length', b'length']) -> None: ... global___AudioChunk = AudioChunk +@typing.final class ChunksRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -98,10 +102,11 @@ class ChunksRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., sample_format: global___SampleFormat.ValueType=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name', 'sample_format', b'sample_format']) -> None: + def ClearField(self, field_name: typing.Literal['name', b'name', 'sample_format', b'sample_format']) -> None: ... global___ChunksRequest = ChunksRequest +@typing.final class ChunksResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor INFO_FIELD_NUMBER: builtins.int @@ -118,16 +123,17 @@ class ChunksResponse(google.protobuf.message.Message): def __init__(self, *, info: global___AudioChunkInfo | None=..., chunk: global___AudioChunk | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['chunk', b'chunk', 'info', b'info', 'type', b'type']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['chunk', b'chunk', 'info', b'info', 'type', b'type']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['chunk', b'chunk', 'info', b'info', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['chunk', b'chunk', 'info', b'info', 'type', b'type']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['type', b'type']) -> typing_extensions.Literal['info', 'chunk'] | None: + def WhichOneof(self, oneof_group: typing.Literal['type', b'type']) -> typing.Literal['info', 'chunk'] | None: ... global___ChunksResponse = ChunksResponse +@typing.final class PropertiesRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -137,10 +143,11 @@ class PropertiesRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: ... global___PropertiesRequest = PropertiesRequest +@typing.final class PropertiesResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor CHANNEL_COUNT_FIELD_NUMBER: builtins.int @@ -151,22 +158,22 @@ class PropertiesResponse(google.protobuf.message.Message): IS_FLOAT_FIELD_NUMBER: builtins.int IS_INTERLEAVED_FIELD_NUMBER: builtins.int channel_count: builtins.int - - @property - def latency(self) -> google.protobuf.duration_pb2.Duration: - ... sample_rate: builtins.int sample_size: builtins.int is_big_endian: builtins.bool is_float: builtins.bool is_interleaved: builtins.bool + @property + def latency(self) -> google.protobuf.duration_pb2.Duration: + ... + def __init__(self, *, channel_count: builtins.int=..., latency: google.protobuf.duration_pb2.Duration | None=..., sample_rate: builtins.int=..., sample_size: builtins.int=..., is_big_endian: builtins.bool=..., is_float: builtins.bool=..., is_interleaved: builtins.bool=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['latency', b'latency']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['latency', b'latency']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['channel_count', b'channel_count', 'is_big_endian', b'is_big_endian', 'is_float', b'is_float', 'is_interleaved', b'is_interleaved', 'latency', b'latency', 'sample_rate', b'sample_rate', 'sample_size', b'sample_size']) -> None: + def ClearField(self, field_name: typing.Literal['channel_count', b'channel_count', 'is_big_endian', b'is_big_endian', 'is_float', b'is_float', 'is_interleaved', b'is_interleaved', 'latency', b'latency', 'sample_rate', b'sample_rate', 'sample_size', b'sample_size']) -> None: ... global___PropertiesResponse = PropertiesResponse \ No newline at end of file diff --git a/src/viam/gen/component/base/v1/base_grpc.py b/src/viam/gen/component/base/v1/base_grpc.py index 596a2e5e3..74f8f2e90 100644 --- a/src/viam/gen/component/base/v1/base_grpc.py +++ b/src/viam/gen/component/base/v1/base_grpc.py @@ -2,6 +2,7 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common @@ -31,8 +32,53 @@ async def SetVelocity(self, stream: 'grpclib.server.Stream[component.base.v1.bas async def Stop(self, stream: 'grpclib.server.Stream[component.base.v1.base_pb2.StopRequest, component.base.v1.base_pb2.StopResponse]') -> None: pass + @abc.abstractmethod + async def IsMoving(self, stream: 'grpclib.server.Stream[component.base.v1.base_pb2.IsMovingRequest, component.base.v1.base_pb2.IsMovingResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + + @abc.abstractmethod + async def GetProperties(self, stream: 'grpclib.server.Stream[component.base.v1.base_pb2.GetPropertiesRequest, component.base.v1.base_pb2.GetPropertiesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.base.v1.BaseService/MoveStraight': grpclib.const.Handler(self.MoveStraight, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.MoveStraightRequest, component.base.v1.base_pb2.MoveStraightResponse), '/viam.component.base.v1.BaseService/Spin': grpclib.const.Handler(self.Spin, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.SpinRequest, component.base.v1.base_pb2.SpinResponse), '/viam.component.base.v1.BaseService/SetPower': grpclib.const.Handler(self.SetPower, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.SetPowerRequest, component.base.v1.base_pb2.SetPowerResponse), '/viam.component.base.v1.BaseService/SetVelocity': grpclib.const.Handler(self.SetVelocity, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.SetVelocityRequest, component.base.v1.base_pb2.SetVelocityResponse), '/viam.component.base.v1.BaseService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.StopRequest, component.base.v1.base_pb2.StopResponse)} + return {'/viam.component.base.v1.BaseService/MoveStraight': grpclib.const.Handler(self.MoveStraight, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.MoveStraightRequest, component.base.v1.base_pb2.MoveStraightResponse), '/viam.component.base.v1.BaseService/Spin': grpclib.const.Handler(self.Spin, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.SpinRequest, component.base.v1.base_pb2.SpinResponse), '/viam.component.base.v1.BaseService/SetPower': grpclib.const.Handler(self.SetPower, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.SetPowerRequest, component.base.v1.base_pb2.SetPowerResponse), '/viam.component.base.v1.BaseService/SetVelocity': grpclib.const.Handler(self.SetVelocity, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.SetVelocityRequest, component.base.v1.base_pb2.SetVelocityResponse), '/viam.component.base.v1.BaseService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.StopRequest, component.base.v1.base_pb2.StopResponse), '/viam.component.base.v1.BaseService/IsMoving': grpclib.const.Handler(self.IsMoving, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.IsMovingRequest, component.base.v1.base_pb2.IsMovingResponse), '/viam.component.base.v1.BaseService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.base.v1.BaseService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse), '/viam.component.base.v1.BaseService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, component.base.v1.base_pb2.GetPropertiesRequest, component.base.v1.base_pb2.GetPropertiesResponse)} + +class UnimplementedBaseServiceBase(BaseServiceBase): + + async def MoveStraight(self, stream: 'grpclib.server.Stream[component.base.v1.base_pb2.MoveStraightRequest, component.base.v1.base_pb2.MoveStraightResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Spin(self, stream: 'grpclib.server.Stream[component.base.v1.base_pb2.SpinRequest, component.base.v1.base_pb2.SpinResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetPower(self, stream: 'grpclib.server.Stream[component.base.v1.base_pb2.SetPowerRequest, component.base.v1.base_pb2.SetPowerResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetVelocity(self, stream: 'grpclib.server.Stream[component.base.v1.base_pb2.SetVelocityRequest, component.base.v1.base_pb2.SetVelocityResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Stop(self, stream: 'grpclib.server.Stream[component.base.v1.base_pb2.StopRequest, component.base.v1.base_pb2.StopResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def IsMoving(self, stream: 'grpclib.server.Stream[component.base.v1.base_pb2.IsMovingRequest, component.base.v1.base_pb2.IsMovingResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetProperties(self, stream: 'grpclib.server.Stream[component.base.v1.base_pb2.GetPropertiesRequest, component.base.v1.base_pb2.GetPropertiesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class BaseServiceStub: @@ -41,4 +87,8 @@ def __init__(self, channel: grpclib.client.Channel) -> None: self.Spin = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.base.v1.BaseService/Spin', component.base.v1.base_pb2.SpinRequest, component.base.v1.base_pb2.SpinResponse) self.SetPower = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.base.v1.BaseService/SetPower', component.base.v1.base_pb2.SetPowerRequest, component.base.v1.base_pb2.SetPowerResponse) self.SetVelocity = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.base.v1.BaseService/SetVelocity', component.base.v1.base_pb2.SetVelocityRequest, component.base.v1.base_pb2.SetVelocityResponse) - self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.base.v1.BaseService/Stop', component.base.v1.base_pb2.StopRequest, component.base.v1.base_pb2.StopResponse) \ No newline at end of file + self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.base.v1.BaseService/Stop', component.base.v1.base_pb2.StopRequest, component.base.v1.base_pb2.StopResponse) + self.IsMoving = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.base.v1.BaseService/IsMoving', component.base.v1.base_pb2.IsMovingRequest, component.base.v1.base_pb2.IsMovingResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.base.v1.BaseService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.base.v1.BaseService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) + self.GetProperties = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.base.v1.BaseService/GetProperties', component.base.v1.base_pb2.GetPropertiesRequest, component.base.v1.base_pb2.GetPropertiesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/base/v1/base_pb2.py b/src/viam/gen/component/base/v1/base_pb2.py index e9a31da42..a60a66ff9 100644 --- a/src/viam/gen/component/base/v1/base_pb2.py +++ b/src/viam/gen/component/base/v1/base_pb2.py @@ -1,47 +1,66 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/base/v1/base.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ccomponent/base/v1/base.proto\x12\x16viam.component.base.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"\x97\x01\n\x13MoveStraightRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n\x0bdistance_mm\x18\x02 \x01(\x03R\ndistanceMm\x12\x1c\n\nmm_per_sec\x18\x03 \x01(\x01R\x08mmPerSec\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x16\n\x14MoveStraightResponse"\x8f\x01\n\x0bSpinRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tangle_deg\x18\x02 \x01(\x01R\x08angleDeg\x12 \n\x0cdegs_per_sec\x18\x03 \x01(\x01R\ndegsPerSec\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cSpinResponse"P\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cStopResponse"\xb8\x01\n\x0fSetPowerRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12/\n\x06linear\x18\x02 \x01(\x0b2\x17.viam.common.v1.Vector3R\x06linear\x121\n\x07angular\x18\x03 \x01(\x0b2\x17.viam.common.v1.Vector3R\x07angular\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x12\n\x10SetPowerResponse"\xbb\x01\n\x12SetVelocityRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12/\n\x06linear\x18\x02 \x01(\x0b2\x17.viam.common.v1.Vector3R\x06linear\x121\n\x07angular\x18\x03 \x01(\x0b2\x17.viam.common.v1.Vector3R\x07angular\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x15\n\x13SetVelocityResponse2\xf5\x05\n\x0bBaseService\x12\xa3\x01\n\x0cMoveStraight\x12+.viam.component.base.v1.MoveStraightRequest\x1a,.viam.component.base.v1.MoveStraightResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/base/{name}/move_straight\x12\x82\x01\n\x04Spin\x12#.viam.component.base.v1.SpinRequest\x1a$.viam.component.base.v1.SpinResponse"/\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/component/base/{name}/spin\x12\x93\x01\n\x08SetPower\x12\'.viam.component.base.v1.SetPowerRequest\x1a(.viam.component.base.v1.SetPowerResponse"4\x82\xd3\xe4\x93\x02.",/viam/api/v1/component/base/{name}/set_power\x12\x9f\x01\n\x0bSetVelocity\x12*.viam.component.base.v1.SetVelocityRequest\x1a+.viam.component.base.v1.SetVelocityResponse"7\x82\xd3\xe4\x93\x021"//viam/api/v1/component/base/{name}/set_velocity\x12\x82\x01\n\x04Stop\x12#.viam.component.base.v1.StopRequest\x1a$.viam.component.base.v1.StopResponse"/\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/component/base/{name}/stopB?\n\x1acom.viam.component.base.v1Z!go.viam.com/api/component/base/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.base.v1.base_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1acom.viam.component.base.v1Z!go.viam.com/api/component/base/v1' - _BASESERVICE.methods_by_name['MoveStraight']._options = None - _BASESERVICE.methods_by_name['MoveStraight']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/base/{name}/move_straight' - _BASESERVICE.methods_by_name['Spin']._options = None - _BASESERVICE.methods_by_name['Spin']._serialized_options = b'\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/component/base/{name}/spin' - _BASESERVICE.methods_by_name['SetPower']._options = None - _BASESERVICE.methods_by_name['SetPower']._serialized_options = b'\x82\xd3\xe4\x93\x02.",/viam/api/v1/component/base/{name}/set_power' - _BASESERVICE.methods_by_name['SetVelocity']._options = None - _BASESERVICE.methods_by_name['SetVelocity']._serialized_options = b'\x82\xd3\xe4\x93\x021"//viam/api/v1/component/base/{name}/set_velocity' - _BASESERVICE.methods_by_name['Stop']._options = None - _BASESERVICE.methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/component/base/{name}/stop' - _MOVESTRAIGHTREQUEST._serialized_start = 141 - _MOVESTRAIGHTREQUEST._serialized_end = 292 - _MOVESTRAIGHTRESPONSE._serialized_start = 294 - _MOVESTRAIGHTRESPONSE._serialized_end = 316 - _SPINREQUEST._serialized_start = 319 - _SPINREQUEST._serialized_end = 462 - _SPINRESPONSE._serialized_start = 464 - _SPINRESPONSE._serialized_end = 478 - _STOPREQUEST._serialized_start = 480 - _STOPREQUEST._serialized_end = 560 - _STOPRESPONSE._serialized_start = 562 - _STOPRESPONSE._serialized_end = 576 - _SETPOWERREQUEST._serialized_start = 579 - _SETPOWERREQUEST._serialized_end = 763 - _SETPOWERRESPONSE._serialized_start = 765 - _SETPOWERRESPONSE._serialized_end = 783 - _SETVELOCITYREQUEST._serialized_start = 786 - _SETVELOCITYREQUEST._serialized_end = 973 - _SETVELOCITYRESPONSE._serialized_start = 975 - _SETVELOCITYRESPONSE._serialized_end = 996 - _BASESERVICE._serialized_start = 999 - _BASESERVICE._serialized_end = 1756 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ccomponent/base/v1/base.proto\x12\x16viam.component.base.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"\x97\x01\n\x13MoveStraightRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n\x0bdistance_mm\x18\x02 \x01(\x03R\ndistanceMm\x12\x1c\n\nmm_per_sec\x18\x03 \x01(\x01R\x08mmPerSec\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x16\n\x14MoveStraightResponse"\x8f\x01\n\x0bSpinRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tangle_deg\x18\x02 \x01(\x01R\x08angleDeg\x12 \n\x0cdegs_per_sec\x18\x03 \x01(\x01R\ndegsPerSec\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cSpinResponse"P\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cStopResponse"\xb8\x01\n\x0fSetPowerRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12/\n\x06linear\x18\x02 \x01(\x0b2\x17.viam.common.v1.Vector3R\x06linear\x121\n\x07angular\x18\x03 \x01(\x0b2\x17.viam.common.v1.Vector3R\x07angular\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x12\n\x10SetPowerResponse"\xbb\x01\n\x12SetVelocityRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12/\n\x06linear\x18\x02 \x01(\x0b2\x17.viam.common.v1.Vector3R\x06linear\x121\n\x07angular\x18\x03 \x01(\x0b2\x17.viam.common.v1.Vector3R\x07angular\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x15\n\x13SetVelocityResponse"%\n\x0fIsMovingRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"/\n\x10IsMovingResponse\x12\x1b\n\tis_moving\x18\x01 \x01(\x08R\x08isMoving"Y\n\x14GetPropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\xac\x01\n\x15GetPropertiesResponse\x12!\n\x0cwidth_meters\x18\x01 \x01(\x01R\x0bwidthMeters\x122\n\x15turning_radius_meters\x18\x02 \x01(\x01R\x13turningRadiusMeters\x12<\n\x1awheel_circumference_meters\x18\x03 \x01(\x01R\x18wheelCircumferenceMeters2\xe1\n\n\x0bBaseService\x12\xa7\x01\n\x0cMoveStraight\x12+.viam.component.base.v1.MoveStraightRequest\x1a,.viam.component.base.v1.MoveStraightResponse"<\xa0\x92)\x01\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/base/{name}/move_straight\x12\x86\x01\n\x04Spin\x12#.viam.component.base.v1.SpinRequest\x1a$.viam.component.base.v1.SpinResponse"3\xa0\x92)\x01\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/component/base/{name}/spin\x12\x97\x01\n\x08SetPower\x12\'.viam.component.base.v1.SetPowerRequest\x1a(.viam.component.base.v1.SetPowerResponse"8\xa0\x92)\x01\x82\xd3\xe4\x93\x02.",/viam/api/v1/component/base/{name}/set_power\x12\xa3\x01\n\x0bSetVelocity\x12*.viam.component.base.v1.SetVelocityRequest\x1a+.viam.component.base.v1.SetVelocityResponse";\xa0\x92)\x01\x82\xd3\xe4\x93\x021"//viam/api/v1/component/base/{name}/set_velocity\x12\x82\x01\n\x04Stop\x12#.viam.component.base.v1.StopRequest\x1a$.viam.component.base.v1.StopResponse"/\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/component/base/{name}/stop\x12\x93\x01\n\x08IsMoving\x12\'.viam.component.base.v1.IsMovingRequest\x1a(.viam.component.base.v1.IsMovingResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/base/{name}/is_moving\x12\x87\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"5\x82\xd3\xe4\x93\x02/"-/viam/api/v1/component/base/{name}/do_command\x12\x93\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"5\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/base/{name}/geometries\x12\xa3\x01\n\rGetProperties\x12,.viam.component.base.v1.GetPropertiesRequest\x1a-.viam.component.base.v1.GetPropertiesResponse"5\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/base/{name}/propertiesB?\n\x1acom.viam.component.base.v1Z!go.viam.com/api/component/base/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.base.v1.base_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1acom.viam.component.base.v1Z!go.viam.com/api/component/base/v1' + _globals['_BASESERVICE'].methods_by_name['MoveStraight']._loaded_options = None + _globals['_BASESERVICE'].methods_by_name['MoveStraight']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/base/{name}/move_straight' + _globals['_BASESERVICE'].methods_by_name['Spin']._loaded_options = None + _globals['_BASESERVICE'].methods_by_name['Spin']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/component/base/{name}/spin' + _globals['_BASESERVICE'].methods_by_name['SetPower']._loaded_options = None + _globals['_BASESERVICE'].methods_by_name['SetPower']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02.",/viam/api/v1/component/base/{name}/set_power' + _globals['_BASESERVICE'].methods_by_name['SetVelocity']._loaded_options = None + _globals['_BASESERVICE'].methods_by_name['SetVelocity']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x021"//viam/api/v1/component/base/{name}/set_velocity' + _globals['_BASESERVICE'].methods_by_name['Stop']._loaded_options = None + _globals['_BASESERVICE'].methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/component/base/{name}/stop' + _globals['_BASESERVICE'].methods_by_name['IsMoving']._loaded_options = None + _globals['_BASESERVICE'].methods_by_name['IsMoving']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/base/{name}/is_moving' + _globals['_BASESERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_BASESERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x02/"-/viam/api/v1/component/base/{name}/do_command' + _globals['_BASESERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_BASESERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/base/{name}/geometries' + _globals['_BASESERVICE'].methods_by_name['GetProperties']._loaded_options = None + _globals['_BASESERVICE'].methods_by_name['GetProperties']._serialized_options = b'\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/base/{name}/properties' + _globals['_MOVESTRAIGHTREQUEST']._serialized_start = 141 + _globals['_MOVESTRAIGHTREQUEST']._serialized_end = 292 + _globals['_MOVESTRAIGHTRESPONSE']._serialized_start = 294 + _globals['_MOVESTRAIGHTRESPONSE']._serialized_end = 316 + _globals['_SPINREQUEST']._serialized_start = 319 + _globals['_SPINREQUEST']._serialized_end = 462 + _globals['_SPINRESPONSE']._serialized_start = 464 + _globals['_SPINRESPONSE']._serialized_end = 478 + _globals['_STOPREQUEST']._serialized_start = 480 + _globals['_STOPREQUEST']._serialized_end = 560 + _globals['_STOPRESPONSE']._serialized_start = 562 + _globals['_STOPRESPONSE']._serialized_end = 576 + _globals['_SETPOWERREQUEST']._serialized_start = 579 + _globals['_SETPOWERREQUEST']._serialized_end = 763 + _globals['_SETPOWERRESPONSE']._serialized_start = 765 + _globals['_SETPOWERRESPONSE']._serialized_end = 783 + _globals['_SETVELOCITYREQUEST']._serialized_start = 786 + _globals['_SETVELOCITYREQUEST']._serialized_end = 973 + _globals['_SETVELOCITYRESPONSE']._serialized_start = 975 + _globals['_SETVELOCITYRESPONSE']._serialized_end = 996 + _globals['_ISMOVINGREQUEST']._serialized_start = 998 + _globals['_ISMOVINGREQUEST']._serialized_end = 1035 + _globals['_ISMOVINGRESPONSE']._serialized_start = 1037 + _globals['_ISMOVINGRESPONSE']._serialized_end = 1084 + _globals['_GETPROPERTIESREQUEST']._serialized_start = 1086 + _globals['_GETPROPERTIESREQUEST']._serialized_end = 1175 + _globals['_GETPROPERTIESRESPONSE']._serialized_start = 1178 + _globals['_GETPROPERTIESRESPONSE']._serialized_end = 1350 + _globals['_BASESERVICE']._serialized_start = 1353 + _globals['_BASESERVICE']._serialized_end = 2730 \ No newline at end of file diff --git a/src/viam/gen/component/base/v1/base_pb2.pyi b/src/viam/gen/component/base/v1/base_pb2.pyi index 52fe72a3a..2918d63ce 100644 --- a/src/viam/gen/component/base/v1/base_pb2.pyi +++ b/src/viam/gen/component/base/v1/base_pb2.pyi @@ -7,13 +7,10 @@ from .... import common import google.protobuf.descriptor import google.protobuf.message import google.protobuf.struct_pb2 -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class MoveStraightRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -34,13 +31,14 @@ class MoveStraightRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., distance_mm: builtins.int=..., mm_per_sec: builtins.float=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['distance_mm', b'distance_mm', 'extra', b'extra', 'mm_per_sec', b'mm_per_sec', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['distance_mm', b'distance_mm', 'extra', b'extra', 'mm_per_sec', b'mm_per_sec', 'name', b'name']) -> None: ... global___MoveStraightRequest = MoveStraightRequest +@typing.final class MoveStraightResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -48,6 +46,7 @@ class MoveStraightResponse(google.protobuf.message.Message): ... global___MoveStraightResponse = MoveStraightResponse +@typing.final class SpinRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -68,13 +67,14 @@ class SpinRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., angle_deg: builtins.float=..., degs_per_sec: builtins.float=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['angle_deg', b'angle_deg', 'degs_per_sec', b'degs_per_sec', 'extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['angle_deg', b'angle_deg', 'degs_per_sec', b'degs_per_sec', 'extra', b'extra', 'name', b'name']) -> None: ... global___SpinRequest = SpinRequest +@typing.final class SpinResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -82,6 +82,7 @@ class SpinResponse(google.protobuf.message.Message): ... global___SpinResponse = SpinResponse +@typing.final class StopRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -96,13 +97,14 @@ class StopRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___StopRequest = StopRequest +@typing.final class StopResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -110,6 +112,7 @@ class StopResponse(google.protobuf.message.Message): ... global___StopResponse = StopResponse +@typing.final class SetPowerRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -134,13 +137,14 @@ class SetPowerRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., linear: common.v1.common_pb2.Vector3 | None=..., angular: common.v1.common_pb2.Vector3 | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['angular', b'angular', 'extra', b'extra', 'linear', b'linear']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['angular', b'angular', 'extra', b'extra', 'linear', b'linear']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['angular', b'angular', 'extra', b'extra', 'linear', b'linear', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['angular', b'angular', 'extra', b'extra', 'linear', b'linear', 'name', b'name']) -> None: ... global___SetPowerRequest = SetPowerRequest +@typing.final class SetPowerResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -148,6 +152,7 @@ class SetPowerResponse(google.protobuf.message.Message): ... global___SetPowerResponse = SetPowerResponse +@typing.final class SetVelocityRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -172,16 +177,82 @@ class SetVelocityRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., linear: common.v1.common_pb2.Vector3 | None=..., angular: common.v1.common_pb2.Vector3 | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['angular', b'angular', 'extra', b'extra', 'linear', b'linear']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['angular', b'angular', 'extra', b'extra', 'linear', b'linear']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['angular', b'angular', 'extra', b'extra', 'linear', b'linear', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['angular', b'angular', 'extra', b'extra', 'linear', b'linear', 'name', b'name']) -> None: ... global___SetVelocityRequest = SetVelocityRequest +@typing.final class SetVelocityResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor def __init__(self) -> None: ... -global___SetVelocityResponse = SetVelocityResponse \ No newline at end of file +global___SetVelocityResponse = SetVelocityResponse + +@typing.final +class IsMovingRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___IsMovingRequest = IsMovingRequest + +@typing.final +class IsMovingResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + IS_MOVING_FIELD_NUMBER: builtins.int + is_moving: builtins.bool + + def __init__(self, *, is_moving: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['is_moving', b'is_moving']) -> None: + ... +global___IsMovingResponse = IsMovingResponse + +@typing.final +class GetPropertiesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of the base' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetPropertiesRequest = GetPropertiesRequest + +@typing.final +class GetPropertiesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + WIDTH_METERS_FIELD_NUMBER: builtins.int + TURNING_RADIUS_METERS_FIELD_NUMBER: builtins.int + WHEEL_CIRCUMFERENCE_METERS_FIELD_NUMBER: builtins.int + width_meters: builtins.float + turning_radius_meters: builtins.float + wheel_circumference_meters: builtins.float + + def __init__(self, *, width_meters: builtins.float=..., turning_radius_meters: builtins.float=..., wheel_circumference_meters: builtins.float=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['turning_radius_meters', b'turning_radius_meters', 'wheel_circumference_meters', b'wheel_circumference_meters', 'width_meters', b'width_meters']) -> None: + ... +global___GetPropertiesResponse = GetPropertiesResponse \ No newline at end of file diff --git a/src/viam/gen/component/board/v1/board_grpc.py b/src/viam/gen/component/board/v1/board_grpc.py index 68147d590..8e061fc61 100644 --- a/src/viam/gen/component/board/v1/board_grpc.py +++ b/src/viam/gen/component/board/v1/board_grpc.py @@ -2,19 +2,17 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common import google.api.annotations_pb2 +import google.protobuf.duration_pb2 import google.protobuf.struct_pb2 from .... import component class BoardServiceBase(abc.ABC): - @abc.abstractmethod - async def Status(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.StatusRequest, component.board.v1.board_pb2.StatusResponse]') -> None: - pass - @abc.abstractmethod async def SetGPIO(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.SetGPIORequest, component.board.v1.board_pb2.SetGPIOResponse]') -> None: pass @@ -39,26 +37,91 @@ async def PWMFrequency(self, stream: 'grpclib.server.Stream[component.board.v1.b async def SetPWMFrequency(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.SetPWMFrequencyRequest, component.board.v1.board_pb2.SetPWMFrequencyResponse]') -> None: pass + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + @abc.abstractmethod async def ReadAnalogReader(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.ReadAnalogReaderRequest, component.board.v1.board_pb2.ReadAnalogReaderResponse]') -> None: pass + @abc.abstractmethod + async def WriteAnalog(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.WriteAnalogRequest, component.board.v1.board_pb2.WriteAnalogResponse]') -> None: + pass + @abc.abstractmethod async def GetDigitalInterruptValue(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.GetDigitalInterruptValueRequest, component.board.v1.board_pb2.GetDigitalInterruptValueResponse]') -> None: pass + @abc.abstractmethod + async def StreamTicks(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.StreamTicksRequest, component.board.v1.board_pb2.StreamTicksResponse]') -> None: + pass + + @abc.abstractmethod + async def SetPowerMode(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.SetPowerModeRequest, component.board.v1.board_pb2.SetPowerModeResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.board.v1.BoardService/Status': grpclib.const.Handler(self.Status, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.StatusRequest, component.board.v1.board_pb2.StatusResponse), '/viam.component.board.v1.BoardService/SetGPIO': grpclib.const.Handler(self.SetGPIO, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.SetGPIORequest, component.board.v1.board_pb2.SetGPIOResponse), '/viam.component.board.v1.BoardService/GetGPIO': grpclib.const.Handler(self.GetGPIO, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.GetGPIORequest, component.board.v1.board_pb2.GetGPIOResponse), '/viam.component.board.v1.BoardService/PWM': grpclib.const.Handler(self.PWM, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.PWMRequest, component.board.v1.board_pb2.PWMResponse), '/viam.component.board.v1.BoardService/SetPWM': grpclib.const.Handler(self.SetPWM, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.SetPWMRequest, component.board.v1.board_pb2.SetPWMResponse), '/viam.component.board.v1.BoardService/PWMFrequency': grpclib.const.Handler(self.PWMFrequency, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.PWMFrequencyRequest, component.board.v1.board_pb2.PWMFrequencyResponse), '/viam.component.board.v1.BoardService/SetPWMFrequency': grpclib.const.Handler(self.SetPWMFrequency, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.SetPWMFrequencyRequest, component.board.v1.board_pb2.SetPWMFrequencyResponse), '/viam.component.board.v1.BoardService/ReadAnalogReader': grpclib.const.Handler(self.ReadAnalogReader, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.ReadAnalogReaderRequest, component.board.v1.board_pb2.ReadAnalogReaderResponse), '/viam.component.board.v1.BoardService/GetDigitalInterruptValue': grpclib.const.Handler(self.GetDigitalInterruptValue, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.GetDigitalInterruptValueRequest, component.board.v1.board_pb2.GetDigitalInterruptValueResponse)} + return {'/viam.component.board.v1.BoardService/SetGPIO': grpclib.const.Handler(self.SetGPIO, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.SetGPIORequest, component.board.v1.board_pb2.SetGPIOResponse), '/viam.component.board.v1.BoardService/GetGPIO': grpclib.const.Handler(self.GetGPIO, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.GetGPIORequest, component.board.v1.board_pb2.GetGPIOResponse), '/viam.component.board.v1.BoardService/PWM': grpclib.const.Handler(self.PWM, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.PWMRequest, component.board.v1.board_pb2.PWMResponse), '/viam.component.board.v1.BoardService/SetPWM': grpclib.const.Handler(self.SetPWM, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.SetPWMRequest, component.board.v1.board_pb2.SetPWMResponse), '/viam.component.board.v1.BoardService/PWMFrequency': grpclib.const.Handler(self.PWMFrequency, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.PWMFrequencyRequest, component.board.v1.board_pb2.PWMFrequencyResponse), '/viam.component.board.v1.BoardService/SetPWMFrequency': grpclib.const.Handler(self.SetPWMFrequency, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.SetPWMFrequencyRequest, component.board.v1.board_pb2.SetPWMFrequencyResponse), '/viam.component.board.v1.BoardService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.board.v1.BoardService/ReadAnalogReader': grpclib.const.Handler(self.ReadAnalogReader, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.ReadAnalogReaderRequest, component.board.v1.board_pb2.ReadAnalogReaderResponse), '/viam.component.board.v1.BoardService/WriteAnalog': grpclib.const.Handler(self.WriteAnalog, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.WriteAnalogRequest, component.board.v1.board_pb2.WriteAnalogResponse), '/viam.component.board.v1.BoardService/GetDigitalInterruptValue': grpclib.const.Handler(self.GetDigitalInterruptValue, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.GetDigitalInterruptValueRequest, component.board.v1.board_pb2.GetDigitalInterruptValueResponse), '/viam.component.board.v1.BoardService/StreamTicks': grpclib.const.Handler(self.StreamTicks, grpclib.const.Cardinality.UNARY_STREAM, component.board.v1.board_pb2.StreamTicksRequest, component.board.v1.board_pb2.StreamTicksResponse), '/viam.component.board.v1.BoardService/SetPowerMode': grpclib.const.Handler(self.SetPowerMode, grpclib.const.Cardinality.UNARY_UNARY, component.board.v1.board_pb2.SetPowerModeRequest, component.board.v1.board_pb2.SetPowerModeResponse), '/viam.component.board.v1.BoardService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedBoardServiceBase(BoardServiceBase): + + async def SetGPIO(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.SetGPIORequest, component.board.v1.board_pb2.SetGPIOResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGPIO(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.GetGPIORequest, component.board.v1.board_pb2.GetGPIOResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def PWM(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.PWMRequest, component.board.v1.board_pb2.PWMResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetPWM(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.SetPWMRequest, component.board.v1.board_pb2.SetPWMResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def PWMFrequency(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.PWMFrequencyRequest, component.board.v1.board_pb2.PWMFrequencyResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetPWMFrequency(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.SetPWMFrequencyRequest, component.board.v1.board_pb2.SetPWMFrequencyResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ReadAnalogReader(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.ReadAnalogReaderRequest, component.board.v1.board_pb2.ReadAnalogReaderResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def WriteAnalog(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.WriteAnalogRequest, component.board.v1.board_pb2.WriteAnalogResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetDigitalInterruptValue(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.GetDigitalInterruptValueRequest, component.board.v1.board_pb2.GetDigitalInterruptValueResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def StreamTicks(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.StreamTicksRequest, component.board.v1.board_pb2.StreamTicksResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetPowerMode(self, stream: 'grpclib.server.Stream[component.board.v1.board_pb2.SetPowerModeRequest, component.board.v1.board_pb2.SetPowerModeResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class BoardServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: - self.Status = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/Status', component.board.v1.board_pb2.StatusRequest, component.board.v1.board_pb2.StatusResponse) self.SetGPIO = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/SetGPIO', component.board.v1.board_pb2.SetGPIORequest, component.board.v1.board_pb2.SetGPIOResponse) self.GetGPIO = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/GetGPIO', component.board.v1.board_pb2.GetGPIORequest, component.board.v1.board_pb2.GetGPIOResponse) self.PWM = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/PWM', component.board.v1.board_pb2.PWMRequest, component.board.v1.board_pb2.PWMResponse) self.SetPWM = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/SetPWM', component.board.v1.board_pb2.SetPWMRequest, component.board.v1.board_pb2.SetPWMResponse) self.PWMFrequency = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/PWMFrequency', component.board.v1.board_pb2.PWMFrequencyRequest, component.board.v1.board_pb2.PWMFrequencyResponse) self.SetPWMFrequency = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/SetPWMFrequency', component.board.v1.board_pb2.SetPWMFrequencyRequest, component.board.v1.board_pb2.SetPWMFrequencyResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) self.ReadAnalogReader = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/ReadAnalogReader', component.board.v1.board_pb2.ReadAnalogReaderRequest, component.board.v1.board_pb2.ReadAnalogReaderResponse) - self.GetDigitalInterruptValue = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/GetDigitalInterruptValue', component.board.v1.board_pb2.GetDigitalInterruptValueRequest, component.board.v1.board_pb2.GetDigitalInterruptValueResponse) \ No newline at end of file + self.WriteAnalog = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/WriteAnalog', component.board.v1.board_pb2.WriteAnalogRequest, component.board.v1.board_pb2.WriteAnalogResponse) + self.GetDigitalInterruptValue = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/GetDigitalInterruptValue', component.board.v1.board_pb2.GetDigitalInterruptValueRequest, component.board.v1.board_pb2.GetDigitalInterruptValueResponse) + self.StreamTicks = grpclib.client.UnaryStreamMethod(channel, '/viam.component.board.v1.BoardService/StreamTicks', component.board.v1.board_pb2.StreamTicksRequest, component.board.v1.board_pb2.StreamTicksResponse) + self.SetPowerMode = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/SetPowerMode', component.board.v1.board_pb2.SetPowerModeRequest, component.board.v1.board_pb2.SetPowerModeResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.board.v1.BoardService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/board/v1/board_pb2.py b/src/viam/gen/component/board/v1/board_pb2.py index 5a596deb4..c1160e21f 100644 --- a/src/viam/gen/component/board/v1/board_pb2.py +++ b/src/viam/gen/component/board/v1/board_pb2.py @@ -1,71 +1,103 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/board/v1/board.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ecomponent/board/v1/board.proto\x12\x17viam.component.board.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"R\n\rStatusRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"E\n\x0eStatusResponse\x123\n\x06status\x18\x01 \x01(\x0b2\x1b.viam.common.v1.BoardStatusR\x06status"y\n\x0eSetGPIORequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12\x12\n\x04high\x18\x03 \x01(\x08R\x04high\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x11\n\x0fSetGPIOResponse"e\n\x0eGetGPIORequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"%\n\x0fGetGPIOResponse\x12\x12\n\x04high\x18\x01 \x01(\x08R\x04high"a\n\nPWMRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"3\n\x0bPWMResponse\x12$\n\x0eduty_cycle_pct\x18\x01 \x01(\x01R\x0cdutyCyclePct"\x8a\x01\n\rSetPWMRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12$\n\x0eduty_cycle_pct\x18\x03 \x01(\x01R\x0cdutyCyclePct\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x10\n\x0eSetPWMResponse"j\n\x13PWMFrequencyRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"9\n\x14PWMFrequencyResponse\x12!\n\x0cfrequency_hz\x18\x01 \x01(\x04R\x0bfrequencyHz"\x90\x01\n\x16SetPWMFrequencyRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12!\n\x0cfrequency_hz\x18\x03 \x01(\x04R\x0bfrequencyHz\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x19\n\x17SetPWMFrequencyResponse"\x95\x01\n\x17ReadAnalogReaderRequest\x12\x1d\n\nboard_name\x18\x01 \x01(\tR\tboardName\x12,\n\x12analog_reader_name\x18\x02 \x01(\tR\x10analogReaderName\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"0\n\x18ReadAnalogReaderResponse\x12\x14\n\x05value\x18\x01 \x01(\x05R\x05value"\xa5\x01\n\x1fGetDigitalInterruptValueRequest\x12\x1d\n\nboard_name\x18\x01 \x01(\tR\tboardName\x124\n\x16digital_interrupt_name\x18\x02 \x01(\tR\x14digitalInterruptName\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"8\n GetDigitalInterruptValueResponse\x12\x14\n\x05value\x18\x01 \x01(\x03R\x05value2\xed\x0b\n\x0cBoardService\x12\x8d\x01\n\x06Status\x12&.viam.component.board.v1.StatusRequest\x1a\'.viam.component.board.v1.StatusResponse"2\x82\xd3\xe4\x93\x02,\x12*/viam/api/v1/component/board/{name}/status\x12\x8e\x01\n\x07SetGPIO\x12\'.viam.component.board.v1.SetGPIORequest\x1a(.viam.component.board.v1.SetGPIOResponse"0\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/board/{name}/gpio\x12\x8e\x01\n\x07GetGPIO\x12\'.viam.component.board.v1.GetGPIORequest\x1a(.viam.component.board.v1.GetGPIOResponse"0\x82\xd3\xe4\x93\x02*\x12(/viam/api/v1/component/board/{name}/gpio\x12\x81\x01\n\x03PWM\x12#.viam.component.board.v1.PWMRequest\x1a$.viam.component.board.v1.PWMResponse"/\x82\xd3\xe4\x93\x02)\x12\'/viam/api/v1/component/board/{name}/pwm\x12\x8a\x01\n\x06SetPWM\x12&.viam.component.board.v1.SetPWMRequest\x1a\'.viam.component.board.v1.SetPWMResponse"/\x82\xd3\xe4\x93\x02)\x1a\'/viam/api/v1/component/board/{name}/pwm\x12\xa1\x01\n\x0cPWMFrequency\x12,.viam.component.board.v1.PWMFrequencyRequest\x1a-.viam.component.board.v1.PWMFrequencyResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/board/{name}/pwm_freq\x12\xaa\x01\n\x0fSetPWMFrequency\x12/.viam.component.board.v1.SetPWMFrequencyRequest\x1a0.viam.component.board.v1.SetPWMFrequencyResponse"4\x82\xd3\xe4\x93\x02.\x1a,/viam/api/v1/component/board/{name}/pwm_freq\x12\xd2\x01\n\x10ReadAnalogReader\x120.viam.component.board.v1.ReadAnalogReaderRequest\x1a1.viam.component.board.v1.ReadAnalogReaderResponse"Y\x82\xd3\xe4\x93\x02S\x12Q/viam/api/v1/component/board/{board_name}/analog_reader/{analog_reader_name}/read\x12\xf3\x01\n\x18GetDigitalInterruptValue\x128.viam.component.board.v1.GetDigitalInterruptValueRequest\x1a9.viam.component.board.v1.GetDigitalInterruptValueResponse"b\x82\xd3\xe4\x93\x02\\\x12Z/viam/api/v1/component/board/{board_name}/digital_interrupt/{digital_interrupt_name}/valueBA\n\x1bcom.viam.component.board.v1Z"go.viam.com/api/component/board/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.board.v1.board_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1bcom.viam.component.board.v1Z"go.viam.com/api/component/board/v1' - _BOARDSERVICE.methods_by_name['Status']._options = None - _BOARDSERVICE.methods_by_name['Status']._serialized_options = b'\x82\xd3\xe4\x93\x02,\x12*/viam/api/v1/component/board/{name}/status' - _BOARDSERVICE.methods_by_name['SetGPIO']._options = None - _BOARDSERVICE.methods_by_name['SetGPIO']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/board/{name}/gpio' - _BOARDSERVICE.methods_by_name['GetGPIO']._options = None - _BOARDSERVICE.methods_by_name['GetGPIO']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x12(/viam/api/v1/component/board/{name}/gpio' - _BOARDSERVICE.methods_by_name['PWM']._options = None - _BOARDSERVICE.methods_by_name['PWM']._serialized_options = b"\x82\xd3\xe4\x93\x02)\x12'/viam/api/v1/component/board/{name}/pwm" - _BOARDSERVICE.methods_by_name['SetPWM']._options = None - _BOARDSERVICE.methods_by_name['SetPWM']._serialized_options = b"\x82\xd3\xe4\x93\x02)\x1a'/viam/api/v1/component/board/{name}/pwm" - _BOARDSERVICE.methods_by_name['PWMFrequency']._options = None - _BOARDSERVICE.methods_by_name['PWMFrequency']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/board/{name}/pwm_freq' - _BOARDSERVICE.methods_by_name['SetPWMFrequency']._options = None - _BOARDSERVICE.methods_by_name['SetPWMFrequency']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x1a,/viam/api/v1/component/board/{name}/pwm_freq' - _BOARDSERVICE.methods_by_name['ReadAnalogReader']._options = None - _BOARDSERVICE.methods_by_name['ReadAnalogReader']._serialized_options = b'\x82\xd3\xe4\x93\x02S\x12Q/viam/api/v1/component/board/{board_name}/analog_reader/{analog_reader_name}/read' - _BOARDSERVICE.methods_by_name['GetDigitalInterruptValue']._options = None - _BOARDSERVICE.methods_by_name['GetDigitalInterruptValue']._serialized_options = b'\x82\xd3\xe4\x93\x02\\\x12Z/viam/api/v1/component/board/{board_name}/digital_interrupt/{digital_interrupt_name}/value' - _STATUSREQUEST._serialized_start = 143 - _STATUSREQUEST._serialized_end = 225 - _STATUSRESPONSE._serialized_start = 227 - _STATUSRESPONSE._serialized_end = 296 - _SETGPIOREQUEST._serialized_start = 298 - _SETGPIOREQUEST._serialized_end = 419 - _SETGPIORESPONSE._serialized_start = 421 - _SETGPIORESPONSE._serialized_end = 438 - _GETGPIOREQUEST._serialized_start = 440 - _GETGPIOREQUEST._serialized_end = 541 - _GETGPIORESPONSE._serialized_start = 543 - _GETGPIORESPONSE._serialized_end = 580 - _PWMREQUEST._serialized_start = 582 - _PWMREQUEST._serialized_end = 679 - _PWMRESPONSE._serialized_start = 681 - _PWMRESPONSE._serialized_end = 732 - _SETPWMREQUEST._serialized_start = 735 - _SETPWMREQUEST._serialized_end = 873 - _SETPWMRESPONSE._serialized_start = 875 - _SETPWMRESPONSE._serialized_end = 891 - _PWMFREQUENCYREQUEST._serialized_start = 893 - _PWMFREQUENCYREQUEST._serialized_end = 999 - _PWMFREQUENCYRESPONSE._serialized_start = 1001 - _PWMFREQUENCYRESPONSE._serialized_end = 1058 - _SETPWMFREQUENCYREQUEST._serialized_start = 1061 - _SETPWMFREQUENCYREQUEST._serialized_end = 1205 - _SETPWMFREQUENCYRESPONSE._serialized_start = 1207 - _SETPWMFREQUENCYRESPONSE._serialized_end = 1232 - _READANALOGREADERREQUEST._serialized_start = 1235 - _READANALOGREADERREQUEST._serialized_end = 1384 - _READANALOGREADERRESPONSE._serialized_start = 1386 - _READANALOGREADERRESPONSE._serialized_end = 1434 - _GETDIGITALINTERRUPTVALUEREQUEST._serialized_start = 1437 - _GETDIGITALINTERRUPTVALUEREQUEST._serialized_end = 1602 - _GETDIGITALINTERRUPTVALUERESPONSE._serialized_start = 1604 - _GETDIGITALINTERRUPTVALUERESPONSE._serialized_end = 1660 - _BOARDSERVICE._serialized_start = 1663 - _BOARDSERVICE._serialized_end = 3180 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ecomponent/board/v1/board.proto\x12\x17viam.component.board.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto"\xb9\x02\n\x06Status\x12F\n\x07analogs\x18\x01 \x03(\x0b2,.viam.component.board.v1.Status.AnalogsEntryR\x07analogs\x12e\n\x12digital_interrupts\x18\x02 \x03(\x0b26.viam.component.board.v1.Status.DigitalInterruptsEntryR\x11digitalInterrupts\x1a:\n\x0cAnalogsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x05R\x05value:\x028\x01\x1aD\n\x16DigitalInterruptsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x03R\x05value:\x028\x01"y\n\x0eSetGPIORequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12\x12\n\x04high\x18\x03 \x01(\x08R\x04high\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x11\n\x0fSetGPIOResponse"e\n\x0eGetGPIORequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"%\n\x0fGetGPIOResponse\x12\x12\n\x04high\x18\x01 \x01(\x08R\x04high"a\n\nPWMRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"3\n\x0bPWMResponse\x12$\n\x0eduty_cycle_pct\x18\x01 \x01(\x01R\x0cdutyCyclePct"\x8a\x01\n\rSetPWMRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12$\n\x0eduty_cycle_pct\x18\x03 \x01(\x01R\x0cdutyCyclePct\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x10\n\x0eSetPWMResponse"j\n\x13PWMFrequencyRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"9\n\x14PWMFrequencyResponse\x12!\n\x0cfrequency_hz\x18\x01 \x01(\x04R\x0bfrequencyHz"\x90\x01\n\x16SetPWMFrequencyRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12!\n\x0cfrequency_hz\x18\x03 \x01(\x04R\x0bfrequencyHz\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x19\n\x17SetPWMFrequencyResponse"\x95\x01\n\x17ReadAnalogReaderRequest\x12\x1d\n\nboard_name\x18\x01 \x01(\tR\tboardName\x12,\n\x12analog_reader_name\x18\x02 \x01(\tR\x10analogReaderName\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x87\x01\n\x18ReadAnalogReaderResponse\x12\x14\n\x05value\x18\x01 \x01(\x05R\x05value\x12\x1b\n\tmin_range\x18\x02 \x01(\x02R\x08minRange\x12\x1b\n\tmax_range\x18\x03 \x01(\x02R\x08maxRange\x12\x1b\n\tstep_size\x18\x04 \x01(\x02R\x08stepSize"\x7f\n\x12WriteAnalogRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03pin\x18\x02 \x01(\tR\x03pin\x12\x14\n\x05value\x18\x03 \x01(\x05R\x05value\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x15\n\x13WriteAnalogResponse"\xa5\x01\n\x1fGetDigitalInterruptValueRequest\x12\x1d\n\nboard_name\x18\x01 \x01(\tR\tboardName\x124\n\x16digital_interrupt_name\x18\x02 \x01(\tR\x14digitalInterruptName\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"8\n GetDigitalInterruptValueResponse\x12\x14\n\x05value\x18\x01 \x01(\x03R\x05value"t\n\x12StreamTicksRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tpin_names\x18\x02 \x03(\tR\x08pinNames\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"X\n\x13StreamTicksResponse\x12\x19\n\x08pin_name\x18\x01 \x01(\tR\x07pinName\x12\x12\n\x04time\x18\x02 \x01(\x04R\x04time\x12\x12\n\x04high\x18\x03 \x01(\x08R\x04high"\xe4\x01\n\x13SetPowerModeRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12A\n\npower_mode\x18\x02 \x01(\x0e2".viam.component.board.v1.PowerModeR\tpowerMode\x12:\n\x08duration\x18\x03 \x01(\x0b2\x19.google.protobuf.DurationH\x00R\x08duration\x88\x01\x01\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\x0b\n\t_duration"\x16\n\x14SetPowerModeResponse*[\n\tPowerMode\x12\x1a\n\x16POWER_MODE_UNSPECIFIED\x10\x00\x12\x15\n\x11POWER_MODE_NORMAL\x10\x01\x12\x1b\n\x17POWER_MODE_OFFLINE_DEEP\x10\x022\xf0\x10\n\x0cBoardService\x12\x8e\x01\n\x07SetGPIO\x12\'.viam.component.board.v1.SetGPIORequest\x1a(.viam.component.board.v1.SetGPIOResponse"0\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/board/{name}/gpio\x12\x8e\x01\n\x07GetGPIO\x12\'.viam.component.board.v1.GetGPIORequest\x1a(.viam.component.board.v1.GetGPIOResponse"0\x82\xd3\xe4\x93\x02*\x12(/viam/api/v1/component/board/{name}/gpio\x12\x81\x01\n\x03PWM\x12#.viam.component.board.v1.PWMRequest\x1a$.viam.component.board.v1.PWMResponse"/\x82\xd3\xe4\x93\x02)\x12\'/viam/api/v1/component/board/{name}/pwm\x12\x8a\x01\n\x06SetPWM\x12&.viam.component.board.v1.SetPWMRequest\x1a\'.viam.component.board.v1.SetPWMResponse"/\x82\xd3\xe4\x93\x02)\x1a\'/viam/api/v1/component/board/{name}/pwm\x12\xa1\x01\n\x0cPWMFrequency\x12,.viam.component.board.v1.PWMFrequencyRequest\x1a-.viam.component.board.v1.PWMFrequencyResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/board/{name}/pwm_freq\x12\xaa\x01\n\x0fSetPWMFrequency\x12/.viam.component.board.v1.SetPWMFrequencyRequest\x1a0.viam.component.board.v1.SetPWMFrequencyResponse"4\x82\xd3\xe4\x93\x02.\x1a,/viam/api/v1/component/board/{name}/pwm_freq\x12\x88\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"6\x82\xd3\xe4\x93\x020"./viam/api/v1/component/board/{name}/do_command\x12\xd2\x01\n\x10ReadAnalogReader\x120.viam.component.board.v1.ReadAnalogReaderRequest\x1a1.viam.component.board.v1.ReadAnalogReaderResponse"Y\x82\xd3\xe4\x93\x02S\x12Q/viam/api/v1/component/board/{board_name}/analog_reader/{analog_reader_name}/read\x12\xa2\x01\n\x0bWriteAnalog\x12+.viam.component.board.v1.WriteAnalogRequest\x1a,.viam.component.board.v1.WriteAnalogResponse"8\x82\xd3\xe4\x93\x022\x1a0/viam/api/v1/component/board/{name}/analog_write\x12\xf3\x01\n\x18GetDigitalInterruptValue\x128.viam.component.board.v1.GetDigitalInterruptValueRequest\x1a9.viam.component.board.v1.GetDigitalInterruptValueResponse"b\x82\xd3\xe4\x93\x02\\\x12Z/viam/api/v1/component/board/{board_name}/digital_interrupt/{digital_interrupt_name}/value\x12\xa3\x01\n\x0bStreamTicks\x12+.viam.component.board.v1.StreamTicksRequest\x1a,.viam.component.board.v1.StreamTicksResponse"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/board/{name}/tick_stream0\x01\x12\xa3\x01\n\x0cSetPowerMode\x12,.viam.component.board.v1.SetPowerModeRequest\x1a-.viam.component.board.v1.SetPowerModeResponse"6\x82\xd3\xe4\x93\x020\x1a./viam/api/v1/component/board/{name}/power_mode\x12\x94\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"6\x82\xd3\xe4\x93\x020\x12./viam/api/v1/component/board/{name}/geometriesBA\n\x1bcom.viam.component.board.v1Z"go.viam.com/api/component/board/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.board.v1.board_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1bcom.viam.component.board.v1Z"go.viam.com/api/component/board/v1' + _globals['_STATUS_ANALOGSENTRY']._loaded_options = None + _globals['_STATUS_ANALOGSENTRY']._serialized_options = b'8\x01' + _globals['_STATUS_DIGITALINTERRUPTSENTRY']._loaded_options = None + _globals['_STATUS_DIGITALINTERRUPTSENTRY']._serialized_options = b'8\x01' + _globals['_BOARDSERVICE'].methods_by_name['SetGPIO']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['SetGPIO']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/board/{name}/gpio' + _globals['_BOARDSERVICE'].methods_by_name['GetGPIO']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['GetGPIO']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x12(/viam/api/v1/component/board/{name}/gpio' + _globals['_BOARDSERVICE'].methods_by_name['PWM']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['PWM']._serialized_options = b"\x82\xd3\xe4\x93\x02)\x12'/viam/api/v1/component/board/{name}/pwm" + _globals['_BOARDSERVICE'].methods_by_name['SetPWM']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['SetPWM']._serialized_options = b"\x82\xd3\xe4\x93\x02)\x1a'/viam/api/v1/component/board/{name}/pwm" + _globals['_BOARDSERVICE'].methods_by_name['PWMFrequency']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['PWMFrequency']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/board/{name}/pwm_freq' + _globals['_BOARDSERVICE'].methods_by_name['SetPWMFrequency']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['SetPWMFrequency']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x1a,/viam/api/v1/component/board/{name}/pwm_freq' + _globals['_BOARDSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x020"./viam/api/v1/component/board/{name}/do_command' + _globals['_BOARDSERVICE'].methods_by_name['ReadAnalogReader']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['ReadAnalogReader']._serialized_options = b'\x82\xd3\xe4\x93\x02S\x12Q/viam/api/v1/component/board/{board_name}/analog_reader/{analog_reader_name}/read' + _globals['_BOARDSERVICE'].methods_by_name['WriteAnalog']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['WriteAnalog']._serialized_options = b'\x82\xd3\xe4\x93\x022\x1a0/viam/api/v1/component/board/{name}/analog_write' + _globals['_BOARDSERVICE'].methods_by_name['GetDigitalInterruptValue']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['GetDigitalInterruptValue']._serialized_options = b'\x82\xd3\xe4\x93\x02\\\x12Z/viam/api/v1/component/board/{board_name}/digital_interrupt/{digital_interrupt_name}/value' + _globals['_BOARDSERVICE'].methods_by_name['StreamTicks']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['StreamTicks']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/board/{name}/tick_stream' + _globals['_BOARDSERVICE'].methods_by_name['SetPowerMode']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['SetPowerMode']._serialized_options = b'\x82\xd3\xe4\x93\x020\x1a./viam/api/v1/component/board/{name}/power_mode' + _globals['_BOARDSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_BOARDSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x020\x12./viam/api/v1/component/board/{name}/geometries' + _globals['_POWERMODE']._serialized_start = 2558 + _globals['_POWERMODE']._serialized_end = 2649 + _globals['_STATUS']._serialized_start = 176 + _globals['_STATUS']._serialized_end = 489 + _globals['_STATUS_ANALOGSENTRY']._serialized_start = 361 + _globals['_STATUS_ANALOGSENTRY']._serialized_end = 419 + _globals['_STATUS_DIGITALINTERRUPTSENTRY']._serialized_start = 421 + _globals['_STATUS_DIGITALINTERRUPTSENTRY']._serialized_end = 489 + _globals['_SETGPIOREQUEST']._serialized_start = 491 + _globals['_SETGPIOREQUEST']._serialized_end = 612 + _globals['_SETGPIORESPONSE']._serialized_start = 614 + _globals['_SETGPIORESPONSE']._serialized_end = 631 + _globals['_GETGPIOREQUEST']._serialized_start = 633 + _globals['_GETGPIOREQUEST']._serialized_end = 734 + _globals['_GETGPIORESPONSE']._serialized_start = 736 + _globals['_GETGPIORESPONSE']._serialized_end = 773 + _globals['_PWMREQUEST']._serialized_start = 775 + _globals['_PWMREQUEST']._serialized_end = 872 + _globals['_PWMRESPONSE']._serialized_start = 874 + _globals['_PWMRESPONSE']._serialized_end = 925 + _globals['_SETPWMREQUEST']._serialized_start = 928 + _globals['_SETPWMREQUEST']._serialized_end = 1066 + _globals['_SETPWMRESPONSE']._serialized_start = 1068 + _globals['_SETPWMRESPONSE']._serialized_end = 1084 + _globals['_PWMFREQUENCYREQUEST']._serialized_start = 1086 + _globals['_PWMFREQUENCYREQUEST']._serialized_end = 1192 + _globals['_PWMFREQUENCYRESPONSE']._serialized_start = 1194 + _globals['_PWMFREQUENCYRESPONSE']._serialized_end = 1251 + _globals['_SETPWMFREQUENCYREQUEST']._serialized_start = 1254 + _globals['_SETPWMFREQUENCYREQUEST']._serialized_end = 1398 + _globals['_SETPWMFREQUENCYRESPONSE']._serialized_start = 1400 + _globals['_SETPWMFREQUENCYRESPONSE']._serialized_end = 1425 + _globals['_READANALOGREADERREQUEST']._serialized_start = 1428 + _globals['_READANALOGREADERREQUEST']._serialized_end = 1577 + _globals['_READANALOGREADERRESPONSE']._serialized_start = 1580 + _globals['_READANALOGREADERRESPONSE']._serialized_end = 1715 + _globals['_WRITEANALOGREQUEST']._serialized_start = 1717 + _globals['_WRITEANALOGREQUEST']._serialized_end = 1844 + _globals['_WRITEANALOGRESPONSE']._serialized_start = 1846 + _globals['_WRITEANALOGRESPONSE']._serialized_end = 1867 + _globals['_GETDIGITALINTERRUPTVALUEREQUEST']._serialized_start = 1870 + _globals['_GETDIGITALINTERRUPTVALUEREQUEST']._serialized_end = 2035 + _globals['_GETDIGITALINTERRUPTVALUERESPONSE']._serialized_start = 2037 + _globals['_GETDIGITALINTERRUPTVALUERESPONSE']._serialized_end = 2093 + _globals['_STREAMTICKSREQUEST']._serialized_start = 2095 + _globals['_STREAMTICKSREQUEST']._serialized_end = 2211 + _globals['_STREAMTICKSRESPONSE']._serialized_start = 2213 + _globals['_STREAMTICKSRESPONSE']._serialized_end = 2301 + _globals['_SETPOWERMODEREQUEST']._serialized_start = 2304 + _globals['_SETPOWERMODEREQUEST']._serialized_end = 2532 + _globals['_SETPOWERMODERESPONSE']._serialized_start = 2534 + _globals['_SETPOWERMODERESPONSE']._serialized_end = 2556 + _globals['_BOARDSERVICE']._serialized_start = 2652 + _globals['_BOARDSERVICE']._serialized_end = 4812 \ No newline at end of file diff --git a/src/viam/gen/component/board/v1/board_pb2.pyi b/src/viam/gen/component/board/v1/board_pb2.pyi index 67f701231..028f4c2ed 100644 --- a/src/viam/gen/component/board/v1/board_pb2.pyi +++ b/src/viam/gen/component/board/v1/board_pb2.pyi @@ -3,55 +3,88 @@ isort:skip_file """ import builtins -from .... import common +import collections.abc import google.protobuf.descriptor +import google.protobuf.duration_pb2 +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import google.protobuf.struct_pb2 import sys -if sys.version_info >= (3, 8): +import typing +if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor -class StatusRequest(google.protobuf.message.Message): +class _PowerMode: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PowerModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PowerMode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + POWER_MODE_UNSPECIFIED: _PowerMode.ValueType + POWER_MODE_NORMAL: _PowerMode.ValueType + POWER_MODE_OFFLINE_DEEP: _PowerMode.ValueType + +class PowerMode(_PowerMode, metaclass=_PowerModeEnumTypeWrapper): + """Power Management API""" +POWER_MODE_UNSPECIFIED: PowerMode.ValueType +POWER_MODE_NORMAL: PowerMode.ValueType +POWER_MODE_OFFLINE_DEEP: PowerMode.ValueType +global___PowerMode = PowerMode + +@typing.final +class Status(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - EXTRA_FIELD_NUMBER: builtins.int - name: builtins.str - @property - def extra(self) -> google.protobuf.struct_pb2.Struct: - """Additional arguments to the method""" + @typing.final + class AnalogsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.int - def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: - ... + def __init__(self, *, key: builtins.str=..., value: builtins.int=...) -> None: + ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: - ... + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: - ... -global___StatusRequest = StatusRequest + @typing.final + class DigitalInterruptsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.int -class StatusResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - STATUS_FIELD_NUMBER: builtins.int + def __init__(self, *, key: builtins.str=..., value: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... + ANALOGS_FIELD_NUMBER: builtins.int + DIGITAL_INTERRUPTS_FIELD_NUMBER: builtins.int @property - def status(self) -> common.v1.common_pb2.BoardStatus: + def analogs(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.int]: ... - def __init__(self, *, status: common.v1.common_pb2.BoardStatus | None=...) -> None: + @property + def digital_interrupts(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.int]: ... - def HasField(self, field_name: typing_extensions.Literal['status', b'status']) -> builtins.bool: + def __init__(self, *, analogs: collections.abc.Mapping[builtins.str, builtins.int] | None=..., digital_interrupts: collections.abc.Mapping[builtins.str, builtins.int] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['status', b'status']) -> None: + def ClearField(self, field_name: typing.Literal['analogs', b'analogs', 'digital_interrupts', b'digital_interrupts']) -> None: ... -global___StatusResponse = StatusResponse +global___Status = Status +@typing.final class SetGPIORequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -69,13 +102,14 @@ class SetGPIORequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., pin: builtins.str=..., high: builtins.bool=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'high', b'high', 'name', b'name', 'pin', b'pin']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'high', b'high', 'name', b'name', 'pin', b'pin']) -> None: ... global___SetGPIORequest = SetGPIORequest +@typing.final class SetGPIOResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -83,6 +117,7 @@ class SetGPIOResponse(google.protobuf.message.Message): ... global___SetGPIOResponse = SetGPIOResponse +@typing.final class GetGPIORequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -98,13 +133,14 @@ class GetGPIORequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., pin: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name', 'pin', b'pin']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'pin', b'pin']) -> None: ... global___GetGPIORequest = GetGPIORequest +@typing.final class GetGPIOResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor HIGH_FIELD_NUMBER: builtins.int @@ -113,10 +149,11 @@ class GetGPIOResponse(google.protobuf.message.Message): def __init__(self, *, high: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['high', b'high']) -> None: + def ClearField(self, field_name: typing.Literal['high', b'high']) -> None: ... global___GetGPIOResponse = GetGPIOResponse +@typing.final class PWMRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -132,13 +169,14 @@ class PWMRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., pin: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name', 'pin', b'pin']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'pin', b'pin']) -> None: ... global___PWMRequest = PWMRequest +@typing.final class PWMResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor DUTY_CYCLE_PCT_FIELD_NUMBER: builtins.int @@ -148,10 +186,11 @@ class PWMResponse(google.protobuf.message.Message): def __init__(self, *, duty_cycle_pct: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['duty_cycle_pct', b'duty_cycle_pct']) -> None: + def ClearField(self, field_name: typing.Literal['duty_cycle_pct', b'duty_cycle_pct']) -> None: ... global___PWMResponse = PWMResponse +@typing.final class SetPWMRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -170,13 +209,14 @@ class SetPWMRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., pin: builtins.str=..., duty_cycle_pct: builtins.float=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['duty_cycle_pct', b'duty_cycle_pct', 'extra', b'extra', 'name', b'name', 'pin', b'pin']) -> None: + def ClearField(self, field_name: typing.Literal['duty_cycle_pct', b'duty_cycle_pct', 'extra', b'extra', 'name', b'name', 'pin', b'pin']) -> None: ... global___SetPWMRequest = SetPWMRequest +@typing.final class SetPWMResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -184,6 +224,7 @@ class SetPWMResponse(google.protobuf.message.Message): ... global___SetPWMResponse = SetPWMResponse +@typing.final class PWMFrequencyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -199,13 +240,14 @@ class PWMFrequencyRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., pin: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name', 'pin', b'pin']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'pin', b'pin']) -> None: ... global___PWMFrequencyRequest = PWMFrequencyRequest +@typing.final class PWMFrequencyResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor FREQUENCY_HZ_FIELD_NUMBER: builtins.int @@ -214,10 +256,11 @@ class PWMFrequencyResponse(google.protobuf.message.Message): def __init__(self, *, frequency_hz: builtins.int=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['frequency_hz', b'frequency_hz']) -> None: + def ClearField(self, field_name: typing.Literal['frequency_hz', b'frequency_hz']) -> None: ... global___PWMFrequencyResponse = PWMFrequencyResponse +@typing.final class SetPWMFrequencyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -235,13 +278,14 @@ class SetPWMFrequencyRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., pin: builtins.str=..., frequency_hz: builtins.int=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'frequency_hz', b'frequency_hz', 'name', b'name', 'pin', b'pin']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'frequency_hz', b'frequency_hz', 'name', b'name', 'pin', b'pin']) -> None: ... global___SetPWMFrequencyRequest = SetPWMFrequencyRequest +@typing.final class SetPWMFrequencyResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -249,6 +293,7 @@ class SetPWMFrequencyResponse(google.protobuf.message.Message): ... global___SetPWMFrequencyResponse = SetPWMFrequencyResponse +@typing.final class ReadAnalogReaderRequest(google.protobuf.message.Message): """Analog Reader""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -265,25 +310,67 @@ class ReadAnalogReaderRequest(google.protobuf.message.Message): def __init__(self, *, board_name: builtins.str=..., analog_reader_name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['analog_reader_name', b'analog_reader_name', 'board_name', b'board_name', 'extra', b'extra']) -> None: + def ClearField(self, field_name: typing.Literal['analog_reader_name', b'analog_reader_name', 'board_name', b'board_name', 'extra', b'extra']) -> None: ... global___ReadAnalogReaderRequest = ReadAnalogReaderRequest +@typing.final class ReadAnalogReaderResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor VALUE_FIELD_NUMBER: builtins.int + MIN_RANGE_FIELD_NUMBER: builtins.int + MAX_RANGE_FIELD_NUMBER: builtins.int + STEP_SIZE_FIELD_NUMBER: builtins.int value: builtins.int + min_range: builtins.float + max_range: builtins.float + step_size: builtins.float - def __init__(self, *, value: builtins.int=...) -> None: + def __init__(self, *, value: builtins.int=..., min_range: builtins.float=..., max_range: builtins.float=..., step_size: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['max_range', b'max_range', 'min_range', b'min_range', 'step_size', b'step_size', 'value', b'value']) -> None: ... global___ReadAnalogReaderResponse = ReadAnalogReaderResponse +@typing.final +class WriteAnalogRequest(google.protobuf.message.Message): + """Analog Writer""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + PIN_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + pin: builtins.str + value: builtins.int + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., pin: builtins.str=..., value: builtins.int=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'pin', b'pin', 'value', b'value']) -> None: + ... +global___WriteAnalogRequest = WriteAnalogRequest + +@typing.final +class WriteAnalogResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___WriteAnalogResponse = WriteAnalogResponse + +@typing.final class GetDigitalInterruptValueRequest(google.protobuf.message.Message): """Digital Interrupt""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -300,13 +387,14 @@ class GetDigitalInterruptValueRequest(google.protobuf.message.Message): def __init__(self, *, board_name: builtins.str=..., digital_interrupt_name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['board_name', b'board_name', 'digital_interrupt_name', b'digital_interrupt_name', 'extra', b'extra']) -> None: + def ClearField(self, field_name: typing.Literal['board_name', b'board_name', 'digital_interrupt_name', b'digital_interrupt_name', 'extra', b'extra']) -> None: ... global___GetDigitalInterruptValueRequest = GetDigitalInterruptValueRequest +@typing.final class GetDigitalInterruptValueResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor VALUE_FIELD_NUMBER: builtins.int @@ -315,6 +403,94 @@ class GetDigitalInterruptValueResponse(google.protobuf.message.Message): def __init__(self, *, value: builtins.int=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['value', b'value']) -> None: + ... +global___GetDigitalInterruptValueResponse = GetDigitalInterruptValueResponse + +@typing.final +class StreamTicksRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + PIN_NAMES_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Board name' + + @property + def pin_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Name of digital interrupts to recieve ticks from""" + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., pin_names: collections.abc.Iterable[builtins.str] | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'pin_names', b'pin_names']) -> None: + ... +global___StreamTicksRequest = StreamTicksRequest + +@typing.final +class StreamTicksResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PIN_NAME_FIELD_NUMBER: builtins.int + TIME_FIELD_NUMBER: builtins.int + HIGH_FIELD_NUMBER: builtins.int + pin_name: builtins.str + 'name of interrupt' + time: builtins.int + 'Time in nanoseconds of a tick' + high: builtins.bool + 'Value high or low of the tick' + + def __init__(self, *, pin_name: builtins.str=..., time: builtins.int=..., high: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['high', b'high', 'pin_name', b'pin_name', 'time', b'time']) -> None: + ... +global___StreamTicksResponse = StreamTicksResponse + +@typing.final +class SetPowerModeRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + POWER_MODE_FIELD_NUMBER: builtins.int + DURATION_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'name of board' + power_mode: global___PowerMode.ValueType + 'Requested power mode' + + @property + def duration(self) -> google.protobuf.duration_pb2.Duration: + """Requested duration to stay in `power_mode`""" + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., power_mode: global___PowerMode.ValueType=..., duration: google.protobuf.duration_pb2.Duration | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_duration', b'_duration', 'duration', b'duration', 'extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_duration', b'_duration', 'duration', b'duration', 'extra', b'extra', 'name', b'name', 'power_mode', b'power_mode']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_duration', b'_duration']) -> typing.Literal['duration'] | None: + ... +global___SetPowerModeRequest = SetPowerModeRequest + +@typing.final +class SetPowerModeResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... -global___GetDigitalInterruptValueResponse = GetDigitalInterruptValueResponse \ No newline at end of file +global___SetPowerModeResponse = SetPowerModeResponse \ No newline at end of file diff --git a/src/viam/gen/component/button/__init__.py b/src/viam/gen/component/button/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/component/button/v1/__init__.py b/src/viam/gen/component/button/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/component/button/v1/button_grpc.py b/src/viam/gen/component/button/v1/button_grpc.py new file mode 100644 index 000000000..f36d03d0b --- /dev/null +++ b/src/viam/gen/component/button/v1/button_grpc.py @@ -0,0 +1,38 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from .... import common +import google.api.annotations_pb2 +import google.protobuf.struct_pb2 +from .... import component + +class ButtonServiceBase(abc.ABC): + + @abc.abstractmethod + async def Push(self, stream: 'grpclib.server.Stream[component.button.v1.button_pb2.PushRequest, component.button.v1.button_pb2.PushResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.component.button.v1.ButtonService/Push': grpclib.const.Handler(self.Push, grpclib.const.Cardinality.UNARY_UNARY, component.button.v1.button_pb2.PushRequest, component.button.v1.button_pb2.PushResponse), '/viam.component.button.v1.ButtonService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedButtonServiceBase(ButtonServiceBase): + + async def Push(self, stream: 'grpclib.server.Stream[component.button.v1.button_pb2.PushRequest, component.button.v1.button_pb2.PushResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class ButtonServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.Push = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.button.v1.ButtonService/Push', component.button.v1.button_pb2.PushRequest, component.button.v1.button_pb2.PushResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.button.v1.ButtonService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/component/button/v1/button_pb2.py b/src/viam/gen/component/button/v1/button_pb2.py new file mode 100644 index 000000000..42eda925e --- /dev/null +++ b/src/viam/gen/component/button/v1/button_pb2.py @@ -0,0 +1,28 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/button/v1/button.proto') +_sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n component/button/v1/button.proto\x12\x18viam.component.button.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"P\n\x0bPushRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cPushResponse2\xa6\x02\n\rButtonService\x12\x88\x01\n\x04Push\x12%.viam.component.button.v1.PushRequest\x1a&.viam.component.button.v1.PushResponse"1\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/button/{name}/push\x12\x89\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"7\x82\xd3\xe4\x93\x021"//viam/api/v1/component/button/{name}/do_commandBC\n\x1ccom.viam.component.button.v1Z#go.viam.com/api/component/button/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.button.v1.button_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1ccom.viam.component.button.v1Z#go.viam.com/api/component/button/v1' + _globals['_BUTTONSERVICE'].methods_by_name['Push']._loaded_options = None + _globals['_BUTTONSERVICE'].methods_by_name['Push']._serialized_options = b'\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/button/{name}/push' + _globals['_BUTTONSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_BUTTONSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x021"//viam/api/v1/component/button/{name}/do_command' + _globals['_PUSHREQUEST']._serialized_start = 146 + _globals['_PUSHREQUEST']._serialized_end = 226 + _globals['_PUSHRESPONSE']._serialized_start = 228 + _globals['_PUSHRESPONSE']._serialized_end = 242 + _globals['_BUTTONSERVICE']._serialized_start = 245 + _globals['_BUTTONSERVICE']._serialized_end = 539 \ No newline at end of file diff --git a/src/viam/gen/component/button/v1/button_pb2.pyi b/src/viam/gen/component/button/v1/button_pb2.pyi new file mode 100644 index 000000000..86357245b --- /dev/null +++ b/src/viam/gen/component/button/v1/button_pb2.pyi @@ -0,0 +1,39 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import google.protobuf.struct_pb2 +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class PushRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___PushRequest = PushRequest + +@typing.final +class PushResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___PushResponse = PushResponse \ No newline at end of file diff --git a/src/viam/gen/component/camera/v1/camera_grpc.py b/src/viam/gen/component/camera/v1/camera_grpc.py index 4491a667e..1f89619ab 100644 --- a/src/viam/gen/component/camera/v1/camera_grpc.py +++ b/src/viam/gen/component/camera/v1/camera_grpc.py @@ -2,10 +2,13 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import common import google.api.annotations_pb2 import google.api.httpbody_pb2 +import google.protobuf.struct_pb2 from .... import component class CameraServiceBase(abc.ABC): @@ -14,6 +17,10 @@ class CameraServiceBase(abc.ABC): async def GetImage(self, stream: 'grpclib.server.Stream[component.camera.v1.camera_pb2.GetImageRequest, component.camera.v1.camera_pb2.GetImageResponse]') -> None: pass + @abc.abstractmethod + async def GetImages(self, stream: 'grpclib.server.Stream[component.camera.v1.camera_pb2.GetImagesRequest, component.camera.v1.camera_pb2.GetImagesResponse]') -> None: + pass + @abc.abstractmethod async def RenderFrame(self, stream: 'grpclib.server.Stream[component.camera.v1.camera_pb2.RenderFrameRequest, google.api.httpbody_pb2.HttpBody]') -> None: pass @@ -26,13 +33,47 @@ async def GetPointCloud(self, stream: 'grpclib.server.Stream[component.camera.v1 async def GetProperties(self, stream: 'grpclib.server.Stream[component.camera.v1.camera_pb2.GetPropertiesRequest, component.camera.v1.camera_pb2.GetPropertiesResponse]') -> None: pass + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.camera.v1.CameraService/GetImage': grpclib.const.Handler(self.GetImage, grpclib.const.Cardinality.UNARY_UNARY, component.camera.v1.camera_pb2.GetImageRequest, component.camera.v1.camera_pb2.GetImageResponse), '/viam.component.camera.v1.CameraService/RenderFrame': grpclib.const.Handler(self.RenderFrame, grpclib.const.Cardinality.UNARY_UNARY, component.camera.v1.camera_pb2.RenderFrameRequest, google.api.httpbody_pb2.HttpBody), '/viam.component.camera.v1.CameraService/GetPointCloud': grpclib.const.Handler(self.GetPointCloud, grpclib.const.Cardinality.UNARY_UNARY, component.camera.v1.camera_pb2.GetPointCloudRequest, component.camera.v1.camera_pb2.GetPointCloudResponse), '/viam.component.camera.v1.CameraService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, component.camera.v1.camera_pb2.GetPropertiesRequest, component.camera.v1.camera_pb2.GetPropertiesResponse)} + return {'/viam.component.camera.v1.CameraService/GetImage': grpclib.const.Handler(self.GetImage, grpclib.const.Cardinality.UNARY_UNARY, component.camera.v1.camera_pb2.GetImageRequest, component.camera.v1.camera_pb2.GetImageResponse), '/viam.component.camera.v1.CameraService/GetImages': grpclib.const.Handler(self.GetImages, grpclib.const.Cardinality.UNARY_UNARY, component.camera.v1.camera_pb2.GetImagesRequest, component.camera.v1.camera_pb2.GetImagesResponse), '/viam.component.camera.v1.CameraService/RenderFrame': grpclib.const.Handler(self.RenderFrame, grpclib.const.Cardinality.UNARY_UNARY, component.camera.v1.camera_pb2.RenderFrameRequest, google.api.httpbody_pb2.HttpBody), '/viam.component.camera.v1.CameraService/GetPointCloud': grpclib.const.Handler(self.GetPointCloud, grpclib.const.Cardinality.UNARY_UNARY, component.camera.v1.camera_pb2.GetPointCloudRequest, component.camera.v1.camera_pb2.GetPointCloudResponse), '/viam.component.camera.v1.CameraService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, component.camera.v1.camera_pb2.GetPropertiesRequest, component.camera.v1.camera_pb2.GetPropertiesResponse), '/viam.component.camera.v1.CameraService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.camera.v1.CameraService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedCameraServiceBase(CameraServiceBase): + + async def GetImage(self, stream: 'grpclib.server.Stream[component.camera.v1.camera_pb2.GetImageRequest, component.camera.v1.camera_pb2.GetImageResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetImages(self, stream: 'grpclib.server.Stream[component.camera.v1.camera_pb2.GetImagesRequest, component.camera.v1.camera_pb2.GetImagesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RenderFrame(self, stream: 'grpclib.server.Stream[component.camera.v1.camera_pb2.RenderFrameRequest, google.api.httpbody_pb2.HttpBody]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPointCloud(self, stream: 'grpclib.server.Stream[component.camera.v1.camera_pb2.GetPointCloudRequest, component.camera.v1.camera_pb2.GetPointCloudResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetProperties(self, stream: 'grpclib.server.Stream[component.camera.v1.camera_pb2.GetPropertiesRequest, component.camera.v1.camera_pb2.GetPropertiesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class CameraServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: self.GetImage = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.camera.v1.CameraService/GetImage', component.camera.v1.camera_pb2.GetImageRequest, component.camera.v1.camera_pb2.GetImageResponse) + self.GetImages = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.camera.v1.CameraService/GetImages', component.camera.v1.camera_pb2.GetImagesRequest, component.camera.v1.camera_pb2.GetImagesResponse) self.RenderFrame = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.camera.v1.CameraService/RenderFrame', component.camera.v1.camera_pb2.RenderFrameRequest, google.api.httpbody_pb2.HttpBody) self.GetPointCloud = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.camera.v1.CameraService/GetPointCloud', component.camera.v1.camera_pb2.GetPointCloudRequest, component.camera.v1.camera_pb2.GetPointCloudResponse) - self.GetProperties = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.camera.v1.CameraService/GetProperties', component.camera.v1.camera_pb2.GetPropertiesRequest, component.camera.v1.camera_pb2.GetPropertiesResponse) \ No newline at end of file + self.GetProperties = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.camera.v1.CameraService/GetProperties', component.camera.v1.camera_pb2.GetPropertiesRequest, component.camera.v1.camera_pb2.GetPropertiesResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.camera.v1.CameraService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.camera.v1.CameraService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/camera/v1/camera_pb2.py b/src/viam/gen/component/camera/v1/camera_pb2.py index a0d9286a6..72c64f2f8 100644 --- a/src/viam/gen/component/camera/v1/camera_pb2.py +++ b/src/viam/gen/component/camera/v1/camera_pb2.py @@ -1,50 +1,67 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/camera/v1/camera.proto') _sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.api import httpbody_pb2 as google_dot_api_dot_httpbody__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n component/camera/v1/camera.proto\x12\x18viam.component.camera.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x19google/api/httpbody.proto"B\n\x0fGetImageRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tmime_type\x18\x02 \x01(\tR\x08mimeType"}\n\x10GetImageResponse\x12\x1b\n\tmime_type\x18\x01 \x01(\tR\x08mimeType\x12\x14\n\x05image\x18\x02 \x01(\x0cR\x05image\x12\x19\n\x08width_px\x18\x03 \x01(\x03R\x07widthPx\x12\x1b\n\theight_px\x18\x04 \x01(\x03R\x08heightPx"E\n\x12RenderFrameRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tmime_type\x18\x02 \x01(\tR\x08mimeType"G\n\x14GetPointCloudRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tmime_type\x18\x02 \x01(\tR\x08mimeType"U\n\x15GetPointCloudResponse\x12\x1b\n\tmime_type\x18\x01 \x01(\tR\x08mimeType\x12\x1f\n\x0bpoint_cloud\x18\x02 \x01(\x0cR\npointCloud"*\n\x14GetPropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x81\x02\n\x15GetPropertiesResponse\x12!\n\x0csupports_pcd\x18\x01 \x01(\x08R\x0bsupportsPcd\x12`\n\x14intrinsic_parameters\x18\x02 \x01(\x0b2-.viam.component.camera.v1.IntrinsicParametersR\x13intrinsicParameters\x12c\n\x15distortion_parameters\x18\x03 \x01(\x0b2..viam.component.camera.v1.DistortionParametersR\x14distortionParameters"E\n\x07Webcams\x12:\n\x07webcams\x18\x01 \x03(\x0b2 .viam.component.camera.v1.WebcamR\x07webcams"z\n\x06Webcam\x12\x14\n\x05label\x18\x01 \x01(\tR\x05label\x12\x16\n\x06status\x18\x02 \x01(\tR\x06status\x12B\n\nproperties\x18\x03 \x03(\x0b2".viam.component.camera.v1.PropertyR\nproperties"\x92\x01\n\x08Property\x125\n\x05video\x18\x01 \x01(\x0b2\x1f.viam.component.camera.v1.VideoR\x05video\x12\x14\n\x05width\x18\x02 \x01(\x05R\x05width\x12\x16\n\x06height\x18\x03 \x01(\x05R\x06height\x12!\n\x0cframe_format\x18\x04 \x01(\tR\x0bframeFormat"X\n\x05Video\x12\x14\n\x05width\x18\x01 \x01(\x05R\x05width\x12\x16\n\x06height\x18\x02 \x01(\x05R\x06height\x12!\n\x0cframe_format\x18\x03 \x01(\tR\x0bframeFormat"\xc9\x01\n\x13IntrinsicParameters\x12\x19\n\x08width_px\x18\x01 \x01(\rR\x07widthPx\x12\x1b\n\theight_px\x18\x02 \x01(\rR\x08heightPx\x12\x1c\n\nfocal_x_px\x18\x03 \x01(\x01R\x08focalXPx\x12\x1c\n\nfocal_y_px\x18\x04 \x01(\x01R\x08focalYPx\x12\x1e\n\x0bcenter_x_px\x18\x05 \x01(\x01R\tcenterXPx\x12\x1e\n\x0bcenter_y_px\x18\x06 \x01(\x01R\tcenterYPx"L\n\x14DistortionParameters\x12\x14\n\x05model\x18\x01 \x01(\tR\x05model\x12\x1e\n\nparameters\x18\x02 \x03(\x01R\nparameters2\x8f\x05\n\rCameraService\x12\x95\x01\n\x08GetImage\x12).viam.component.camera.v1.GetImageRequest\x1a*.viam.component.camera.v1.GetImageResponse"2\x82\xd3\xe4\x93\x02,\x12*/viam/api/v1/component/camera/{name}/image\x12\x8c\x01\n\x0bRenderFrame\x12,.viam.component.camera.v1.RenderFrameRequest\x1a\x14.google.api.HttpBody"9\x82\xd3\xe4\x93\x023\x121/viam/api/v1/component/camera/{name}/render_frame\x12\xaa\x01\n\rGetPointCloud\x12..viam.component.camera.v1.GetPointCloudRequest\x1a/.viam.component.camera.v1.GetPointCloudResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/camera/{name}/point_cloud\x12\xa9\x01\n\rGetProperties\x12..viam.component.camera.v1.GetPropertiesRequest\x1a/.viam.component.camera.v1.GetPropertiesResponse"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/camera/{name}/propertiesBC\n\x1ccom.viam.component.camera.v1Z#go.viam.com/api/component/camera/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.camera.v1.camera_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1ccom.viam.component.camera.v1Z#go.viam.com/api/component/camera/v1' - _CAMERASERVICE.methods_by_name['GetImage']._options = None - _CAMERASERVICE.methods_by_name['GetImage']._serialized_options = b'\x82\xd3\xe4\x93\x02,\x12*/viam/api/v1/component/camera/{name}/image' - _CAMERASERVICE.methods_by_name['RenderFrame']._options = None - _CAMERASERVICE.methods_by_name['RenderFrame']._serialized_options = b'\x82\xd3\xe4\x93\x023\x121/viam/api/v1/component/camera/{name}/render_frame' - _CAMERASERVICE.methods_by_name['GetPointCloud']._options = None - _CAMERASERVICE.methods_by_name['GetPointCloud']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/camera/{name}/point_cloud' - _CAMERASERVICE.methods_by_name['GetProperties']._options = None - _CAMERASERVICE.methods_by_name['GetProperties']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/camera/{name}/properties' - _GETIMAGEREQUEST._serialized_start = 119 - _GETIMAGEREQUEST._serialized_end = 185 - _GETIMAGERESPONSE._serialized_start = 187 - _GETIMAGERESPONSE._serialized_end = 312 - _RENDERFRAMEREQUEST._serialized_start = 314 - _RENDERFRAMEREQUEST._serialized_end = 383 - _GETPOINTCLOUDREQUEST._serialized_start = 385 - _GETPOINTCLOUDREQUEST._serialized_end = 456 - _GETPOINTCLOUDRESPONSE._serialized_start = 458 - _GETPOINTCLOUDRESPONSE._serialized_end = 543 - _GETPROPERTIESREQUEST._serialized_start = 545 - _GETPROPERTIESREQUEST._serialized_end = 587 - _GETPROPERTIESRESPONSE._serialized_start = 590 - _GETPROPERTIESRESPONSE._serialized_end = 847 - _WEBCAMS._serialized_start = 849 - _WEBCAMS._serialized_end = 918 - _WEBCAM._serialized_start = 920 - _WEBCAM._serialized_end = 1042 - _PROPERTY._serialized_start = 1045 - _PROPERTY._serialized_end = 1191 - _VIDEO._serialized_start = 1193 - _VIDEO._serialized_end = 1281 - _INTRINSICPARAMETERS._serialized_start = 1284 - _INTRINSICPARAMETERS._serialized_end = 1485 - _DISTORTIONPARAMETERS._serialized_start = 1487 - _DISTORTIONPARAMETERS._serialized_end = 1563 - _CAMERASERVICE._serialized_start = 1566 - _CAMERASERVICE._serialized_end = 2221 \ No newline at end of file +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n component/camera/v1/camera.proto\x12\x18viam.component.camera.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x19google/api/httpbody.proto\x1a\x1cgoogle/protobuf/struct.proto"q\n\x0fGetImageRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tmime_type\x18\x02 \x01(\tR\x08mimeType\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"E\n\x10GetImageResponse\x12\x1b\n\tmime_type\x18\x01 \x01(\tR\x08mimeType\x12\x14\n\x05image\x18\x02 \x01(\x0cR\x05image"&\n\x10GetImagesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x9d\x01\n\x11GetImagesResponse\x127\n\x06images\x18\x01 \x03(\x0b2\x1f.viam.component.camera.v1.ImageR\x06images\x12O\n\x11response_metadata\x18\xa4\x92\x05 \x01(\x0b2 .viam.common.v1.ResponseMetadataR\x10responseMetadata"x\n\x05Image\x12\x1f\n\x0bsource_name\x18\x01 \x01(\tR\nsourceName\x128\n\x06format\x18\x02 \x01(\x0e2 .viam.component.camera.v1.FormatR\x06format\x12\x14\n\x05image\x18\x03 \x01(\x0cR\x05image"t\n\x12RenderFrameRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tmime_type\x18\x02 \x01(\tR\x08mimeType\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"v\n\x14GetPointCloudRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tmime_type\x18\x02 \x01(\tR\x08mimeType\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"U\n\x15GetPointCloudResponse\x12\x1b\n\tmime_type\x18\x01 \x01(\tR\x08mimeType\x12\x1f\n\x0bpoint_cloud\x18\x02 \x01(\x0cR\npointCloud"*\n\x14GetPropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\xd3\x02\n\x15GetPropertiesResponse\x12!\n\x0csupports_pcd\x18\x01 \x01(\x08R\x0bsupportsPcd\x12`\n\x14intrinsic_parameters\x18\x02 \x01(\x0b2-.viam.component.camera.v1.IntrinsicParametersR\x13intrinsicParameters\x12c\n\x15distortion_parameters\x18\x03 \x01(\x0b2..viam.component.camera.v1.DistortionParametersR\x14distortionParameters\x12\x1d\n\nmime_types\x18\x04 \x03(\tR\tmimeTypes\x12"\n\nframe_rate\x18\x05 \x01(\x02H\x00R\tframeRate\x88\x01\x01B\r\n\x0b_frame_rate"E\n\x07Webcams\x12:\n\x07webcams\x18\x01 \x03(\x0b2 .viam.component.camera.v1.WebcamR\x07webcams"\x9e\x01\n\x06Webcam\x12\x14\n\x05label\x18\x01 \x01(\tR\x05label\x12\x16\n\x06status\x18\x02 \x01(\tR\x06status\x12B\n\nproperties\x18\x03 \x03(\x0b2".viam.component.camera.v1.PropertyR\nproperties\x12\x12\n\x04name\x18\x04 \x01(\tR\x04name\x12\x0e\n\x02id\x18\x05 \x01(\tR\x02id"\x84\x01\n\x08Property\x12\x19\n\x08width_px\x18\x01 \x01(\x05R\x07widthPx\x12\x1b\n\theight_px\x18\x02 \x01(\x05R\x08heightPx\x12!\n\x0cframe_format\x18\x03 \x01(\tR\x0bframeFormat\x12\x1d\n\nframe_rate\x18\x04 \x01(\x02R\tframeRate"\xc9\x01\n\x13IntrinsicParameters\x12\x19\n\x08width_px\x18\x01 \x01(\rR\x07widthPx\x12\x1b\n\theight_px\x18\x02 \x01(\rR\x08heightPx\x12\x1c\n\nfocal_x_px\x18\x03 \x01(\x01R\x08focalXPx\x12\x1c\n\nfocal_y_px\x18\x04 \x01(\x01R\x08focalYPx\x12\x1e\n\x0bcenter_x_px\x18\x05 \x01(\x01R\tcenterXPx\x12\x1e\n\x0bcenter_y_px\x18\x06 \x01(\x01R\tcenterYPx"L\n\x14DistortionParameters\x12\x14\n\x05model\x18\x01 \x01(\tR\x05model\x12\x1e\n\nparameters\x18\x02 \x03(\x01R\nparameters*l\n\x06Format\x12\x16\n\x12FORMAT_UNSPECIFIED\x10\x00\x12\x13\n\x0fFORMAT_RAW_RGBA\x10\x01\x12\x14\n\x10FORMAT_RAW_DEPTH\x10\x02\x12\x0f\n\x0bFORMAT_JPEG\x10\x03\x12\x0e\n\nFORMAT_PNG\x10\x042\xcf\x08\n\rCameraService\x12\x95\x01\n\x08GetImage\x12).viam.component.camera.v1.GetImageRequest\x1a*.viam.component.camera.v1.GetImageResponse"2\x82\xd3\xe4\x93\x02,\x12*/viam/api/v1/component/camera/{name}/image\x12\x99\x01\n\tGetImages\x12*.viam.component.camera.v1.GetImagesRequest\x1a+.viam.component.camera.v1.GetImagesResponse"3\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/component/camera/{name}/images\x12\x8c\x01\n\x0bRenderFrame\x12,.viam.component.camera.v1.RenderFrameRequest\x1a\x14.google.api.HttpBody"9\x82\xd3\xe4\x93\x023\x121/viam/api/v1/component/camera/{name}/render_frame\x12\xaa\x01\n\rGetPointCloud\x12..viam.component.camera.v1.GetPointCloudRequest\x1a/.viam.component.camera.v1.GetPointCloudResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/camera/{name}/point_cloud\x12\xa9\x01\n\rGetProperties\x12..viam.component.camera.v1.GetPropertiesRequest\x1a/.viam.component.camera.v1.GetPropertiesResponse"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/camera/{name}/properties\x12\x89\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"7\x82\xd3\xe4\x93\x021"//viam/api/v1/component/camera/{name}/do_command\x12\x95\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/camera/{name}/geometriesBC\n\x1ccom.viam.component.camera.v1Z#go.viam.com/api/component/camera/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.camera.v1.camera_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1ccom.viam.component.camera.v1Z#go.viam.com/api/component/camera/v1' + _globals['_CAMERASERVICE'].methods_by_name['GetImage']._loaded_options = None + _globals['_CAMERASERVICE'].methods_by_name['GetImage']._serialized_options = b'\x82\xd3\xe4\x93\x02,\x12*/viam/api/v1/component/camera/{name}/image' + _globals['_CAMERASERVICE'].methods_by_name['GetImages']._loaded_options = None + _globals['_CAMERASERVICE'].methods_by_name['GetImages']._serialized_options = b'\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/component/camera/{name}/images' + _globals['_CAMERASERVICE'].methods_by_name['RenderFrame']._loaded_options = None + _globals['_CAMERASERVICE'].methods_by_name['RenderFrame']._serialized_options = b'\x82\xd3\xe4\x93\x023\x121/viam/api/v1/component/camera/{name}/render_frame' + _globals['_CAMERASERVICE'].methods_by_name['GetPointCloud']._loaded_options = None + _globals['_CAMERASERVICE'].methods_by_name['GetPointCloud']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/camera/{name}/point_cloud' + _globals['_CAMERASERVICE'].methods_by_name['GetProperties']._loaded_options = None + _globals['_CAMERASERVICE'].methods_by_name['GetProperties']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/camera/{name}/properties' + _globals['_CAMERASERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_CAMERASERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x021"//viam/api/v1/component/camera/{name}/do_command' + _globals['_CAMERASERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_CAMERASERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/camera/{name}/geometries' + _globals['_FORMAT']._serialized_start = 2041 + _globals['_FORMAT']._serialized_end = 2149 + _globals['_GETIMAGEREQUEST']._serialized_start = 173 + _globals['_GETIMAGEREQUEST']._serialized_end = 286 + _globals['_GETIMAGERESPONSE']._serialized_start = 288 + _globals['_GETIMAGERESPONSE']._serialized_end = 357 + _globals['_GETIMAGESREQUEST']._serialized_start = 359 + _globals['_GETIMAGESREQUEST']._serialized_end = 397 + _globals['_GETIMAGESRESPONSE']._serialized_start = 400 + _globals['_GETIMAGESRESPONSE']._serialized_end = 557 + _globals['_IMAGE']._serialized_start = 559 + _globals['_IMAGE']._serialized_end = 679 + _globals['_RENDERFRAMEREQUEST']._serialized_start = 681 + _globals['_RENDERFRAMEREQUEST']._serialized_end = 797 + _globals['_GETPOINTCLOUDREQUEST']._serialized_start = 799 + _globals['_GETPOINTCLOUDREQUEST']._serialized_end = 917 + _globals['_GETPOINTCLOUDRESPONSE']._serialized_start = 919 + _globals['_GETPOINTCLOUDRESPONSE']._serialized_end = 1004 + _globals['_GETPROPERTIESREQUEST']._serialized_start = 1006 + _globals['_GETPROPERTIESREQUEST']._serialized_end = 1048 + _globals['_GETPROPERTIESRESPONSE']._serialized_start = 1051 + _globals['_GETPROPERTIESRESPONSE']._serialized_end = 1390 + _globals['_WEBCAMS']._serialized_start = 1392 + _globals['_WEBCAMS']._serialized_end = 1461 + _globals['_WEBCAM']._serialized_start = 1464 + _globals['_WEBCAM']._serialized_end = 1622 + _globals['_PROPERTY']._serialized_start = 1625 + _globals['_PROPERTY']._serialized_end = 1757 + _globals['_INTRINSICPARAMETERS']._serialized_start = 1760 + _globals['_INTRINSICPARAMETERS']._serialized_end = 1961 + _globals['_DISTORTIONPARAMETERS']._serialized_start = 1963 + _globals['_DISTORTIONPARAMETERS']._serialized_end = 2039 + _globals['_CAMERASERVICE']._serialized_start = 2152 + _globals['_CAMERASERVICE']._serialized_end = 3255 \ No newline at end of file diff --git a/src/viam/gen/component/camera/v1/camera_pb2.pyi b/src/viam/gen/component/camera/v1/camera_pb2.pyi index dbc320077..912398f54 100644 --- a/src/viam/gen/component/camera/v1/camera_pb2.pyi +++ b/src/viam/gen/component/camera/v1/camera_pb2.pyi @@ -4,86 +4,192 @@ isort:skip_file """ import builtins import collections.abc +from .... import common import google.protobuf.descriptor import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper import google.protobuf.message +import google.protobuf.struct_pb2 import sys -if sys.version_info >= (3, 8): +import typing +if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _Format: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _FormatEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_Format.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + FORMAT_UNSPECIFIED: _Format.ValueType + FORMAT_RAW_RGBA: _Format.ValueType + FORMAT_RAW_DEPTH: _Format.ValueType + FORMAT_JPEG: _Format.ValueType + FORMAT_PNG: _Format.ValueType + +class Format(_Format, metaclass=_FormatEnumTypeWrapper): + ... +FORMAT_UNSPECIFIED: Format.ValueType +FORMAT_RAW_RGBA: Format.ValueType +FORMAT_RAW_DEPTH: Format.ValueType +FORMAT_JPEG: Format.ValueType +FORMAT_PNG: Format.ValueType +global___Format = Format + +@typing.final class GetImageRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int MIME_TYPE_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a camera' mime_type: builtins.str 'Requested MIME type of response' - def __init__(self, *, name: builtins.str=..., mime_type: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., mime_type: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['mime_type', b'mime_type', 'name', b'name']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'mime_type', b'mime_type', 'name', b'name']) -> None: ... global___GetImageRequest = GetImageRequest +@typing.final class GetImageResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MIME_TYPE_FIELD_NUMBER: builtins.int IMAGE_FIELD_NUMBER: builtins.int - WIDTH_PX_FIELD_NUMBER: builtins.int - HEIGHT_PX_FIELD_NUMBER: builtins.int mime_type: builtins.str 'Actual MIME type of response' image: builtins.bytes 'Frame in bytes' - width_px: builtins.int - 'Width of frame in px' - height_px: builtins.int - 'Height of frame in px' - def __init__(self, *, mime_type: builtins.str=..., image: builtins.bytes=..., width_px: builtins.int=..., height_px: builtins.int=...) -> None: + def __init__(self, *, mime_type: builtins.str=..., image: builtins.bytes=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['height_px', b'height_px', 'image', b'image', 'mime_type', b'mime_type', 'width_px', b'width_px']) -> None: + def ClearField(self, field_name: typing.Literal['image', b'image', 'mime_type', b'mime_type']) -> None: ... global___GetImageResponse = GetImageResponse +@typing.final +class GetImagesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of a camera' + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___GetImagesRequest = GetImagesRequest + +@typing.final +class GetImagesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + IMAGES_FIELD_NUMBER: builtins.int + RESPONSE_METADATA_FIELD_NUMBER: builtins.int + + @property + def images(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Image]: + """list of images returned from the camera system""" + + @property + def response_metadata(self) -> common.v1.common_pb2.ResponseMetadata: + """contains timestamp data""" + + def __init__(self, *, images: collections.abc.Iterable[global___Image] | None=..., response_metadata: common.v1.common_pb2.ResponseMetadata | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['response_metadata', b'response_metadata']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['images', b'images', 'response_metadata', b'response_metadata']) -> None: + ... +global___GetImagesResponse = GetImagesResponse + +@typing.final +class Image(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SOURCE_NAME_FIELD_NUMBER: builtins.int + FORMAT_FIELD_NUMBER: builtins.int + IMAGE_FIELD_NUMBER: builtins.int + source_name: builtins.str + 'the name of the sensor where the image came from' + format: global___Format.ValueType + 'format of the response image bytes' + image: builtins.bytes + 'image in bytes' + + def __init__(self, *, source_name: builtins.str=..., format: global___Format.ValueType=..., image: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['format', b'format', 'image', b'image', 'source_name', b'source_name']) -> None: + ... +global___Image = Image + +@typing.final class RenderFrameRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int MIME_TYPE_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a camera' mime_type: builtins.str 'Requested MIME type of response' - def __init__(self, *, name: builtins.str=..., mime_type: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., mime_type: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['mime_type', b'mime_type', 'name', b'name']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'mime_type', b'mime_type', 'name', b'name']) -> None: ... global___RenderFrameRequest = RenderFrameRequest +@typing.final class GetPointCloudRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int MIME_TYPE_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a camera' mime_type: builtins.str 'Requested MIME type of response' - def __init__(self, *, name: builtins.str=..., mime_type: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., mime_type: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['mime_type', b'mime_type', 'name', b'name']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'mime_type', b'mime_type', 'name', b'name']) -> None: ... global___GetPointCloudRequest = GetPointCloudRequest +@typing.final class GetPointCloudResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MIME_TYPE_FIELD_NUMBER: builtins.int @@ -96,10 +202,11 @@ class GetPointCloudResponse(google.protobuf.message.Message): def __init__(self, *, mime_type: builtins.str=..., point_cloud: builtins.bytes=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['mime_type', b'mime_type', 'point_cloud', b'point_cloud']) -> None: + def ClearField(self, field_name: typing.Literal['mime_type', b'mime_type', 'point_cloud', b'point_cloud']) -> None: ... global___GetPointCloudResponse = GetPointCloudResponse +@typing.final class GetPropertiesRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -109,36 +216,55 @@ class GetPropertiesRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: ... global___GetPropertiesRequest = GetPropertiesRequest +@typing.final class GetPropertiesResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor SUPPORTS_PCD_FIELD_NUMBER: builtins.int INTRINSIC_PARAMETERS_FIELD_NUMBER: builtins.int DISTORTION_PARAMETERS_FIELD_NUMBER: builtins.int + MIME_TYPES_FIELD_NUMBER: builtins.int + FRAME_RATE_FIELD_NUMBER: builtins.int supports_pcd: builtins.bool 'A boolean property determining whether the camera supports the return of pointcloud data' + frame_rate: builtins.float + 'Optional camera frame rate for image capture timing' @property def intrinsic_parameters(self) -> global___IntrinsicParameters: - """Parameters for doing a perspective of a 3D scene to a 2D plane""" + """Parameters for doing a perspective of a 3D scene to a 2D plane + If camera does not provide intrinsic parameters, leave the field empty + Initializing the parameters with 0-values is considered an error + """ @property def distortion_parameters(self) -> global___DistortionParameters: - """Parameters for modeling lens distortion in cameras""" + """Parameters for modeling lens distortion in cameras + If camera does not provide distortion parameters, leave the field empty + Initializing the parameters with 0-values is considered an error + """ + + @property + def mime_types(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Supported MIME types by the camera""" - def __init__(self, *, supports_pcd: builtins.bool=..., intrinsic_parameters: global___IntrinsicParameters | None=..., distortion_parameters: global___DistortionParameters | None=...) -> None: + def __init__(self, *, supports_pcd: builtins.bool=..., intrinsic_parameters: global___IntrinsicParameters | None=..., distortion_parameters: global___DistortionParameters | None=..., mime_types: collections.abc.Iterable[builtins.str] | None=..., frame_rate: builtins.float | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['distortion_parameters', b'distortion_parameters', 'intrinsic_parameters', b'intrinsic_parameters']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['_frame_rate', b'_frame_rate', 'distortion_parameters', b'distortion_parameters', 'frame_rate', b'frame_rate', 'intrinsic_parameters', b'intrinsic_parameters']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['distortion_parameters', b'distortion_parameters', 'intrinsic_parameters', b'intrinsic_parameters', 'supports_pcd', b'supports_pcd']) -> None: + def ClearField(self, field_name: typing.Literal['_frame_rate', b'_frame_rate', 'distortion_parameters', b'distortion_parameters', 'frame_rate', b'frame_rate', 'intrinsic_parameters', b'intrinsic_parameters', 'mime_types', b'mime_types', 'supports_pcd', b'supports_pcd']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_frame_rate', b'_frame_rate']) -> typing.Literal['frame_rate'] | None: ... global___GetPropertiesResponse = GetPropertiesResponse +@typing.final class Webcams(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor WEBCAMS_FIELD_NUMBER: builtins.int @@ -150,77 +276,62 @@ class Webcams(google.protobuf.message.Message): def __init__(self, *, webcams: collections.abc.Iterable[global___Webcam] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['webcams', b'webcams']) -> None: + def ClearField(self, field_name: typing.Literal['webcams', b'webcams']) -> None: ... global___Webcams = Webcams +@typing.final class Webcam(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor LABEL_FIELD_NUMBER: builtins.int STATUS_FIELD_NUMBER: builtins.int PROPERTIES_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + ID_FIELD_NUMBER: builtins.int label: builtins.str - 'Camera driver label' + 'Camera driver label (for internal use only)' status: builtins.str 'Camera driver status' + name: builtins.str + 'Camera human-readable driver name' + id: builtins.str + 'Camera unique identifier' @property def properties(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Property]: """Camera properties""" - def __init__(self, *, label: builtins.str=..., status: builtins.str=..., properties: collections.abc.Iterable[global___Property] | None=...) -> None: + def __init__(self, *, label: builtins.str=..., status: builtins.str=..., properties: collections.abc.Iterable[global___Property] | None=..., name: builtins.str=..., id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['label', b'label', 'properties', b'properties', 'status', b'status']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id', 'label', b'label', 'name', b'name', 'properties', b'properties', 'status', b'status']) -> None: ... global___Webcam = Webcam +@typing.final class Property(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - VIDEO_FIELD_NUMBER: builtins.int - WIDTH_FIELD_NUMBER: builtins.int - HEIGHT_FIELD_NUMBER: builtins.int + WIDTH_PX_FIELD_NUMBER: builtins.int + HEIGHT_PX_FIELD_NUMBER: builtins.int FRAME_FORMAT_FIELD_NUMBER: builtins.int - - @property - def video(self) -> global___Video: - """Camera video properties""" - width: builtins.int - 'Video resolution width' - height: builtins.int - 'Video resolution height' + FRAME_RATE_FIELD_NUMBER: builtins.int + width_px: builtins.int + 'Video resolution width in px' + height_px: builtins.int + 'Video resolution height in px' frame_format: builtins.str 'Video frame format' + frame_rate: builtins.float + 'Video frame rate in fps' - def __init__(self, *, video: global___Video | None=..., width: builtins.int=..., height: builtins.int=..., frame_format: builtins.str=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['video', b'video']) -> builtins.bool: + def __init__(self, *, width_px: builtins.int=..., height_px: builtins.int=..., frame_format: builtins.str=..., frame_rate: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['frame_format', b'frame_format', 'height', b'height', 'video', b'video', 'width', b'width']) -> None: + def ClearField(self, field_name: typing.Literal['frame_format', b'frame_format', 'frame_rate', b'frame_rate', 'height_px', b'height_px', 'width_px', b'width_px']) -> None: ... global___Property = Property -class Video(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - WIDTH_FIELD_NUMBER: builtins.int - HEIGHT_FIELD_NUMBER: builtins.int - FRAME_FORMAT_FIELD_NUMBER: builtins.int - width: builtins.int - 'Video resolution width' - height: builtins.int - 'Video resolution height' - frame_format: builtins.str - 'Video frame format' - - def __init__(self, *, width: builtins.int=..., height: builtins.int=..., frame_format: builtins.str=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['frame_format', b'frame_format', 'height', b'height', 'width', b'width']) -> None: - ... -global___Video = Video - +@typing.final class IntrinsicParameters(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor WIDTH_PX_FIELD_NUMBER: builtins.int @@ -239,10 +350,11 @@ class IntrinsicParameters(google.protobuf.message.Message): def __init__(self, *, width_px: builtins.int=..., height_px: builtins.int=..., focal_x_px: builtins.float=..., focal_y_px: builtins.float=..., center_x_px: builtins.float=..., center_y_px: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['center_x_px', b'center_x_px', 'center_y_px', b'center_y_px', 'focal_x_px', b'focal_x_px', 'focal_y_px', b'focal_y_px', 'height_px', b'height_px', 'width_px', b'width_px']) -> None: + def ClearField(self, field_name: typing.Literal['center_x_px', b'center_x_px', 'center_y_px', b'center_y_px', 'focal_x_px', b'focal_x_px', 'focal_y_px', b'focal_y_px', 'height_px', b'height_px', 'width_px', b'width_px']) -> None: ... global___IntrinsicParameters = IntrinsicParameters +@typing.final class DistortionParameters(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MODEL_FIELD_NUMBER: builtins.int @@ -256,6 +368,6 @@ class DistortionParameters(google.protobuf.message.Message): def __init__(self, *, model: builtins.str=..., parameters: collections.abc.Iterable[builtins.float] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['model', b'model', 'parameters', b'parameters']) -> None: + def ClearField(self, field_name: typing.Literal['model', b'model', 'parameters', b'parameters']) -> None: ... global___DistortionParameters = DistortionParameters \ No newline at end of file diff --git a/src/viam/gen/component/encoder/__init__.py b/src/viam/gen/component/encoder/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/component/encoder/v1/__init__.py b/src/viam/gen/component/encoder/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/component/encoder/v1/encoder_grpc.py b/src/viam/gen/component/encoder/v1/encoder_grpc.py new file mode 100644 index 000000000..64c2ee93a --- /dev/null +++ b/src/viam/gen/component/encoder/v1/encoder_grpc.py @@ -0,0 +1,62 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from .... import common +import google.api.annotations_pb2 +import google.protobuf.struct_pb2 +from .... import component + +class EncoderServiceBase(abc.ABC): + + @abc.abstractmethod + async def GetPosition(self, stream: 'grpclib.server.Stream[component.encoder.v1.encoder_pb2.GetPositionRequest, component.encoder.v1.encoder_pb2.GetPositionResponse]') -> None: + pass + + @abc.abstractmethod + async def ResetPosition(self, stream: 'grpclib.server.Stream[component.encoder.v1.encoder_pb2.ResetPositionRequest, component.encoder.v1.encoder_pb2.ResetPositionResponse]') -> None: + pass + + @abc.abstractmethod + async def GetProperties(self, stream: 'grpclib.server.Stream[component.encoder.v1.encoder_pb2.GetPropertiesRequest, component.encoder.v1.encoder_pb2.GetPropertiesResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.component.encoder.v1.EncoderService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.encoder.v1.encoder_pb2.GetPositionRequest, component.encoder.v1.encoder_pb2.GetPositionResponse), '/viam.component.encoder.v1.EncoderService/ResetPosition': grpclib.const.Handler(self.ResetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.encoder.v1.encoder_pb2.ResetPositionRequest, component.encoder.v1.encoder_pb2.ResetPositionResponse), '/viam.component.encoder.v1.EncoderService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, component.encoder.v1.encoder_pb2.GetPropertiesRequest, component.encoder.v1.encoder_pb2.GetPropertiesResponse), '/viam.component.encoder.v1.EncoderService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.encoder.v1.EncoderService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedEncoderServiceBase(EncoderServiceBase): + + async def GetPosition(self, stream: 'grpclib.server.Stream[component.encoder.v1.encoder_pb2.GetPositionRequest, component.encoder.v1.encoder_pb2.GetPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ResetPosition(self, stream: 'grpclib.server.Stream[component.encoder.v1.encoder_pb2.ResetPositionRequest, component.encoder.v1.encoder_pb2.ResetPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetProperties(self, stream: 'grpclib.server.Stream[component.encoder.v1.encoder_pb2.GetPropertiesRequest, component.encoder.v1.encoder_pb2.GetPropertiesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class EncoderServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.GetPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.encoder.v1.EncoderService/GetPosition', component.encoder.v1.encoder_pb2.GetPositionRequest, component.encoder.v1.encoder_pb2.GetPositionResponse) + self.ResetPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.encoder.v1.EncoderService/ResetPosition', component.encoder.v1.encoder_pb2.ResetPositionRequest, component.encoder.v1.encoder_pb2.ResetPositionResponse) + self.GetProperties = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.encoder.v1.EncoderService/GetProperties', component.encoder.v1.encoder_pb2.GetPropertiesRequest, component.encoder.v1.encoder_pb2.GetPropertiesResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.encoder.v1.EncoderService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.encoder.v1.EncoderService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/encoder/v1/encoder_pb2.py b/src/viam/gen/component/encoder/v1/encoder_pb2.py new file mode 100644 index 000000000..286b8bd15 --- /dev/null +++ b/src/viam/gen/component/encoder/v1/encoder_pb2.py @@ -0,0 +1,44 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/encoder/v1/encoder.proto') +_sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n"component/encoder/v1/encoder.proto\x12\x19viam.component.encoder.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"\xbc\x01\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12Q\n\rposition_type\x18\x02 \x01(\x0e2\'.viam.component.encoder.v1.PositionTypeH\x00R\x0cpositionType\x88\x01\x01\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\x10\n\x0e_position_type"y\n\x13GetPositionResponse\x12\x14\n\x05value\x18\x01 \x01(\x02R\x05value\x12L\n\rposition_type\x18\x02 \x01(\x0e2\'.viam.component.encoder.v1.PositionTypeR\x0cpositionType"Y\n\x14ResetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x17\n\x15ResetPositionResponse"Y\n\x14GetPropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x83\x01\n\x15GetPropertiesResponse\x122\n\x15ticks_count_supported\x18\x01 \x01(\x08R\x13ticksCountSupported\x126\n\x17angle_degrees_supported\x18\x02 \x01(\x08R\x15angleDegreesSupported*m\n\x0cPositionType\x12\x1d\n\x19POSITION_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19POSITION_TYPE_TICKS_COUNT\x10\x01\x12\x1f\n\x1bPOSITION_TYPE_ANGLE_DEGREES\x10\x022\xc7\x06\n\x0eEncoderService\x12\xa8\x01\n\x0bGetPosition\x12-.viam.component.encoder.v1.GetPositionRequest\x1a..viam.component.encoder.v1.GetPositionResponse":\x82\xd3\xe4\x93\x024\x122/viam/api/v1/component/encoder/{name}/get_position\x12\xb0\x01\n\rResetPosition\x12/.viam.component.encoder.v1.ResetPositionRequest\x1a0.viam.component.encoder.v1.ResetPositionResponse"<\x82\xd3\xe4\x93\x026\x124/viam/api/v1/component/encoder/{name}/reset_position\x12\xb0\x01\n\rGetProperties\x12/.viam.component.encoder.v1.GetPropertiesRequest\x1a0.viam.component.encoder.v1.GetPropertiesResponse"<\x82\xd3\xe4\x93\x026"4/viam/api/v1/component/encoder/{name}/get_properties\x12\x8a\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/encoder/{name}/do_command\x12\x96\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/encoder/{name}/geometriesBE\n\x1dcom.viam.component.encoder.v1Z$go.viam.com/api/component/encoder/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.encoder.v1.encoder_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1dcom.viam.component.encoder.v1Z$go.viam.com/api/component/encoder/v1' + _globals['_ENCODERSERVICE'].methods_by_name['GetPosition']._loaded_options = None + _globals['_ENCODERSERVICE'].methods_by_name['GetPosition']._serialized_options = b'\x82\xd3\xe4\x93\x024\x122/viam/api/v1/component/encoder/{name}/get_position' + _globals['_ENCODERSERVICE'].methods_by_name['ResetPosition']._loaded_options = None + _globals['_ENCODERSERVICE'].methods_by_name['ResetPosition']._serialized_options = b'\x82\xd3\xe4\x93\x026\x124/viam/api/v1/component/encoder/{name}/reset_position' + _globals['_ENCODERSERVICE'].methods_by_name['GetProperties']._loaded_options = None + _globals['_ENCODERSERVICE'].methods_by_name['GetProperties']._serialized_options = b'\x82\xd3\xe4\x93\x026"4/viam/api/v1/component/encoder/{name}/get_properties' + _globals['_ENCODERSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_ENCODERSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/encoder/{name}/do_command' + _globals['_ENCODERSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_ENCODERSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/encoder/{name}/geometries' + _globals['_POSITIONTYPE']._serialized_start = 804 + _globals['_POSITIONTYPE']._serialized_end = 913 + _globals['_GETPOSITIONREQUEST']._serialized_start = 150 + _globals['_GETPOSITIONREQUEST']._serialized_end = 338 + _globals['_GETPOSITIONRESPONSE']._serialized_start = 340 + _globals['_GETPOSITIONRESPONSE']._serialized_end = 461 + _globals['_RESETPOSITIONREQUEST']._serialized_start = 463 + _globals['_RESETPOSITIONREQUEST']._serialized_end = 552 + _globals['_RESETPOSITIONRESPONSE']._serialized_start = 554 + _globals['_RESETPOSITIONRESPONSE']._serialized_end = 577 + _globals['_GETPROPERTIESREQUEST']._serialized_start = 579 + _globals['_GETPROPERTIESREQUEST']._serialized_end = 668 + _globals['_GETPROPERTIESRESPONSE']._serialized_start = 671 + _globals['_GETPROPERTIESRESPONSE']._serialized_end = 802 + _globals['_ENCODERSERVICE']._serialized_start = 916 + _globals['_ENCODERSERVICE']._serialized_end = 1755 \ No newline at end of file diff --git a/src/viam/gen/component/encoder/v1/encoder_pb2.pyi b/src/viam/gen/component/encoder/v1/encoder_pb2.pyi new file mode 100644 index 000000000..5f30d983c --- /dev/null +++ b/src/viam/gen/component/encoder/v1/encoder_pb2.pyi @@ -0,0 +1,147 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import google.protobuf.struct_pb2 +import sys +import typing +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _PositionType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PositionTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PositionType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + POSITION_TYPE_UNSPECIFIED: _PositionType.ValueType + POSITION_TYPE_TICKS_COUNT: _PositionType.ValueType + "Return type for relative encoders that report\n how far they've gone from a start position\n " + POSITION_TYPE_ANGLE_DEGREES: _PositionType.ValueType + 'Return type for absolute encoders that report\n their position in degrees along the radial axis\n ' + +class PositionType(_PositionType, metaclass=_PositionTypeEnumTypeWrapper): + ... +POSITION_TYPE_UNSPECIFIED: PositionType.ValueType +POSITION_TYPE_TICKS_COUNT: PositionType.ValueType +"Return type for relative encoders that report\nhow far they've gone from a start position\n" +POSITION_TYPE_ANGLE_DEGREES: PositionType.ValueType +'Return type for absolute encoders that report\ntheir position in degrees along the radial axis\n' +global___PositionType = PositionType + +@typing.final +class GetPositionRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + POSITION_TYPE_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of encoder' + position_type: global___PositionType.ValueType + 'If supplied, the response will return the specified\n position type. If the driver does not implement\n the requested type, this call will return an error.\n If position type is not specified, the response\n will return a default according to the driver.\n ' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., position_type: global___PositionType.ValueType | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_position_type', b'_position_type', 'extra', b'extra', 'position_type', b'position_type']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_position_type', b'_position_type', 'extra', b'extra', 'name', b'name', 'position_type', b'position_type']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_position_type', b'_position_type']) -> typing.Literal['position_type'] | None: + ... +global___GetPositionRequest = GetPositionRequest + +@typing.final +class GetPositionResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + VALUE_FIELD_NUMBER: builtins.int + POSITION_TYPE_FIELD_NUMBER: builtins.int + value: builtins.float + position_type: global___PositionType.ValueType + + def __init__(self, *, value: builtins.float=..., position_type: global___PositionType.ValueType=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['position_type', b'position_type', 'value', b'value']) -> None: + ... +global___GetPositionResponse = GetPositionResponse + +@typing.final +class ResetPositionRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of an encoder' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___ResetPositionRequest = ResetPositionRequest + +@typing.final +class ResetPositionResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___ResetPositionResponse = ResetPositionResponse + +@typing.final +class GetPropertiesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of the encoder' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetPropertiesRequest = GetPropertiesRequest + +@typing.final +class GetPropertiesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TICKS_COUNT_SUPPORTED_FIELD_NUMBER: builtins.int + ANGLE_DEGREES_SUPPORTED_FIELD_NUMBER: builtins.int + ticks_count_supported: builtins.bool + angle_degrees_supported: builtins.bool + + def __init__(self, *, ticks_count_supported: builtins.bool=..., angle_degrees_supported: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['angle_degrees_supported', b'angle_degrees_supported', 'ticks_count_supported', b'ticks_count_supported']) -> None: + ... +global___GetPropertiesResponse = GetPropertiesResponse \ No newline at end of file diff --git a/src/viam/gen/component/gantry/v1/gantry_grpc.py b/src/viam/gen/component/gantry/v1/gantry_grpc.py index c8eb06309..26e13b99d 100644 --- a/src/viam/gen/component/gantry/v1/gantry_grpc.py +++ b/src/viam/gen/component/gantry/v1/gantry_grpc.py @@ -2,6 +2,7 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common @@ -19,6 +20,10 @@ async def GetPosition(self, stream: 'grpclib.server.Stream[component.gantry.v1.g async def MoveToPosition(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.MoveToPositionRequest, component.gantry.v1.gantry_pb2.MoveToPositionResponse]') -> None: pass + @abc.abstractmethod + async def Home(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.HomeRequest, component.gantry.v1.gantry_pb2.HomeResponse]') -> None: + pass + @abc.abstractmethod async def GetLengths(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.GetLengthsRequest, component.gantry.v1.gantry_pb2.GetLengthsResponse]') -> None: pass @@ -27,13 +32,55 @@ async def GetLengths(self, stream: 'grpclib.server.Stream[component.gantry.v1.ga async def Stop(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.StopRequest, component.gantry.v1.gantry_pb2.StopResponse]') -> None: pass + @abc.abstractmethod + async def IsMoving(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.IsMovingRequest, component.gantry.v1.gantry_pb2.IsMovingResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.gantry.v1.GantryService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.gantry.v1.gantry_pb2.GetPositionRequest, component.gantry.v1.gantry_pb2.GetPositionResponse), '/viam.component.gantry.v1.GantryService/MoveToPosition': grpclib.const.Handler(self.MoveToPosition, grpclib.const.Cardinality.UNARY_UNARY, component.gantry.v1.gantry_pb2.MoveToPositionRequest, component.gantry.v1.gantry_pb2.MoveToPositionResponse), '/viam.component.gantry.v1.GantryService/GetLengths': grpclib.const.Handler(self.GetLengths, grpclib.const.Cardinality.UNARY_UNARY, component.gantry.v1.gantry_pb2.GetLengthsRequest, component.gantry.v1.gantry_pb2.GetLengthsResponse), '/viam.component.gantry.v1.GantryService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.gantry.v1.gantry_pb2.StopRequest, component.gantry.v1.gantry_pb2.StopResponse)} + return {'/viam.component.gantry.v1.GantryService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.gantry.v1.gantry_pb2.GetPositionRequest, component.gantry.v1.gantry_pb2.GetPositionResponse), '/viam.component.gantry.v1.GantryService/MoveToPosition': grpclib.const.Handler(self.MoveToPosition, grpclib.const.Cardinality.UNARY_UNARY, component.gantry.v1.gantry_pb2.MoveToPositionRequest, component.gantry.v1.gantry_pb2.MoveToPositionResponse), '/viam.component.gantry.v1.GantryService/Home': grpclib.const.Handler(self.Home, grpclib.const.Cardinality.UNARY_UNARY, component.gantry.v1.gantry_pb2.HomeRequest, component.gantry.v1.gantry_pb2.HomeResponse), '/viam.component.gantry.v1.GantryService/GetLengths': grpclib.const.Handler(self.GetLengths, grpclib.const.Cardinality.UNARY_UNARY, component.gantry.v1.gantry_pb2.GetLengthsRequest, component.gantry.v1.gantry_pb2.GetLengthsResponse), '/viam.component.gantry.v1.GantryService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.gantry.v1.gantry_pb2.StopRequest, component.gantry.v1.gantry_pb2.StopResponse), '/viam.component.gantry.v1.GantryService/IsMoving': grpclib.const.Handler(self.IsMoving, grpclib.const.Cardinality.UNARY_UNARY, component.gantry.v1.gantry_pb2.IsMovingRequest, component.gantry.v1.gantry_pb2.IsMovingResponse), '/viam.component.gantry.v1.GantryService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.gantry.v1.GantryService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedGantryServiceBase(GantryServiceBase): + + async def GetPosition(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.GetPositionRequest, component.gantry.v1.gantry_pb2.GetPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def MoveToPosition(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.MoveToPositionRequest, component.gantry.v1.gantry_pb2.MoveToPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Home(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.HomeRequest, component.gantry.v1.gantry_pb2.HomeResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetLengths(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.GetLengthsRequest, component.gantry.v1.gantry_pb2.GetLengthsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Stop(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.StopRequest, component.gantry.v1.gantry_pb2.StopResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def IsMoving(self, stream: 'grpclib.server.Stream[component.gantry.v1.gantry_pb2.IsMovingRequest, component.gantry.v1.gantry_pb2.IsMovingResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class GantryServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: self.GetPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gantry.v1.GantryService/GetPosition', component.gantry.v1.gantry_pb2.GetPositionRequest, component.gantry.v1.gantry_pb2.GetPositionResponse) self.MoveToPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gantry.v1.GantryService/MoveToPosition', component.gantry.v1.gantry_pb2.MoveToPositionRequest, component.gantry.v1.gantry_pb2.MoveToPositionResponse) + self.Home = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gantry.v1.GantryService/Home', component.gantry.v1.gantry_pb2.HomeRequest, component.gantry.v1.gantry_pb2.HomeResponse) self.GetLengths = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gantry.v1.GantryService/GetLengths', component.gantry.v1.gantry_pb2.GetLengthsRequest, component.gantry.v1.gantry_pb2.GetLengthsResponse) - self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gantry.v1.GantryService/Stop', component.gantry.v1.gantry_pb2.StopRequest, component.gantry.v1.gantry_pb2.StopResponse) \ No newline at end of file + self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gantry.v1.GantryService/Stop', component.gantry.v1.gantry_pb2.StopRequest, component.gantry.v1.gantry_pb2.StopResponse) + self.IsMoving = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gantry.v1.GantryService/IsMoving', component.gantry.v1.gantry_pb2.IsMovingRequest, component.gantry.v1.gantry_pb2.IsMovingResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gantry.v1.GantryService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gantry.v1.GantryService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/gantry/v1/gantry_pb2.py b/src/viam/gen/component/gantry/v1/gantry_pb2.py index 37e17edb9..876dc91bc 100644 --- a/src/viam/gen/component/gantry/v1/gantry_pb2.py +++ b/src/viam/gen/component/gantry/v1/gantry_pb2.py @@ -1,43 +1,62 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/gantry/v1/gantry.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n component/gantry/v1/gantry.proto\x12\x18viam.component.gantry.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"W\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"8\n\x13GetPositionResponse\x12!\n\x0cpositions_mm\x18\x01 \x03(\x01R\x0bpositionsMm"\xcf\x01\n\x15MoveToPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12!\n\x0cpositions_mm\x18\x02 \x03(\x01R\x0bpositionsMm\x12@\n\x0bworld_state\x18\x03 \x01(\x0b2\x1a.viam.common.v1.WorldStateH\x00R\nworldState\x88\x01\x01\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\x0e\n\x0c_world_state"\x18\n\x16MoveToPositionResponse"V\n\x11GetLengthsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"3\n\x12GetLengthsResponse\x12\x1d\n\nlengths_mm\x18\x01 \x03(\x01R\tlengthsMm"P\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cStopResponse"g\n\x06Status\x12!\n\x0cpositions_mm\x18\x01 \x03(\x01R\x0bpositionsMm\x12\x1d\n\nlengths_mm\x18\x02 \x03(\x01R\tlengthsMm\x12\x1b\n\tis_moving\x18\x03 \x01(\x08R\x08isMoving2\x8b\x05\n\rGantryService\x12\xa1\x01\n\x0bGetPosition\x12,.viam.component.gantry.v1.GetPositionRequest\x1a-.viam.component.gantry.v1.GetPositionResponse"5\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/gantry/{name}/position\x12\xaa\x01\n\x0eMoveToPosition\x12/.viam.component.gantry.v1.MoveToPositionRequest\x1a0.viam.component.gantry.v1.MoveToPositionResponse"5\x82\xd3\xe4\x93\x02/\x1a-/viam/api/v1/component/gantry/{name}/position\x12\x9d\x01\n\nGetLengths\x12+.viam.component.gantry.v1.GetLengthsRequest\x1a,.viam.component.gantry.v1.GetLengthsResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/gantry/{name}/lengths\x12\x88\x01\n\x04Stop\x12%.viam.component.gantry.v1.StopRequest\x1a&.viam.component.gantry.v1.StopResponse"1\x82\xd3\xe4\x93\x02+")/viam/api/v1/component/gantry/{name}/stopBC\n\x1ccom.viam.component.gantry.v1Z#go.viam.com/api/component/gantry/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.gantry.v1.gantry_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1ccom.viam.component.gantry.v1Z#go.viam.com/api/component/gantry/v1' - _GANTRYSERVICE.methods_by_name['GetPosition']._options = None - _GANTRYSERVICE.methods_by_name['GetPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/gantry/{name}/position' - _GANTRYSERVICE.methods_by_name['MoveToPosition']._options = None - _GANTRYSERVICE.methods_by_name['MoveToPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02/\x1a-/viam/api/v1/component/gantry/{name}/position' - _GANTRYSERVICE.methods_by_name['GetLengths']._options = None - _GANTRYSERVICE.methods_by_name['GetLengths']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/gantry/{name}/lengths' - _GANTRYSERVICE.methods_by_name['Stop']._options = None - _GANTRYSERVICE.methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02+")/viam/api/v1/component/gantry/{name}/stop' - _GETPOSITIONREQUEST._serialized_start = 146 - _GETPOSITIONREQUEST._serialized_end = 233 - _GETPOSITIONRESPONSE._serialized_start = 235 - _GETPOSITIONRESPONSE._serialized_end = 291 - _MOVETOPOSITIONREQUEST._serialized_start = 294 - _MOVETOPOSITIONREQUEST._serialized_end = 501 - _MOVETOPOSITIONRESPONSE._serialized_start = 503 - _MOVETOPOSITIONRESPONSE._serialized_end = 527 - _GETLENGTHSREQUEST._serialized_start = 529 - _GETLENGTHSREQUEST._serialized_end = 615 - _GETLENGTHSRESPONSE._serialized_start = 617 - _GETLENGTHSRESPONSE._serialized_end = 668 - _STOPREQUEST._serialized_start = 670 - _STOPREQUEST._serialized_end = 750 - _STOPRESPONSE._serialized_start = 752 - _STOPRESPONSE._serialized_end = 766 - _STATUS._serialized_start = 768 - _STATUS._serialized_end = 871 - _GANTRYSERVICE._serialized_start = 874 - _GANTRYSERVICE._serialized_end = 1525 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n component/gantry/v1/gantry.proto\x12\x18viam.component.gantry.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"W\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"8\n\x13GetPositionResponse\x12!\n\x0cpositions_mm\x18\x01 \x03(\x01R\x0bpositionsMm"\xa8\x01\n\x15MoveToPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12!\n\x0cpositions_mm\x18\x02 \x03(\x01R\x0bpositionsMm\x12)\n\x11speeds_mm_per_sec\x18\x03 \x03(\x01R\x0espeedsMmPerSec\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x18\n\x16MoveToPositionResponse"P\n\x0bHomeRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"$\n\x0cHomeResponse\x12\x14\n\x05homed\x18\x01 \x01(\x08R\x05homed"V\n\x11GetLengthsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"3\n\x12GetLengthsResponse\x12\x1d\n\nlengths_mm\x18\x01 \x03(\x01R\tlengthsMm"P\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cStopResponse"g\n\x06Status\x12!\n\x0cpositions_mm\x18\x01 \x03(\x01R\x0bpositionsMm\x12\x1d\n\nlengths_mm\x18\x02 \x03(\x01R\tlengthsMm\x12\x1b\n\tis_moving\x18\x03 \x01(\x08R\x08isMoving"%\n\x0fIsMovingRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"/\n\x10IsMovingResponse\x12\x1b\n\tis_moving\x18\x01 \x01(\x08R\x08isMoving2\xda\t\n\rGantryService\x12\xa1\x01\n\x0bGetPosition\x12,.viam.component.gantry.v1.GetPositionRequest\x1a-.viam.component.gantry.v1.GetPositionResponse"5\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/gantry/{name}/position\x12\xae\x01\n\x0eMoveToPosition\x12/.viam.component.gantry.v1.MoveToPositionRequest\x1a0.viam.component.gantry.v1.MoveToPositionResponse"9\xa0\x92)\x01\x82\xd3\xe4\x93\x02/\x1a-/viam/api/v1/component/gantry/{name}/position\x12\x88\x01\n\x04Home\x12%.viam.component.gantry.v1.HomeRequest\x1a&.viam.component.gantry.v1.HomeResponse"1\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/gantry/{name}/home\x12\x9d\x01\n\nGetLengths\x12+.viam.component.gantry.v1.GetLengthsRequest\x1a,.viam.component.gantry.v1.GetLengthsResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/gantry/{name}/lengths\x12\x88\x01\n\x04Stop\x12%.viam.component.gantry.v1.StopRequest\x1a&.viam.component.gantry.v1.StopResponse"1\x82\xd3\xe4\x93\x02+")/viam/api/v1/component/gantry/{name}/stop\x12\x99\x01\n\x08IsMoving\x12).viam.component.gantry.v1.IsMovingRequest\x1a*.viam.component.gantry.v1.IsMovingResponse"6\x82\xd3\xe4\x93\x020\x12./viam/api/v1/component/gantry/{name}/is_moving\x12\x89\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"7\x82\xd3\xe4\x93\x021"//viam/api/v1/component/gantry/{name}/do_command\x12\x95\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/gantry/{name}/geometriesBC\n\x1ccom.viam.component.gantry.v1Z#go.viam.com/api/component/gantry/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.gantry.v1.gantry_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1ccom.viam.component.gantry.v1Z#go.viam.com/api/component/gantry/v1' + _globals['_GANTRYSERVICE'].methods_by_name['GetPosition']._loaded_options = None + _globals['_GANTRYSERVICE'].methods_by_name['GetPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/gantry/{name}/position' + _globals['_GANTRYSERVICE'].methods_by_name['MoveToPosition']._loaded_options = None + _globals['_GANTRYSERVICE'].methods_by_name['MoveToPosition']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02/\x1a-/viam/api/v1/component/gantry/{name}/position' + _globals['_GANTRYSERVICE'].methods_by_name['Home']._loaded_options = None + _globals['_GANTRYSERVICE'].methods_by_name['Home']._serialized_options = b'\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/gantry/{name}/home' + _globals['_GANTRYSERVICE'].methods_by_name['GetLengths']._loaded_options = None + _globals['_GANTRYSERVICE'].methods_by_name['GetLengths']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/gantry/{name}/lengths' + _globals['_GANTRYSERVICE'].methods_by_name['Stop']._loaded_options = None + _globals['_GANTRYSERVICE'].methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02+")/viam/api/v1/component/gantry/{name}/stop' + _globals['_GANTRYSERVICE'].methods_by_name['IsMoving']._loaded_options = None + _globals['_GANTRYSERVICE'].methods_by_name['IsMoving']._serialized_options = b'\x82\xd3\xe4\x93\x020\x12./viam/api/v1/component/gantry/{name}/is_moving' + _globals['_GANTRYSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_GANTRYSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x021"//viam/api/v1/component/gantry/{name}/do_command' + _globals['_GANTRYSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_GANTRYSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/gantry/{name}/geometries' + _globals['_GETPOSITIONREQUEST']._serialized_start = 146 + _globals['_GETPOSITIONREQUEST']._serialized_end = 233 + _globals['_GETPOSITIONRESPONSE']._serialized_start = 235 + _globals['_GETPOSITIONRESPONSE']._serialized_end = 291 + _globals['_MOVETOPOSITIONREQUEST']._serialized_start = 294 + _globals['_MOVETOPOSITIONREQUEST']._serialized_end = 462 + _globals['_MOVETOPOSITIONRESPONSE']._serialized_start = 464 + _globals['_MOVETOPOSITIONRESPONSE']._serialized_end = 488 + _globals['_HOMEREQUEST']._serialized_start = 490 + _globals['_HOMEREQUEST']._serialized_end = 570 + _globals['_HOMERESPONSE']._serialized_start = 572 + _globals['_HOMERESPONSE']._serialized_end = 608 + _globals['_GETLENGTHSREQUEST']._serialized_start = 610 + _globals['_GETLENGTHSREQUEST']._serialized_end = 696 + _globals['_GETLENGTHSRESPONSE']._serialized_start = 698 + _globals['_GETLENGTHSRESPONSE']._serialized_end = 749 + _globals['_STOPREQUEST']._serialized_start = 751 + _globals['_STOPREQUEST']._serialized_end = 831 + _globals['_STOPRESPONSE']._serialized_start = 833 + _globals['_STOPRESPONSE']._serialized_end = 847 + _globals['_STATUS']._serialized_start = 849 + _globals['_STATUS']._serialized_end = 952 + _globals['_ISMOVINGREQUEST']._serialized_start = 954 + _globals['_ISMOVINGREQUEST']._serialized_end = 991 + _globals['_ISMOVINGRESPONSE']._serialized_start = 993 + _globals['_ISMOVINGRESPONSE']._serialized_end = 1040 + _globals['_GANTRYSERVICE']._serialized_start = 1043 + _globals['_GANTRYSERVICE']._serialized_end = 2285 \ No newline at end of file diff --git a/src/viam/gen/component/gantry/v1/gantry_pb2.pyi b/src/viam/gen/component/gantry/v1/gantry_pb2.pyi index 0f627bd28..f66f0c95b 100644 --- a/src/viam/gen/component/gantry/v1/gantry_pb2.pyi +++ b/src/viam/gen/component/gantry/v1/gantry_pb2.pyi @@ -4,18 +4,14 @@ isort:skip_file """ import builtins import collections.abc -from .... import common import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.message import google.protobuf.struct_pb2 -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class GetPositionRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -29,13 +25,14 @@ class GetPositionRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetPositionRequest = GetPositionRequest +@typing.final class GetPositionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSITIONS_MM_FIELD_NUMBER: builtins.int @@ -47,43 +44,42 @@ class GetPositionResponse(google.protobuf.message.Message): def __init__(self, *, positions_mm: collections.abc.Iterable[builtins.float] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['positions_mm', b'positions_mm']) -> None: + def ClearField(self, field_name: typing.Literal['positions_mm', b'positions_mm']) -> None: ... global___GetPositionResponse = GetPositionResponse +@typing.final class MoveToPositionRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int POSITIONS_MM_FIELD_NUMBER: builtins.int - WORLD_STATE_FIELD_NUMBER: builtins.int + SPEEDS_MM_PER_SEC_FIELD_NUMBER: builtins.int EXTRA_FIELD_NUMBER: builtins.int name: builtins.str @property def positions_mm(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: - ... + """Number of millimeters to move the gantry by respective to each axis.""" @property - def world_state(self) -> common.v1.common_pb2.WorldState: - ... + def speeds_mm_per_sec(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: + """Speeds to move each gantry axis must match length and order of positions_mm.""" @property def extra(self) -> google.protobuf.struct_pb2.Struct: """Additional arguments to the method""" - def __init__(self, *, name: builtins.str=..., positions_mm: collections.abc.Iterable[builtins.float] | None=..., world_state: common.v1.common_pb2.WorldState | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['_world_state', b'_world_state', 'extra', b'extra', 'world_state', b'world_state']) -> builtins.bool: + def __init__(self, *, name: builtins.str=..., positions_mm: collections.abc.Iterable[builtins.float] | None=..., speeds_mm_per_sec: collections.abc.Iterable[builtins.float] | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['_world_state', b'_world_state', 'extra', b'extra', 'name', b'name', 'positions_mm', b'positions_mm', 'world_state', b'world_state']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['_world_state', b'_world_state']) -> typing_extensions.Literal['world_state'] | None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'positions_mm', b'positions_mm', 'speeds_mm_per_sec', b'speeds_mm_per_sec']) -> None: ... global___MoveToPositionRequest = MoveToPositionRequest +@typing.final class MoveToPositionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -91,6 +87,42 @@ class MoveToPositionResponse(google.protobuf.message.Message): ... global___MoveToPositionResponse = MoveToPositionResponse +@typing.final +class HomeRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___HomeRequest = HomeRequest + +@typing.final +class HomeResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + HOMED_FIELD_NUMBER: builtins.int + homed: builtins.bool + 'A bool describing whether the gantry has completed homing' + + def __init__(self, *, homed: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['homed', b'homed']) -> None: + ... +global___HomeResponse = HomeResponse + +@typing.final class GetLengthsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -104,13 +136,14 @@ class GetLengthsRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetLengthsRequest = GetLengthsRequest +@typing.final class GetLengthsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor LENGTHS_MM_FIELD_NUMBER: builtins.int @@ -122,10 +155,11 @@ class GetLengthsResponse(google.protobuf.message.Message): def __init__(self, *, lengths_mm: collections.abc.Iterable[builtins.float] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['lengths_mm', b'lengths_mm']) -> None: + def ClearField(self, field_name: typing.Literal['lengths_mm', b'lengths_mm']) -> None: ... global___GetLengthsResponse = GetLengthsResponse +@typing.final class StopRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -140,13 +174,14 @@ class StopRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___StopRequest = StopRequest +@typing.final class StopResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -154,11 +189,13 @@ class StopResponse(google.protobuf.message.Message): ... global___StopResponse = StopResponse +@typing.final class Status(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSITIONS_MM_FIELD_NUMBER: builtins.int LENGTHS_MM_FIELD_NUMBER: builtins.int IS_MOVING_FIELD_NUMBER: builtins.int + is_moving: builtins.bool @property def positions_mm(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: @@ -167,11 +204,36 @@ class Status(google.protobuf.message.Message): @property def lengths_mm(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: ... - is_moving: builtins.bool def __init__(self, *, positions_mm: collections.abc.Iterable[builtins.float] | None=..., lengths_mm: collections.abc.Iterable[builtins.float] | None=..., is_moving: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['is_moving', b'is_moving', 'lengths_mm', b'lengths_mm', 'positions_mm', b'positions_mm']) -> None: + def ClearField(self, field_name: typing.Literal['is_moving', b'is_moving', 'lengths_mm', b'lengths_mm', 'positions_mm', b'positions_mm']) -> None: + ... +global___Status = Status + +@typing.final +class IsMovingRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___IsMovingRequest = IsMovingRequest + +@typing.final +class IsMovingResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + IS_MOVING_FIELD_NUMBER: builtins.int + is_moving: builtins.bool + + def __init__(self, *, is_moving: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['is_moving', b'is_moving']) -> None: ... -global___Status = Status \ No newline at end of file +global___IsMovingResponse = IsMovingResponse \ No newline at end of file diff --git a/src/viam/gen/component/generic/v1/generic_grpc.py b/src/viam/gen/component/generic/v1/generic_grpc.py index f2e7e501c..006daa3d6 100644 --- a/src/viam/gen/component/generic/v1/generic_grpc.py +++ b/src/viam/gen/component/generic/v1/generic_grpc.py @@ -2,22 +2,36 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import common import google.api.annotations_pb2 -import google.protobuf.struct_pb2 from .... import component class GenericServiceBase(abc.ABC): @abc.abstractmethod - async def DoCommand(self, stream: 'grpclib.server.Stream[component.generic.v1.generic_pb2.DoCommandRequest, component.generic.v1.generic_pb2.DoCommandResponse]') -> None: + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: pass def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.generic.v1.GenericService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, component.generic.v1.generic_pb2.DoCommandRequest, component.generic.v1.generic_pb2.DoCommandResponse)} + return {'/viam.component.generic.v1.GenericService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.generic.v1.GenericService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedGenericServiceBase(GenericServiceBase): + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class GenericServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: - self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.generic.v1.GenericService/DoCommand', component.generic.v1.generic_pb2.DoCommandRequest, component.generic.v1.generic_pb2.DoCommandResponse) \ No newline at end of file + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.generic.v1.GenericService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.generic.v1.GenericService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/generic/v1/generic_pb2.py b/src/viam/gen/component/generic/v1/generic_pb2.py index 876bf1663..115b69c09 100644 --- a/src/viam/gen/component/generic/v1/generic_pb2.py +++ b/src/viam/gen/component/generic/v1/generic_pb2.py @@ -1,22 +1,23 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/generic/v1/generic.proto') _sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n"component/generic/v1/generic.proto\x12\x19viam.component.generic.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"Y\n\x10DoCommandRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x121\n\x07command\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x07command"D\n\x11DoCommandResponse\x12/\n\x06result\x18\x01 \x01(\x0b2\x17.google.protobuf.StructR\x06result2\xb3\x01\n\x0eGenericService\x12\xa0\x01\n\tDoCommand\x12+.viam.component.generic.v1.DoCommandRequest\x1a,.viam.component.generic.v1.DoCommandResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/generic/{name}/do_commandBE\n\x1dcom.viam.component.generic.v1Z$go.viam.com/api/component/generic/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.generic.v1.generic_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1dcom.viam.component.generic.v1Z$go.viam.com/api/component/generic/v1' - _GENERICSERVICE.methods_by_name['DoCommand']._options = None - _GENERICSERVICE.methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/generic/{name}/do_command' - _DOCOMMANDREQUEST._serialized_start = 125 - _DOCOMMANDREQUEST._serialized_end = 214 - _DOCOMMANDRESPONSE._serialized_start = 216 - _DOCOMMANDRESPONSE._serialized_end = 284 - _GENERICSERVICE._serialized_start = 287 - _GENERICSERVICE._serialized_end = 466 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n"component/generic/v1/generic.proto\x12\x19viam.component.generic.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto2\xb6\x02\n\x0eGenericService\x12\x8a\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/generic/{name}/do_command\x12\x96\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/generic/{name}/geometriesBE\n\x1dcom.viam.component.generic.v1Z$go.viam.com/api/component/generic/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.generic.v1.generic_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1dcom.viam.component.generic.v1Z$go.viam.com/api/component/generic/v1' + _globals['_GENERICSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_GENERICSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/generic/{name}/do_command' + _globals['_GENERICSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_GENERICSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/generic/{name}/geometries' + _globals['_GENERICSERVICE']._serialized_start = 120 + _globals['_GENERICSERVICE']._serialized_end = 430 \ No newline at end of file diff --git a/src/viam/gen/component/generic/v1/generic_pb2.pyi b/src/viam/gen/component/generic/v1/generic_pb2.pyi index 876f24e5c..ab0716881 100644 --- a/src/viam/gen/component/generic/v1/generic_pb2.pyi +++ b/src/viam/gen/component/generic/v1/generic_pb2.pyi @@ -2,51 +2,5 @@ @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ -import builtins import google.protobuf.descriptor -import google.protobuf.message -import google.protobuf.struct_pb2 -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class DoCommandRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - COMMAND_FIELD_NUMBER: builtins.int - name: builtins.str - - @property - def command(self) -> google.protobuf.struct_pb2.Struct: - ... - - def __init__(self, *, name: builtins.str=..., command: google.protobuf.struct_pb2.Struct | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['command', b'command']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['command', b'command', 'name', b'name']) -> None: - ... -global___DoCommandRequest = DoCommandRequest - -class DoCommandResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - RESULT_FIELD_NUMBER: builtins.int - - @property - def result(self) -> google.protobuf.struct_pb2.Struct: - ... - - def __init__(self, *, result: google.protobuf.struct_pb2.Struct | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['result', b'result']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['result', b'result']) -> None: - ... -global___DoCommandResponse = DoCommandResponse \ No newline at end of file +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor \ No newline at end of file diff --git a/src/viam/gen/component/gripper/v1/gripper_grpc.py b/src/viam/gen/component/gripper/v1/gripper_grpc.py index 728b85b0a..3431810f5 100644 --- a/src/viam/gen/component/gripper/v1/gripper_grpc.py +++ b/src/viam/gen/component/gripper/v1/gripper_grpc.py @@ -2,9 +2,12 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import common import google.api.annotations_pb2 +import google.protobuf.struct_pb2 from .... import component class GripperServiceBase(abc.ABC): @@ -21,12 +24,47 @@ async def Grab(self, stream: 'grpclib.server.Stream[component.gripper.v1.gripper async def Stop(self, stream: 'grpclib.server.Stream[component.gripper.v1.gripper_pb2.StopRequest, component.gripper.v1.gripper_pb2.StopResponse]') -> None: pass + @abc.abstractmethod + async def IsMoving(self, stream: 'grpclib.server.Stream[component.gripper.v1.gripper_pb2.IsMovingRequest, component.gripper.v1.gripper_pb2.IsMovingResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.gripper.v1.GripperService/Open': grpclib.const.Handler(self.Open, grpclib.const.Cardinality.UNARY_UNARY, component.gripper.v1.gripper_pb2.OpenRequest, component.gripper.v1.gripper_pb2.OpenResponse), '/viam.component.gripper.v1.GripperService/Grab': grpclib.const.Handler(self.Grab, grpclib.const.Cardinality.UNARY_UNARY, component.gripper.v1.gripper_pb2.GrabRequest, component.gripper.v1.gripper_pb2.GrabResponse), '/viam.component.gripper.v1.GripperService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.gripper.v1.gripper_pb2.StopRequest, component.gripper.v1.gripper_pb2.StopResponse)} + return {'/viam.component.gripper.v1.GripperService/Open': grpclib.const.Handler(self.Open, grpclib.const.Cardinality.UNARY_UNARY, component.gripper.v1.gripper_pb2.OpenRequest, component.gripper.v1.gripper_pb2.OpenResponse), '/viam.component.gripper.v1.GripperService/Grab': grpclib.const.Handler(self.Grab, grpclib.const.Cardinality.UNARY_UNARY, component.gripper.v1.gripper_pb2.GrabRequest, component.gripper.v1.gripper_pb2.GrabResponse), '/viam.component.gripper.v1.GripperService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.gripper.v1.gripper_pb2.StopRequest, component.gripper.v1.gripper_pb2.StopResponse), '/viam.component.gripper.v1.GripperService/IsMoving': grpclib.const.Handler(self.IsMoving, grpclib.const.Cardinality.UNARY_UNARY, component.gripper.v1.gripper_pb2.IsMovingRequest, component.gripper.v1.gripper_pb2.IsMovingResponse), '/viam.component.gripper.v1.GripperService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.gripper.v1.GripperService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedGripperServiceBase(GripperServiceBase): + + async def Open(self, stream: 'grpclib.server.Stream[component.gripper.v1.gripper_pb2.OpenRequest, component.gripper.v1.gripper_pb2.OpenResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Grab(self, stream: 'grpclib.server.Stream[component.gripper.v1.gripper_pb2.GrabRequest, component.gripper.v1.gripper_pb2.GrabResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Stop(self, stream: 'grpclib.server.Stream[component.gripper.v1.gripper_pb2.StopRequest, component.gripper.v1.gripper_pb2.StopResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def IsMoving(self, stream: 'grpclib.server.Stream[component.gripper.v1.gripper_pb2.IsMovingRequest, component.gripper.v1.gripper_pb2.IsMovingResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class GripperServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: self.Open = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gripper.v1.GripperService/Open', component.gripper.v1.gripper_pb2.OpenRequest, component.gripper.v1.gripper_pb2.OpenResponse) self.Grab = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gripper.v1.GripperService/Grab', component.gripper.v1.gripper_pb2.GrabRequest, component.gripper.v1.gripper_pb2.GrabResponse) - self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gripper.v1.GripperService/Stop', component.gripper.v1.gripper_pb2.StopRequest, component.gripper.v1.gripper_pb2.StopResponse) \ No newline at end of file + self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gripper.v1.GripperService/Stop', component.gripper.v1.gripper_pb2.StopRequest, component.gripper.v1.gripper_pb2.StopResponse) + self.IsMoving = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gripper.v1.GripperService/IsMoving', component.gripper.v1.gripper_pb2.IsMovingRequest, component.gripper.v1.gripper_pb2.IsMovingResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gripper.v1.GripperService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.gripper.v1.GripperService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/gripper/v1/gripper_pb2.py b/src/viam/gen/component/gripper/v1/gripper_pb2.py index 92a6a4db9..315d394bd 100644 --- a/src/viam/gen/component/gripper/v1/gripper_pb2.py +++ b/src/viam/gen/component/gripper/v1/gripper_pb2.py @@ -1,33 +1,48 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/gripper/v1/gripper.proto') _sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n"component/gripper/v1/gripper.proto\x12\x19viam.component.gripper.v1\x1a\x1cgoogle/api/annotations.proto"!\n\x0bOpenRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x0e\n\x0cOpenResponse"!\n\x0bGrabRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"(\n\x0cGrabResponse\x12\x18\n\x07success\x18\x01 \x01(\x08R\x07success"!\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x0e\n\x0cStopResponse2\xba\x03\n\x0eGripperService\x12\x8b\x01\n\x04Open\x12&.viam.component.gripper.v1.OpenRequest\x1a\'.viam.component.gripper.v1.OpenResponse"2\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/gripper/{name}/open\x12\x8b\x01\n\x04Grab\x12&.viam.component.gripper.v1.GrabRequest\x1a\'.viam.component.gripper.v1.GrabResponse"2\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/gripper/{name}/grab\x12\x8b\x01\n\x04Stop\x12&.viam.component.gripper.v1.StopRequest\x1a\'.viam.component.gripper.v1.StopResponse"2\x82\xd3\xe4\x93\x02,"*/viam/api/v1/component/gripper/{name}/stopBE\n\x1dcom.viam.component.gripper.v1Z$go.viam.com/api/component/gripper/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.gripper.v1.gripper_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1dcom.viam.component.gripper.v1Z$go.viam.com/api/component/gripper/v1' - _GRIPPERSERVICE.methods_by_name['Open']._options = None - _GRIPPERSERVICE.methods_by_name['Open']._serialized_options = b'\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/gripper/{name}/open' - _GRIPPERSERVICE.methods_by_name['Grab']._options = None - _GRIPPERSERVICE.methods_by_name['Grab']._serialized_options = b'\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/gripper/{name}/grab' - _GRIPPERSERVICE.methods_by_name['Stop']._options = None - _GRIPPERSERVICE.methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02,"*/viam/api/v1/component/gripper/{name}/stop' - _OPENREQUEST._serialized_start = 95 - _OPENREQUEST._serialized_end = 128 - _OPENRESPONSE._serialized_start = 130 - _OPENRESPONSE._serialized_end = 144 - _GRABREQUEST._serialized_start = 146 - _GRABREQUEST._serialized_end = 179 - _GRABRESPONSE._serialized_start = 181 - _GRABRESPONSE._serialized_end = 221 - _STOPREQUEST._serialized_start = 223 - _STOPREQUEST._serialized_end = 256 - _STOPRESPONSE._serialized_start = 258 - _STOPRESPONSE._serialized_end = 272 - _GRIPPERSERVICE._serialized_start = 275 - _GRIPPERSERVICE._serialized_end = 717 \ No newline at end of file +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n"component/gripper/v1/gripper.proto\x12\x19viam.component.gripper.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"P\n\x0bOpenRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cOpenResponse"P\n\x0bGrabRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"W\n\x0cGrabResponse\x12\x18\n\x07success\x18\x01 \x01(\x08R\x07success\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"P\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cStopResponse"%\n\x0fIsMovingRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"/\n\x10IsMovingResponse\x12\x1b\n\tis_moving\x18\x01 \x01(\x08R\x08isMoving2\x87\x07\n\x0eGripperService\x12\x8f\x01\n\x04Open\x12&.viam.component.gripper.v1.OpenRequest\x1a\'.viam.component.gripper.v1.OpenResponse"6\xa0\x92)\x01\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/gripper/{name}/open\x12\x8f\x01\n\x04Grab\x12&.viam.component.gripper.v1.GrabRequest\x1a\'.viam.component.gripper.v1.GrabResponse"6\xa0\x92)\x01\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/gripper/{name}/grab\x12\x8b\x01\n\x04Stop\x12&.viam.component.gripper.v1.StopRequest\x1a\'.viam.component.gripper.v1.StopResponse"2\x82\xd3\xe4\x93\x02,"*/viam/api/v1/component/gripper/{name}/stop\x12\x9c\x01\n\x08IsMoving\x12*.viam.component.gripper.v1.IsMovingRequest\x1a+.viam.component.gripper.v1.IsMovingResponse"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/gripper/{name}/is_moving\x12\x8a\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/gripper/{name}/do_command\x12\x96\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/gripper/{name}/geometriesBE\n\x1dcom.viam.component.gripper.v1Z$go.viam.com/api/component/gripper/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.gripper.v1.gripper_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1dcom.viam.component.gripper.v1Z$go.viam.com/api/component/gripper/v1' + _globals['_GRIPPERSERVICE'].methods_by_name['Open']._loaded_options = None + _globals['_GRIPPERSERVICE'].methods_by_name['Open']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/gripper/{name}/open' + _globals['_GRIPPERSERVICE'].methods_by_name['Grab']._loaded_options = None + _globals['_GRIPPERSERVICE'].methods_by_name['Grab']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/gripper/{name}/grab' + _globals['_GRIPPERSERVICE'].methods_by_name['Stop']._loaded_options = None + _globals['_GRIPPERSERVICE'].methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02,"*/viam/api/v1/component/gripper/{name}/stop' + _globals['_GRIPPERSERVICE'].methods_by_name['IsMoving']._loaded_options = None + _globals['_GRIPPERSERVICE'].methods_by_name['IsMoving']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/gripper/{name}/is_moving' + _globals['_GRIPPERSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_GRIPPERSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/component/gripper/{name}/do_command' + _globals['_GRIPPERSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_GRIPPERSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/gripper/{name}/geometries' + _globals['_OPENREQUEST']._serialized_start = 149 + _globals['_OPENREQUEST']._serialized_end = 229 + _globals['_OPENRESPONSE']._serialized_start = 231 + _globals['_OPENRESPONSE']._serialized_end = 245 + _globals['_GRABREQUEST']._serialized_start = 247 + _globals['_GRABREQUEST']._serialized_end = 327 + _globals['_GRABRESPONSE']._serialized_start = 329 + _globals['_GRABRESPONSE']._serialized_end = 416 + _globals['_STOPREQUEST']._serialized_start = 418 + _globals['_STOPREQUEST']._serialized_end = 498 + _globals['_STOPRESPONSE']._serialized_start = 500 + _globals['_STOPRESPONSE']._serialized_end = 514 + _globals['_ISMOVINGREQUEST']._serialized_start = 516 + _globals['_ISMOVINGREQUEST']._serialized_end = 553 + _globals['_ISMOVINGRESPONSE']._serialized_start = 555 + _globals['_ISMOVINGRESPONSE']._serialized_end = 602 + _globals['_GRIPPERSERVICE']._serialized_start = 605 + _globals['_GRIPPERSERVICE']._serialized_end = 1508 \ No newline at end of file diff --git a/src/viam/gen/component/gripper/v1/gripper_pb2.pyi b/src/viam/gen/component/gripper/v1/gripper_pb2.pyi index e9748bc41..077ce8af0 100644 --- a/src/viam/gen/component/gripper/v1/gripper_pb2.pyi +++ b/src/viam/gen/component/gripper/v1/gripper_pb2.pyi @@ -5,25 +5,32 @@ isort:skip_file import builtins import google.protobuf.descriptor import google.protobuf.message -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import google.protobuf.struct_pb2 +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class OpenRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___OpenRequest = OpenRequest +@typing.final class OpenResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -31,46 +38,100 @@ class OpenResponse(google.protobuf.message.Message): ... global___OpenResponse = OpenResponse +@typing.final class GrabRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GrabRequest = GrabRequest +@typing.final class GrabResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor SUCCESS_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int success: builtins.bool - def __init__(self, *, success: builtins.bool=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, success: builtins.bool=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['success', b'success']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'success', b'success']) -> None: ... global___GrabResponse = GrabResponse +@typing.final class StopRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a gripper' - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___StopRequest = StopRequest +@typing.final class StopResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor def __init__(self) -> None: ... -global___StopResponse = StopResponse \ No newline at end of file +global___StopResponse = StopResponse + +@typing.final +class IsMovingRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___IsMovingRequest = IsMovingRequest + +@typing.final +class IsMovingResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + IS_MOVING_FIELD_NUMBER: builtins.int + is_moving: builtins.bool + + def __init__(self, *, is_moving: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['is_moving', b'is_moving']) -> None: + ... +global___IsMovingResponse = IsMovingResponse \ No newline at end of file diff --git a/src/viam/gen/component/inputcontroller/v1/input_controller_grpc.py b/src/viam/gen/component/inputcontroller/v1/input_controller_grpc.py index 773596ca9..e019ed225 100644 --- a/src/viam/gen/component/inputcontroller/v1/input_controller_grpc.py +++ b/src/viam/gen/component/inputcontroller/v1/input_controller_grpc.py @@ -2,9 +2,12 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import common import google.api.annotations_pb2 +import google.protobuf.struct_pb2 import google.protobuf.timestamp_pb2 from .... import component @@ -26,8 +29,36 @@ async def StreamEvents(self, stream: 'grpclib.server.Stream[component.inputcontr async def TriggerEvent(self, stream: 'grpclib.server.Stream[component.inputcontroller.v1.input_controller_pb2.TriggerEventRequest, component.inputcontroller.v1.input_controller_pb2.TriggerEventResponse]') -> None: pass + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.inputcontroller.v1.InputControllerService/GetControls': grpclib.const.Handler(self.GetControls, grpclib.const.Cardinality.UNARY_UNARY, component.inputcontroller.v1.input_controller_pb2.GetControlsRequest, component.inputcontroller.v1.input_controller_pb2.GetControlsResponse), '/viam.component.inputcontroller.v1.InputControllerService/GetEvents': grpclib.const.Handler(self.GetEvents, grpclib.const.Cardinality.UNARY_UNARY, component.inputcontroller.v1.input_controller_pb2.GetEventsRequest, component.inputcontroller.v1.input_controller_pb2.GetEventsResponse), '/viam.component.inputcontroller.v1.InputControllerService/StreamEvents': grpclib.const.Handler(self.StreamEvents, grpclib.const.Cardinality.UNARY_STREAM, component.inputcontroller.v1.input_controller_pb2.StreamEventsRequest, component.inputcontroller.v1.input_controller_pb2.StreamEventsResponse), '/viam.component.inputcontroller.v1.InputControllerService/TriggerEvent': grpclib.const.Handler(self.TriggerEvent, grpclib.const.Cardinality.UNARY_UNARY, component.inputcontroller.v1.input_controller_pb2.TriggerEventRequest, component.inputcontroller.v1.input_controller_pb2.TriggerEventResponse)} + return {'/viam.component.inputcontroller.v1.InputControllerService/GetControls': grpclib.const.Handler(self.GetControls, grpclib.const.Cardinality.UNARY_UNARY, component.inputcontroller.v1.input_controller_pb2.GetControlsRequest, component.inputcontroller.v1.input_controller_pb2.GetControlsResponse), '/viam.component.inputcontroller.v1.InputControllerService/GetEvents': grpclib.const.Handler(self.GetEvents, grpclib.const.Cardinality.UNARY_UNARY, component.inputcontroller.v1.input_controller_pb2.GetEventsRequest, component.inputcontroller.v1.input_controller_pb2.GetEventsResponse), '/viam.component.inputcontroller.v1.InputControllerService/StreamEvents': grpclib.const.Handler(self.StreamEvents, grpclib.const.Cardinality.UNARY_STREAM, component.inputcontroller.v1.input_controller_pb2.StreamEventsRequest, component.inputcontroller.v1.input_controller_pb2.StreamEventsResponse), '/viam.component.inputcontroller.v1.InputControllerService/TriggerEvent': grpclib.const.Handler(self.TriggerEvent, grpclib.const.Cardinality.UNARY_UNARY, component.inputcontroller.v1.input_controller_pb2.TriggerEventRequest, component.inputcontroller.v1.input_controller_pb2.TriggerEventResponse), '/viam.component.inputcontroller.v1.InputControllerService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.inputcontroller.v1.InputControllerService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedInputControllerServiceBase(InputControllerServiceBase): + + async def GetControls(self, stream: 'grpclib.server.Stream[component.inputcontroller.v1.input_controller_pb2.GetControlsRequest, component.inputcontroller.v1.input_controller_pb2.GetControlsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetEvents(self, stream: 'grpclib.server.Stream[component.inputcontroller.v1.input_controller_pb2.GetEventsRequest, component.inputcontroller.v1.input_controller_pb2.GetEventsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def StreamEvents(self, stream: 'grpclib.server.Stream[component.inputcontroller.v1.input_controller_pb2.StreamEventsRequest, component.inputcontroller.v1.input_controller_pb2.StreamEventsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def TriggerEvent(self, stream: 'grpclib.server.Stream[component.inputcontroller.v1.input_controller_pb2.TriggerEventRequest, component.inputcontroller.v1.input_controller_pb2.TriggerEventResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class InputControllerServiceStub: @@ -35,4 +66,6 @@ def __init__(self, channel: grpclib.client.Channel) -> None: self.GetControls = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.inputcontroller.v1.InputControllerService/GetControls', component.inputcontroller.v1.input_controller_pb2.GetControlsRequest, component.inputcontroller.v1.input_controller_pb2.GetControlsResponse) self.GetEvents = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.inputcontroller.v1.InputControllerService/GetEvents', component.inputcontroller.v1.input_controller_pb2.GetEventsRequest, component.inputcontroller.v1.input_controller_pb2.GetEventsResponse) self.StreamEvents = grpclib.client.UnaryStreamMethod(channel, '/viam.component.inputcontroller.v1.InputControllerService/StreamEvents', component.inputcontroller.v1.input_controller_pb2.StreamEventsRequest, component.inputcontroller.v1.input_controller_pb2.StreamEventsResponse) - self.TriggerEvent = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.inputcontroller.v1.InputControllerService/TriggerEvent', component.inputcontroller.v1.input_controller_pb2.TriggerEventRequest, component.inputcontroller.v1.input_controller_pb2.TriggerEventResponse) \ No newline at end of file + self.TriggerEvent = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.inputcontroller.v1.InputControllerService/TriggerEvent', component.inputcontroller.v1.input_controller_pb2.TriggerEventRequest, component.inputcontroller.v1.input_controller_pb2.TriggerEventResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.inputcontroller.v1.InputControllerService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.inputcontroller.v1.InputControllerService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/inputcontroller/v1/input_controller_pb2.py b/src/viam/gen/component/inputcontroller/v1/input_controller_pb2.py index a2b19b414..c1273036c 100644 --- a/src/viam/gen/component/inputcontroller/v1/input_controller_pb2.py +++ b/src/viam/gen/component/inputcontroller/v1/input_controller_pb2.py @@ -1,46 +1,55 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/inputcontroller/v1/input_controller.proto') _sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n3component/inputcontroller/v1/input_controller.proto\x12!viam.component.inputcontroller.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/protobuf/timestamp.proto"4\n\x12GetControlsRequest\x12\x1e\n\ncontroller\x18\x01 \x01(\tR\ncontroller"1\n\x13GetControlsResponse\x12\x1a\n\x08controls\x18\x01 \x03(\tR\x08controls"2\n\x10GetEventsRequest\x12\x1e\n\ncontroller\x18\x01 \x01(\tR\ncontroller"U\n\x11GetEventsResponse\x12@\n\x06events\x18\x01 \x03(\x0b2(.viam.component.inputcontroller.v1.EventR\x06events"u\n\x13TriggerEventRequest\x12\x1e\n\ncontroller\x18\x01 \x01(\tR\ncontroller\x12>\n\x05event\x18\x02 \x01(\x0b2(.viam.component.inputcontroller.v1.EventR\x05event"\x16\n\x14TriggerEventResponse"}\n\x05Event\x12.\n\x04time\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\x04time\x12\x14\n\x05event\x18\x02 \x01(\tR\x05event\x12\x18\n\x07control\x18\x03 \x01(\tR\x07control\x12\x14\n\x05value\x18\x04 \x01(\x01R\x05value"\xf3\x01\n\x13StreamEventsRequest\x12\x1e\n\ncontroller\x18\x01 \x01(\tR\ncontroller\x12U\n\x06events\x18\x02 \x03(\x0b2=.viam.component.inputcontroller.v1.StreamEventsRequest.EventsR\x06events\x1ae\n\x06Events\x12\x18\n\x07control\x18\x01 \x01(\tR\x07control\x12\x16\n\x06events\x18\x02 \x03(\tR\x06events\x12)\n\x10cancelled_events\x18\x03 \x03(\tR\x0fcancelledEvents"V\n\x14StreamEventsResponse\x12>\n\x05event\x18\x01 \x01(\x0b2(.viam.component.inputcontroller.v1.EventR\x05event"J\n\x06Status\x12@\n\x06events\x18\x01 \x03(\x0b2(.viam.component.inputcontroller.v1.EventR\x06events2\x85\x06\n\x16InputControllerService\x12\xb8\x01\n\x0bGetControls\x125.viam.component.inputcontroller.v1.GetControlsRequest\x1a6.viam.component.inputcontroller.v1.GetControlsResponse":\x82\xd3\xe4\x93\x024\x122/viam/api/v1/component/input/{controller}/controls\x12\xb0\x01\n\tGetEvents\x123.viam.component.inputcontroller.v1.GetEventsRequest\x1a4.viam.component.inputcontroller.v1.GetEventsResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/input/{controller}/events\x12\xc1\x01\n\x0cStreamEvents\x126.viam.component.inputcontroller.v1.StreamEventsRequest\x1a7.viam.component.inputcontroller.v1.StreamEventsResponse">\x82\xd3\xe4\x93\x028\x126/viam/api/v1/component/input/{controller}/event_stream0\x01\x12\xb8\x01\n\x0cTriggerEvent\x126.viam.component.inputcontroller.v1.TriggerEventRequest\x1a7.viam.component.inputcontroller.v1.TriggerEventResponse"7\x82\xd3\xe4\x93\x021"//viam/api/v1/component/input/{controller}/eventBU\n%com.viam.component.inputcontroller.v1Z,go.viam.com/api/component/inputcontroller/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.inputcontroller.v1.input_controller_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n%com.viam.component.inputcontroller.v1Z,go.viam.com/api/component/inputcontroller/v1' - _INPUTCONTROLLERSERVICE.methods_by_name['GetControls']._options = None - _INPUTCONTROLLERSERVICE.methods_by_name['GetControls']._serialized_options = b'\x82\xd3\xe4\x93\x024\x122/viam/api/v1/component/input/{controller}/controls' - _INPUTCONTROLLERSERVICE.methods_by_name['GetEvents']._options = None - _INPUTCONTROLLERSERVICE.methods_by_name['GetEvents']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/input/{controller}/events' - _INPUTCONTROLLERSERVICE.methods_by_name['StreamEvents']._options = None - _INPUTCONTROLLERSERVICE.methods_by_name['StreamEvents']._serialized_options = b'\x82\xd3\xe4\x93\x028\x126/viam/api/v1/component/input/{controller}/event_stream' - _INPUTCONTROLLERSERVICE.methods_by_name['TriggerEvent']._options = None - _INPUTCONTROLLERSERVICE.methods_by_name['TriggerEvent']._serialized_options = b'\x82\xd3\xe4\x93\x021"//viam/api/v1/component/input/{controller}/event' - _GETCONTROLSREQUEST._serialized_start = 153 - _GETCONTROLSREQUEST._serialized_end = 205 - _GETCONTROLSRESPONSE._serialized_start = 207 - _GETCONTROLSRESPONSE._serialized_end = 256 - _GETEVENTSREQUEST._serialized_start = 258 - _GETEVENTSREQUEST._serialized_end = 308 - _GETEVENTSRESPONSE._serialized_start = 310 - _GETEVENTSRESPONSE._serialized_end = 395 - _TRIGGEREVENTREQUEST._serialized_start = 397 - _TRIGGEREVENTREQUEST._serialized_end = 514 - _TRIGGEREVENTRESPONSE._serialized_start = 516 - _TRIGGEREVENTRESPONSE._serialized_end = 538 - _EVENT._serialized_start = 540 - _EVENT._serialized_end = 665 - _STREAMEVENTSREQUEST._serialized_start = 668 - _STREAMEVENTSREQUEST._serialized_end = 911 - _STREAMEVENTSREQUEST_EVENTS._serialized_start = 810 - _STREAMEVENTSREQUEST_EVENTS._serialized_end = 911 - _STREAMEVENTSRESPONSE._serialized_start = 913 - _STREAMEVENTSRESPONSE._serialized_end = 999 - _STATUS._serialized_start = 1001 - _STATUS._serialized_end = 1075 - _INPUTCONTROLLERSERVICE._serialized_start = 1078 - _INPUTCONTROLLERSERVICE._serialized_end = 1851 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n3component/inputcontroller/v1/input_controller.proto\x12!viam.component.inputcontroller.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"c\n\x12GetControlsRequest\x12\x1e\n\ncontroller\x18\x01 \x01(\tR\ncontroller\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"1\n\x13GetControlsResponse\x12\x1a\n\x08controls\x18\x01 \x03(\tR\x08controls"a\n\x10GetEventsRequest\x12\x1e\n\ncontroller\x18\x01 \x01(\tR\ncontroller\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"U\n\x11GetEventsResponse\x12@\n\x06events\x18\x01 \x03(\x0b2(.viam.component.inputcontroller.v1.EventR\x06events"\xa4\x01\n\x13TriggerEventRequest\x12\x1e\n\ncontroller\x18\x01 \x01(\tR\ncontroller\x12>\n\x05event\x18\x02 \x01(\x0b2(.viam.component.inputcontroller.v1.EventR\x05event\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x16\n\x14TriggerEventResponse"}\n\x05Event\x12.\n\x04time\x18\x01 \x01(\x0b2\x1a.google.protobuf.TimestampR\x04time\x12\x14\n\x05event\x18\x02 \x01(\tR\x05event\x12\x18\n\x07control\x18\x03 \x01(\tR\x07control\x12\x14\n\x05value\x18\x04 \x01(\x01R\x05value"\xa2\x02\n\x13StreamEventsRequest\x12\x1e\n\ncontroller\x18\x01 \x01(\tR\ncontroller\x12U\n\x06events\x18\x02 \x03(\x0b2=.viam.component.inputcontroller.v1.StreamEventsRequest.EventsR\x06events\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra\x1ae\n\x06Events\x12\x18\n\x07control\x18\x01 \x01(\tR\x07control\x12\x16\n\x06events\x18\x02 \x03(\tR\x06events\x12)\n\x10cancelled_events\x18\x03 \x03(\tR\x0fcancelledEvents"V\n\x14StreamEventsResponse\x12>\n\x05event\x18\x01 \x01(\x0b2(.viam.component.inputcontroller.v1.EventR\x05event"J\n\x06Status\x12@\n\x06events\x18\x01 \x03(\x0b2(.viam.component.inputcontroller.v1.EventR\x06events2\xa7\x08\n\x16InputControllerService\x12\xb8\x01\n\x0bGetControls\x125.viam.component.inputcontroller.v1.GetControlsRequest\x1a6.viam.component.inputcontroller.v1.GetControlsResponse":\x82\xd3\xe4\x93\x024\x122/viam/api/v1/component/input/{controller}/controls\x12\xb0\x01\n\tGetEvents\x123.viam.component.inputcontroller.v1.GetEventsRequest\x1a4.viam.component.inputcontroller.v1.GetEventsResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/input/{controller}/events\x12\xc1\x01\n\x0cStreamEvents\x126.viam.component.inputcontroller.v1.StreamEventsRequest\x1a7.viam.component.inputcontroller.v1.StreamEventsResponse">\x82\xd3\xe4\x93\x028\x126/viam/api/v1/component/input/{controller}/event_stream0\x01\x12\xb8\x01\n\x0cTriggerEvent\x126.viam.component.inputcontroller.v1.TriggerEventRequest\x1a7.viam.component.inputcontroller.v1.TriggerEventResponse"7\x82\xd3\xe4\x93\x021"//viam/api/v1/component/input/{controller}/event\x12\x88\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"6\x82\xd3\xe4\x93\x020"./viam/api/v1/component/input/{name}/do_command\x12\x94\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"6\x82\xd3\xe4\x93\x020\x12./viam/api/v1/component/input/{name}/geometriesBU\n%com.viam.component.inputcontroller.v1Z,go.viam.com/api/component/inputcontroller/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.inputcontroller.v1.input_controller_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n%com.viam.component.inputcontroller.v1Z,go.viam.com/api/component/inputcontroller/v1' + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['GetControls']._loaded_options = None + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['GetControls']._serialized_options = b'\x82\xd3\xe4\x93\x024\x122/viam/api/v1/component/input/{controller}/controls' + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['GetEvents']._loaded_options = None + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['GetEvents']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/input/{controller}/events' + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['StreamEvents']._loaded_options = None + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['StreamEvents']._serialized_options = b'\x82\xd3\xe4\x93\x028\x126/viam/api/v1/component/input/{controller}/event_stream' + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['TriggerEvent']._loaded_options = None + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['TriggerEvent']._serialized_options = b'\x82\xd3\xe4\x93\x021"//viam/api/v1/component/input/{controller}/event' + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x020"./viam/api/v1/component/input/{name}/do_command' + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_INPUTCONTROLLERSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x020\x12./viam/api/v1/component/input/{name}/geometries' + _globals['_GETCONTROLSREQUEST']._serialized_start = 207 + _globals['_GETCONTROLSREQUEST']._serialized_end = 306 + _globals['_GETCONTROLSRESPONSE']._serialized_start = 308 + _globals['_GETCONTROLSRESPONSE']._serialized_end = 357 + _globals['_GETEVENTSREQUEST']._serialized_start = 359 + _globals['_GETEVENTSREQUEST']._serialized_end = 456 + _globals['_GETEVENTSRESPONSE']._serialized_start = 458 + _globals['_GETEVENTSRESPONSE']._serialized_end = 543 + _globals['_TRIGGEREVENTREQUEST']._serialized_start = 546 + _globals['_TRIGGEREVENTREQUEST']._serialized_end = 710 + _globals['_TRIGGEREVENTRESPONSE']._serialized_start = 712 + _globals['_TRIGGEREVENTRESPONSE']._serialized_end = 734 + _globals['_EVENT']._serialized_start = 736 + _globals['_EVENT']._serialized_end = 861 + _globals['_STREAMEVENTSREQUEST']._serialized_start = 864 + _globals['_STREAMEVENTSREQUEST']._serialized_end = 1154 + _globals['_STREAMEVENTSREQUEST_EVENTS']._serialized_start = 1053 + _globals['_STREAMEVENTSREQUEST_EVENTS']._serialized_end = 1154 + _globals['_STREAMEVENTSRESPONSE']._serialized_start = 1156 + _globals['_STREAMEVENTSRESPONSE']._serialized_end = 1242 + _globals['_STATUS']._serialized_start = 1244 + _globals['_STATUS']._serialized_end = 1318 + _globals['_INPUTCONTROLLERSERVICE']._serialized_start = 1321 + _globals['_INPUTCONTROLLERSERVICE']._serialized_end = 2384 \ No newline at end of file diff --git a/src/viam/gen/component/inputcontroller/v1/input_controller_pb2.pyi b/src/viam/gen/component/inputcontroller/v1/input_controller_pb2.pyi index 059a64e0f..ad7acd929 100644 --- a/src/viam/gen/component/inputcontroller/v1/input_controller_pb2.pyi +++ b/src/viam/gen/component/inputcontroller/v1/input_controller_pb2.pyi @@ -7,27 +7,34 @@ import collections.abc import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.message +import google.protobuf.struct_pb2 import google.protobuf.timestamp_pb2 -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class GetControlsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor CONTROLLER_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int controller: builtins.str 'Name of an input controller' - def __init__(self, *, controller: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, controller: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['controller', b'controller']) -> None: + def ClearField(self, field_name: typing.Literal['controller', b'controller', 'extra', b'extra']) -> None: ... global___GetControlsRequest = GetControlsRequest +@typing.final class GetControlsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor CONTROLS_FIELD_NUMBER: builtins.int @@ -41,23 +48,33 @@ class GetControlsResponse(google.protobuf.message.Message): def __init__(self, *, controls: collections.abc.Iterable[builtins.str] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['controls', b'controls']) -> None: + def ClearField(self, field_name: typing.Literal['controls', b'controls']) -> None: ... global___GetControlsResponse = GetControlsResponse +@typing.final class GetEventsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor CONTROLLER_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int controller: builtins.str 'Name of an input controller' - def __init__(self, *, controller: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, controller: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['controller', b'controller']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['controller', b'controller', 'extra', b'extra']) -> None: ... global___GetEventsRequest = GetEventsRequest +@typing.final class GetEventsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor EVENTS_FIELD_NUMBER: builtins.int @@ -71,14 +88,16 @@ class GetEventsResponse(google.protobuf.message.Message): def __init__(self, *, events: collections.abc.Iterable[global___Event] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['events', b'events']) -> None: + def ClearField(self, field_name: typing.Literal['events', b'events']) -> None: ... global___GetEventsResponse = GetEventsResponse +@typing.final class TriggerEventRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor CONTROLLER_FIELD_NUMBER: builtins.int EVENT_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int controller: builtins.str 'Name of an input controller' @@ -86,16 +105,21 @@ class TriggerEventRequest(google.protobuf.message.Message): def event(self) -> global___Event: """Digitally assert a given event""" - def __init__(self, *, controller: builtins.str=..., event: global___Event | None=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, controller: builtins.str=..., event: global___Event | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['event', b'event']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['event', b'event', 'extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['controller', b'controller', 'event', b'event']) -> None: + def ClearField(self, field_name: typing.Literal['controller', b'controller', 'event', b'event', 'extra', b'extra']) -> None: ... global___TriggerEventRequest = TriggerEventRequest +@typing.final class TriggerEventResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -103,16 +127,13 @@ class TriggerEventResponse(google.protobuf.message.Message): ... global___TriggerEventResponse = TriggerEventResponse +@typing.final class Event(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor TIME_FIELD_NUMBER: builtins.int EVENT_FIELD_NUMBER: builtins.int CONTROL_FIELD_NUMBER: builtins.int VALUE_FIELD_NUMBER: builtins.int - - @property - def time(self) -> google.protobuf.timestamp_pb2.Timestamp: - """Timestamp of event""" event: builtins.str 'An event type (eg: ButtonPress, ButtonRelease)' control: builtins.str @@ -120,19 +141,25 @@ class Event(google.protobuf.message.Message): value: builtins.float '0 or 1 for buttons, -1.0 to +1.0 for axes' + @property + def time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """Timestamp of event""" + def __init__(self, *, time: google.protobuf.timestamp_pb2.Timestamp | None=..., event: builtins.str=..., control: builtins.str=..., value: builtins.float=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['time', b'time']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['time', b'time']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['control', b'control', 'event', b'event', 'time', b'time', 'value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['control', b'control', 'event', b'event', 'time', b'time', 'value', b'value']) -> None: ... global___Event = Event +@typing.final class StreamEventsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor + @typing.final class Events(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor CONTROL_FIELD_NUMBER: builtins.int @@ -143,9 +170,7 @@ class StreamEventsRequest(google.protobuf.message.Message): @property def events(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: - """Specify which event types to recieve events for - To Do (FA): Right now this can be an empty list, but we should error in this case as opening a stream with no messages is expensive - """ + """Specify which event types to recieve events for""" @property def cancelled_events(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: @@ -156,10 +181,11 @@ class StreamEventsRequest(google.protobuf.message.Message): def __init__(self, *, control: builtins.str=..., events: collections.abc.Iterable[builtins.str] | None=..., cancelled_events: collections.abc.Iterable[builtins.str] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['cancelled_events', b'cancelled_events', 'control', b'control', 'events', b'events']) -> None: + def ClearField(self, field_name: typing.Literal['cancelled_events', b'cancelled_events', 'control', b'control', 'events', b'events']) -> None: ... CONTROLLER_FIELD_NUMBER: builtins.int EVENTS_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int controller: builtins.str 'Name of an input controller' @@ -167,13 +193,21 @@ class StreamEventsRequest(google.protobuf.message.Message): def events(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___StreamEventsRequest.Events]: """A list of Events""" - def __init__(self, *, controller: builtins.str=..., events: collections.abc.Iterable[global___StreamEventsRequest.Events] | None=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, controller: builtins.str=..., events: collections.abc.Iterable[global___StreamEventsRequest.Events] | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['controller', b'controller', 'events', b'events']) -> None: + def ClearField(self, field_name: typing.Literal['controller', b'controller', 'events', b'events', 'extra', b'extra']) -> None: ... global___StreamEventsRequest = StreamEventsRequest +@typing.final class StreamEventsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor EVENT_FIELD_NUMBER: builtins.int @@ -185,13 +219,14 @@ class StreamEventsResponse(google.protobuf.message.Message): def __init__(self, *, event: global___Event | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['event', b'event']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['event', b'event']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['event', b'event']) -> None: + def ClearField(self, field_name: typing.Literal['event', b'event']) -> None: ... global___StreamEventsResponse = StreamEventsResponse +@typing.final class Status(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor EVENTS_FIELD_NUMBER: builtins.int @@ -203,6 +238,6 @@ class Status(google.protobuf.message.Message): def __init__(self, *, events: collections.abc.Iterable[global___Event] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['events', b'events']) -> None: + def ClearField(self, field_name: typing.Literal['events', b'events']) -> None: ... global___Status = Status \ No newline at end of file diff --git a/src/viam/gen/component/motor/v1/motor_grpc.py b/src/viam/gen/component/motor/v1/motor_grpc.py index e9334f97e..23af3a8f2 100644 --- a/src/viam/gen/component/motor/v1/motor_grpc.py +++ b/src/viam/gen/component/motor/v1/motor_grpc.py @@ -2,8 +2,10 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import common import google.api.annotations_pb2 import google.protobuf.struct_pb2 from .... import component @@ -22,6 +24,10 @@ async def GoFor(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb async def GoTo(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.GoToRequest, component.motor.v1.motor_pb2.GoToResponse]') -> None: pass + @abc.abstractmethod + async def SetRPM(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.SetRPMRequest, component.motor.v1.motor_pb2.SetRPMResponse]') -> None: + pass + @abc.abstractmethod async def ResetZeroPosition(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.ResetZeroPositionRequest, component.motor.v1.motor_pb2.ResetZeroPositionResponse]') -> None: pass @@ -42,8 +48,58 @@ async def Stop(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2 async def IsPowered(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.IsPoweredRequest, component.motor.v1.motor_pb2.IsPoweredResponse]') -> None: pass + @abc.abstractmethod + async def IsMoving(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.IsMovingRequest, component.motor.v1.motor_pb2.IsMovingResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.motor.v1.MotorService/SetPower': grpclib.const.Handler(self.SetPower, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.SetPowerRequest, component.motor.v1.motor_pb2.SetPowerResponse), '/viam.component.motor.v1.MotorService/GoFor': grpclib.const.Handler(self.GoFor, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.GoForRequest, component.motor.v1.motor_pb2.GoForResponse), '/viam.component.motor.v1.MotorService/GoTo': grpclib.const.Handler(self.GoTo, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.GoToRequest, component.motor.v1.motor_pb2.GoToResponse), '/viam.component.motor.v1.MotorService/ResetZeroPosition': grpclib.const.Handler(self.ResetZeroPosition, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.ResetZeroPositionRequest, component.motor.v1.motor_pb2.ResetZeroPositionResponse), '/viam.component.motor.v1.MotorService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.GetPositionRequest, component.motor.v1.motor_pb2.GetPositionResponse), '/viam.component.motor.v1.MotorService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.GetPropertiesRequest, component.motor.v1.motor_pb2.GetPropertiesResponse), '/viam.component.motor.v1.MotorService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.StopRequest, component.motor.v1.motor_pb2.StopResponse), '/viam.component.motor.v1.MotorService/IsPowered': grpclib.const.Handler(self.IsPowered, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.IsPoweredRequest, component.motor.v1.motor_pb2.IsPoweredResponse)} + return {'/viam.component.motor.v1.MotorService/SetPower': grpclib.const.Handler(self.SetPower, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.SetPowerRequest, component.motor.v1.motor_pb2.SetPowerResponse), '/viam.component.motor.v1.MotorService/GoFor': grpclib.const.Handler(self.GoFor, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.GoForRequest, component.motor.v1.motor_pb2.GoForResponse), '/viam.component.motor.v1.MotorService/GoTo': grpclib.const.Handler(self.GoTo, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.GoToRequest, component.motor.v1.motor_pb2.GoToResponse), '/viam.component.motor.v1.MotorService/SetRPM': grpclib.const.Handler(self.SetRPM, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.SetRPMRequest, component.motor.v1.motor_pb2.SetRPMResponse), '/viam.component.motor.v1.MotorService/ResetZeroPosition': grpclib.const.Handler(self.ResetZeroPosition, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.ResetZeroPositionRequest, component.motor.v1.motor_pb2.ResetZeroPositionResponse), '/viam.component.motor.v1.MotorService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.GetPositionRequest, component.motor.v1.motor_pb2.GetPositionResponse), '/viam.component.motor.v1.MotorService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.GetPropertiesRequest, component.motor.v1.motor_pb2.GetPropertiesResponse), '/viam.component.motor.v1.MotorService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.StopRequest, component.motor.v1.motor_pb2.StopResponse), '/viam.component.motor.v1.MotorService/IsPowered': grpclib.const.Handler(self.IsPowered, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.IsPoweredRequest, component.motor.v1.motor_pb2.IsPoweredResponse), '/viam.component.motor.v1.MotorService/IsMoving': grpclib.const.Handler(self.IsMoving, grpclib.const.Cardinality.UNARY_UNARY, component.motor.v1.motor_pb2.IsMovingRequest, component.motor.v1.motor_pb2.IsMovingResponse), '/viam.component.motor.v1.MotorService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.motor.v1.MotorService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedMotorServiceBase(MotorServiceBase): + + async def SetPower(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.SetPowerRequest, component.motor.v1.motor_pb2.SetPowerResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GoFor(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.GoForRequest, component.motor.v1.motor_pb2.GoForResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GoTo(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.GoToRequest, component.motor.v1.motor_pb2.GoToResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetRPM(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.SetRPMRequest, component.motor.v1.motor_pb2.SetRPMResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ResetZeroPosition(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.ResetZeroPositionRequest, component.motor.v1.motor_pb2.ResetZeroPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPosition(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.GetPositionRequest, component.motor.v1.motor_pb2.GetPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetProperties(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.GetPropertiesRequest, component.motor.v1.motor_pb2.GetPropertiesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Stop(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.StopRequest, component.motor.v1.motor_pb2.StopResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def IsPowered(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.IsPoweredRequest, component.motor.v1.motor_pb2.IsPoweredResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def IsMoving(self, stream: 'grpclib.server.Stream[component.motor.v1.motor_pb2.IsMovingRequest, component.motor.v1.motor_pb2.IsMovingResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class MotorServiceStub: @@ -51,8 +107,12 @@ def __init__(self, channel: grpclib.client.Channel) -> None: self.SetPower = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/SetPower', component.motor.v1.motor_pb2.SetPowerRequest, component.motor.v1.motor_pb2.SetPowerResponse) self.GoFor = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/GoFor', component.motor.v1.motor_pb2.GoForRequest, component.motor.v1.motor_pb2.GoForResponse) self.GoTo = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/GoTo', component.motor.v1.motor_pb2.GoToRequest, component.motor.v1.motor_pb2.GoToResponse) + self.SetRPM = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/SetRPM', component.motor.v1.motor_pb2.SetRPMRequest, component.motor.v1.motor_pb2.SetRPMResponse) self.ResetZeroPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/ResetZeroPosition', component.motor.v1.motor_pb2.ResetZeroPositionRequest, component.motor.v1.motor_pb2.ResetZeroPositionResponse) self.GetPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/GetPosition', component.motor.v1.motor_pb2.GetPositionRequest, component.motor.v1.motor_pb2.GetPositionResponse) self.GetProperties = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/GetProperties', component.motor.v1.motor_pb2.GetPropertiesRequest, component.motor.v1.motor_pb2.GetPropertiesResponse) self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/Stop', component.motor.v1.motor_pb2.StopRequest, component.motor.v1.motor_pb2.StopResponse) - self.IsPowered = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/IsPowered', component.motor.v1.motor_pb2.IsPoweredRequest, component.motor.v1.motor_pb2.IsPoweredResponse) \ No newline at end of file + self.IsPowered = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/IsPowered', component.motor.v1.motor_pb2.IsPoweredRequest, component.motor.v1.motor_pb2.IsPoweredResponse) + self.IsMoving = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/IsMoving', component.motor.v1.motor_pb2.IsMovingRequest, component.motor.v1.motor_pb2.IsMovingResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.motor.v1.MotorService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/motor/v1/motor_pb2.py b/src/viam/gen/component/motor/v1/motor_pb2.py index c969727a3..17bb3d440 100644 --- a/src/viam/gen/component/motor/v1/motor_pb2.py +++ b/src/viam/gen/component/motor/v1/motor_pb2.py @@ -1,66 +1,86 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/motor/v1/motor.proto') _sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ecomponent/motor/v1/motor.proto\x12\x17viam.component.motor.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"q\n\x0fSetPowerRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tpower_pct\x18\x02 \x01(\x01R\x08powerPct\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x12\n\x10SetPowerResponse"\x85\x01\n\x0cGoForRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03rpm\x18\x02 \x01(\x01R\x03rpm\x12 \n\x0brevolutions\x18\x03 \x01(\x01R\x0brevolutions\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0f\n\rGoForResponse"\x95\x01\n\x0bGoToRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03rpm\x18\x02 \x01(\x01R\x03rpm\x121\n\x14position_revolutions\x18\x03 \x01(\x01R\x13positionRevolutions\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cGoToResponse"u\n\x18ResetZeroPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06offset\x18\x02 \x01(\x01R\x06offset\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x1b\n\x19ResetZeroPositionResponse"W\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"1\n\x13GetPositionResponse\x12\x1a\n\x08position\x18\x01 \x01(\x01R\x08position"P\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cStopResponse"U\n\x10IsPoweredRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"(\n\x11IsPoweredResponse\x12\x13\n\x05is_on\x18\x01 \x01(\x08R\x04isOn"Y\n\x14GetPropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"F\n\x15GetPropertiesResponse\x12-\n\x12position_reporting\x18\x01 \x01(\x08R\x11positionReporting"\x8f\x01\n\x06Status\x12\x1d\n\nis_powered\x18\x01 \x01(\x08R\tisPowered\x12-\n\x12position_reporting\x18\x02 \x01(\x08R\x11positionReporting\x12\x1a\n\x08position\x18\x03 \x01(\x01R\x08position\x12\x1b\n\tis_moving\x18\x04 \x01(\x08R\x08isMoving2\xd2\t\n\x0cMotorService\x12\x92\x01\n\x08SetPower\x12(.viam.component.motor.v1.SetPowerRequest\x1a).viam.component.motor.v1.SetPowerResponse"1\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/motor/{name}/power\x12\x8a\x01\n\x05GoFor\x12%.viam.component.motor.v1.GoForRequest\x1a&.viam.component.motor.v1.GoForResponse"2\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/motor/{name}/go_for\x12\x86\x01\n\x04GoTo\x12$.viam.component.motor.v1.GoToRequest\x1a%.viam.component.motor.v1.GoToResponse"1\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/motor/{name}/go_to\x12\xac\x01\n\x11ResetZeroPosition\x121.viam.component.motor.v1.ResetZeroPositionRequest\x1a2.viam.component.motor.v1.ResetZeroPositionResponse"0\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/motor/{name}/zero\x12\x9e\x01\n\x0bGetPosition\x12+.viam.component.motor.v1.GetPositionRequest\x1a,.viam.component.motor.v1.GetPositionResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/motor/{name}/position\x12\xa4\x01\n\rGetProperties\x12-.viam.component.motor.v1.GetPropertiesRequest\x1a..viam.component.motor.v1.GetPropertiesResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/motor/{name}/features\x12\x85\x01\n\x04Stop\x12$.viam.component.motor.v1.StopRequest\x1a%.viam.component.motor.v1.StopResponse"0\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/motor/{name}/stop\x12\x97\x01\n\tIsPowered\x12).viam.component.motor.v1.IsPoweredRequest\x1a*.viam.component.motor.v1.IsPoweredResponse"3\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/component/motor/{name}/poweredBA\n\x1bcom.viam.component.motor.v1Z"go.viam.com/api/component/motor/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.motor.v1.motor_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1bcom.viam.component.motor.v1Z"go.viam.com/api/component/motor/v1' - _MOTORSERVICE.methods_by_name['SetPower']._options = None - _MOTORSERVICE.methods_by_name['SetPower']._serialized_options = b'\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/motor/{name}/power' - _MOTORSERVICE.methods_by_name['GoFor']._options = None - _MOTORSERVICE.methods_by_name['GoFor']._serialized_options = b'\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/motor/{name}/go_for' - _MOTORSERVICE.methods_by_name['GoTo']._options = None - _MOTORSERVICE.methods_by_name['GoTo']._serialized_options = b'\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/motor/{name}/go_to' - _MOTORSERVICE.methods_by_name['ResetZeroPosition']._options = None - _MOTORSERVICE.methods_by_name['ResetZeroPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/motor/{name}/zero' - _MOTORSERVICE.methods_by_name['GetPosition']._options = None - _MOTORSERVICE.methods_by_name['GetPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/motor/{name}/position' - _MOTORSERVICE.methods_by_name['GetProperties']._options = None - _MOTORSERVICE.methods_by_name['GetProperties']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/motor/{name}/features' - _MOTORSERVICE.methods_by_name['Stop']._options = None - _MOTORSERVICE.methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/motor/{name}/stop' - _MOTORSERVICE.methods_by_name['IsPowered']._options = None - _MOTORSERVICE.methods_by_name['IsPowered']._serialized_options = b'\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/component/motor/{name}/powered' - _SETPOWERREQUEST._serialized_start = 119 - _SETPOWERREQUEST._serialized_end = 232 - _SETPOWERRESPONSE._serialized_start = 234 - _SETPOWERRESPONSE._serialized_end = 252 - _GOFORREQUEST._serialized_start = 255 - _GOFORREQUEST._serialized_end = 388 - _GOFORRESPONSE._serialized_start = 390 - _GOFORRESPONSE._serialized_end = 405 - _GOTOREQUEST._serialized_start = 408 - _GOTOREQUEST._serialized_end = 557 - _GOTORESPONSE._serialized_start = 559 - _GOTORESPONSE._serialized_end = 573 - _RESETZEROPOSITIONREQUEST._serialized_start = 575 - _RESETZEROPOSITIONREQUEST._serialized_end = 692 - _RESETZEROPOSITIONRESPONSE._serialized_start = 694 - _RESETZEROPOSITIONRESPONSE._serialized_end = 721 - _GETPOSITIONREQUEST._serialized_start = 723 - _GETPOSITIONREQUEST._serialized_end = 810 - _GETPOSITIONRESPONSE._serialized_start = 812 - _GETPOSITIONRESPONSE._serialized_end = 861 - _STOPREQUEST._serialized_start = 863 - _STOPREQUEST._serialized_end = 943 - _STOPRESPONSE._serialized_start = 945 - _STOPRESPONSE._serialized_end = 959 - _ISPOWEREDREQUEST._serialized_start = 961 - _ISPOWEREDREQUEST._serialized_end = 1046 - _ISPOWEREDRESPONSE._serialized_start = 1048 - _ISPOWEREDRESPONSE._serialized_end = 1088 - _GETPROPERTIESREQUEST._serialized_start = 1090 - _GETPROPERTIESREQUEST._serialized_end = 1179 - _GETPROPERTIESRESPONSE._serialized_start = 1181 - _GETPROPERTIESRESPONSE._serialized_end = 1251 - _STATUS._serialized_start = 1254 - _STATUS._serialized_end = 1397 - _MOTORSERVICE._serialized_start = 1400 - _MOTORSERVICE._serialized_end = 2634 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ecomponent/motor/v1/motor.proto\x12\x17viam.component.motor.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"q\n\x0fSetPowerRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tpower_pct\x18\x02 \x01(\x01R\x08powerPct\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x12\n\x10SetPowerResponse"\x85\x01\n\x0cGoForRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03rpm\x18\x02 \x01(\x01R\x03rpm\x12 \n\x0brevolutions\x18\x03 \x01(\x01R\x0brevolutions\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0f\n\rGoForResponse"\x95\x01\n\x0bGoToRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03rpm\x18\x02 \x01(\x01R\x03rpm\x121\n\x14position_revolutions\x18\x03 \x01(\x01R\x13positionRevolutions\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cGoToResponse"d\n\rSetRPMRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x10\n\x03rpm\x18\x02 \x01(\x01R\x03rpm\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x10\n\x0eSetRPMResponse"u\n\x18ResetZeroPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x16\n\x06offset\x18\x02 \x01(\x01R\x06offset\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x1b\n\x19ResetZeroPositionResponse"W\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"1\n\x13GetPositionResponse\x12\x1a\n\x08position\x18\x01 \x01(\x01R\x08position"P\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cStopResponse"U\n\x10IsPoweredRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"E\n\x11IsPoweredResponse\x12\x13\n\x05is_on\x18\x01 \x01(\x08R\x04isOn\x12\x1b\n\tpower_pct\x18\x02 \x01(\x01R\x08powerPct"Y\n\x14GetPropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"F\n\x15GetPropertiesResponse\x12-\n\x12position_reporting\x18\x01 \x01(\x08R\x11positionReporting"`\n\x06Status\x12\x1d\n\nis_powered\x18\x01 \x01(\x08R\tisPowered\x12\x1a\n\x08position\x18\x03 \x01(\x01R\x08position\x12\x1b\n\tis_moving\x18\x04 \x01(\x08R\x08isMoving"%\n\x0fIsMovingRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"/\n\x10IsMovingResponse\x12\x1b\n\tis_moving\x18\x01 \x01(\x08R\x08isMoving2\xae\x0e\n\x0cMotorService\x12\x96\x01\n\x08SetPower\x12(.viam.component.motor.v1.SetPowerRequest\x1a).viam.component.motor.v1.SetPowerResponse"5\xa0\x92)\x01\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/motor/{name}/power\x12\x8e\x01\n\x05GoFor\x12%.viam.component.motor.v1.GoForRequest\x1a&.viam.component.motor.v1.GoForResponse"6\xa0\x92)\x01\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/motor/{name}/go_for\x12\x8a\x01\n\x04GoTo\x12$.viam.component.motor.v1.GoToRequest\x1a%.viam.component.motor.v1.GoToResponse"5\xa0\x92)\x01\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/motor/{name}/go_to\x12\x92\x01\n\x06SetRPM\x12&.viam.component.motor.v1.SetRPMRequest\x1a\'.viam.component.motor.v1.SetRPMResponse"7\xa0\x92)\x01\x82\xd3\xe4\x93\x02-\x1a+/viam/api/v1/component/motor/{name}/set_rpm\x12\xac\x01\n\x11ResetZeroPosition\x121.viam.component.motor.v1.ResetZeroPositionRequest\x1a2.viam.component.motor.v1.ResetZeroPositionResponse"0\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/motor/{name}/zero\x12\x9e\x01\n\x0bGetPosition\x12+.viam.component.motor.v1.GetPositionRequest\x1a,.viam.component.motor.v1.GetPositionResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/motor/{name}/position\x12\xa4\x01\n\rGetProperties\x12-.viam.component.motor.v1.GetPropertiesRequest\x1a..viam.component.motor.v1.GetPropertiesResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/motor/{name}/features\x12\x85\x01\n\x04Stop\x12$.viam.component.motor.v1.StopRequest\x1a%.viam.component.motor.v1.StopResponse"0\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/motor/{name}/stop\x12\x97\x01\n\tIsPowered\x12).viam.component.motor.v1.IsPoweredRequest\x1a*.viam.component.motor.v1.IsPoweredResponse"3\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/component/motor/{name}/powered\x12\x96\x01\n\x08IsMoving\x12(.viam.component.motor.v1.IsMovingRequest\x1a).viam.component.motor.v1.IsMovingResponse"5\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/motor/{name}/is_moving\x12\x88\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"6\x82\xd3\xe4\x93\x020"./viam/api/v1/component/motor/{name}/do_command\x12\x94\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"6\x82\xd3\xe4\x93\x020\x12./viam/api/v1/component/motor/{name}/geometriesBA\n\x1bcom.viam.component.motor.v1Z"go.viam.com/api/component/motor/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.motor.v1.motor_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1bcom.viam.component.motor.v1Z"go.viam.com/api/component/motor/v1' + _globals['_MOTORSERVICE'].methods_by_name['SetPower']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['SetPower']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/motor/{name}/power' + _globals['_MOTORSERVICE'].methods_by_name['GoFor']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['GoFor']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02,\x1a*/viam/api/v1/component/motor/{name}/go_for' + _globals['_MOTORSERVICE'].methods_by_name['GoTo']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['GoTo']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02+\x1a)/viam/api/v1/component/motor/{name}/go_to' + _globals['_MOTORSERVICE'].methods_by_name['SetRPM']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['SetRPM']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02-\x1a+/viam/api/v1/component/motor/{name}/set_rpm' + _globals['_MOTORSERVICE'].methods_by_name['ResetZeroPosition']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['ResetZeroPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/motor/{name}/zero' + _globals['_MOTORSERVICE'].methods_by_name['GetPosition']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['GetPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/motor/{name}/position' + _globals['_MOTORSERVICE'].methods_by_name['GetProperties']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['GetProperties']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/motor/{name}/features' + _globals['_MOTORSERVICE'].methods_by_name['Stop']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/motor/{name}/stop' + _globals['_MOTORSERVICE'].methods_by_name['IsPowered']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['IsPowered']._serialized_options = b'\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/component/motor/{name}/powered' + _globals['_MOTORSERVICE'].methods_by_name['IsMoving']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['IsMoving']._serialized_options = b'\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/motor/{name}/is_moving' + _globals['_MOTORSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x020"./viam/api/v1/component/motor/{name}/do_command' + _globals['_MOTORSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_MOTORSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x020\x12./viam/api/v1/component/motor/{name}/geometries' + _globals['_SETPOWERREQUEST']._serialized_start = 143 + _globals['_SETPOWERREQUEST']._serialized_end = 256 + _globals['_SETPOWERRESPONSE']._serialized_start = 258 + _globals['_SETPOWERRESPONSE']._serialized_end = 276 + _globals['_GOFORREQUEST']._serialized_start = 279 + _globals['_GOFORREQUEST']._serialized_end = 412 + _globals['_GOFORRESPONSE']._serialized_start = 414 + _globals['_GOFORRESPONSE']._serialized_end = 429 + _globals['_GOTOREQUEST']._serialized_start = 432 + _globals['_GOTOREQUEST']._serialized_end = 581 + _globals['_GOTORESPONSE']._serialized_start = 583 + _globals['_GOTORESPONSE']._serialized_end = 597 + _globals['_SETRPMREQUEST']._serialized_start = 599 + _globals['_SETRPMREQUEST']._serialized_end = 699 + _globals['_SETRPMRESPONSE']._serialized_start = 701 + _globals['_SETRPMRESPONSE']._serialized_end = 717 + _globals['_RESETZEROPOSITIONREQUEST']._serialized_start = 719 + _globals['_RESETZEROPOSITIONREQUEST']._serialized_end = 836 + _globals['_RESETZEROPOSITIONRESPONSE']._serialized_start = 838 + _globals['_RESETZEROPOSITIONRESPONSE']._serialized_end = 865 + _globals['_GETPOSITIONREQUEST']._serialized_start = 867 + _globals['_GETPOSITIONREQUEST']._serialized_end = 954 + _globals['_GETPOSITIONRESPONSE']._serialized_start = 956 + _globals['_GETPOSITIONRESPONSE']._serialized_end = 1005 + _globals['_STOPREQUEST']._serialized_start = 1007 + _globals['_STOPREQUEST']._serialized_end = 1087 + _globals['_STOPRESPONSE']._serialized_start = 1089 + _globals['_STOPRESPONSE']._serialized_end = 1103 + _globals['_ISPOWEREDREQUEST']._serialized_start = 1105 + _globals['_ISPOWEREDREQUEST']._serialized_end = 1190 + _globals['_ISPOWEREDRESPONSE']._serialized_start = 1192 + _globals['_ISPOWEREDRESPONSE']._serialized_end = 1261 + _globals['_GETPROPERTIESREQUEST']._serialized_start = 1263 + _globals['_GETPROPERTIESREQUEST']._serialized_end = 1352 + _globals['_GETPROPERTIESRESPONSE']._serialized_start = 1354 + _globals['_GETPROPERTIESRESPONSE']._serialized_end = 1424 + _globals['_STATUS']._serialized_start = 1426 + _globals['_STATUS']._serialized_end = 1522 + _globals['_ISMOVINGREQUEST']._serialized_start = 1524 + _globals['_ISMOVINGREQUEST']._serialized_end = 1561 + _globals['_ISMOVINGRESPONSE']._serialized_start = 1563 + _globals['_ISMOVINGRESPONSE']._serialized_end = 1610 + _globals['_MOTORSERVICE']._serialized_start = 1613 + _globals['_MOTORSERVICE']._serialized_end = 3451 \ No newline at end of file diff --git a/src/viam/gen/component/motor/v1/motor_pb2.pyi b/src/viam/gen/component/motor/v1/motor_pb2.pyi index 57e98ec55..2a43f4f6e 100644 --- a/src/viam/gen/component/motor/v1/motor_pb2.pyi +++ b/src/viam/gen/component/motor/v1/motor_pb2.pyi @@ -6,13 +6,10 @@ import builtins import google.protobuf.descriptor import google.protobuf.message import google.protobuf.struct_pb2 -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class SetPowerRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -30,13 +27,14 @@ class SetPowerRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., power_pct: builtins.float=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name', 'power_pct', b'power_pct']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'power_pct', b'power_pct']) -> None: ... global___SetPowerRequest = SetPowerRequest +@typing.final class SetPowerResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -44,6 +42,7 @@ class SetPowerResponse(google.protobuf.message.Message): ... global___SetPowerResponse = SetPowerResponse +@typing.final class GoForRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -64,13 +63,14 @@ class GoForRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., rpm: builtins.float=..., revolutions: builtins.float=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name', 'revolutions', b'revolutions', 'rpm', b'rpm']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'revolutions', b'revolutions', 'rpm', b'rpm']) -> None: ... global___GoForRequest = GoForRequest +@typing.final class GoForResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -78,6 +78,7 @@ class GoForResponse(google.protobuf.message.Message): ... global___GoForResponse = GoForResponse +@typing.final class GoToRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -98,13 +99,14 @@ class GoToRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., rpm: builtins.float=..., position_revolutions: builtins.float=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name', 'position_revolutions', b'position_revolutions', 'rpm', b'rpm']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'position_revolutions', b'position_revolutions', 'rpm', b'rpm']) -> None: ... global___GoToRequest = GoToRequest +@typing.final class GoToResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -112,6 +114,40 @@ class GoToResponse(google.protobuf.message.Message): ... global___GoToResponse = GoToResponse +@typing.final +class SetRPMRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + RPM_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of a motor' + rpm: builtins.float + 'Speed of motor travel in rotations per minute' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., rpm: builtins.float=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'rpm', b'rpm']) -> None: + ... +global___SetRPMRequest = SetRPMRequest + +@typing.final +class SetRPMResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___SetRPMResponse = SetRPMResponse + +@typing.final class ResetZeroPositionRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -129,13 +165,14 @@ class ResetZeroPositionRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., offset: builtins.float=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name', 'offset', b'offset']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'offset', b'offset']) -> None: ... global___ResetZeroPositionRequest = ResetZeroPositionRequest +@typing.final class ResetZeroPositionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -143,6 +180,7 @@ class ResetZeroPositionResponse(google.protobuf.message.Message): ... global___ResetZeroPositionResponse = ResetZeroPositionResponse +@typing.final class GetPositionRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -157,13 +195,14 @@ class GetPositionRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetPositionRequest = GetPositionRequest +@typing.final class GetPositionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSITION_FIELD_NUMBER: builtins.int @@ -173,10 +212,11 @@ class GetPositionResponse(google.protobuf.message.Message): def __init__(self, *, position: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['position', b'position']) -> None: + def ClearField(self, field_name: typing.Literal['position', b'position']) -> None: ... global___GetPositionResponse = GetPositionResponse +@typing.final class StopRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -191,13 +231,14 @@ class StopRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___StopRequest = StopRequest +@typing.final class StopResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -205,6 +246,7 @@ class StopResponse(google.protobuf.message.Message): ... global___StopResponse = StopResponse +@typing.final class IsPoweredRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -219,26 +261,31 @@ class IsPoweredRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___IsPoweredRequest = IsPoweredRequest +@typing.final class IsPoweredResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor IS_ON_FIELD_NUMBER: builtins.int + POWER_PCT_FIELD_NUMBER: builtins.int is_on: builtins.bool 'Returns true if the motor is on' + power_pct: builtins.float + 'Returns power percent (from 0 to 1, or from -1 to 1 for motors that support negative power),\n based on the last command sent to motor. If the last command was a stop command, this value\n will be 0.\n ' - def __init__(self, *, is_on: builtins.bool=...) -> None: + def __init__(self, *, is_on: builtins.bool=..., power_pct: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['is_on', b'is_on']) -> None: + def ClearField(self, field_name: typing.Literal['is_on', b'is_on', 'power_pct', b'power_pct']) -> None: ... global___IsPoweredResponse = IsPoweredResponse +@typing.final class GetPropertiesRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -253,13 +300,14 @@ class GetPropertiesRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetPropertiesRequest = GetPropertiesRequest +@typing.final class GetPropertiesResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSITION_REPORTING_FIELD_NUMBER: builtins.int @@ -269,28 +317,52 @@ class GetPropertiesResponse(google.protobuf.message.Message): def __init__(self, *, position_reporting: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['position_reporting', b'position_reporting']) -> None: + def ClearField(self, field_name: typing.Literal['position_reporting', b'position_reporting']) -> None: ... global___GetPropertiesResponse = GetPropertiesResponse +@typing.final class Status(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor IS_POWERED_FIELD_NUMBER: builtins.int - POSITION_REPORTING_FIELD_NUMBER: builtins.int POSITION_FIELD_NUMBER: builtins.int IS_MOVING_FIELD_NUMBER: builtins.int is_powered: builtins.bool 'Returns true if the motor is powered' - position_reporting: builtins.bool - 'Returns true if the motor has position support' position: builtins.float 'Returns current position of the motor relative to its home' is_moving: builtins.bool 'Returns true if the motor is moving' - def __init__(self, *, is_powered: builtins.bool=..., position_reporting: builtins.bool=..., position: builtins.float=..., is_moving: builtins.bool=...) -> None: + def __init__(self, *, is_powered: builtins.bool=..., position: builtins.float=..., is_moving: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['is_moving', b'is_moving', 'is_powered', b'is_powered', 'position', b'position']) -> None: + ... +global___Status = Status + +@typing.final +class IsMovingRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___IsMovingRequest = IsMovingRequest + +@typing.final +class IsMovingResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + IS_MOVING_FIELD_NUMBER: builtins.int + is_moving: builtins.bool + + def __init__(self, *, is_moving: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['is_moving', b'is_moving', 'is_powered', b'is_powered', 'position', b'position', 'position_reporting', b'position_reporting']) -> None: + def ClearField(self, field_name: typing.Literal['is_moving', b'is_moving']) -> None: ... -global___Status = Status \ No newline at end of file +global___IsMovingResponse = IsMovingResponse \ No newline at end of file diff --git a/src/viam/gen/component/movementsensor/v1/movementsensor_grpc.py b/src/viam/gen/component/movementsensor/v1/movementsensor_grpc.py index 9188c7f57..b2cd97189 100644 --- a/src/viam/gen/component/movementsensor/v1/movementsensor_grpc.py +++ b/src/viam/gen/component/movementsensor/v1/movementsensor_grpc.py @@ -2,10 +2,12 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common import google.api.annotations_pb2 +import google.protobuf.struct_pb2 from .... import component class MovementSensorServiceBase(abc.ABC): @@ -38,8 +40,59 @@ async def GetProperties(self, stream: 'grpclib.server.Stream[component.movements async def GetAccuracy(self, stream: 'grpclib.server.Stream[component.movementsensor.v1.movementsensor_pb2.GetAccuracyRequest, component.movementsensor.v1.movementsensor_pb2.GetAccuracyResponse]') -> None: pass + @abc.abstractmethod + async def GetLinearAcceleration(self, stream: 'grpclib.server.Stream[component.movementsensor.v1.movementsensor_pb2.GetLinearAccelerationRequest, component.movementsensor.v1.movementsensor_pb2.GetLinearAccelerationResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + + @abc.abstractmethod + async def GetReadings(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.movementsensor.v1.MovementSensorService/GetLinearVelocity': grpclib.const.Handler(self.GetLinearVelocity, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetLinearVelocityRequest, component.movementsensor.v1.movementsensor_pb2.GetLinearVelocityResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetAngularVelocity': grpclib.const.Handler(self.GetAngularVelocity, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetAngularVelocityRequest, component.movementsensor.v1.movementsensor_pb2.GetAngularVelocityResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetCompassHeading': grpclib.const.Handler(self.GetCompassHeading, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetCompassHeadingRequest, component.movementsensor.v1.movementsensor_pb2.GetCompassHeadingResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetOrientation': grpclib.const.Handler(self.GetOrientation, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetOrientationRequest, component.movementsensor.v1.movementsensor_pb2.GetOrientationResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetPositionRequest, component.movementsensor.v1.movementsensor_pb2.GetPositionResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetPropertiesRequest, component.movementsensor.v1.movementsensor_pb2.GetPropertiesResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetAccuracy': grpclib.const.Handler(self.GetAccuracy, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetAccuracyRequest, component.movementsensor.v1.movementsensor_pb2.GetAccuracyResponse)} + return {'/viam.component.movementsensor.v1.MovementSensorService/GetLinearVelocity': grpclib.const.Handler(self.GetLinearVelocity, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetLinearVelocityRequest, component.movementsensor.v1.movementsensor_pb2.GetLinearVelocityResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetAngularVelocity': grpclib.const.Handler(self.GetAngularVelocity, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetAngularVelocityRequest, component.movementsensor.v1.movementsensor_pb2.GetAngularVelocityResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetCompassHeading': grpclib.const.Handler(self.GetCompassHeading, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetCompassHeadingRequest, component.movementsensor.v1.movementsensor_pb2.GetCompassHeadingResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetOrientation': grpclib.const.Handler(self.GetOrientation, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetOrientationRequest, component.movementsensor.v1.movementsensor_pb2.GetOrientationResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetPositionRequest, component.movementsensor.v1.movementsensor_pb2.GetPositionResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetPropertiesRequest, component.movementsensor.v1.movementsensor_pb2.GetPropertiesResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetAccuracy': grpclib.const.Handler(self.GetAccuracy, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetAccuracyRequest, component.movementsensor.v1.movementsensor_pb2.GetAccuracyResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetLinearAcceleration': grpclib.const.Handler(self.GetLinearAcceleration, grpclib.const.Cardinality.UNARY_UNARY, component.movementsensor.v1.movementsensor_pb2.GetLinearAccelerationRequest, component.movementsensor.v1.movementsensor_pb2.GetLinearAccelerationResponse), '/viam.component.movementsensor.v1.MovementSensorService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse), '/viam.component.movementsensor.v1.MovementSensorService/GetReadings': grpclib.const.Handler(self.GetReadings, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse)} + +class UnimplementedMovementSensorServiceBase(MovementSensorServiceBase): + + async def GetLinearVelocity(self, stream: 'grpclib.server.Stream[component.movementsensor.v1.movementsensor_pb2.GetLinearVelocityRequest, component.movementsensor.v1.movementsensor_pb2.GetLinearVelocityResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetAngularVelocity(self, stream: 'grpclib.server.Stream[component.movementsensor.v1.movementsensor_pb2.GetAngularVelocityRequest, component.movementsensor.v1.movementsensor_pb2.GetAngularVelocityResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetCompassHeading(self, stream: 'grpclib.server.Stream[component.movementsensor.v1.movementsensor_pb2.GetCompassHeadingRequest, component.movementsensor.v1.movementsensor_pb2.GetCompassHeadingResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetOrientation(self, stream: 'grpclib.server.Stream[component.movementsensor.v1.movementsensor_pb2.GetOrientationRequest, component.movementsensor.v1.movementsensor_pb2.GetOrientationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPosition(self, stream: 'grpclib.server.Stream[component.movementsensor.v1.movementsensor_pb2.GetPositionRequest, component.movementsensor.v1.movementsensor_pb2.GetPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetProperties(self, stream: 'grpclib.server.Stream[component.movementsensor.v1.movementsensor_pb2.GetPropertiesRequest, component.movementsensor.v1.movementsensor_pb2.GetPropertiesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetAccuracy(self, stream: 'grpclib.server.Stream[component.movementsensor.v1.movementsensor_pb2.GetAccuracyRequest, component.movementsensor.v1.movementsensor_pb2.GetAccuracyResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetLinearAcceleration(self, stream: 'grpclib.server.Stream[component.movementsensor.v1.movementsensor_pb2.GetLinearAccelerationRequest, component.movementsensor.v1.movementsensor_pb2.GetLinearAccelerationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetReadings(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class MovementSensorServiceStub: @@ -50,4 +103,8 @@ def __init__(self, channel: grpclib.client.Channel) -> None: self.GetOrientation = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.movementsensor.v1.MovementSensorService/GetOrientation', component.movementsensor.v1.movementsensor_pb2.GetOrientationRequest, component.movementsensor.v1.movementsensor_pb2.GetOrientationResponse) self.GetPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.movementsensor.v1.MovementSensorService/GetPosition', component.movementsensor.v1.movementsensor_pb2.GetPositionRequest, component.movementsensor.v1.movementsensor_pb2.GetPositionResponse) self.GetProperties = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.movementsensor.v1.MovementSensorService/GetProperties', component.movementsensor.v1.movementsensor_pb2.GetPropertiesRequest, component.movementsensor.v1.movementsensor_pb2.GetPropertiesResponse) - self.GetAccuracy = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.movementsensor.v1.MovementSensorService/GetAccuracy', component.movementsensor.v1.movementsensor_pb2.GetAccuracyRequest, component.movementsensor.v1.movementsensor_pb2.GetAccuracyResponse) \ No newline at end of file + self.GetAccuracy = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.movementsensor.v1.MovementSensorService/GetAccuracy', component.movementsensor.v1.movementsensor_pb2.GetAccuracyRequest, component.movementsensor.v1.movementsensor_pb2.GetAccuracyResponse) + self.GetLinearAcceleration = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.movementsensor.v1.MovementSensorService/GetLinearAcceleration', component.movementsensor.v1.movementsensor_pb2.GetLinearAccelerationRequest, component.movementsensor.v1.movementsensor_pb2.GetLinearAccelerationResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.movementsensor.v1.MovementSensorService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.movementsensor.v1.MovementSensorService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) + self.GetReadings = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.movementsensor.v1.MovementSensorService/GetReadings', common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse) \ No newline at end of file diff --git a/src/viam/gen/component/movementsensor/v1/movementsensor_pb2.py b/src/viam/gen/component/movementsensor/v1/movementsensor_pb2.py index b851859dc..9b69ba69c 100644 --- a/src/viam/gen/component/movementsensor/v1/movementsensor_pb2.py +++ b/src/viam/gen/component/movementsensor/v1/movementsensor_pb2.py @@ -1,62 +1,78 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/movementsensor/v1/movementsensor.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n0component/movementsensor/v1/movementsensor.proto\x12 viam.component.movementsensor.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto".\n\x18GetLinearVelocityRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"]\n\x19GetLinearVelocityResponse\x12@\n\x0flinear_velocity\x18\x01 \x01(\x0b2\x17.viam.common.v1.Vector3R\x0elinearVelocity"/\n\x19GetAngularVelocityRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"`\n\x1aGetAngularVelocityResponse\x12B\n\x10angular_velocity\x18\x01 \x01(\x0b2\x17.viam.common.v1.Vector3R\x0fangularVelocity".\n\x18GetCompassHeadingRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"1\n\x19GetCompassHeadingResponse\x12\x14\n\x05value\x18\x01 \x01(\x01R\x05value"+\n\x15GetOrientationRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"W\n\x16GetOrientationResponse\x12=\n\x0borientation\x18\x01 \x01(\x0b2\x1b.viam.common.v1.OrientationR\x0borientation"(\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"p\n\x13GetPositionResponse\x128\n\ncoordinate\x18\x01 \x01(\x0b2\x18.viam.common.v1.GeoPointR\ncoordinate\x12\x1f\n\x0baltitude_mm\x18\x02 \x01(\x02R\naltitudeMm"*\n\x14GetPropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\xb1\x02\n\x15GetPropertiesResponse\x12:\n\x19linear_velocity_supported\x18\x01 \x01(\x08R\x17linearVelocitySupported\x12<\n\x1aangular_velocity_supported\x18\x02 \x01(\x08R\x18angularVelocitySupported\x123\n\x15orientation_supported\x18\x03 \x01(\x08R\x14orientationSupported\x12-\n\x12position_supported\x18\x04 \x01(\x08R\x11positionSupported\x12:\n\x19compass_heading_supported\x18\x05 \x01(\x08R\x17compassHeadingSupported"(\n\x12GetAccuracyRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\xbc\x01\n\x13GetAccuracyResponse\x12f\n\x0baccuracy_mm\x18\x01 \x03(\x0b2E.viam.component.movementsensor.v1.GetAccuracyResponse.AccuracyMmEntryR\naccuracyMm\x1a=\n\x0fAccuracyMmEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n\x05value\x18\x02 \x01(\x02R\x05value:\x028\x012\x9e\x0b\n\x15MovementSensorService\x12\xd2\x01\n\x11GetLinearVelocity\x12:.viam.component.movementsensor.v1.GetLinearVelocityRequest\x1a;.viam.component.movementsensor.v1.GetLinearVelocityResponse"D\x82\xd3\xe4\x93\x02>\x12\x12\x12\x12\x12\x12.viam.component.movementsensor.v1.GetLinearAccelerationRequest\x1a?.viam.component.movementsensor.v1.GetLinearAccelerationResponse"H\x82\xd3\xe4\x93\x02B\x12@/viam/api/v1/component/movementsensor/{name}/linear_acceleration\x12\x91\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"?\x82\xd3\xe4\x93\x029"7/viam/api/v1/component/movementsensor/{name}/do_command\x12\x9d\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"?\x82\xd3\xe4\x93\x029\x127/viam/api/v1/component/movementsensor/{name}/geometries\x12\x95\x01\n\x0bGetReadings\x12".viam.common.v1.GetReadingsRequest\x1a#.viam.common.v1.GetReadingsResponse"=\x82\xd3\xe4\x93\x027\x125/viam/api/v1/component/movementsensor/{name}/readingsBS\n$com.viam.component.movementsensor.v1Z+go.viam.com/api/component/movementsensor/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.movementsensor.v1.movementsensor_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n$com.viam.component.movementsensor.v1Z+go.viam.com/api/component/movementsensor/v1' + _globals['_GETACCURACYRESPONSE_ACCURACYENTRY']._loaded_options = None + _globals['_GETACCURACYRESPONSE_ACCURACYENTRY']._serialized_options = b'8\x01' + _globals['_MOVEMENTSENSORSERVICE'].methods_by_name['GetLinearVelocity']._loaded_options = None + _globals['_MOVEMENTSENSORSERVICE'].methods_by_name['GetLinearVelocity']._serialized_options = b'\x82\xd3\xe4\x93\x02>\x12\x12= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import google.protobuf.struct_pb2 +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class GetLinearVelocityRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a movement sensor' - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetLinearVelocityRequest = GetLinearVelocityRequest +@typing.final class GetLinearVelocityResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor LINEAR_VELOCITY_FIELD_NUMBER: builtins.int @property def linear_velocity(self) -> common.v1.common_pb2.Vector3: - """linear velocity contains velocity in mm/s across x/y/z axes""" + """Linear velocity in m/s across x/y/z axes""" def __init__(self, *, linear_velocity: common.v1.common_pb2.Vector3 | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['linear_velocity', b'linear_velocity']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['linear_velocity', b'linear_velocity']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['linear_velocity', b'linear_velocity']) -> None: + def ClearField(self, field_name: typing.Literal['linear_velocity', b'linear_velocity']) -> None: ... global___GetLinearVelocityResponse = GetLinearVelocityResponse +@typing.final class GetAngularVelocityRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a movement sensor' - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetAngularVelocityRequest = GetAngularVelocityRequest +@typing.final class GetAngularVelocityResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ANGULAR_VELOCITY_FIELD_NUMBER: builtins.int @property def angular_velocity(self) -> common.v1.common_pb2.Vector3: - """angular velocity contains velocity in degrees/s across x/y/z axes""" + """Angular velocity in degrees/s across x/y/z axes""" def __init__(self, *, angular_velocity: common.v1.common_pb2.Vector3 | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['angular_velocity', b'angular_velocity']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['angular_velocity', b'angular_velocity']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['angular_velocity', b'angular_velocity']) -> None: + def ClearField(self, field_name: typing.Literal['angular_velocity', b'angular_velocity']) -> None: ... global___GetAngularVelocityResponse = GetAngularVelocityResponse +@typing.final class GetCompassHeadingRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a movement sensor' - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetCompassHeadingRequest = GetCompassHeadingRequest +@typing.final class GetCompassHeadingResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor VALUE_FIELD_NUMBER: builtins.int value: builtins.float - 'A number from 0-359 where\n 0 is North, 90 is East, 180 is South, and 270 is West\n ' + 'A number from 0-359 in degrees where\n 0 is North, 90 is East, 180 is South, and 270 is West\n ' def __init__(self, *, value: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['value', b'value']) -> None: ... global___GetCompassHeadingResponse = GetCompassHeadingResponse +@typing.final class GetOrientationRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a movement sensor' - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetOrientationRequest = GetOrientationRequest +@typing.final class GetOrientationResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ORIENTATION_FIELD_NUMBER: builtins.int @property def orientation(self) -> common.v1.common_pb2.Orientation: - ... + """Orientation is returned as an orientation message with + OX OY OZ as unit-normalized components of the axis of the vector, and Theta in degrees + """ def __init__(self, *, orientation: common.v1.common_pb2.Orientation | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['orientation', b'orientation']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['orientation', b'orientation']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['orientation', b'orientation']) -> None: + def ClearField(self, field_name: typing.Literal['orientation', b'orientation']) -> None: ... global___GetOrientationResponse = GetOrientationResponse +@typing.final class GetPositionRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a movement sensor' - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetPositionRequest = GetPositionRequest +@typing.final class GetPositionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor COORDINATE_FIELD_NUMBER: builtins.int - ALTITUDE_MM_FIELD_NUMBER: builtins.int + ALTITUDE_M_FIELD_NUMBER: builtins.int + altitude_m: builtins.float @property def coordinate(self) -> common.v1.common_pb2.GeoPoint: - ... - altitude_mm: builtins.float + """Position is returned in a coordinate of latitute and longitude + and an altidue in meters + """ - def __init__(self, *, coordinate: common.v1.common_pb2.GeoPoint | None=..., altitude_mm: builtins.float=...) -> None: + def __init__(self, *, coordinate: common.v1.common_pb2.GeoPoint | None=..., altitude_m: builtins.float=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['coordinate', b'coordinate']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['coordinate', b'coordinate']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['altitude_mm', b'altitude_mm', 'coordinate', b'coordinate']) -> None: + def ClearField(self, field_name: typing.Literal['altitude_m', b'altitude_m', 'coordinate', b'coordinate']) -> None: ... global___GetPositionResponse = GetPositionResponse +@typing.final class GetPropertiesRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a movement sensor' - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetPropertiesRequest = GetPropertiesRequest +@typing.final class GetPropertiesResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor LINEAR_VELOCITY_SUPPORTED_FIELD_NUMBER: builtins.int @@ -187,36 +248,49 @@ class GetPropertiesResponse(google.protobuf.message.Message): ORIENTATION_SUPPORTED_FIELD_NUMBER: builtins.int POSITION_SUPPORTED_FIELD_NUMBER: builtins.int COMPASS_HEADING_SUPPORTED_FIELD_NUMBER: builtins.int + LINEAR_ACCELERATION_SUPPORTED_FIELD_NUMBER: builtins.int linear_velocity_supported: builtins.bool angular_velocity_supported: builtins.bool orientation_supported: builtins.bool position_supported: builtins.bool compass_heading_supported: builtins.bool + linear_acceleration_supported: builtins.bool - def __init__(self, *, linear_velocity_supported: builtins.bool=..., angular_velocity_supported: builtins.bool=..., orientation_supported: builtins.bool=..., position_supported: builtins.bool=..., compass_heading_supported: builtins.bool=...) -> None: + def __init__(self, *, linear_velocity_supported: builtins.bool=..., angular_velocity_supported: builtins.bool=..., orientation_supported: builtins.bool=..., position_supported: builtins.bool=..., compass_heading_supported: builtins.bool=..., linear_acceleration_supported: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['angular_velocity_supported', b'angular_velocity_supported', 'compass_heading_supported', b'compass_heading_supported', 'linear_velocity_supported', b'linear_velocity_supported', 'orientation_supported', b'orientation_supported', 'position_supported', b'position_supported']) -> None: + def ClearField(self, field_name: typing.Literal['angular_velocity_supported', b'angular_velocity_supported', 'compass_heading_supported', b'compass_heading_supported', 'linear_acceleration_supported', b'linear_acceleration_supported', 'linear_velocity_supported', b'linear_velocity_supported', 'orientation_supported', b'orientation_supported', 'position_supported', b'position_supported']) -> None: ... global___GetPropertiesResponse = GetPropertiesResponse +@typing.final class GetAccuracyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a movement sensor' - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetAccuracyRequest = GetAccuracyRequest +@typing.final class GetAccuracyResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - class AccuracyMmEntry(google.protobuf.message.Message): + @typing.final + class AccuracyEntry(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor KEY_FIELD_NUMBER: builtins.int VALUE_FIELD_NUMBER: builtins.int @@ -226,17 +300,85 @@ class GetAccuracyResponse(google.protobuf.message.Message): def __init__(self, *, key: builtins.str=..., value: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: ... - ACCURACY_MM_FIELD_NUMBER: builtins.int + ACCURACY_FIELD_NUMBER: builtins.int + POSITION_HDOP_FIELD_NUMBER: builtins.int + POSITION_VDOP_FIELD_NUMBER: builtins.int + POSITION_NMEA_GGA_FIX_FIELD_NUMBER: builtins.int + COMPASS_DEGREES_ERROR_FIELD_NUMBER: builtins.int + position_hdop: builtins.float + position_vdop: builtins.float + position_nmea_gga_fix: builtins.int + compass_degrees_error: builtins.float @property - def accuracy_mm(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.float]: + def accuracy(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.float]: + ... + + def __init__(self, *, accuracy: collections.abc.Mapping[builtins.str, builtins.float] | None=..., position_hdop: builtins.float | None=..., position_vdop: builtins.float | None=..., position_nmea_gga_fix: builtins.int | None=..., compass_degrees_error: builtins.float | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_compass_degrees_error', b'_compass_degrees_error', '_position_hdop', b'_position_hdop', '_position_nmea_gga_fix', b'_position_nmea_gga_fix', '_position_vdop', b'_position_vdop', 'compass_degrees_error', b'compass_degrees_error', 'position_hdop', b'position_hdop', 'position_nmea_gga_fix', b'position_nmea_gga_fix', 'position_vdop', b'position_vdop']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_compass_degrees_error', b'_compass_degrees_error', '_position_hdop', b'_position_hdop', '_position_nmea_gga_fix', b'_position_nmea_gga_fix', '_position_vdop', b'_position_vdop', 'accuracy', b'accuracy', 'compass_degrees_error', b'compass_degrees_error', 'position_hdop', b'position_hdop', 'position_nmea_gga_fix', b'position_nmea_gga_fix', 'position_vdop', b'position_vdop']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_compass_degrees_error', b'_compass_degrees_error']) -> typing.Literal['compass_degrees_error'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_position_hdop', b'_position_hdop']) -> typing.Literal['position_hdop'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_position_nmea_gga_fix', b'_position_nmea_gga_fix']) -> typing.Literal['position_nmea_gga_fix'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_position_vdop', b'_position_vdop']) -> typing.Literal['position_vdop'] | None: + ... +global___GetAccuracyResponse = GetAccuracyResponse + +@typing.final +class GetLinearAccelerationRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of a movement sensor' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetLinearAccelerationRequest = GetLinearAccelerationRequest + +@typing.final +class GetLinearAccelerationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LINEAR_ACCELERATION_FIELD_NUMBER: builtins.int + + @property + def linear_acceleration(self) -> common.v1.common_pb2.Vector3: + """Linear acceleration in m/s across x/y/z axes""" + + def __init__(self, *, linear_acceleration: common.v1.common_pb2.Vector3 | None=...) -> None: ... - def __init__(self, *, accuracy_mm: collections.abc.Mapping[builtins.str, builtins.float] | None=...) -> None: + def HasField(self, field_name: typing.Literal['linear_acceleration', b'linear_acceleration']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['accuracy_mm', b'accuracy_mm']) -> None: + def ClearField(self, field_name: typing.Literal['linear_acceleration', b'linear_acceleration']) -> None: ... -global___GetAccuracyResponse = GetAccuracyResponse \ No newline at end of file +global___GetLinearAccelerationResponse = GetLinearAccelerationResponse \ No newline at end of file diff --git a/src/viam/gen/component/posetracker/v1/pose_tracker_grpc.py b/src/viam/gen/component/posetracker/v1/pose_tracker_grpc.py index 0865a4a74..b02015369 100644 --- a/src/viam/gen/component/posetracker/v1/pose_tracker_grpc.py +++ b/src/viam/gen/component/posetracker/v1/pose_tracker_grpc.py @@ -2,10 +2,12 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common import google.api.annotations_pb2 +import google.protobuf.struct_pb2 from .... import component class PoseTrackerServiceBase(abc.ABC): @@ -14,10 +16,31 @@ class PoseTrackerServiceBase(abc.ABC): async def GetPoses(self, stream: 'grpclib.server.Stream[component.posetracker.v1.pose_tracker_pb2.GetPosesRequest, component.posetracker.v1.pose_tracker_pb2.GetPosesResponse]') -> None: pass + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.posetracker.v1.PoseTrackerService/GetPoses': grpclib.const.Handler(self.GetPoses, grpclib.const.Cardinality.UNARY_UNARY, component.posetracker.v1.pose_tracker_pb2.GetPosesRequest, component.posetracker.v1.pose_tracker_pb2.GetPosesResponse)} + return {'/viam.component.posetracker.v1.PoseTrackerService/GetPoses': grpclib.const.Handler(self.GetPoses, grpclib.const.Cardinality.UNARY_UNARY, component.posetracker.v1.pose_tracker_pb2.GetPosesRequest, component.posetracker.v1.pose_tracker_pb2.GetPosesResponse), '/viam.component.posetracker.v1.PoseTrackerService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.posetracker.v1.PoseTrackerService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedPoseTrackerServiceBase(PoseTrackerServiceBase): + + async def GetPoses(self, stream: 'grpclib.server.Stream[component.posetracker.v1.pose_tracker_pb2.GetPosesRequest, component.posetracker.v1.pose_tracker_pb2.GetPosesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class PoseTrackerServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: - self.GetPoses = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.posetracker.v1.PoseTrackerService/GetPoses', component.posetracker.v1.pose_tracker_pb2.GetPosesRequest, component.posetracker.v1.pose_tracker_pb2.GetPosesResponse) \ No newline at end of file + self.GetPoses = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.posetracker.v1.PoseTrackerService/GetPoses', component.posetracker.v1.pose_tracker_pb2.GetPosesRequest, component.posetracker.v1.pose_tracker_pb2.GetPosesResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.posetracker.v1.PoseTrackerService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.posetracker.v1.PoseTrackerService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/posetracker/v1/pose_tracker_pb2.py b/src/viam/gen/component/posetracker/v1/pose_tracker_pb2.py index 1b76977ab..fe089e8e6 100644 --- a/src/viam/gen/component/posetracker/v1/pose_tracker_pb2.py +++ b/src/viam/gen/component/posetracker/v1/pose_tracker_pb2.py @@ -1,26 +1,34 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/posetracker/v1/pose_tracker.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n+component/posetracker/v1/pose_tracker.proto\x12\x1dviam.component.posetracker.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto"D\n\x0fGetPosesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n\nbody_names\x18\x02 \x03(\tR\tbodyNames"\xcc\x01\n\x10GetPosesResponse\x12]\n\nbody_poses\x18\x01 \x03(\x0b2>.viam.component.posetracker.v1.GetPosesResponse.BodyPosesEntryR\tbodyPoses\x1aY\n\x0eBodyPosesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x121\n\x05value\x18\x02 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x05value:\x028\x012\xbc\x01\n\x12PoseTrackerService\x12\xa5\x01\n\x08GetPoses\x12..viam.component.posetracker.v1.GetPosesRequest\x1a/.viam.component.posetracker.v1.GetPosesResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/pose_tracker/{name}/posesB5\n\x15com.viam.component.v1Z\x1cgo.viam.com/api/component/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.posetracker.v1.pose_tracker_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x15com.viam.component.v1Z\x1cgo.viam.com/api/component/v1' - _GETPOSESRESPONSE_BODYPOSESENTRY._options = None - _GETPOSESRESPONSE_BODYPOSESENTRY._serialized_options = b'8\x01' - _POSETRACKERSERVICE.methods_by_name['GetPoses']._options = None - _POSETRACKERSERVICE.methods_by_name['GetPoses']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/pose_tracker/{name}/poses' - _GETPOSESREQUEST._serialized_start = 132 - _GETPOSESREQUEST._serialized_end = 200 - _GETPOSESRESPONSE._serialized_start = 203 - _GETPOSESRESPONSE._serialized_end = 407 - _GETPOSESRESPONSE_BODYPOSESENTRY._serialized_start = 318 - _GETPOSESRESPONSE_BODYPOSESENTRY._serialized_end = 407 - _POSETRACKERSERVICE._serialized_start = 410 - _POSETRACKERSERVICE._serialized_end = 598 \ No newline at end of file +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n+component/posetracker/v1/pose_tracker.proto\x12\x1dviam.component.posetracker.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"s\n\x0fGetPosesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n\nbody_names\x18\x02 \x03(\tR\tbodyNames\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\xcc\x01\n\x10GetPosesResponse\x12]\n\nbody_poses\x18\x01 \x03(\x0b2>.viam.component.posetracker.v1.GetPosesResponse.BodyPosesEntryR\tbodyPoses\x1aY\n\x0eBodyPosesEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x121\n\x05value\x18\x02 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x05value:\x028\x012\xec\x03\n\x12PoseTrackerService\x12\xa5\x01\n\x08GetPoses\x12..viam.component.posetracker.v1.GetPosesRequest\x1a/.viam.component.posetracker.v1.GetPosesResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/pose_tracker/{name}/poses\x12\x8f\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"=\x82\xd3\xe4\x93\x027"5/viam/api/v1/component/pose_tracker/{name}/do_command\x12\x9b\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"=\x82\xd3\xe4\x93\x027\x125/viam/api/v1/component/pose_tracker/{name}/geometriesB5\n\x15com.viam.component.v1Z\x1cgo.viam.com/api/component/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.posetracker.v1.pose_tracker_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x15com.viam.component.v1Z\x1cgo.viam.com/api/component/v1' + _globals['_GETPOSESRESPONSE_BODYPOSESENTRY']._loaded_options = None + _globals['_GETPOSESRESPONSE_BODYPOSESENTRY']._serialized_options = b'8\x01' + _globals['_POSETRACKERSERVICE'].methods_by_name['GetPoses']._loaded_options = None + _globals['_POSETRACKERSERVICE'].methods_by_name['GetPoses']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/pose_tracker/{name}/poses' + _globals['_POSETRACKERSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_POSETRACKERSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x027"5/viam/api/v1/component/pose_tracker/{name}/do_command' + _globals['_POSETRACKERSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_POSETRACKERSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x027\x125/viam/api/v1/component/pose_tracker/{name}/geometries' + _globals['_GETPOSESREQUEST']._serialized_start = 162 + _globals['_GETPOSESREQUEST']._serialized_end = 277 + _globals['_GETPOSESRESPONSE']._serialized_start = 280 + _globals['_GETPOSESRESPONSE']._serialized_end = 484 + _globals['_GETPOSESRESPONSE_BODYPOSESENTRY']._serialized_start = 395 + _globals['_GETPOSESRESPONSE_BODYPOSESENTRY']._serialized_end = 484 + _globals['_POSETRACKERSERVICE']._serialized_start = 487 + _globals['_POSETRACKERSERVICE']._serialized_end = 979 \ No newline at end of file diff --git a/src/viam/gen/component/posetracker/v1/pose_tracker_pb2.pyi b/src/viam/gen/component/posetracker/v1/pose_tracker_pb2.pyi index 93f505727..1ff7259f1 100644 --- a/src/viam/gen/component/posetracker/v1/pose_tracker_pb2.pyi +++ b/src/viam/gen/component/posetracker/v1/pose_tracker_pb2.pyi @@ -8,17 +8,16 @@ from .... import common import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.message -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import google.protobuf.struct_pb2 +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class GetPosesRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int BODY_NAMES_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of the pose tracker' @@ -29,16 +28,25 @@ class GetPosesRequest(google.protobuf.message.Message): poses are returned """ - def __init__(self, *, name: builtins.str=..., body_names: collections.abc.Iterable[builtins.str] | None=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., body_names: collections.abc.Iterable[builtins.str] | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['body_names', b'body_names', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['body_names', b'body_names', 'extra', b'extra', 'name', b'name']) -> None: ... global___GetPosesRequest = GetPosesRequest +@typing.final class GetPosesResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor + @typing.final class BodyPosesEntry(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor KEY_FIELD_NUMBER: builtins.int @@ -52,10 +60,10 @@ class GetPosesResponse(google.protobuf.message.Message): def __init__(self, *, key: builtins.str=..., value: common.v1.common_pb2.PoseInFrame | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['value', b'value']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: ... BODY_POSES_FIELD_NUMBER: builtins.int @@ -66,6 +74,6 @@ class GetPosesResponse(google.protobuf.message.Message): def __init__(self, *, body_poses: collections.abc.Mapping[builtins.str, common.v1.common_pb2.PoseInFrame] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['body_poses', b'body_poses']) -> None: + def ClearField(self, field_name: typing.Literal['body_poses', b'body_poses']) -> None: ... global___GetPosesResponse = GetPosesResponse \ No newline at end of file diff --git a/src/viam/gen/component/powersensor/__init__.py b/src/viam/gen/component/powersensor/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/component/powersensor/v1/__init__.py b/src/viam/gen/component/powersensor/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/component/powersensor/v1/powersensor_grpc.py b/src/viam/gen/component/powersensor/v1/powersensor_grpc.py new file mode 100644 index 000000000..147144f8d --- /dev/null +++ b/src/viam/gen/component/powersensor/v1/powersensor_grpc.py @@ -0,0 +1,62 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from .... import common +import google.api.annotations_pb2 +import google.protobuf.struct_pb2 +from .... import component + +class PowerSensorServiceBase(abc.ABC): + + @abc.abstractmethod + async def GetVoltage(self, stream: 'grpclib.server.Stream[component.powersensor.v1.powersensor_pb2.GetVoltageRequest, component.powersensor.v1.powersensor_pb2.GetVoltageResponse]') -> None: + pass + + @abc.abstractmethod + async def GetCurrent(self, stream: 'grpclib.server.Stream[component.powersensor.v1.powersensor_pb2.GetCurrentRequest, component.powersensor.v1.powersensor_pb2.GetCurrentResponse]') -> None: + pass + + @abc.abstractmethod + async def GetPower(self, stream: 'grpclib.server.Stream[component.powersensor.v1.powersensor_pb2.GetPowerRequest, component.powersensor.v1.powersensor_pb2.GetPowerResponse]') -> None: + pass + + @abc.abstractmethod + async def GetReadings(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.component.powersensor.v1.PowerSensorService/GetVoltage': grpclib.const.Handler(self.GetVoltage, grpclib.const.Cardinality.UNARY_UNARY, component.powersensor.v1.powersensor_pb2.GetVoltageRequest, component.powersensor.v1.powersensor_pb2.GetVoltageResponse), '/viam.component.powersensor.v1.PowerSensorService/GetCurrent': grpclib.const.Handler(self.GetCurrent, grpclib.const.Cardinality.UNARY_UNARY, component.powersensor.v1.powersensor_pb2.GetCurrentRequest, component.powersensor.v1.powersensor_pb2.GetCurrentResponse), '/viam.component.powersensor.v1.PowerSensorService/GetPower': grpclib.const.Handler(self.GetPower, grpclib.const.Cardinality.UNARY_UNARY, component.powersensor.v1.powersensor_pb2.GetPowerRequest, component.powersensor.v1.powersensor_pb2.GetPowerResponse), '/viam.component.powersensor.v1.PowerSensorService/GetReadings': grpclib.const.Handler(self.GetReadings, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse), '/viam.component.powersensor.v1.PowerSensorService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedPowerSensorServiceBase(PowerSensorServiceBase): + + async def GetVoltage(self, stream: 'grpclib.server.Stream[component.powersensor.v1.powersensor_pb2.GetVoltageRequest, component.powersensor.v1.powersensor_pb2.GetVoltageResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetCurrent(self, stream: 'grpclib.server.Stream[component.powersensor.v1.powersensor_pb2.GetCurrentRequest, component.powersensor.v1.powersensor_pb2.GetCurrentResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPower(self, stream: 'grpclib.server.Stream[component.powersensor.v1.powersensor_pb2.GetPowerRequest, component.powersensor.v1.powersensor_pb2.GetPowerResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetReadings(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class PowerSensorServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.GetVoltage = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.powersensor.v1.PowerSensorService/GetVoltage', component.powersensor.v1.powersensor_pb2.GetVoltageRequest, component.powersensor.v1.powersensor_pb2.GetVoltageResponse) + self.GetCurrent = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.powersensor.v1.PowerSensorService/GetCurrent', component.powersensor.v1.powersensor_pb2.GetCurrentRequest, component.powersensor.v1.powersensor_pb2.GetCurrentResponse) + self.GetPower = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.powersensor.v1.PowerSensorService/GetPower', component.powersensor.v1.powersensor_pb2.GetPowerRequest, component.powersensor.v1.powersensor_pb2.GetPowerResponse) + self.GetReadings = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.powersensor.v1.PowerSensorService/GetReadings', common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.powersensor.v1.PowerSensorService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/component/powersensor/v1/powersensor_pb2.py b/src/viam/gen/component/powersensor/v1/powersensor_pb2.py new file mode 100644 index 000000000..cef983f91 --- /dev/null +++ b/src/viam/gen/component/powersensor/v1/powersensor_pb2.py @@ -0,0 +1,42 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/powersensor/v1/powersensor.proto') +_sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n*component/powersensor/v1/powersensor.proto\x12\x1dviam.component.powersensor.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"V\n\x11GetVoltageRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"?\n\x12GetVoltageResponse\x12\x14\n\x05volts\x18\x01 \x01(\x01R\x05volts\x12\x13\n\x05is_ac\x18\x02 \x01(\x08R\x04isAc"V\n\x11GetCurrentRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"C\n\x12GetCurrentResponse\x12\x18\n\x07amperes\x18\x01 \x01(\x01R\x07amperes\x12\x13\n\x05is_ac\x18\x02 \x01(\x08R\x04isAc"T\n\x0fGetPowerRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"(\n\x10GetPowerResponse\x12\x14\n\x05watts\x18\x01 \x01(\x01R\x05watts2\xc4\x06\n\x12PowerSensorService\x12\xad\x01\n\nGetVoltage\x120.viam.component.powersensor.v1.GetVoltageRequest\x1a1.viam.component.powersensor.v1.GetVoltageResponse":\x82\xd3\xe4\x93\x024\x122/viam/api/v1/component/power_sensor/{name}/voltage\x12\xad\x01\n\nGetCurrent\x120.viam.component.powersensor.v1.GetCurrentRequest\x1a1.viam.component.powersensor.v1.GetCurrentResponse":\x82\xd3\xe4\x93\x024\x122/viam/api/v1/component/power_sensor/{name}/current\x12\xa5\x01\n\x08GetPower\x12..viam.component.powersensor.v1.GetPowerRequest\x1a/.viam.component.powersensor.v1.GetPowerResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/power_sensor/{name}/power\x12\x93\x01\n\x0bGetReadings\x12".viam.common.v1.GetReadingsRequest\x1a#.viam.common.v1.GetReadingsResponse";\x82\xd3\xe4\x93\x025\x123/viam/api/v1/component/power_sensor/{name}/readings\x12\x8f\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"=\x82\xd3\xe4\x93\x027"5/viam/api/v1/component/power_sensor/{name}/do_commandBM\n!com.viam.component.powersensor.v1Z(go.viam.com/api/component/powersensor/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.powersensor.v1.powersensor_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n!com.viam.component.powersensor.v1Z(go.viam.com/api/component/powersensor/v1' + _globals['_POWERSENSORSERVICE'].methods_by_name['GetVoltage']._loaded_options = None + _globals['_POWERSENSORSERVICE'].methods_by_name['GetVoltage']._serialized_options = b'\x82\xd3\xe4\x93\x024\x122/viam/api/v1/component/power_sensor/{name}/voltage' + _globals['_POWERSENSORSERVICE'].methods_by_name['GetCurrent']._loaded_options = None + _globals['_POWERSENSORSERVICE'].methods_by_name['GetCurrent']._serialized_options = b'\x82\xd3\xe4\x93\x024\x122/viam/api/v1/component/power_sensor/{name}/current' + _globals['_POWERSENSORSERVICE'].methods_by_name['GetPower']._loaded_options = None + _globals['_POWERSENSORSERVICE'].methods_by_name['GetPower']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/component/power_sensor/{name}/power' + _globals['_POWERSENSORSERVICE'].methods_by_name['GetReadings']._loaded_options = None + _globals['_POWERSENSORSERVICE'].methods_by_name['GetReadings']._serialized_options = b'\x82\xd3\xe4\x93\x025\x123/viam/api/v1/component/power_sensor/{name}/readings' + _globals['_POWERSENSORSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_POWERSENSORSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x027"5/viam/api/v1/component/power_sensor/{name}/do_command' + _globals['_GETVOLTAGEREQUEST']._serialized_start = 161 + _globals['_GETVOLTAGEREQUEST']._serialized_end = 247 + _globals['_GETVOLTAGERESPONSE']._serialized_start = 249 + _globals['_GETVOLTAGERESPONSE']._serialized_end = 312 + _globals['_GETCURRENTREQUEST']._serialized_start = 314 + _globals['_GETCURRENTREQUEST']._serialized_end = 400 + _globals['_GETCURRENTRESPONSE']._serialized_start = 402 + _globals['_GETCURRENTRESPONSE']._serialized_end = 469 + _globals['_GETPOWERREQUEST']._serialized_start = 471 + _globals['_GETPOWERREQUEST']._serialized_end = 555 + _globals['_GETPOWERRESPONSE']._serialized_start = 557 + _globals['_GETPOWERRESPONSE']._serialized_end = 597 + _globals['_POWERSENSORSERVICE']._serialized_start = 600 + _globals['_POWERSENSORSERVICE']._serialized_end = 1436 \ No newline at end of file diff --git a/src/viam/gen/component/powersensor/v1/powersensor_pb2.pyi b/src/viam/gen/component/powersensor/v1/powersensor_pb2.pyi new file mode 100644 index 000000000..d7fdb4669 --- /dev/null +++ b/src/viam/gen/component/powersensor/v1/powersensor_pb2.pyi @@ -0,0 +1,124 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import google.protobuf.struct_pb2 +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class GetVoltageRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of a power sensor' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetVoltageRequest = GetVoltageRequest + +@typing.final +class GetVoltageResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + VOLTS_FIELD_NUMBER: builtins.int + IS_AC_FIELD_NUMBER: builtins.int + volts: builtins.float + 'Voltage in volts' + is_ac: builtins.bool + 'Bool describing whether the voltage is DC or AC' + + def __init__(self, *, volts: builtins.float=..., is_ac: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['is_ac', b'is_ac', 'volts', b'volts']) -> None: + ... +global___GetVoltageResponse = GetVoltageResponse + +@typing.final +class GetCurrentRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of a power sensor' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetCurrentRequest = GetCurrentRequest + +@typing.final +class GetCurrentResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + AMPERES_FIELD_NUMBER: builtins.int + IS_AC_FIELD_NUMBER: builtins.int + amperes: builtins.float + 'Current in amperes' + is_ac: builtins.bool + 'Bool descibing whether the current is DC or AC' + + def __init__(self, *, amperes: builtins.float=..., is_ac: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['amperes', b'amperes', 'is_ac', b'is_ac']) -> None: + ... +global___GetCurrentResponse = GetCurrentResponse + +@typing.final +class GetPowerRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of a power sensor' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetPowerRequest = GetPowerRequest + +@typing.final +class GetPowerResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + WATTS_FIELD_NUMBER: builtins.int + watts: builtins.float + 'Power in watts' + + def __init__(self, *, watts: builtins.float=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['watts', b'watts']) -> None: + ... +global___GetPowerResponse = GetPowerResponse \ No newline at end of file diff --git a/src/viam/gen/component/sensor/v1/sensor_grpc.py b/src/viam/gen/component/sensor/v1/sensor_grpc.py index b7592eeba..18980fe41 100644 --- a/src/viam/gen/component/sensor/v1/sensor_grpc.py +++ b/src/viam/gen/component/sensor/v1/sensor_grpc.py @@ -2,22 +2,44 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import common import google.api.annotations_pb2 -import google.protobuf.struct_pb2 from .... import component class SensorServiceBase(abc.ABC): @abc.abstractmethod - async def GetReadings(self, stream: 'grpclib.server.Stream[component.sensor.v1.sensor_pb2.GetReadingsRequest, component.sensor.v1.sensor_pb2.GetReadingsResponse]') -> None: + async def GetReadings(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: pass def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.sensor.v1.SensorService/GetReadings': grpclib.const.Handler(self.GetReadings, grpclib.const.Cardinality.UNARY_UNARY, component.sensor.v1.sensor_pb2.GetReadingsRequest, component.sensor.v1.sensor_pb2.GetReadingsResponse)} + return {'/viam.component.sensor.v1.SensorService/GetReadings': grpclib.const.Handler(self.GetReadings, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse), '/viam.component.sensor.v1.SensorService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.sensor.v1.SensorService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedSensorServiceBase(SensorServiceBase): + + async def GetReadings(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class SensorServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: - self.GetReadings = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.sensor.v1.SensorService/GetReadings', component.sensor.v1.sensor_pb2.GetReadingsRequest, component.sensor.v1.sensor_pb2.GetReadingsResponse) \ No newline at end of file + self.GetReadings = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.sensor.v1.SensorService/GetReadings', common.v1.common_pb2.GetReadingsRequest, common.v1.common_pb2.GetReadingsResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.sensor.v1.SensorService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.sensor.v1.SensorService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/sensor/v1/sensor_pb2.py b/src/viam/gen/component/sensor/v1/sensor_pb2.py index 3837e70c9..d9236b72c 100644 --- a/src/viam/gen/component/sensor/v1/sensor_pb2.py +++ b/src/viam/gen/component/sensor/v1/sensor_pb2.py @@ -1,26 +1,25 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/sensor/v1/sensor.proto') _sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n component/sensor/v1/sensor.proto\x12\x18viam.component.sensor.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"(\n\x12GetReadingsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\xc3\x01\n\x13GetReadingsResponse\x12W\n\x08readings\x18\x01 \x03(\x0b2;.viam.component.sensor.v1.GetReadingsResponse.ReadingsEntryR\x08readings\x1aS\n\rReadingsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12,\n\x05value\x18\x02 \x01(\x0b2\x16.google.protobuf.ValueR\x05value:\x028\x012\xb3\x01\n\rSensorService\x12\xa1\x01\n\x0bGetReadings\x12,.viam.component.sensor.v1.GetReadingsRequest\x1a-.viam.component.sensor.v1.GetReadingsResponse"5\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/sensor/{name}/readingsBC\n\x1ccom.viam.component.sensor.v1Z#go.viam.com/api/component/sensor/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.sensor.v1.sensor_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1ccom.viam.component.sensor.v1Z#go.viam.com/api/component/sensor/v1' - _GETREADINGSRESPONSE_READINGSENTRY._options = None - _GETREADINGSRESPONSE_READINGSENTRY._serialized_options = b'8\x01' - _SENSORSERVICE.methods_by_name['GetReadings']._options = None - _SENSORSERVICE.methods_by_name['GetReadings']._serialized_options = b'\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/sensor/{name}/readings' - _GETREADINGSREQUEST._serialized_start = 122 - _GETREADINGSREQUEST._serialized_end = 162 - _GETREADINGSRESPONSE._serialized_start = 165 - _GETREADINGSRESPONSE._serialized_end = 360 - _GETREADINGSRESPONSE_READINGSENTRY._serialized_start = 277 - _GETREADINGSRESPONSE_READINGSENTRY._serialized_end = 360 - _SENSORSERVICE._serialized_start = 363 - _SENSORSERVICE._serialized_end = 542 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n component/sensor/v1/sensor.proto\x12\x18viam.component.sensor.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto2\xc3\x03\n\rSensorService\x12\x8d\x01\n\x0bGetReadings\x12".viam.common.v1.GetReadingsRequest\x1a#.viam.common.v1.GetReadingsResponse"5\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/sensor/{name}/readings\x12\x89\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"7\x82\xd3\xe4\x93\x021"//viam/api/v1/component/sensor/{name}/do_command\x12\x95\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/sensor/{name}/geometriesBC\n\x1ccom.viam.component.sensor.v1Z#go.viam.com/api/component/sensor/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.sensor.v1.sensor_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1ccom.viam.component.sensor.v1Z#go.viam.com/api/component/sensor/v1' + _globals['_SENSORSERVICE'].methods_by_name['GetReadings']._loaded_options = None + _globals['_SENSORSERVICE'].methods_by_name['GetReadings']._serialized_options = b'\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/sensor/{name}/readings' + _globals['_SENSORSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_SENSORSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x021"//viam/api/v1/component/sensor/{name}/do_command' + _globals['_SENSORSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_SENSORSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/component/sensor/{name}/geometries' + _globals['_SENSORSERVICE']._serialized_start = 117 + _globals['_SENSORSERVICE']._serialized_end = 568 \ No newline at end of file diff --git a/src/viam/gen/component/sensor/v1/sensor_pb2.pyi b/src/viam/gen/component/sensor/v1/sensor_pb2.pyi index 74b8f2e4c..ab0716881 100644 --- a/src/viam/gen/component/sensor/v1/sensor_pb2.pyi +++ b/src/viam/gen/component/sensor/v1/sensor_pb2.pyi @@ -2,61 +2,5 @@ @generated by mypy-protobuf. Do not edit manually! isort:skip_file """ -import builtins -import collections.abc import google.protobuf.descriptor -import google.protobuf.internal.containers -import google.protobuf.message -import google.protobuf.struct_pb2 -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class GetReadingsRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - name: builtins.str - - def __init__(self, *, name: builtins.str=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: - ... -global___GetReadingsRequest = GetReadingsRequest - -class GetReadingsResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - class ReadingsEntry(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - KEY_FIELD_NUMBER: builtins.int - VALUE_FIELD_NUMBER: builtins.int - key: builtins.str - - @property - def value(self) -> google.protobuf.struct_pb2.Value: - ... - - def __init__(self, *, key: builtins.str=..., value: google.protobuf.struct_pb2.Value | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: - ... - READINGS_FIELD_NUMBER: builtins.int - - @property - def readings(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, google.protobuf.struct_pb2.Value]: - ... - - def __init__(self, *, readings: collections.abc.Mapping[builtins.str, google.protobuf.struct_pb2.Value] | None=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['readings', b'readings']) -> None: - ... -global___GetReadingsResponse = GetReadingsResponse \ No newline at end of file +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor \ No newline at end of file diff --git a/src/viam/gen/component/servo/v1/servo_grpc.py b/src/viam/gen/component/servo/v1/servo_grpc.py index c11f1b896..f2cd1ad2a 100644 --- a/src/viam/gen/component/servo/v1/servo_grpc.py +++ b/src/viam/gen/component/servo/v1/servo_grpc.py @@ -2,9 +2,12 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import common import google.api.annotations_pb2 +import google.protobuf.struct_pb2 from .... import component class ServoServiceBase(abc.ABC): @@ -21,12 +24,47 @@ async def GetPosition(self, stream: 'grpclib.server.Stream[component.servo.v1.se async def Stop(self, stream: 'grpclib.server.Stream[component.servo.v1.servo_pb2.StopRequest, component.servo.v1.servo_pb2.StopResponse]') -> None: pass + @abc.abstractmethod + async def IsMoving(self, stream: 'grpclib.server.Stream[component.servo.v1.servo_pb2.IsMovingRequest, component.servo.v1.servo_pb2.IsMovingResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + @abc.abstractmethod + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.component.servo.v1.ServoService/Move': grpclib.const.Handler(self.Move, grpclib.const.Cardinality.UNARY_UNARY, component.servo.v1.servo_pb2.MoveRequest, component.servo.v1.servo_pb2.MoveResponse), '/viam.component.servo.v1.ServoService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.servo.v1.servo_pb2.GetPositionRequest, component.servo.v1.servo_pb2.GetPositionResponse), '/viam.component.servo.v1.ServoService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.servo.v1.servo_pb2.StopRequest, component.servo.v1.servo_pb2.StopResponse)} + return {'/viam.component.servo.v1.ServoService/Move': grpclib.const.Handler(self.Move, grpclib.const.Cardinality.UNARY_UNARY, component.servo.v1.servo_pb2.MoveRequest, component.servo.v1.servo_pb2.MoveResponse), '/viam.component.servo.v1.ServoService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.servo.v1.servo_pb2.GetPositionRequest, component.servo.v1.servo_pb2.GetPositionResponse), '/viam.component.servo.v1.ServoService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.servo.v1.servo_pb2.StopRequest, component.servo.v1.servo_pb2.StopResponse), '/viam.component.servo.v1.ServoService/IsMoving': grpclib.const.Handler(self.IsMoving, grpclib.const.Cardinality.UNARY_UNARY, component.servo.v1.servo_pb2.IsMovingRequest, component.servo.v1.servo_pb2.IsMovingResponse), '/viam.component.servo.v1.ServoService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse), '/viam.component.servo.v1.ServoService/GetGeometries': grpclib.const.Handler(self.GetGeometries, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse)} + +class UnimplementedServoServiceBase(ServoServiceBase): + + async def Move(self, stream: 'grpclib.server.Stream[component.servo.v1.servo_pb2.MoveRequest, component.servo.v1.servo_pb2.MoveResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPosition(self, stream: 'grpclib.server.Stream[component.servo.v1.servo_pb2.GetPositionRequest, component.servo.v1.servo_pb2.GetPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Stop(self, stream: 'grpclib.server.Stream[component.servo.v1.servo_pb2.StopRequest, component.servo.v1.servo_pb2.StopResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def IsMoving(self, stream: 'grpclib.server.Stream[component.servo.v1.servo_pb2.IsMovingRequest, component.servo.v1.servo_pb2.IsMovingResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetGeometries(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class ServoServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: self.Move = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.servo.v1.ServoService/Move', component.servo.v1.servo_pb2.MoveRequest, component.servo.v1.servo_pb2.MoveResponse) self.GetPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.servo.v1.ServoService/GetPosition', component.servo.v1.servo_pb2.GetPositionRequest, component.servo.v1.servo_pb2.GetPositionResponse) - self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.servo.v1.ServoService/Stop', component.servo.v1.servo_pb2.StopRequest, component.servo.v1.servo_pb2.StopResponse) \ No newline at end of file + self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.servo.v1.ServoService/Stop', component.servo.v1.servo_pb2.StopRequest, component.servo.v1.servo_pb2.StopResponse) + self.IsMoving = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.servo.v1.ServoService/IsMoving', component.servo.v1.servo_pb2.IsMovingRequest, component.servo.v1.servo_pb2.IsMovingResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.servo.v1.ServoService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) + self.GetGeometries = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.servo.v1.ServoService/GetGeometries', common.v1.common_pb2.GetGeometriesRequest, common.v1.common_pb2.GetGeometriesResponse) \ No newline at end of file diff --git a/src/viam/gen/component/servo/v1/servo_pb2.py b/src/viam/gen/component/servo/v1/servo_pb2.py index 6bb83c225..da9e9da06 100644 --- a/src/viam/gen/component/servo/v1/servo_pb2.py +++ b/src/viam/gen/component/servo/v1/servo_pb2.py @@ -1,35 +1,50 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/servo/v1/servo.proto') _sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ecomponent/servo/v1/servo.proto\x12\x17viam.component.servo.v1\x1a\x1cgoogle/api/annotations.proto">\n\x0bMoveRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tangle_deg\x18\x02 \x01(\rR\x08angleDeg"\x0e\n\x0cMoveResponse"(\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"8\n\x13GetPositionResponse\x12!\n\x0cposition_deg\x18\x01 \x01(\rR\x0bpositionDeg"!\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x0e\n\x0cStopResponse"H\n\x06Status\x12!\n\x0cposition_deg\x18\x01 \x01(\rR\x0bpositionDeg\x12\x1b\n\tis_moving\x18\x02 \x01(\x08R\x08isMoving2\xbf\x03\n\x0cServoService\x12\x85\x01\n\x04Move\x12$.viam.component.servo.v1.MoveRequest\x1a%.viam.component.servo.v1.MoveResponse"0\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/servo/{name}/move\x12\x9e\x01\n\x0bGetPosition\x12+.viam.component.servo.v1.GetPositionRequest\x1a,.viam.component.servo.v1.GetPositionResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/servo/{name}/position\x12\x85\x01\n\x04Stop\x12$.viam.component.servo.v1.StopRequest\x1a%.viam.component.servo.v1.StopResponse"0\x82\xd3\xe4\x93\x02*"(/viam/api/v1/component/servo/{name}/stopBA\n\x1bcom.viam.component.servo.v1Z"go.viam.com/api/component/servo/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.servo.v1.servo_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1bcom.viam.component.servo.v1Z"go.viam.com/api/component/servo/v1' - _SERVOSERVICE.methods_by_name['Move']._options = None - _SERVOSERVICE.methods_by_name['Move']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/servo/{name}/move' - _SERVOSERVICE.methods_by_name['GetPosition']._options = None - _SERVOSERVICE.methods_by_name['GetPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/servo/{name}/position' - _SERVOSERVICE.methods_by_name['Stop']._options = None - _SERVOSERVICE.methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02*"(/viam/api/v1/component/servo/{name}/stop' - _MOVEREQUEST._serialized_start = 89 - _MOVEREQUEST._serialized_end = 151 - _MOVERESPONSE._serialized_start = 153 - _MOVERESPONSE._serialized_end = 167 - _GETPOSITIONREQUEST._serialized_start = 169 - _GETPOSITIONREQUEST._serialized_end = 209 - _GETPOSITIONRESPONSE._serialized_start = 211 - _GETPOSITIONRESPONSE._serialized_end = 267 - _STOPREQUEST._serialized_start = 269 - _STOPREQUEST._serialized_end = 302 - _STOPRESPONSE._serialized_start = 304 - _STOPRESPONSE._serialized_end = 318 - _STATUS._serialized_start = 320 - _STATUS._serialized_end = 392 - _SERVOSERVICE._serialized_start = 395 - _SERVOSERVICE._serialized_end = 842 \ No newline at end of file +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1ecomponent/servo/v1/servo.proto\x12\x17viam.component.servo.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"m\n\x0bMoveRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tangle_deg\x18\x02 \x01(\rR\x08angleDeg\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cMoveResponse"W\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"8\n\x13GetPositionResponse\x12!\n\x0cposition_deg\x18\x01 \x01(\rR\x0bpositionDeg"P\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cStopResponse"H\n\x06Status\x12!\n\x0cposition_deg\x18\x01 \x01(\rR\x0bpositionDeg\x12\x1b\n\tis_moving\x18\x02 \x01(\x08R\x08isMoving"%\n\x0fIsMovingRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"/\n\x10IsMovingResponse\x12\x1b\n\tis_moving\x18\x01 \x01(\x08R\x08isMoving2\xfe\x06\n\x0cServoService\x12\x89\x01\n\x04Move\x12$.viam.component.servo.v1.MoveRequest\x1a%.viam.component.servo.v1.MoveResponse"4\xa0\x92)\x01\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/servo/{name}/move\x12\x9e\x01\n\x0bGetPosition\x12+.viam.component.servo.v1.GetPositionRequest\x1a,.viam.component.servo.v1.GetPositionResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/servo/{name}/position\x12\x85\x01\n\x04Stop\x12$.viam.component.servo.v1.StopRequest\x1a%.viam.component.servo.v1.StopResponse"0\x82\xd3\xe4\x93\x02*"(/viam/api/v1/component/servo/{name}/stop\x12\x96\x01\n\x08IsMoving\x12(.viam.component.servo.v1.IsMovingRequest\x1a).viam.component.servo.v1.IsMovingResponse"5\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/servo/{name}/is_moving\x12\x88\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"6\x82\xd3\xe4\x93\x020"./viam/api/v1/component/servo/{name}/do_command\x12\x94\x01\n\rGetGeometries\x12$.viam.common.v1.GetGeometriesRequest\x1a%.viam.common.v1.GetGeometriesResponse"6\x82\xd3\xe4\x93\x020\x12./viam/api/v1/component/servo/{name}/geometriesBA\n\x1bcom.viam.component.servo.v1Z"go.viam.com/api/component/servo/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.servo.v1.servo_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1bcom.viam.component.servo.v1Z"go.viam.com/api/component/servo/v1' + _globals['_SERVOSERVICE'].methods_by_name['Move']._loaded_options = None + _globals['_SERVOSERVICE'].methods_by_name['Move']._serialized_options = b'\xa0\x92)\x01\x82\xd3\xe4\x93\x02*\x1a(/viam/api/v1/component/servo/{name}/move' + _globals['_SERVOSERVICE'].methods_by_name['GetPosition']._loaded_options = None + _globals['_SERVOSERVICE'].methods_by_name['GetPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/component/servo/{name}/position' + _globals['_SERVOSERVICE'].methods_by_name['Stop']._loaded_options = None + _globals['_SERVOSERVICE'].methods_by_name['Stop']._serialized_options = b'\x82\xd3\xe4\x93\x02*"(/viam/api/v1/component/servo/{name}/stop' + _globals['_SERVOSERVICE'].methods_by_name['IsMoving']._loaded_options = None + _globals['_SERVOSERVICE'].methods_by_name['IsMoving']._serialized_options = b'\x82\xd3\xe4\x93\x02/\x12-/viam/api/v1/component/servo/{name}/is_moving' + _globals['_SERVOSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_SERVOSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x020"./viam/api/v1/component/servo/{name}/do_command' + _globals['_SERVOSERVICE'].methods_by_name['GetGeometries']._loaded_options = None + _globals['_SERVOSERVICE'].methods_by_name['GetGeometries']._serialized_options = b'\x82\xd3\xe4\x93\x020\x12./viam/api/v1/component/servo/{name}/geometries' + _globals['_MOVEREQUEST']._serialized_start = 143 + _globals['_MOVEREQUEST']._serialized_end = 252 + _globals['_MOVERESPONSE']._serialized_start = 254 + _globals['_MOVERESPONSE']._serialized_end = 268 + _globals['_GETPOSITIONREQUEST']._serialized_start = 270 + _globals['_GETPOSITIONREQUEST']._serialized_end = 357 + _globals['_GETPOSITIONRESPONSE']._serialized_start = 359 + _globals['_GETPOSITIONRESPONSE']._serialized_end = 415 + _globals['_STOPREQUEST']._serialized_start = 417 + _globals['_STOPREQUEST']._serialized_end = 497 + _globals['_STOPRESPONSE']._serialized_start = 499 + _globals['_STOPRESPONSE']._serialized_end = 513 + _globals['_STATUS']._serialized_start = 515 + _globals['_STATUS']._serialized_end = 587 + _globals['_ISMOVINGREQUEST']._serialized_start = 589 + _globals['_ISMOVINGREQUEST']._serialized_end = 626 + _globals['_ISMOVINGRESPONSE']._serialized_start = 628 + _globals['_ISMOVINGRESPONSE']._serialized_end = 675 + _globals['_SERVOSERVICE']._serialized_start = 678 + _globals['_SERVOSERVICE']._serialized_end = 1572 \ No newline at end of file diff --git a/src/viam/gen/component/servo/v1/servo_pb2.pyi b/src/viam/gen/component/servo/v1/servo_pb2.pyi index 5cd5df995..75602b1d0 100644 --- a/src/viam/gen/component/servo/v1/servo_pb2.pyi +++ b/src/viam/gen/component/servo/v1/servo_pb2.pyi @@ -5,29 +5,36 @@ isort:skip_file import builtins import google.protobuf.descriptor import google.protobuf.message -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import google.protobuf.struct_pb2 +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class MoveRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int ANGLE_DEG_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'the name of the servo, as registered' angle_deg: builtins.int - 'the degrees by which to rotate the servo. Accepted values are between 0 and 180' + 'the degrees by which to rotate the servo.' - def __init__(self, *, name: builtins.str=..., angle_deg: builtins.int=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., angle_deg: builtins.int=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['angle_deg', b'angle_deg', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['angle_deg', b'angle_deg', 'extra', b'extra', 'name', b'name']) -> None: ... global___MoveRequest = MoveRequest +@typing.final class MoveResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -35,45 +42,65 @@ class MoveResponse(google.protobuf.message.Message): ... global___MoveResponse = MoveResponse +@typing.final class GetPositionRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'the name of the servo, as registered' - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetPositionRequest = GetPositionRequest +@typing.final class GetPositionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSITION_DEG_FIELD_NUMBER: builtins.int position_deg: builtins.int - 'the degrees from neutral by which the servo is currently rotated. Values are between 0 and 180' + 'the degrees from neutral by which the servo is currently rotated.' def __init__(self, *, position_deg: builtins.int=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['position_deg', b'position_deg']) -> None: + def ClearField(self, field_name: typing.Literal['position_deg', b'position_deg']) -> None: ... global___GetPositionResponse = GetPositionResponse +@typing.final class StopRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'Name of a servo' - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___StopRequest = StopRequest +@typing.final class StopResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -81,6 +108,7 @@ class StopResponse(google.protobuf.message.Message): ... global___StopResponse = StopResponse +@typing.final class Status(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSITION_DEG_FIELD_NUMBER: builtins.int @@ -91,6 +119,32 @@ class Status(google.protobuf.message.Message): def __init__(self, *, position_deg: builtins.int=..., is_moving: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['is_moving', b'is_moving', 'position_deg', b'position_deg']) -> None: + def ClearField(self, field_name: typing.Literal['is_moving', b'is_moving', 'position_deg', b'position_deg']) -> None: + ... +global___Status = Status + +@typing.final +class IsMovingRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___IsMovingRequest = IsMovingRequest + +@typing.final +class IsMovingResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + IS_MOVING_FIELD_NUMBER: builtins.int + is_moving: builtins.bool + + def __init__(self, *, is_moving: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['is_moving', b'is_moving']) -> None: ... -global___Status = Status \ No newline at end of file +global___IsMovingResponse = IsMovingResponse \ No newline at end of file diff --git a/src/viam/gen/component/switch/__init__.py b/src/viam/gen/component/switch/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/component/switch/v1/__init__.py b/src/viam/gen/component/switch/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/component/switch/v1/switch_grpc.py b/src/viam/gen/component/switch/v1/switch_grpc.py new file mode 100644 index 000000000..499cd958d --- /dev/null +++ b/src/viam/gen/component/switch/v1/switch_grpc.py @@ -0,0 +1,54 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from .... import common +import google.api.annotations_pb2 +import google.protobuf.struct_pb2 +from .... import component + +class SwitchServiceBase(abc.ABC): + + @abc.abstractmethod + async def SetPosition(self, stream: 'grpclib.server.Stream[component.switch.v1.switch_pb2.SetPositionRequest, component.switch.v1.switch_pb2.SetPositionResponse]') -> None: + pass + + @abc.abstractmethod + async def GetPosition(self, stream: 'grpclib.server.Stream[component.switch.v1.switch_pb2.GetPositionRequest, component.switch.v1.switch_pb2.GetPositionResponse]') -> None: + pass + + @abc.abstractmethod + async def GetNumberOfPositions(self, stream: 'grpclib.server.Stream[component.switch.v1.switch_pb2.GetNumberOfPositionsRequest, component.switch.v1.switch_pb2.GetNumberOfPositionsResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.component.switch.v1.SwitchService/SetPosition': grpclib.const.Handler(self.SetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.switch.v1.switch_pb2.SetPositionRequest, component.switch.v1.switch_pb2.SetPositionResponse), '/viam.component.switch.v1.SwitchService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, component.switch.v1.switch_pb2.GetPositionRequest, component.switch.v1.switch_pb2.GetPositionResponse), '/viam.component.switch.v1.SwitchService/GetNumberOfPositions': grpclib.const.Handler(self.GetNumberOfPositions, grpclib.const.Cardinality.UNARY_UNARY, component.switch.v1.switch_pb2.GetNumberOfPositionsRequest, component.switch.v1.switch_pb2.GetNumberOfPositionsResponse), '/viam.component.switch.v1.SwitchService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedSwitchServiceBase(SwitchServiceBase): + + async def SetPosition(self, stream: 'grpclib.server.Stream[component.switch.v1.switch_pb2.SetPositionRequest, component.switch.v1.switch_pb2.SetPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPosition(self, stream: 'grpclib.server.Stream[component.switch.v1.switch_pb2.GetPositionRequest, component.switch.v1.switch_pb2.GetPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetNumberOfPositions(self, stream: 'grpclib.server.Stream[component.switch.v1.switch_pb2.GetNumberOfPositionsRequest, component.switch.v1.switch_pb2.GetNumberOfPositionsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class SwitchServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.SetPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.switch.v1.SwitchService/SetPosition', component.switch.v1.switch_pb2.SetPositionRequest, component.switch.v1.switch_pb2.SetPositionResponse) + self.GetPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.switch.v1.SwitchService/GetPosition', component.switch.v1.switch_pb2.GetPositionRequest, component.switch.v1.switch_pb2.GetPositionResponse) + self.GetNumberOfPositions = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.switch.v1.SwitchService/GetNumberOfPositions', component.switch.v1.switch_pb2.GetNumberOfPositionsRequest, component.switch.v1.switch_pb2.GetNumberOfPositionsResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.switch.v1.SwitchService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/component/switch/v1/switch_pb2.py b/src/viam/gen/component/switch/v1/switch_pb2.py new file mode 100644 index 000000000..c901dc32b --- /dev/null +++ b/src/viam/gen/component/switch/v1/switch_pb2.py @@ -0,0 +1,40 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/switch/v1/switch.proto') +_sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n component/switch/v1/switch.proto\x12\x18viam.component.switch.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"s\n\x12SetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1a\n\x08position\x18\x02 \x01(\rR\x08position\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x15\n\x13SetPositionResponse"W\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"1\n\x13GetPositionResponse\x12\x1a\n\x08position\x18\x01 \x01(\rR\x08position"`\n\x1bGetNumberOfPositionsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"N\n\x1cGetNumberOfPositionsResponse\x12.\n\x13number_of_positions\x18\x01 \x01(\rR\x11numberOfPositions2\xb9\x05\n\rSwitchService\x12\xa5\x01\n\x0bSetPosition\x12,.viam.component.switch.v1.SetPositionRequest\x1a-.viam.component.switch.v1.SetPositionResponse"9\x82\xd3\xe4\x93\x023\x1a1/viam/api/v1/component/switch/{name}/set_position\x12\xa5\x01\n\x0bGetPosition\x12,.viam.component.switch.v1.GetPositionRequest\x1a-.viam.component.switch.v1.GetPositionResponse"9\x82\xd3\xe4\x93\x023\x1a1/viam/api/v1/component/switch/{name}/get_position\x12\xcb\x01\n\x14GetNumberOfPositions\x125.viam.component.switch.v1.GetNumberOfPositionsRequest\x1a6.viam.component.switch.v1.GetNumberOfPositionsResponse"D\x82\xd3\xe4\x93\x02>\x1a\x1a google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., position: builtins.int=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'position', b'position']) -> None: + ... +global___SetPositionRequest = SetPositionRequest + +@typing.final +class SetPositionResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___SetPositionResponse = SetPositionResponse + +@typing.final +class GetPositionRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetPositionRequest = GetPositionRequest + +@typing.final +class GetPositionResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + POSITION_FIELD_NUMBER: builtins.int + position: builtins.int + + def __init__(self, *, position: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['position', b'position']) -> None: + ... +global___GetPositionResponse = GetPositionResponse + +@typing.final +class GetNumberOfPositionsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetNumberOfPositionsRequest = GetNumberOfPositionsRequest + +@typing.final +class GetNumberOfPositionsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NUMBER_OF_POSITIONS_FIELD_NUMBER: builtins.int + number_of_positions: builtins.int + + def __init__(self, *, number_of_positions: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['number_of_positions', b'number_of_positions']) -> None: + ... +global___GetNumberOfPositionsResponse = GetNumberOfPositionsResponse \ No newline at end of file diff --git a/src/viam/gen/component/testecho/__init__.py b/src/viam/gen/component/testecho/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/component/testecho/v1/__init__.py b/src/viam/gen/component/testecho/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/component/testecho/v1/testecho_grpc.py b/src/viam/gen/component/testecho/v1/testecho_grpc.py new file mode 100644 index 000000000..fa0051558 --- /dev/null +++ b/src/viam/gen/component/testecho/v1/testecho_grpc.py @@ -0,0 +1,52 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from .... import common +from .... import component + +class TestEchoServiceBase(abc.ABC): + + @abc.abstractmethod + async def Echo(self, stream: 'grpclib.server.Stream[component.testecho.v1.testecho_pb2.EchoRequest, component.testecho.v1.testecho_pb2.EchoResponse]') -> None: + pass + + @abc.abstractmethod + async def EchoMultiple(self, stream: 'grpclib.server.Stream[component.testecho.v1.testecho_pb2.EchoMultipleRequest, component.testecho.v1.testecho_pb2.EchoMultipleResponse]') -> None: + pass + + @abc.abstractmethod + async def EchoBiDi(self, stream: 'grpclib.server.Stream[component.testecho.v1.testecho_pb2.EchoBiDiRequest, component.testecho.v1.testecho_pb2.EchoBiDiResponse]') -> None: + pass + + @abc.abstractmethod + async def Stop(self, stream: 'grpclib.server.Stream[component.testecho.v1.testecho_pb2.StopRequest, component.testecho.v1.testecho_pb2.StopResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.component.testecho.v1.TestEchoService/Echo': grpclib.const.Handler(self.Echo, grpclib.const.Cardinality.UNARY_UNARY, component.testecho.v1.testecho_pb2.EchoRequest, component.testecho.v1.testecho_pb2.EchoResponse), '/viam.component.testecho.v1.TestEchoService/EchoMultiple': grpclib.const.Handler(self.EchoMultiple, grpclib.const.Cardinality.UNARY_STREAM, component.testecho.v1.testecho_pb2.EchoMultipleRequest, component.testecho.v1.testecho_pb2.EchoMultipleResponse), '/viam.component.testecho.v1.TestEchoService/EchoBiDi': grpclib.const.Handler(self.EchoBiDi, grpclib.const.Cardinality.STREAM_STREAM, component.testecho.v1.testecho_pb2.EchoBiDiRequest, component.testecho.v1.testecho_pb2.EchoBiDiResponse), '/viam.component.testecho.v1.TestEchoService/Stop': grpclib.const.Handler(self.Stop, grpclib.const.Cardinality.UNARY_UNARY, component.testecho.v1.testecho_pb2.StopRequest, component.testecho.v1.testecho_pb2.StopResponse)} + +class UnimplementedTestEchoServiceBase(TestEchoServiceBase): + + async def Echo(self, stream: 'grpclib.server.Stream[component.testecho.v1.testecho_pb2.EchoRequest, component.testecho.v1.testecho_pb2.EchoResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def EchoMultiple(self, stream: 'grpclib.server.Stream[component.testecho.v1.testecho_pb2.EchoMultipleRequest, component.testecho.v1.testecho_pb2.EchoMultipleResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def EchoBiDi(self, stream: 'grpclib.server.Stream[component.testecho.v1.testecho_pb2.EchoBiDiRequest, component.testecho.v1.testecho_pb2.EchoBiDiResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Stop(self, stream: 'grpclib.server.Stream[component.testecho.v1.testecho_pb2.StopRequest, component.testecho.v1.testecho_pb2.StopResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class TestEchoServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.Echo = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.testecho.v1.TestEchoService/Echo', component.testecho.v1.testecho_pb2.EchoRequest, component.testecho.v1.testecho_pb2.EchoResponse) + self.EchoMultiple = grpclib.client.UnaryStreamMethod(channel, '/viam.component.testecho.v1.TestEchoService/EchoMultiple', component.testecho.v1.testecho_pb2.EchoMultipleRequest, component.testecho.v1.testecho_pb2.EchoMultipleResponse) + self.EchoBiDi = grpclib.client.StreamStreamMethod(channel, '/viam.component.testecho.v1.TestEchoService/EchoBiDi', component.testecho.v1.testecho_pb2.EchoBiDiRequest, component.testecho.v1.testecho_pb2.EchoBiDiResponse) + self.Stop = grpclib.client.UnaryUnaryMethod(channel, '/viam.component.testecho.v1.TestEchoService/Stop', component.testecho.v1.testecho_pb2.StopRequest, component.testecho.v1.testecho_pb2.StopResponse) \ No newline at end of file diff --git a/src/viam/gen/component/testecho/v1/testecho_pb2.py b/src/viam/gen/component/testecho/v1/testecho_pb2.py new file mode 100644 index 000000000..5df37b3f8 --- /dev/null +++ b/src/viam/gen/component/testecho/v1/testecho_pb2.py @@ -0,0 +1,36 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'component/testecho/v1/testecho.proto') +_sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$component/testecho/v1/testecho.proto\x12\x1aviam.component.testecho.v1\x1a\x16common/v1/common.proto";\n\x0bEchoRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07message\x18\x02 \x01(\tR\x07message"(\n\x0cEchoResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message"C\n\x13EchoMultipleRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07message\x18\x02 \x01(\tR\x07message"0\n\x14EchoMultipleResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message"?\n\x0fEchoBiDiRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07message\x18\x02 \x01(\tR\x07message",\n\x10EchoBiDiResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message"!\n\x0bStopRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x0e\n\x0cStopResponse2\xb3\x03\n\x0fTestEchoService\x12[\n\x04Echo\x12\'.viam.component.testecho.v1.EchoRequest\x1a(.viam.component.testecho.v1.EchoResponse"\x00\x12y\n\x0cEchoMultiple\x12/.viam.component.testecho.v1.EchoMultipleRequest\x1a0.viam.component.testecho.v1.EchoMultipleResponse"\x04\xa0\x92)\x010\x01\x12k\n\x08EchoBiDi\x12+.viam.component.testecho.v1.EchoBiDiRequest\x1a,.viam.component.testecho.v1.EchoBiDiResponse"\x00(\x010\x01\x12[\n\x04Stop\x12\'.viam.component.testecho.v1.StopRequest\x1a(.viam.component.testecho.v1.StopResponse"\x00BG\n\x1ecom.viam.component.testecho.v1Z%go.viam.com/api/component/testecho/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'component.testecho.v1.testecho_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1ecom.viam.component.testecho.v1Z%go.viam.com/api/component/testecho/v1' + _globals['_TESTECHOSERVICE'].methods_by_name['EchoMultiple']._loaded_options = None + _globals['_TESTECHOSERVICE'].methods_by_name['EchoMultiple']._serialized_options = b'\xa0\x92)\x01' + _globals['_ECHOREQUEST']._serialized_start = 92 + _globals['_ECHOREQUEST']._serialized_end = 151 + _globals['_ECHORESPONSE']._serialized_start = 153 + _globals['_ECHORESPONSE']._serialized_end = 193 + _globals['_ECHOMULTIPLEREQUEST']._serialized_start = 195 + _globals['_ECHOMULTIPLEREQUEST']._serialized_end = 262 + _globals['_ECHOMULTIPLERESPONSE']._serialized_start = 264 + _globals['_ECHOMULTIPLERESPONSE']._serialized_end = 312 + _globals['_ECHOBIDIREQUEST']._serialized_start = 314 + _globals['_ECHOBIDIREQUEST']._serialized_end = 377 + _globals['_ECHOBIDIRESPONSE']._serialized_start = 379 + _globals['_ECHOBIDIRESPONSE']._serialized_end = 423 + _globals['_STOPREQUEST']._serialized_start = 425 + _globals['_STOPREQUEST']._serialized_end = 458 + _globals['_STOPRESPONSE']._serialized_start = 460 + _globals['_STOPRESPONSE']._serialized_end = 474 + _globals['_TESTECHOSERVICE']._serialized_start = 477 + _globals['_TESTECHOSERVICE']._serialized_end = 912 \ No newline at end of file diff --git a/src/viam/gen/component/testecho/v1/testecho_pb2.pyi b/src/viam/gen/component/testecho/v1/testecho_pb2.pyi new file mode 100644 index 000000000..976126808 --- /dev/null +++ b/src/viam/gen/component/testecho/v1/testecho_pb2.pyi @@ -0,0 +1,114 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class EchoRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + name: builtins.str + message: builtins.str + + def __init__(self, *, name: builtins.str=..., message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message', 'name', b'name']) -> None: + ... +global___EchoRequest = EchoRequest + +@typing.final +class EchoResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + + def __init__(self, *, message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: + ... +global___EchoResponse = EchoResponse + +@typing.final +class EchoMultipleRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + name: builtins.str + message: builtins.str + + def __init__(self, *, name: builtins.str=..., message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message', 'name', b'name']) -> None: + ... +global___EchoMultipleRequest = EchoMultipleRequest + +@typing.final +class EchoMultipleResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + + def __init__(self, *, message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: + ... +global___EchoMultipleResponse = EchoMultipleResponse + +@typing.final +class EchoBiDiRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + name: builtins.str + message: builtins.str + + def __init__(self, *, name: builtins.str=..., message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message', 'name', b'name']) -> None: + ... +global___EchoBiDiRequest = EchoBiDiRequest + +@typing.final +class EchoBiDiResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + + def __init__(self, *, message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: + ... +global___EchoBiDiResponse = EchoBiDiResponse + +@typing.final +class StopRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___StopRequest = StopRequest + +@typing.final +class StopResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___StopResponse = StopResponse \ No newline at end of file diff --git a/src/viam/gen/module/__init__.py b/src/viam/gen/module/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/module/v1/__init__.py b/src/viam/gen/module/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/module/v1/module_grpc.py b/src/viam/gen/module/v1/module_grpc.py new file mode 100644 index 000000000..8596368db --- /dev/null +++ b/src/viam/gen/module/v1/module_grpc.py @@ -0,0 +1,61 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from ... import app +from ... import robot +from ... import module + +class ModuleServiceBase(abc.ABC): + + @abc.abstractmethod + async def AddResource(self, stream: 'grpclib.server.Stream[module.v1.module_pb2.AddResourceRequest, module.v1.module_pb2.AddResourceResponse]') -> None: + pass + + @abc.abstractmethod + async def ReconfigureResource(self, stream: 'grpclib.server.Stream[module.v1.module_pb2.ReconfigureResourceRequest, module.v1.module_pb2.ReconfigureResourceResponse]') -> None: + pass + + @abc.abstractmethod + async def RemoveResource(self, stream: 'grpclib.server.Stream[module.v1.module_pb2.RemoveResourceRequest, module.v1.module_pb2.RemoveResourceResponse]') -> None: + pass + + @abc.abstractmethod + async def Ready(self, stream: 'grpclib.server.Stream[module.v1.module_pb2.ReadyRequest, module.v1.module_pb2.ReadyResponse]') -> None: + pass + + @abc.abstractmethod + async def ValidateConfig(self, stream: 'grpclib.server.Stream[module.v1.module_pb2.ValidateConfigRequest, module.v1.module_pb2.ValidateConfigResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.module.v1.ModuleService/AddResource': grpclib.const.Handler(self.AddResource, grpclib.const.Cardinality.UNARY_UNARY, module.v1.module_pb2.AddResourceRequest, module.v1.module_pb2.AddResourceResponse), '/viam.module.v1.ModuleService/ReconfigureResource': grpclib.const.Handler(self.ReconfigureResource, grpclib.const.Cardinality.UNARY_UNARY, module.v1.module_pb2.ReconfigureResourceRequest, module.v1.module_pb2.ReconfigureResourceResponse), '/viam.module.v1.ModuleService/RemoveResource': grpclib.const.Handler(self.RemoveResource, grpclib.const.Cardinality.UNARY_UNARY, module.v1.module_pb2.RemoveResourceRequest, module.v1.module_pb2.RemoveResourceResponse), '/viam.module.v1.ModuleService/Ready': grpclib.const.Handler(self.Ready, grpclib.const.Cardinality.UNARY_UNARY, module.v1.module_pb2.ReadyRequest, module.v1.module_pb2.ReadyResponse), '/viam.module.v1.ModuleService/ValidateConfig': grpclib.const.Handler(self.ValidateConfig, grpclib.const.Cardinality.UNARY_UNARY, module.v1.module_pb2.ValidateConfigRequest, module.v1.module_pb2.ValidateConfigResponse)} + +class UnimplementedModuleServiceBase(ModuleServiceBase): + + async def AddResource(self, stream: 'grpclib.server.Stream[module.v1.module_pb2.AddResourceRequest, module.v1.module_pb2.AddResourceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ReconfigureResource(self, stream: 'grpclib.server.Stream[module.v1.module_pb2.ReconfigureResourceRequest, module.v1.module_pb2.ReconfigureResourceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RemoveResource(self, stream: 'grpclib.server.Stream[module.v1.module_pb2.RemoveResourceRequest, module.v1.module_pb2.RemoveResourceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Ready(self, stream: 'grpclib.server.Stream[module.v1.module_pb2.ReadyRequest, module.v1.module_pb2.ReadyResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ValidateConfig(self, stream: 'grpclib.server.Stream[module.v1.module_pb2.ValidateConfigRequest, module.v1.module_pb2.ValidateConfigResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class ModuleServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.AddResource = grpclib.client.UnaryUnaryMethod(channel, '/viam.module.v1.ModuleService/AddResource', module.v1.module_pb2.AddResourceRequest, module.v1.module_pb2.AddResourceResponse) + self.ReconfigureResource = grpclib.client.UnaryUnaryMethod(channel, '/viam.module.v1.ModuleService/ReconfigureResource', module.v1.module_pb2.ReconfigureResourceRequest, module.v1.module_pb2.ReconfigureResourceResponse) + self.RemoveResource = grpclib.client.UnaryUnaryMethod(channel, '/viam.module.v1.ModuleService/RemoveResource', module.v1.module_pb2.RemoveResourceRequest, module.v1.module_pb2.RemoveResourceResponse) + self.Ready = grpclib.client.UnaryUnaryMethod(channel, '/viam.module.v1.ModuleService/Ready', module.v1.module_pb2.ReadyRequest, module.v1.module_pb2.ReadyResponse) + self.ValidateConfig = grpclib.client.UnaryUnaryMethod(channel, '/viam.module.v1.ModuleService/ValidateConfig', module.v1.module_pb2.ValidateConfigRequest, module.v1.module_pb2.ValidateConfigResponse) \ No newline at end of file diff --git a/src/viam/gen/module/v1/module_pb2.py b/src/viam/gen/module/v1/module_pb2.py new file mode 100644 index 000000000..c7b47b408 --- /dev/null +++ b/src/viam/gen/module/v1/module_pb2.py @@ -0,0 +1,43 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'module/v1/module.proto') +_sym_db = _symbol_database.Default() +from ...app.v1 import robot_pb2 as app_dot_v1_dot_robot__pb2 +from ...robot.v1 import robot_pb2 as robot_dot_v1_dot_robot__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16module/v1/module.proto\x12\x0eviam.module.v1\x1a\x12app/v1/robot.proto\x1a\x14robot/v1/robot.proto"n\n\x12AddResourceRequest\x124\n\x06config\x18\x01 \x01(\x0b2\x1c.viam.app.v1.ComponentConfigR\x06config\x12"\n\x0cdependencies\x18\x02 \x03(\tR\x0cdependencies"\x15\n\x13AddResourceResponse"v\n\x1aReconfigureResourceRequest\x124\n\x06config\x18\x01 \x01(\x0b2\x1c.viam.app.v1.ComponentConfigR\x06config\x12"\n\x0cdependencies\x18\x02 \x03(\tR\x0cdependencies"\x1d\n\x1bReconfigureResourceResponse"+\n\x15RemoveResourceRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x18\n\x16RemoveResourceResponse"h\n\x11HandlerDefinition\x12;\n\x07subtype\x18\x01 \x01(\x0b2!.viam.robot.v1.ResourceRPCSubtypeR\x07subtype\x12\x16\n\x06models\x18\x02 \x03(\tR\x06models"K\n\nHandlerMap\x12=\n\x08handlers\x18\x01 \x03(\x0b2!.viam.module.v1.HandlerDefinitionR\x08handlers"X\n\x0cReadyRequest\x12%\n\x0eparent_address\x18\x01 \x01(\tR\rparentAddress\x12!\n\x0cwebrtc_offer\x18\x02 \x01(\tR\x0bwebrtcOffer"\x86\x01\n\rReadyResponse\x12\x14\n\x05ready\x18\x01 \x01(\x08R\x05ready\x12:\n\nhandlermap\x18\x02 \x01(\x0b2\x1a.viam.module.v1.HandlerMapR\nhandlermap\x12#\n\rwebrtc_answer\x18\x03 \x01(\tR\x0cwebrtcAnswer"M\n\x15ValidateConfigRequest\x124\n\x06config\x18\x01 \x01(\x0b2\x1c.viam.app.v1.ComponentConfigR\x06config"<\n\x16ValidateConfigResponse\x12"\n\x0cdependencies\x18\x01 \x03(\tR\x0cdependencies2\xdf\x03\n\rModuleService\x12V\n\x0bAddResource\x12".viam.module.v1.AddResourceRequest\x1a#.viam.module.v1.AddResourceResponse\x12n\n\x13ReconfigureResource\x12*.viam.module.v1.ReconfigureResourceRequest\x1a+.viam.module.v1.ReconfigureResourceResponse\x12_\n\x0eRemoveResource\x12%.viam.module.v1.RemoveResourceRequest\x1a&.viam.module.v1.RemoveResourceResponse\x12D\n\x05Ready\x12\x1c.viam.module.v1.ReadyRequest\x1a\x1d.viam.module.v1.ReadyResponse\x12_\n\x0eValidateConfig\x12%.viam.module.v1.ValidateConfigRequest\x1a&.viam.module.v1.ValidateConfigResponseB\x1bZ\x19go.viam.com/api/module/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'module.v1.module_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x19go.viam.com/api/module/v1' + _globals['_ADDRESOURCEREQUEST']._serialized_start = 84 + _globals['_ADDRESOURCEREQUEST']._serialized_end = 194 + _globals['_ADDRESOURCERESPONSE']._serialized_start = 196 + _globals['_ADDRESOURCERESPONSE']._serialized_end = 217 + _globals['_RECONFIGURERESOURCEREQUEST']._serialized_start = 219 + _globals['_RECONFIGURERESOURCEREQUEST']._serialized_end = 337 + _globals['_RECONFIGURERESOURCERESPONSE']._serialized_start = 339 + _globals['_RECONFIGURERESOURCERESPONSE']._serialized_end = 368 + _globals['_REMOVERESOURCEREQUEST']._serialized_start = 370 + _globals['_REMOVERESOURCEREQUEST']._serialized_end = 413 + _globals['_REMOVERESOURCERESPONSE']._serialized_start = 415 + _globals['_REMOVERESOURCERESPONSE']._serialized_end = 439 + _globals['_HANDLERDEFINITION']._serialized_start = 441 + _globals['_HANDLERDEFINITION']._serialized_end = 545 + _globals['_HANDLERMAP']._serialized_start = 547 + _globals['_HANDLERMAP']._serialized_end = 622 + _globals['_READYREQUEST']._serialized_start = 624 + _globals['_READYREQUEST']._serialized_end = 712 + _globals['_READYRESPONSE']._serialized_start = 715 + _globals['_READYRESPONSE']._serialized_end = 849 + _globals['_VALIDATECONFIGREQUEST']._serialized_start = 851 + _globals['_VALIDATECONFIGREQUEST']._serialized_end = 928 + _globals['_VALIDATECONFIGRESPONSE']._serialized_start = 930 + _globals['_VALIDATECONFIGRESPONSE']._serialized_end = 990 + _globals['_MODULESERVICE']._serialized_start = 993 + _globals['_MODULESERVICE']._serialized_end = 1472 \ No newline at end of file diff --git a/src/viam/gen/module/v1/module_pb2.pyi b/src/viam/gen/module/v1/module_pb2.pyi new file mode 100644 index 000000000..543cc8ec2 --- /dev/null +++ b/src/viam/gen/module/v1/module_pb2.pyi @@ -0,0 +1,211 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +from ... import app +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +from ... import robot +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class AddResourceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CONFIG_FIELD_NUMBER: builtins.int + DEPENDENCIES_FIELD_NUMBER: builtins.int + + @property + def config(self) -> app.v1.robot_pb2.ComponentConfig: + ... + + @property + def dependencies(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, config: app.v1.robot_pb2.ComponentConfig | None=..., dependencies: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['config', b'config']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['config', b'config', 'dependencies', b'dependencies']) -> None: + ... +global___AddResourceRequest = AddResourceRequest + +@typing.final +class AddResourceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___AddResourceResponse = AddResourceResponse + +@typing.final +class ReconfigureResourceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CONFIG_FIELD_NUMBER: builtins.int + DEPENDENCIES_FIELD_NUMBER: builtins.int + + @property + def config(self) -> app.v1.robot_pb2.ComponentConfig: + ... + + @property + def dependencies(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, config: app.v1.robot_pb2.ComponentConfig | None=..., dependencies: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['config', b'config']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['config', b'config', 'dependencies', b'dependencies']) -> None: + ... +global___ReconfigureResourceRequest = ReconfigureResourceRequest + +@typing.final +class ReconfigureResourceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___ReconfigureResourceResponse = ReconfigureResourceResponse + +@typing.final +class RemoveResourceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___RemoveResourceRequest = RemoveResourceRequest + +@typing.final +class RemoveResourceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___RemoveResourceResponse = RemoveResourceResponse + +@typing.final +class HandlerDefinition(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SUBTYPE_FIELD_NUMBER: builtins.int + MODELS_FIELD_NUMBER: builtins.int + + @property + def subtype(self) -> robot.v1.robot_pb2.ResourceRPCSubtype: + ... + + @property + def models(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, subtype: robot.v1.robot_pb2.ResourceRPCSubtype | None=..., models: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['subtype', b'subtype']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['models', b'models', 'subtype', b'subtype']) -> None: + ... +global___HandlerDefinition = HandlerDefinition + +@typing.final +class HandlerMap(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + HANDLERS_FIELD_NUMBER: builtins.int + + @property + def handlers(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___HandlerDefinition]: + ... + + def __init__(self, *, handlers: collections.abc.Iterable[global___HandlerDefinition] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['handlers', b'handlers']) -> None: + ... +global___HandlerMap = HandlerMap + +@typing.final +class ReadyRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PARENT_ADDRESS_FIELD_NUMBER: builtins.int + WEBRTC_OFFER_FIELD_NUMBER: builtins.int + parent_address: builtins.str + webrtc_offer: builtins.str + + def __init__(self, *, parent_address: builtins.str=..., webrtc_offer: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['parent_address', b'parent_address', 'webrtc_offer', b'webrtc_offer']) -> None: + ... +global___ReadyRequest = ReadyRequest + +@typing.final +class ReadyResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + READY_FIELD_NUMBER: builtins.int + HANDLERMAP_FIELD_NUMBER: builtins.int + WEBRTC_ANSWER_FIELD_NUMBER: builtins.int + ready: builtins.bool + webrtc_answer: builtins.str + + @property + def handlermap(self) -> global___HandlerMap: + ... + + def __init__(self, *, ready: builtins.bool=..., handlermap: global___HandlerMap | None=..., webrtc_answer: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['handlermap', b'handlermap']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['handlermap', b'handlermap', 'ready', b'ready', 'webrtc_answer', b'webrtc_answer']) -> None: + ... +global___ReadyResponse = ReadyResponse + +@typing.final +class ValidateConfigRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CONFIG_FIELD_NUMBER: builtins.int + + @property + def config(self) -> app.v1.robot_pb2.ComponentConfig: + ... + + def __init__(self, *, config: app.v1.robot_pb2.ComponentConfig | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['config', b'config']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['config', b'config']) -> None: + ... +global___ValidateConfigRequest = ValidateConfigRequest + +@typing.final +class ValidateConfigResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DEPENDENCIES_FIELD_NUMBER: builtins.int + + @property + def dependencies(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, dependencies: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['dependencies', b'dependencies']) -> None: + ... +global___ValidateConfigResponse = ValidateConfigResponse \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/examples/echo/v1/echo_grpc.py b/src/viam/gen/proto/rpc/examples/echo/v1/echo_grpc.py index c2499be29..0b6c22512 100644 --- a/src/viam/gen/proto/rpc/examples/echo/v1/echo_grpc.py +++ b/src/viam/gen/proto/rpc/examples/echo/v1/echo_grpc.py @@ -2,6 +2,7 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server import google.api.annotations_pb2 @@ -24,6 +25,17 @@ async def EchoBiDi(self, stream: 'grpclib.server.Stream[proto.rpc.examples.echo. def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: return {'/proto.rpc.examples.echo.v1.EchoService/Echo': grpclib.const.Handler(self.Echo, grpclib.const.Cardinality.UNARY_UNARY, proto.rpc.examples.echo.v1.echo_pb2.EchoRequest, proto.rpc.examples.echo.v1.echo_pb2.EchoResponse), '/proto.rpc.examples.echo.v1.EchoService/EchoMultiple': grpclib.const.Handler(self.EchoMultiple, grpclib.const.Cardinality.UNARY_STREAM, proto.rpc.examples.echo.v1.echo_pb2.EchoMultipleRequest, proto.rpc.examples.echo.v1.echo_pb2.EchoMultipleResponse), '/proto.rpc.examples.echo.v1.EchoService/EchoBiDi': grpclib.const.Handler(self.EchoBiDi, grpclib.const.Cardinality.STREAM_STREAM, proto.rpc.examples.echo.v1.echo_pb2.EchoBiDiRequest, proto.rpc.examples.echo.v1.echo_pb2.EchoBiDiResponse)} +class UnimplementedEchoServiceBase(EchoServiceBase): + + async def Echo(self, stream: 'grpclib.server.Stream[proto.rpc.examples.echo.v1.echo_pb2.EchoRequest, proto.rpc.examples.echo.v1.echo_pb2.EchoResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def EchoMultiple(self, stream: 'grpclib.server.Stream[proto.rpc.examples.echo.v1.echo_pb2.EchoMultipleRequest, proto.rpc.examples.echo.v1.echo_pb2.EchoMultipleResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def EchoBiDi(self, stream: 'grpclib.server.Stream[proto.rpc.examples.echo.v1.echo_pb2.EchoBiDiRequest, proto.rpc.examples.echo.v1.echo_pb2.EchoBiDiResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + class EchoServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: diff --git a/src/viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py b/src/viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py index 814a1b6e3..f98a31cca 100644 --- a/src/viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py +++ b/src/viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py @@ -1,29 +1,32 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'proto/rpc/examples/echo/v1/echo.proto') _sym_db = _symbol_database.Default() from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%proto/rpc/examples/echo/v1/echo.proto\x12\x1aproto.rpc.examples.echo.v1\x1a\x1cgoogle/api/annotations.proto"\'\n\x0bEchoRequest\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message"(\n\x0cEchoResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message"/\n\x13EchoMultipleRequest\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message"0\n\x14EchoMultipleResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message"+\n\x0fEchoBiDiRequest\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message",\n\x10EchoBiDiResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message2\xf4\x02\n\x0bEchoService\x12\x80\x01\n\x04Echo\x12\'.proto.rpc.examples.echo.v1.EchoRequest\x1a(.proto.rpc.examples.echo.v1.EchoResponse"%\x82\xd3\xe4\x93\x02\x1f"\x1a/rpc/examples/echo/v1/echo:\x01*\x12u\n\x0cEchoMultiple\x12/.proto.rpc.examples.echo.v1.EchoMultipleRequest\x1a0.proto.rpc.examples.echo.v1.EchoMultipleResponse"\x000\x01\x12k\n\x08EchoBiDi\x12+.proto.rpc.examples.echo.v1.EchoBiDiRequest\x1a,.proto.rpc.examples.echo.v1.EchoBiDiResponse"\x00(\x010\x01B.Z,go.viam.com/utils/proto/rpc/examples/echo/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.rpc.examples.echo.v1.echo_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z,go.viam.com/utils/proto/rpc/examples/echo/v1' - _ECHOSERVICE.methods_by_name['Echo']._options = None - _ECHOSERVICE.methods_by_name['Echo']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1f"\x1a/rpc/examples/echo/v1/echo:\x01*' - _ECHOREQUEST._serialized_start = 99 - _ECHOREQUEST._serialized_end = 138 - _ECHORESPONSE._serialized_start = 140 - _ECHORESPONSE._serialized_end = 180 - _ECHOMULTIPLEREQUEST._serialized_start = 182 - _ECHOMULTIPLEREQUEST._serialized_end = 229 - _ECHOMULTIPLERESPONSE._serialized_start = 231 - _ECHOMULTIPLERESPONSE._serialized_end = 279 - _ECHOBIDIREQUEST._serialized_start = 281 - _ECHOBIDIREQUEST._serialized_end = 324 - _ECHOBIDIRESPONSE._serialized_start = 326 - _ECHOBIDIRESPONSE._serialized_end = 370 - _ECHOSERVICE._serialized_start = 373 - _ECHOSERVICE._serialized_end = 745 \ No newline at end of file +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.rpc.examples.echo.v1.echo_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z,go.viam.com/utils/proto/rpc/examples/echo/v1' + _globals['_ECHOSERVICE'].methods_by_name['Echo']._loaded_options = None + _globals['_ECHOSERVICE'].methods_by_name['Echo']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1f"\x1a/rpc/examples/echo/v1/echo:\x01*' + _globals['_ECHOREQUEST']._serialized_start = 99 + _globals['_ECHOREQUEST']._serialized_end = 138 + _globals['_ECHORESPONSE']._serialized_start = 140 + _globals['_ECHORESPONSE']._serialized_end = 180 + _globals['_ECHOMULTIPLEREQUEST']._serialized_start = 182 + _globals['_ECHOMULTIPLEREQUEST']._serialized_end = 229 + _globals['_ECHOMULTIPLERESPONSE']._serialized_start = 231 + _globals['_ECHOMULTIPLERESPONSE']._serialized_end = 279 + _globals['_ECHOBIDIREQUEST']._serialized_start = 281 + _globals['_ECHOBIDIREQUEST']._serialized_end = 324 + _globals['_ECHOBIDIRESPONSE']._serialized_start = 326 + _globals['_ECHOBIDIRESPONSE']._serialized_end = 370 + _globals['_ECHOSERVICE']._serialized_start = 373 + _globals['_ECHOSERVICE']._serialized_end = 745 \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/examples/echo/v1/echo_pb2.pyi b/src/viam/gen/proto/rpc/examples/echo/v1/echo_pb2.pyi index 7e99af2cd..a53728a86 100644 --- a/src/viam/gen/proto/rpc/examples/echo/v1/echo_pb2.pyi +++ b/src/viam/gen/proto/rpc/examples/echo/v1/echo_pb2.pyi @@ -5,13 +5,10 @@ isort:skip_file import builtins import google.protobuf.descriptor import google.protobuf.message -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class EchoRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MESSAGE_FIELD_NUMBER: builtins.int @@ -20,10 +17,11 @@ class EchoRequest(google.protobuf.message.Message): def __init__(self, *, message: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['message', b'message']) -> None: + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: ... global___EchoRequest = EchoRequest +@typing.final class EchoResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MESSAGE_FIELD_NUMBER: builtins.int @@ -32,10 +30,11 @@ class EchoResponse(google.protobuf.message.Message): def __init__(self, *, message: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['message', b'message']) -> None: + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: ... global___EchoResponse = EchoResponse +@typing.final class EchoMultipleRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MESSAGE_FIELD_NUMBER: builtins.int @@ -44,10 +43,11 @@ class EchoMultipleRequest(google.protobuf.message.Message): def __init__(self, *, message: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['message', b'message']) -> None: + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: ... global___EchoMultipleRequest = EchoMultipleRequest +@typing.final class EchoMultipleResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MESSAGE_FIELD_NUMBER: builtins.int @@ -56,10 +56,11 @@ class EchoMultipleResponse(google.protobuf.message.Message): def __init__(self, *, message: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['message', b'message']) -> None: + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: ... global___EchoMultipleResponse = EchoMultipleResponse +@typing.final class EchoBiDiRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MESSAGE_FIELD_NUMBER: builtins.int @@ -68,10 +69,11 @@ class EchoBiDiRequest(google.protobuf.message.Message): def __init__(self, *, message: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['message', b'message']) -> None: + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: ... global___EchoBiDiRequest = EchoBiDiRequest +@typing.final class EchoBiDiResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MESSAGE_FIELD_NUMBER: builtins.int @@ -80,6 +82,6 @@ class EchoBiDiResponse(google.protobuf.message.Message): def __init__(self, *, message: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['message', b'message']) -> None: + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: ... global___EchoBiDiResponse = EchoBiDiResponse \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/examples/echoresource/__init__.py b/src/viam/gen/proto/rpc/examples/echoresource/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/proto/rpc/examples/echoresource/v1/__init__.py b/src/viam/gen/proto/rpc/examples/echoresource/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/proto/rpc/examples/echoresource/v1/echoresource_grpc.py b/src/viam/gen/proto/rpc/examples/echoresource/v1/echoresource_grpc.py new file mode 100644 index 000000000..61e02a849 --- /dev/null +++ b/src/viam/gen/proto/rpc/examples/echoresource/v1/echoresource_grpc.py @@ -0,0 +1,43 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from ...... import proto + +class EchoResourceServiceBase(abc.ABC): + + @abc.abstractmethod + async def EchoResource(self, stream: 'grpclib.server.Stream[proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceResponse]') -> None: + pass + + @abc.abstractmethod + async def EchoResourceMultiple(self, stream: 'grpclib.server.Stream[proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceMultipleRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceMultipleResponse]') -> None: + pass + + @abc.abstractmethod + async def EchoResourceBiDi(self, stream: 'grpclib.server.Stream[proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceBiDiRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceBiDiResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/proto.rpc.examples.echoresource.v1.EchoResourceService/EchoResource': grpclib.const.Handler(self.EchoResource, grpclib.const.Cardinality.UNARY_UNARY, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceResponse), '/proto.rpc.examples.echoresource.v1.EchoResourceService/EchoResourceMultiple': grpclib.const.Handler(self.EchoResourceMultiple, grpclib.const.Cardinality.UNARY_STREAM, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceMultipleRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceMultipleResponse), '/proto.rpc.examples.echoresource.v1.EchoResourceService/EchoResourceBiDi': grpclib.const.Handler(self.EchoResourceBiDi, grpclib.const.Cardinality.STREAM_STREAM, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceBiDiRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceBiDiResponse)} + +class UnimplementedEchoResourceServiceBase(EchoResourceServiceBase): + + async def EchoResource(self, stream: 'grpclib.server.Stream[proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def EchoResourceMultiple(self, stream: 'grpclib.server.Stream[proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceMultipleRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceMultipleResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def EchoResourceBiDi(self, stream: 'grpclib.server.Stream[proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceBiDiRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceBiDiResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class EchoResourceServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.EchoResource = grpclib.client.UnaryUnaryMethod(channel, '/proto.rpc.examples.echoresource.v1.EchoResourceService/EchoResource', proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceResponse) + self.EchoResourceMultiple = grpclib.client.UnaryStreamMethod(channel, '/proto.rpc.examples.echoresource.v1.EchoResourceService/EchoResourceMultiple', proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceMultipleRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceMultipleResponse) + self.EchoResourceBiDi = grpclib.client.StreamStreamMethod(channel, '/proto.rpc.examples.echoresource.v1.EchoResourceService/EchoResourceBiDi', proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceBiDiRequest, proto.rpc.examples.echoresource.v1.echoresource_pb2.EchoResourceBiDiResponse) \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.py b/src/viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.py new file mode 100644 index 000000000..ecbe78a70 --- /dev/null +++ b/src/viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.py @@ -0,0 +1,29 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'proto/rpc/examples/echoresource/v1/echoresource.proto') +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n5proto/rpc/examples/echoresource/v1/echoresource.proto\x12"proto.rpc.examples.echoresource.v1"C\n\x13EchoResourceRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07message\x18\x02 \x01(\tR\x07message"0\n\x14EchoResourceResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message"K\n\x1bEchoResourceMultipleRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07message\x18\x02 \x01(\tR\x07message"8\n\x1cEchoResourceMultipleResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message"G\n\x17EchoResourceBiDiRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07message\x18\x02 \x01(\tR\x07message"4\n\x18EchoResourceBiDiResponse\x12\x18\n\x07message\x18\x01 \x01(\tR\x07message2\xd1\x03\n\x13EchoResourceService\x12\x83\x01\n\x0cEchoResource\x127.proto.rpc.examples.echoresource.v1.EchoResourceRequest\x1a8.proto.rpc.examples.echoresource.v1.EchoResourceResponse"\x00\x12\x9d\x01\n\x14EchoResourceMultiple\x12?.proto.rpc.examples.echoresource.v1.EchoResourceMultipleRequest\x1a@.proto.rpc.examples.echoresource.v1.EchoResourceMultipleResponse"\x000\x01\x12\x93\x01\n\x10EchoResourceBiDi\x12;.proto.rpc.examples.echoresource.v1.EchoResourceBiDiRequest\x1a<.proto.rpc.examples.echoresource.v1.EchoResourceBiDiResponse"\x00(\x010\x01B6Z4go.viam.com/utils/proto/rpc/examples/echoresource/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.rpc.examples.echoresource.v1.echoresource_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z4go.viam.com/utils/proto/rpc/examples/echoresource/v1' + _globals['_ECHORESOURCEREQUEST']._serialized_start = 93 + _globals['_ECHORESOURCEREQUEST']._serialized_end = 160 + _globals['_ECHORESOURCERESPONSE']._serialized_start = 162 + _globals['_ECHORESOURCERESPONSE']._serialized_end = 210 + _globals['_ECHORESOURCEMULTIPLEREQUEST']._serialized_start = 212 + _globals['_ECHORESOURCEMULTIPLEREQUEST']._serialized_end = 287 + _globals['_ECHORESOURCEMULTIPLERESPONSE']._serialized_start = 289 + _globals['_ECHORESOURCEMULTIPLERESPONSE']._serialized_end = 345 + _globals['_ECHORESOURCEBIDIREQUEST']._serialized_start = 347 + _globals['_ECHORESOURCEBIDIREQUEST']._serialized_end = 418 + _globals['_ECHORESOURCEBIDIRESPONSE']._serialized_start = 420 + _globals['_ECHORESOURCEBIDIRESPONSE']._serialized_end = 472 + _globals['_ECHORESOURCESERVICE']._serialized_start = 475 + _globals['_ECHORESOURCESERVICE']._serialized_end = 940 \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.pyi b/src/viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.pyi new file mode 100644 index 000000000..0033cb7f6 --- /dev/null +++ b/src/viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.pyi @@ -0,0 +1,93 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class EchoResourceRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + name: builtins.str + message: builtins.str + + def __init__(self, *, name: builtins.str=..., message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message', 'name', b'name']) -> None: + ... +global___EchoResourceRequest = EchoResourceRequest + +@typing.final +class EchoResourceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + + def __init__(self, *, message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: + ... +global___EchoResourceResponse = EchoResourceResponse + +@typing.final +class EchoResourceMultipleRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + name: builtins.str + message: builtins.str + + def __init__(self, *, name: builtins.str=..., message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message', 'name', b'name']) -> None: + ... +global___EchoResourceMultipleRequest = EchoResourceMultipleRequest + +@typing.final +class EchoResourceMultipleResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + + def __init__(self, *, message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: + ... +global___EchoResourceMultipleResponse = EchoResourceMultipleResponse + +@typing.final +class EchoResourceBiDiRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + name: builtins.str + message: builtins.str + + def __init__(self, *, name: builtins.str=..., message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message', 'name', b'name']) -> None: + ... +global___EchoResourceBiDiRequest = EchoResourceBiDiRequest + +@typing.final +class EchoResourceBiDiResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MESSAGE_FIELD_NUMBER: builtins.int + message: builtins.str + + def __init__(self, *, message: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['message', b'message']) -> None: + ... +global___EchoResourceBiDiResponse = EchoResourceBiDiResponse \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/examples/fileupload/v1/fileupload_grpc.py b/src/viam/gen/proto/rpc/examples/fileupload/v1/fileupload_grpc.py deleted file mode 100644 index 75914a2fa..000000000 --- a/src/viam/gen/proto/rpc/examples/fileupload/v1/fileupload_grpc.py +++ /dev/null @@ -1,21 +0,0 @@ -import abc -import typing -import grpclib.const -import grpclib.client -if typing.TYPE_CHECKING: - import grpclib.server -from ...... import proto - -class FileUploadServiceBase(abc.ABC): - - @abc.abstractmethod - async def UploadFile(self, stream: 'grpclib.server.Stream[proto.rpc.examples.fileupload.v1.fileupload_pb2.UploadFileRequest, proto.rpc.examples.fileupload.v1.fileupload_pb2.UploadFileResponse]') -> None: - pass - - def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/proto.rpc.examples.fileupload.v1.FileUploadService/UploadFile': grpclib.const.Handler(self.UploadFile, grpclib.const.Cardinality.STREAM_STREAM, proto.rpc.examples.fileupload.v1.fileupload_pb2.UploadFileRequest, proto.rpc.examples.fileupload.v1.fileupload_pb2.UploadFileResponse)} - -class FileUploadServiceStub: - - def __init__(self, channel: grpclib.client.Channel) -> None: - self.UploadFile = grpclib.client.StreamStreamMethod(channel, '/proto.rpc.examples.fileupload.v1.FileUploadService/UploadFile', proto.rpc.examples.fileupload.v1.fileupload_pb2.UploadFileRequest, proto.rpc.examples.fileupload.v1.fileupload_pb2.UploadFileResponse) \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.py b/src/viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.py deleted file mode 100644 index 9ef4ba9d0..000000000 --- a/src/viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.py +++ /dev/null @@ -1,18 +0,0 @@ -"""Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import symbol_database as _symbol_database -_sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n1proto/rpc/examples/fileupload/v1/fileupload.proto\x12 proto.rpc.examples.fileupload.v1"R\n\x11UploadFileRequest\x12\x14\n\x04name\x18\x01 \x01(\tH\x00R\x04name\x12\x1f\n\nchunk_data\x18\x02 \x01(\x0cH\x00R\tchunkDataB\x06\n\x04data"<\n\x12UploadFileResponse\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04size\x18\x02 \x01(\x03R\x04size2\x92\x01\n\x11FileUploadService\x12}\n\nUploadFile\x123.proto.rpc.examples.fileupload.v1.UploadFileRequest\x1a4.proto.rpc.examples.fileupload.v1.UploadFileResponse"\x00(\x010\x01B4Z2go.viam.com/utils/proto/rpc/examples/fileupload/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.rpc.examples.fileupload.v1.fileupload_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z2go.viam.com/utils/proto/rpc/examples/fileupload/v1' - _UPLOADFILEREQUEST._serialized_start = 87 - _UPLOADFILEREQUEST._serialized_end = 169 - _UPLOADFILERESPONSE._serialized_start = 171 - _UPLOADFILERESPONSE._serialized_end = 231 - _FILEUPLOADSERVICE._serialized_start = 234 - _FILEUPLOADSERVICE._serialized_end = 380 \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.pyi b/src/viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.pyi deleted file mode 100644 index f44bddd57..000000000 --- a/src/viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.pyi +++ /dev/null @@ -1,47 +0,0 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -""" -import builtins -import google.protobuf.descriptor -import google.protobuf.message -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -class UploadFileRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - CHUNK_DATA_FIELD_NUMBER: builtins.int - name: builtins.str - chunk_data: builtins.bytes - - def __init__(self, *, name: builtins.str=..., chunk_data: builtins.bytes=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['chunk_data', b'chunk_data', 'data', b'data', 'name', b'name']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['chunk_data', b'chunk_data', 'data', b'data', 'name', b'name']) -> None: - ... - - def WhichOneof(self, oneof_group: typing_extensions.Literal['data', b'data']) -> typing_extensions.Literal['name', 'chunk_data'] | None: - ... -global___UploadFileRequest = UploadFileRequest - -class UploadFileResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - SIZE_FIELD_NUMBER: builtins.int - name: builtins.str - size: builtins.int - - def __init__(self, *, name: builtins.str=..., size: builtins.int=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['name', b'name', 'size', b'size']) -> None: - ... -global___UploadFileResponse = UploadFileResponse \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/v1/auth_grpc.py b/src/viam/gen/proto/rpc/v1/auth_grpc.py index 166489cfe..53f78317d 100644 --- a/src/viam/gen/proto/rpc/v1/auth_grpc.py +++ b/src/viam/gen/proto/rpc/v1/auth_grpc.py @@ -2,6 +2,7 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server import google.api.annotations_pb2 @@ -16,6 +17,11 @@ async def Authenticate(self, stream: 'grpclib.server.Stream[proto.rpc.v1.auth_pb def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: return {'/proto.rpc.v1.AuthService/Authenticate': grpclib.const.Handler(self.Authenticate, grpclib.const.Cardinality.UNARY_UNARY, proto.rpc.v1.auth_pb2.AuthenticateRequest, proto.rpc.v1.auth_pb2.AuthenticateResponse)} +class UnimplementedAuthServiceBase(AuthServiceBase): + + async def Authenticate(self, stream: 'grpclib.server.Stream[proto.rpc.v1.auth_pb2.AuthenticateRequest, proto.rpc.v1.auth_pb2.AuthenticateResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + class AuthServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: @@ -30,6 +36,11 @@ async def AuthenticateTo(self, stream: 'grpclib.server.Stream[proto.rpc.v1.auth_ def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: return {'/proto.rpc.v1.ExternalAuthService/AuthenticateTo': grpclib.const.Handler(self.AuthenticateTo, grpclib.const.Cardinality.UNARY_UNARY, proto.rpc.v1.auth_pb2.AuthenticateToRequest, proto.rpc.v1.auth_pb2.AuthenticateToResponse)} +class UnimplementedExternalAuthServiceBase(ExternalAuthServiceBase): + + async def AuthenticateTo(self, stream: 'grpclib.server.Stream[proto.rpc.v1.auth_pb2.AuthenticateToRequest, proto.rpc.v1.auth_pb2.AuthenticateToResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + class ExternalAuthServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: diff --git a/src/viam/gen/proto/rpc/v1/auth_pb2.py b/src/viam/gen/proto/rpc/v1/auth_pb2.py index 7eefc19f6..270c7c7c0 100644 --- a/src/viam/gen/proto/rpc/v1/auth_pb2.py +++ b/src/viam/gen/proto/rpc/v1/auth_pb2.py @@ -1,31 +1,34 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'proto/rpc/v1/auth.proto') _sym_db = _symbol_database.Default() from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17proto/rpc/v1/auth.proto\x12\x0cproto.rpc.v1\x1a\x1cgoogle/api/annotations.proto";\n\x0bCredentials\x12\x12\n\x04type\x18\x01 \x01(\tR\x04type\x12\x18\n\x07payload\x18\x02 \x01(\tR\x07payload"j\n\x13AuthenticateRequest\x12\x16\n\x06entity\x18\x01 \x01(\tR\x06entity\x12;\n\x0bcredentials\x18\x02 \x01(\x0b2\x19.proto.rpc.v1.CredentialsR\x0bcredentials"9\n\x14AuthenticateResponse\x12!\n\x0caccess_token\x18\x01 \x01(\tR\x0baccessToken"/\n\x15AuthenticateToRequest\x12\x16\n\x06entity\x18\x01 \x01(\tR\x06entity";\n\x16AuthenticateToResponse\x12!\n\x0caccess_token\x18\x01 \x01(\tR\x0baccessToken2\x82\x01\n\x0bAuthService\x12s\n\x0cAuthenticate\x12!.proto.rpc.v1.AuthenticateRequest\x1a".proto.rpc.v1.AuthenticateResponse"\x1c\x82\xd3\xe4\x93\x02\x16"\x14/rpc/v1/authenticate2\x93\x01\n\x13ExternalAuthService\x12|\n\x0eAuthenticateTo\x12#.proto.rpc.v1.AuthenticateToRequest\x1a$.proto.rpc.v1.AuthenticateToResponse"\x1f\x82\xd3\xe4\x93\x02\x19"\x17/rpc/v1/authenticate_toB Z\x1ego.viam.com/utils/proto/rpc/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.rpc.v1.auth_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z\x1ego.viam.com/utils/proto/rpc/v1' - _AUTHSERVICE.methods_by_name['Authenticate']._options = None - _AUTHSERVICE.methods_by_name['Authenticate']._serialized_options = b'\x82\xd3\xe4\x93\x02\x16"\x14/rpc/v1/authenticate' - _EXTERNALAUTHSERVICE.methods_by_name['AuthenticateTo']._options = None - _EXTERNALAUTHSERVICE.methods_by_name['AuthenticateTo']._serialized_options = b'\x82\xd3\xe4\x93\x02\x19"\x17/rpc/v1/authenticate_to' - _CREDENTIALS._serialized_start = 71 - _CREDENTIALS._serialized_end = 130 - _AUTHENTICATEREQUEST._serialized_start = 132 - _AUTHENTICATEREQUEST._serialized_end = 238 - _AUTHENTICATERESPONSE._serialized_start = 240 - _AUTHENTICATERESPONSE._serialized_end = 297 - _AUTHENTICATETOREQUEST._serialized_start = 299 - _AUTHENTICATETOREQUEST._serialized_end = 346 - _AUTHENTICATETORESPONSE._serialized_start = 348 - _AUTHENTICATETORESPONSE._serialized_end = 407 - _AUTHSERVICE._serialized_start = 410 - _AUTHSERVICE._serialized_end = 540 - _EXTERNALAUTHSERVICE._serialized_start = 543 - _EXTERNALAUTHSERVICE._serialized_end = 690 \ No newline at end of file +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.rpc.v1.auth_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x1ego.viam.com/utils/proto/rpc/v1' + _globals['_AUTHSERVICE'].methods_by_name['Authenticate']._loaded_options = None + _globals['_AUTHSERVICE'].methods_by_name['Authenticate']._serialized_options = b'\x82\xd3\xe4\x93\x02\x16"\x14/rpc/v1/authenticate' + _globals['_EXTERNALAUTHSERVICE'].methods_by_name['AuthenticateTo']._loaded_options = None + _globals['_EXTERNALAUTHSERVICE'].methods_by_name['AuthenticateTo']._serialized_options = b'\x82\xd3\xe4\x93\x02\x19"\x17/rpc/v1/authenticate_to' + _globals['_CREDENTIALS']._serialized_start = 71 + _globals['_CREDENTIALS']._serialized_end = 130 + _globals['_AUTHENTICATEREQUEST']._serialized_start = 132 + _globals['_AUTHENTICATEREQUEST']._serialized_end = 238 + _globals['_AUTHENTICATERESPONSE']._serialized_start = 240 + _globals['_AUTHENTICATERESPONSE']._serialized_end = 297 + _globals['_AUTHENTICATETOREQUEST']._serialized_start = 299 + _globals['_AUTHENTICATETOREQUEST']._serialized_end = 346 + _globals['_AUTHENTICATETORESPONSE']._serialized_start = 348 + _globals['_AUTHENTICATETORESPONSE']._serialized_end = 407 + _globals['_AUTHSERVICE']._serialized_start = 410 + _globals['_AUTHSERVICE']._serialized_end = 540 + _globals['_EXTERNALAUTHSERVICE']._serialized_start = 543 + _globals['_EXTERNALAUTHSERVICE']._serialized_end = 690 \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/v1/auth_pb2.pyi b/src/viam/gen/proto/rpc/v1/auth_pb2.pyi index 7a2a8d6c2..ef1f52861 100644 --- a/src/viam/gen/proto/rpc/v1/auth_pb2.pyi +++ b/src/viam/gen/proto/rpc/v1/auth_pb2.pyi @@ -5,13 +5,10 @@ isort:skip_file import builtins import google.protobuf.descriptor import google.protobuf.message -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class Credentials(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor TYPE_FIELD_NUMBER: builtins.int @@ -24,10 +21,11 @@ class Credentials(google.protobuf.message.Message): def __init__(self, *, type: builtins.str=..., payload: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['payload', b'payload', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['payload', b'payload', 'type', b'type']) -> None: ... global___Credentials = Credentials +@typing.final class AuthenticateRequest(google.protobuf.message.Message): """An AuthenticateRequest contains the credentials used to authenticate.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -42,13 +40,14 @@ class AuthenticateRequest(google.protobuf.message.Message): def __init__(self, *, entity: builtins.str=..., credentials: global___Credentials | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['credentials', b'credentials']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['credentials', b'credentials']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['credentials', b'credentials', 'entity', b'entity']) -> None: + def ClearField(self, field_name: typing.Literal['credentials', b'credentials', 'entity', b'entity']) -> None: ... global___AuthenticateRequest = AuthenticateRequest +@typing.final class AuthenticateResponse(google.protobuf.message.Message): """An AuthenticateResponse is returned after successful authentication.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -59,10 +58,11 @@ class AuthenticateResponse(google.protobuf.message.Message): def __init__(self, *, access_token: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['access_token', b'access_token']) -> None: + def ClearField(self, field_name: typing.Literal['access_token', b'access_token']) -> None: ... global___AuthenticateResponse = AuthenticateResponse +@typing.final class AuthenticateToRequest(google.protobuf.message.Message): """An AuthenticateToRequest contains the entity to authenticate to.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -72,10 +72,11 @@ class AuthenticateToRequest(google.protobuf.message.Message): def __init__(self, *, entity: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['entity', b'entity']) -> None: + def ClearField(self, field_name: typing.Literal['entity', b'entity']) -> None: ... global___AuthenticateToRequest = AuthenticateToRequest +@typing.final class AuthenticateToResponse(google.protobuf.message.Message): """An AuthenticateResponse is returned after successful authentication.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -86,6 +87,6 @@ class AuthenticateToResponse(google.protobuf.message.Message): def __init__(self, *, access_token: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['access_token', b'access_token']) -> None: + def ClearField(self, field_name: typing.Literal['access_token', b'access_token']) -> None: ... global___AuthenticateToResponse = AuthenticateToResponse \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py b/src/viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py index a2672efd3..bddaa46de 100644 --- a/src/viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py +++ b/src/viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py @@ -1,40 +1,43 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'proto/rpc/webrtc/v1/grpc.proto') _sym_db = _symbol_database.Default() from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 from google.rpc import status_pb2 as google_dot_rpc_dot_status__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eproto/rpc/webrtc/v1/grpc.proto\x12\x13proto.rpc.webrtc.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x17google/rpc/status.proto"5\n\rPacketMessage\x12\x12\n\x04data\x18\x01 \x01(\x0cR\x04data\x12\x10\n\x03eom\x18\x02 \x01(\x08R\x03eom"\x18\n\x06Stream\x12\x0e\n\x02id\x18\x01 \x01(\x04R\x02id"\xc8\x01\n\x07Request\x123\n\x06stream\x18\x01 \x01(\x0b2\x1b.proto.rpc.webrtc.v1.StreamR\x06stream\x12?\n\x07headers\x18\x02 \x01(\x0b2#.proto.rpc.webrtc.v1.RequestHeadersH\x00R\x07headers\x12?\n\x07message\x18\x03 \x01(\x0b2#.proto.rpc.webrtc.v1.RequestMessageH\x00R\x07messageB\x06\n\x04type"\x98\x01\n\x0eRequestHeaders\x12\x16\n\x06method\x18\x01 \x01(\tR\x06method\x129\n\x08metadata\x18\x02 \x01(\x0b2\x1d.proto.rpc.webrtc.v1.MetadataR\x08metadata\x123\n\x07timeout\x18\x03 \x01(\x0b2\x19.google.protobuf.DurationR\x07timeout"\x8e\x01\n\x0eRequestMessage\x12\x1f\n\x0bhas_message\x18\x01 \x01(\x08R\nhasMessage\x12I\n\x0epacket_message\x18\x02 \x01(\x0b2".proto.rpc.webrtc.v1.PacketMessageR\rpacketMessage\x12\x10\n\x03eos\x18\x03 \x01(\x08R\x03eos"\x90\x02\n\x08Response\x123\n\x06stream\x18\x01 \x01(\x0b2\x1b.proto.rpc.webrtc.v1.StreamR\x06stream\x12@\n\x07headers\x18\x02 \x01(\x0b2$.proto.rpc.webrtc.v1.ResponseHeadersH\x00R\x07headers\x12@\n\x07message\x18\x03 \x01(\x0b2$.proto.rpc.webrtc.v1.ResponseMessageH\x00R\x07message\x12C\n\x08trailers\x18\x04 \x01(\x0b2%.proto.rpc.webrtc.v1.ResponseTrailersH\x00R\x08trailersB\x06\n\x04type"L\n\x0fResponseHeaders\x129\n\x08metadata\x18\x01 \x01(\x0b2\x1d.proto.rpc.webrtc.v1.MetadataR\x08metadata"\\\n\x0fResponseMessage\x12I\n\x0epacket_message\x18\x01 \x01(\x0b2".proto.rpc.webrtc.v1.PacketMessageR\rpacketMessage"y\n\x10ResponseTrailers\x12*\n\x06status\x18\x01 \x01(\x0b2\x12.google.rpc.StatusR\x06status\x129\n\x08metadata\x18\x02 \x01(\x0b2\x1d.proto.rpc.webrtc.v1.MetadataR\x08metadata"!\n\x07Strings\x12\x16\n\x06values\x18\x01 \x03(\tR\x06values"\x96\x01\n\x08Metadata\x125\n\x02md\x18\x01 \x03(\x0b2%.proto.rpc.webrtc.v1.Metadata.MdEntryR\x02md\x1aS\n\x07MdEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x122\n\x05value\x18\x02 \x01(\x0b2\x1c.proto.rpc.webrtc.v1.StringsR\x05value:\x028\x01B\'Z%go.viam.com/utils/proto/rpc/webrtc/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.rpc.webrtc.v1.grpc_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z%go.viam.com/utils/proto/rpc/webrtc/v1' - _METADATA_MDENTRY._options = None - _METADATA_MDENTRY._serialized_options = b'8\x01' - _PACKETMESSAGE._serialized_start = 112 - _PACKETMESSAGE._serialized_end = 165 - _STREAM._serialized_start = 167 - _STREAM._serialized_end = 191 - _REQUEST._serialized_start = 194 - _REQUEST._serialized_end = 394 - _REQUESTHEADERS._serialized_start = 397 - _REQUESTHEADERS._serialized_end = 549 - _REQUESTMESSAGE._serialized_start = 552 - _REQUESTMESSAGE._serialized_end = 694 - _RESPONSE._serialized_start = 697 - _RESPONSE._serialized_end = 969 - _RESPONSEHEADERS._serialized_start = 971 - _RESPONSEHEADERS._serialized_end = 1047 - _RESPONSEMESSAGE._serialized_start = 1049 - _RESPONSEMESSAGE._serialized_end = 1141 - _RESPONSETRAILERS._serialized_start = 1143 - _RESPONSETRAILERS._serialized_end = 1264 - _STRINGS._serialized_start = 1266 - _STRINGS._serialized_end = 1299 - _METADATA._serialized_start = 1302 - _METADATA._serialized_end = 1452 - _METADATA_MDENTRY._serialized_start = 1369 - _METADATA_MDENTRY._serialized_end = 1452 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eproto/rpc/webrtc/v1/grpc.proto\x12\x13proto.rpc.webrtc.v1\x1a\x1egoogle/protobuf/duration.proto\x1a\x17google/rpc/status.proto"5\n\rPacketMessage\x12\x12\n\x04data\x18\x01 \x01(\x0cR\x04data\x12\x10\n\x03eom\x18\x02 \x01(\x08R\x03eom"\x18\n\x06Stream\x12\x0e\n\x02id\x18\x01 \x01(\x04R\x02id"\xe9\x01\n\x07Request\x123\n\x06stream\x18\x01 \x01(\x0b2\x1b.proto.rpc.webrtc.v1.StreamR\x06stream\x12?\n\x07headers\x18\x02 \x01(\x0b2#.proto.rpc.webrtc.v1.RequestHeadersH\x00R\x07headers\x12?\n\x07message\x18\x03 \x01(\x0b2#.proto.rpc.webrtc.v1.RequestMessageH\x00R\x07message\x12\x1f\n\nrst_stream\x18\x04 \x01(\x08H\x00R\trstStreamB\x06\n\x04type"\x98\x01\n\x0eRequestHeaders\x12\x16\n\x06method\x18\x01 \x01(\tR\x06method\x129\n\x08metadata\x18\x02 \x01(\x0b2\x1d.proto.rpc.webrtc.v1.MetadataR\x08metadata\x123\n\x07timeout\x18\x03 \x01(\x0b2\x19.google.protobuf.DurationR\x07timeout"\x8e\x01\n\x0eRequestMessage\x12\x1f\n\x0bhas_message\x18\x01 \x01(\x08R\nhasMessage\x12I\n\x0epacket_message\x18\x02 \x01(\x0b2".proto.rpc.webrtc.v1.PacketMessageR\rpacketMessage\x12\x10\n\x03eos\x18\x03 \x01(\x08R\x03eos"\x90\x02\n\x08Response\x123\n\x06stream\x18\x01 \x01(\x0b2\x1b.proto.rpc.webrtc.v1.StreamR\x06stream\x12@\n\x07headers\x18\x02 \x01(\x0b2$.proto.rpc.webrtc.v1.ResponseHeadersH\x00R\x07headers\x12@\n\x07message\x18\x03 \x01(\x0b2$.proto.rpc.webrtc.v1.ResponseMessageH\x00R\x07message\x12C\n\x08trailers\x18\x04 \x01(\x0b2%.proto.rpc.webrtc.v1.ResponseTrailersH\x00R\x08trailersB\x06\n\x04type"L\n\x0fResponseHeaders\x129\n\x08metadata\x18\x01 \x01(\x0b2\x1d.proto.rpc.webrtc.v1.MetadataR\x08metadata"\\\n\x0fResponseMessage\x12I\n\x0epacket_message\x18\x01 \x01(\x0b2".proto.rpc.webrtc.v1.PacketMessageR\rpacketMessage"y\n\x10ResponseTrailers\x12*\n\x06status\x18\x01 \x01(\x0b2\x12.google.rpc.StatusR\x06status\x129\n\x08metadata\x18\x02 \x01(\x0b2\x1d.proto.rpc.webrtc.v1.MetadataR\x08metadata"!\n\x07Strings\x12\x16\n\x06values\x18\x01 \x03(\tR\x06values"\x96\x01\n\x08Metadata\x125\n\x02md\x18\x01 \x03(\x0b2%.proto.rpc.webrtc.v1.Metadata.MdEntryR\x02md\x1aS\n\x07MdEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x122\n\x05value\x18\x02 \x01(\x0b2\x1c.proto.rpc.webrtc.v1.StringsR\x05value:\x028\x01B\'Z%go.viam.com/utils/proto/rpc/webrtc/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.rpc.webrtc.v1.grpc_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z%go.viam.com/utils/proto/rpc/webrtc/v1' + _globals['_METADATA_MDENTRY']._loaded_options = None + _globals['_METADATA_MDENTRY']._serialized_options = b'8\x01' + _globals['_PACKETMESSAGE']._serialized_start = 112 + _globals['_PACKETMESSAGE']._serialized_end = 165 + _globals['_STREAM']._serialized_start = 167 + _globals['_STREAM']._serialized_end = 191 + _globals['_REQUEST']._serialized_start = 194 + _globals['_REQUEST']._serialized_end = 427 + _globals['_REQUESTHEADERS']._serialized_start = 430 + _globals['_REQUESTHEADERS']._serialized_end = 582 + _globals['_REQUESTMESSAGE']._serialized_start = 585 + _globals['_REQUESTMESSAGE']._serialized_end = 727 + _globals['_RESPONSE']._serialized_start = 730 + _globals['_RESPONSE']._serialized_end = 1002 + _globals['_RESPONSEHEADERS']._serialized_start = 1004 + _globals['_RESPONSEHEADERS']._serialized_end = 1080 + _globals['_RESPONSEMESSAGE']._serialized_start = 1082 + _globals['_RESPONSEMESSAGE']._serialized_end = 1174 + _globals['_RESPONSETRAILERS']._serialized_start = 1176 + _globals['_RESPONSETRAILERS']._serialized_end = 1297 + _globals['_STRINGS']._serialized_start = 1299 + _globals['_STRINGS']._serialized_end = 1332 + _globals['_METADATA']._serialized_start = 1335 + _globals['_METADATA']._serialized_end = 1485 + _globals['_METADATA_MDENTRY']._serialized_start = 1402 + _globals['_METADATA_MDENTRY']._serialized_end = 1485 \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/webrtc/v1/grpc_pb2.pyi b/src/viam/gen/proto/rpc/webrtc/v1/grpc_pb2.pyi index 130218686..1e7525ee0 100644 --- a/src/viam/gen/proto/rpc/webrtc/v1/grpc_pb2.pyi +++ b/src/viam/gen/proto/rpc/webrtc/v1/grpc_pb2.pyi @@ -9,13 +9,10 @@ import google.protobuf.duration_pb2 import google.protobuf.internal.containers import google.protobuf.message import google.rpc.status_pb2 -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class PacketMessage(google.protobuf.message.Message): """A PacketMessage is used to packetize large messages (> 64KiB) to be able to safely transmit over WebRTC data channels. @@ -29,10 +26,11 @@ class PacketMessage(google.protobuf.message.Message): def __init__(self, *, data: builtins.bytes=..., eom: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['data', b'data', 'eom', b'eom']) -> None: + def ClearField(self, field_name: typing.Literal['data', b'data', 'eom', b'eom']) -> None: ... global___PacketMessage = PacketMessage +@typing.final class Stream(google.protobuf.message.Message): """A Stream represents an instance of a gRPC stream between a client and a server. @@ -44,10 +42,11 @@ class Stream(google.protobuf.message.Message): def __init__(self, *, id: builtins.int=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: ... global___Stream = Stream +@typing.final class Request(google.protobuf.message.Message): """A Request is a frame coming from a client. It is always associated with a stream where the client assigns the stream @@ -58,6 +57,8 @@ class Request(google.protobuf.message.Message): STREAM_FIELD_NUMBER: builtins.int HEADERS_FIELD_NUMBER: builtins.int MESSAGE_FIELD_NUMBER: builtins.int + RST_STREAM_FIELD_NUMBER: builtins.int + rst_stream: builtins.bool @property def stream(self) -> global___Stream: @@ -71,19 +72,20 @@ class Request(google.protobuf.message.Message): def message(self) -> global___RequestMessage: ... - def __init__(self, *, stream: global___Stream | None=..., headers: global___RequestHeaders | None=..., message: global___RequestMessage | None=...) -> None: + def __init__(self, *, stream: global___Stream | None=..., headers: global___RequestHeaders | None=..., message: global___RequestMessage | None=..., rst_stream: builtins.bool=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['headers', b'headers', 'message', b'message', 'stream', b'stream', 'type', b'type']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['headers', b'headers', 'message', b'message', 'rst_stream', b'rst_stream', 'stream', b'stream', 'type', b'type']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['headers', b'headers', 'message', b'message', 'stream', b'stream', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['headers', b'headers', 'message', b'message', 'rst_stream', b'rst_stream', 'stream', b'stream', 'type', b'type']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['type', b'type']) -> typing_extensions.Literal['headers', 'message'] | None: + def WhichOneof(self, oneof_group: typing.Literal['type', b'type']) -> typing.Literal['headers', 'message', 'rst_stream'] | None: ... global___Request = Request +@typing.final class RequestHeaders(google.protobuf.message.Message): """RequestHeaders describe the unary or streaming call to make.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -103,13 +105,14 @@ class RequestHeaders(google.protobuf.message.Message): def __init__(self, *, method: builtins.str=..., metadata: global___Metadata | None=..., timeout: google.protobuf.duration_pb2.Duration | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['metadata', b'metadata', 'timeout', b'timeout']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['metadata', b'metadata', 'timeout', b'timeout']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['metadata', b'metadata', 'method', b'method', 'timeout', b'timeout']) -> None: + def ClearField(self, field_name: typing.Literal['metadata', b'metadata', 'method', b'method', 'timeout', b'timeout']) -> None: ... global___RequestHeaders = RequestHeaders +@typing.final class RequestMessage(google.protobuf.message.Message): """A RequestMessage contains individual gRPC messages and a potential end-of-stream (EOS) marker. @@ -119,22 +122,23 @@ class RequestMessage(google.protobuf.message.Message): PACKET_MESSAGE_FIELD_NUMBER: builtins.int EOS_FIELD_NUMBER: builtins.int has_message: builtins.bool + eos: builtins.bool @property def packet_message(self) -> global___PacketMessage: ... - eos: builtins.bool def __init__(self, *, has_message: builtins.bool=..., packet_message: global___PacketMessage | None=..., eos: builtins.bool=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['packet_message', b'packet_message']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['packet_message', b'packet_message']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['eos', b'eos', 'has_message', b'has_message', 'packet_message', b'packet_message']) -> None: + def ClearField(self, field_name: typing.Literal['eos', b'eos', 'has_message', b'has_message', 'packet_message', b'packet_message']) -> None: ... global___RequestMessage = RequestMessage +@typing.final class Response(google.protobuf.message.Message): """A Response is a frame coming from a server. It is always associated with a stream where the client assigns the stream @@ -166,16 +170,17 @@ class Response(google.protobuf.message.Message): def __init__(self, *, stream: global___Stream | None=..., headers: global___ResponseHeaders | None=..., message: global___ResponseMessage | None=..., trailers: global___ResponseTrailers | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['headers', b'headers', 'message', b'message', 'stream', b'stream', 'trailers', b'trailers', 'type', b'type']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['headers', b'headers', 'message', b'message', 'stream', b'stream', 'trailers', b'trailers', 'type', b'type']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['headers', b'headers', 'message', b'message', 'stream', b'stream', 'trailers', b'trailers', 'type', b'type']) -> None: + def ClearField(self, field_name: typing.Literal['headers', b'headers', 'message', b'message', 'stream', b'stream', 'trailers', b'trailers', 'type', b'type']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['type', b'type']) -> typing_extensions.Literal['headers', 'message', 'trailers'] | None: + def WhichOneof(self, oneof_group: typing.Literal['type', b'type']) -> typing.Literal['headers', 'message', 'trailers'] | None: ... global___Response = Response +@typing.final class ResponseHeaders(google.protobuf.message.Message): """ResponseHeaders contain custom metadata that are sent to the client before any message or trailers (unless only trailers are sent). @@ -190,13 +195,14 @@ class ResponseHeaders(google.protobuf.message.Message): def __init__(self, *, metadata: global___Metadata | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['metadata', b'metadata']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['metadata', b'metadata']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['metadata', b'metadata']) -> None: + def ClearField(self, field_name: typing.Literal['metadata', b'metadata']) -> None: ... global___ResponseHeaders = ResponseHeaders +@typing.final class ResponseMessage(google.protobuf.message.Message): """ResponseMessage contains the data of a response to a call.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -209,13 +215,14 @@ class ResponseMessage(google.protobuf.message.Message): def __init__(self, *, packet_message: global___PacketMessage | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['packet_message', b'packet_message']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['packet_message', b'packet_message']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['packet_message', b'packet_message']) -> None: + def ClearField(self, field_name: typing.Literal['packet_message', b'packet_message']) -> None: ... global___ResponseMessage = ResponseMessage +@typing.final class ResponseTrailers(google.protobuf.message.Message): """ResponseTrailers contain the status of a response and any custom metadata.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -233,13 +240,14 @@ class ResponseTrailers(google.protobuf.message.Message): def __init__(self, *, status: google.rpc.status_pb2.Status | None=..., metadata: global___Metadata | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['metadata', b'metadata', 'status', b'status']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['metadata', b'metadata', 'status', b'status']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['metadata', b'metadata', 'status', b'status']) -> None: + def ClearField(self, field_name: typing.Literal['metadata', b'metadata', 'status', b'status']) -> None: ... global___ResponseTrailers = ResponseTrailers +@typing.final class Strings(google.protobuf.message.Message): """Strings are a series of values.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -252,16 +260,18 @@ class Strings(google.protobuf.message.Message): def __init__(self, *, values: collections.abc.Iterable[builtins.str] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['values', b'values']) -> None: + def ClearField(self, field_name: typing.Literal['values', b'values']) -> None: ... global___Strings = Strings +@typing.final class Metadata(google.protobuf.message.Message): """Metadata is for custom key values provided by a client or server during a stream. """ DESCRIPTOR: google.protobuf.descriptor.Descriptor + @typing.final class MdEntry(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor KEY_FIELD_NUMBER: builtins.int @@ -275,10 +285,10 @@ class Metadata(google.protobuf.message.Message): def __init__(self, *, key: builtins.str=..., value: global___Strings | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['value', b'value']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: ... MD_FIELD_NUMBER: builtins.int @@ -289,6 +299,6 @@ class Metadata(google.protobuf.message.Message): def __init__(self, *, md: collections.abc.Mapping[builtins.str, global___Strings] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['md', b'md']) -> None: + def ClearField(self, field_name: typing.Literal['md', b'md']) -> None: ... global___Metadata = Metadata \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/webrtc/v1/signaling_grpc.py b/src/viam/gen/proto/rpc/webrtc/v1/signaling_grpc.py index c17da6428..4cf7ec441 100644 --- a/src/viam/gen/proto/rpc/webrtc/v1/signaling_grpc.py +++ b/src/viam/gen/proto/rpc/webrtc/v1/signaling_grpc.py @@ -2,9 +2,11 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server import google.api.annotations_pb2 +import google.protobuf.timestamp_pb2 import google.rpc.status_pb2 from ..... import proto @@ -29,6 +31,20 @@ async def OptionalWebRTCConfig(self, stream: 'grpclib.server.Stream[proto.rpc.we def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: return {'/proto.rpc.webrtc.v1.SignalingService/Call': grpclib.const.Handler(self.Call, grpclib.const.Cardinality.UNARY_STREAM, proto.rpc.webrtc.v1.signaling_pb2.CallRequest, proto.rpc.webrtc.v1.signaling_pb2.CallResponse), '/proto.rpc.webrtc.v1.SignalingService/CallUpdate': grpclib.const.Handler(self.CallUpdate, grpclib.const.Cardinality.UNARY_UNARY, proto.rpc.webrtc.v1.signaling_pb2.CallUpdateRequest, proto.rpc.webrtc.v1.signaling_pb2.CallUpdateResponse), '/proto.rpc.webrtc.v1.SignalingService/Answer': grpclib.const.Handler(self.Answer, grpclib.const.Cardinality.STREAM_STREAM, proto.rpc.webrtc.v1.signaling_pb2.AnswerResponse, proto.rpc.webrtc.v1.signaling_pb2.AnswerRequest), '/proto.rpc.webrtc.v1.SignalingService/OptionalWebRTCConfig': grpclib.const.Handler(self.OptionalWebRTCConfig, grpclib.const.Cardinality.UNARY_UNARY, proto.rpc.webrtc.v1.signaling_pb2.OptionalWebRTCConfigRequest, proto.rpc.webrtc.v1.signaling_pb2.OptionalWebRTCConfigResponse)} +class UnimplementedSignalingServiceBase(SignalingServiceBase): + + async def Call(self, stream: 'grpclib.server.Stream[proto.rpc.webrtc.v1.signaling_pb2.CallRequest, proto.rpc.webrtc.v1.signaling_pb2.CallResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CallUpdate(self, stream: 'grpclib.server.Stream[proto.rpc.webrtc.v1.signaling_pb2.CallUpdateRequest, proto.rpc.webrtc.v1.signaling_pb2.CallUpdateResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Answer(self, stream: 'grpclib.server.Stream[proto.rpc.webrtc.v1.signaling_pb2.AnswerResponse, proto.rpc.webrtc.v1.signaling_pb2.AnswerRequest]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def OptionalWebRTCConfig(self, stream: 'grpclib.server.Stream[proto.rpc.webrtc.v1.signaling_pb2.OptionalWebRTCConfigRequest, proto.rpc.webrtc.v1.signaling_pb2.OptionalWebRTCConfigResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + class SignalingServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: diff --git a/src/viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py b/src/viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py index 5f87399cf..707c11832 100644 --- a/src/viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py +++ b/src/viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py @@ -1,64 +1,70 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'proto/rpc/webrtc/v1/signaling.proto') _sym_db = _symbol_database.Default() from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 from google.rpc import status_pb2 as google_dot_rpc_dot_status__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#proto/rpc/webrtc/v1/signaling.proto\x12\x13proto.rpc.webrtc.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x17google/rpc/status.proto"\xdf\x01\n\x0cICECandidate\x12\x1c\n\tcandidate\x18\x01 \x01(\tR\tcandidate\x12\x1c\n\x07sdp_mid\x18\x02 \x01(\tH\x00R\x06sdpMid\x88\x01\x01\x12+\n\x0fsdpm_line_index\x18\x03 \x01(\rH\x01R\rsdpmLineIndex\x88\x01\x01\x120\n\x11username_fragment\x18\x04 \x01(\tH\x02R\x10usernameFragment\x88\x01\x01B\n\n\x08_sdp_midB\x12\n\x10_sdpm_line_indexB\x14\n\x12_username_fragment"H\n\x0bCallRequest\x12\x10\n\x03sdp\x18\x01 \x01(\tR\x03sdp\x12\'\n\x0fdisable_trickle\x18\x02 \x01(\x08R\x0edisableTrickle")\n\x15CallResponseInitStage\x12\x10\n\x03sdp\x18\x01 \x01(\tR\x03sdp"Z\n\x17CallResponseUpdateStage\x12?\n\tcandidate\x18\x01 \x01(\x0b2!.proto.rpc.webrtc.v1.ICECandidateR\tcandidate"\xb5\x01\n\x0cCallResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12@\n\x04init\x18\x02 \x01(\x0b2*.proto.rpc.webrtc.v1.CallResponseInitStageH\x00R\x04init\x12F\n\x06update\x18\x03 \x01(\x0b2,.proto.rpc.webrtc.v1.CallResponseUpdateStageH\x00R\x06updateB\x07\n\x05stage"\xb6\x01\n\x11CallUpdateRequest\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12A\n\tcandidate\x18\x02 \x01(\x0b2!.proto.rpc.webrtc.v1.ICECandidateH\x00R\tcandidate\x12\x14\n\x04done\x18\x03 \x01(\x08H\x00R\x04done\x12*\n\x05error\x18\x04 \x01(\x0b2\x12.google.rpc.StatusH\x00R\x05errorB\x08\n\x06update"\x14\n\x12CallUpdateResponse"[\n\tICEServer\x12\x12\n\x04urls\x18\x01 \x03(\tR\x04urls\x12\x1a\n\x08username\x18\x02 \x01(\tR\x08username\x12\x1e\n\ncredential\x18\x03 \x01(\tR\ncredential"\x8d\x01\n\x0cWebRTCConfig\x12T\n\x16additional_ice_servers\x18\x01 \x03(\x0b2\x1e.proto.rpc.webrtc.v1.ICEServerR\x14additionalIceServers\x12\'\n\x0fdisable_trickle\x18\x02 \x01(\x08R\x0edisableTrickle"v\n\x16AnswerRequestInitStage\x12\x10\n\x03sdp\x18\x01 \x01(\tR\x03sdp\x12J\n\x0foptional_config\x18\x02 \x01(\x0b2!.proto.rpc.webrtc.v1.WebRTCConfigR\x0eoptionalConfig"[\n\x18AnswerRequestUpdateStage\x12?\n\tcandidate\x18\x01 \x01(\x0b2!.proto.rpc.webrtc.v1.ICECandidateR\tcandidate"\x18\n\x16AnswerRequestDoneStage"E\n\x17AnswerRequestErrorStage\x12*\n\x06status\x18\x01 \x01(\x0b2\x12.google.rpc.StatusR\x06status"\xc1\x02\n\rAnswerRequest\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12A\n\x04init\x18\x02 \x01(\x0b2+.proto.rpc.webrtc.v1.AnswerRequestInitStageH\x00R\x04init\x12G\n\x06update\x18\x03 \x01(\x0b2-.proto.rpc.webrtc.v1.AnswerRequestUpdateStageH\x00R\x06update\x12A\n\x04done\x18\x04 \x01(\x0b2+.proto.rpc.webrtc.v1.AnswerRequestDoneStageH\x00R\x04done\x12D\n\x05error\x18\x05 \x01(\x0b2,.proto.rpc.webrtc.v1.AnswerRequestErrorStageH\x00R\x05errorB\x07\n\x05stage"+\n\x17AnswerResponseInitStage\x12\x10\n\x03sdp\x18\x01 \x01(\tR\x03sdp"\\\n\x19AnswerResponseUpdateStage\x12?\n\tcandidate\x18\x01 \x01(\x0b2!.proto.rpc.webrtc.v1.ICECandidateR\tcandidate"\x19\n\x17AnswerResponseDoneStage"F\n\x18AnswerResponseErrorStage\x12*\n\x06status\x18\x01 \x01(\x0b2\x12.google.rpc.StatusR\x06status"\xc6\x02\n\x0eAnswerResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12B\n\x04init\x18\x02 \x01(\x0b2,.proto.rpc.webrtc.v1.AnswerResponseInitStageH\x00R\x04init\x12H\n\x06update\x18\x03 \x01(\x0b2..proto.rpc.webrtc.v1.AnswerResponseUpdateStageH\x00R\x06update\x12B\n\x04done\x18\x04 \x01(\x0b2,.proto.rpc.webrtc.v1.AnswerResponseDoneStageH\x00R\x04done\x12E\n\x05error\x18\x05 \x01(\x0b2-.proto.rpc.webrtc.v1.AnswerResponseErrorStageH\x00R\x05errorB\x07\n\x05stage"\x1d\n\x1bOptionalWebRTCConfigRequest"Y\n\x1cOptionalWebRTCConfigResponse\x129\n\x06config\x18\x01 \x01(\x0b2!.proto.rpc.webrtc.v1.WebRTCConfigR\x06config2\x86\x04\n\x10SignalingService\x12j\n\x04Call\x12 .proto.rpc.webrtc.v1.CallRequest\x1a!.proto.rpc.webrtc.v1.CallResponse"\x1b\x82\xd3\xe4\x93\x02\x15"\x13/rpc/webrtc/v1/call0\x01\x12\x81\x01\n\nCallUpdate\x12&.proto.rpc.webrtc.v1.CallUpdateRequest\x1a\'.proto.rpc.webrtc.v1.CallUpdateResponse""\x82\xd3\xe4\x93\x02\x1c\x1a\x1a/rpc/webrtc/v1/call_update\x12U\n\x06Answer\x12#.proto.rpc.webrtc.v1.AnswerResponse\x1a".proto.rpc.webrtc.v1.AnswerRequest(\x010\x01\x12\xaa\x01\n\x14OptionalWebRTCConfig\x120.proto.rpc.webrtc.v1.OptionalWebRTCConfigRequest\x1a1.proto.rpc.webrtc.v1.OptionalWebRTCConfigResponse"-\x82\xd3\xe4\x93\x02\'\x12%/rpc/webrtc/v1/optional_webrtc_configB\'Z%go.viam.com/utils/proto/rpc/webrtc/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.rpc.webrtc.v1.signaling_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z%go.viam.com/utils/proto/rpc/webrtc/v1' - _SIGNALINGSERVICE.methods_by_name['Call']._options = None - _SIGNALINGSERVICE.methods_by_name['Call']._serialized_options = b'\x82\xd3\xe4\x93\x02\x15"\x13/rpc/webrtc/v1/call' - _SIGNALINGSERVICE.methods_by_name['CallUpdate']._options = None - _SIGNALINGSERVICE.methods_by_name['CallUpdate']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1c\x1a\x1a/rpc/webrtc/v1/call_update' - _SIGNALINGSERVICE.methods_by_name['OptionalWebRTCConfig']._options = None - _SIGNALINGSERVICE.methods_by_name['OptionalWebRTCConfig']._serialized_options = b"\x82\xd3\xe4\x93\x02'\x12%/rpc/webrtc/v1/optional_webrtc_config" - _ICECANDIDATE._serialized_start = 116 - _ICECANDIDATE._serialized_end = 339 - _CALLREQUEST._serialized_start = 341 - _CALLREQUEST._serialized_end = 413 - _CALLRESPONSEINITSTAGE._serialized_start = 415 - _CALLRESPONSEINITSTAGE._serialized_end = 456 - _CALLRESPONSEUPDATESTAGE._serialized_start = 458 - _CALLRESPONSEUPDATESTAGE._serialized_end = 548 - _CALLRESPONSE._serialized_start = 551 - _CALLRESPONSE._serialized_end = 732 - _CALLUPDATEREQUEST._serialized_start = 735 - _CALLUPDATEREQUEST._serialized_end = 917 - _CALLUPDATERESPONSE._serialized_start = 919 - _CALLUPDATERESPONSE._serialized_end = 939 - _ICESERVER._serialized_start = 941 - _ICESERVER._serialized_end = 1032 - _WEBRTCCONFIG._serialized_start = 1035 - _WEBRTCCONFIG._serialized_end = 1176 - _ANSWERREQUESTINITSTAGE._serialized_start = 1178 - _ANSWERREQUESTINITSTAGE._serialized_end = 1296 - _ANSWERREQUESTUPDATESTAGE._serialized_start = 1298 - _ANSWERREQUESTUPDATESTAGE._serialized_end = 1389 - _ANSWERREQUESTDONESTAGE._serialized_start = 1391 - _ANSWERREQUESTDONESTAGE._serialized_end = 1415 - _ANSWERREQUESTERRORSTAGE._serialized_start = 1417 - _ANSWERREQUESTERRORSTAGE._serialized_end = 1486 - _ANSWERREQUEST._serialized_start = 1489 - _ANSWERREQUEST._serialized_end = 1810 - _ANSWERRESPONSEINITSTAGE._serialized_start = 1812 - _ANSWERRESPONSEINITSTAGE._serialized_end = 1855 - _ANSWERRESPONSEUPDATESTAGE._serialized_start = 1857 - _ANSWERRESPONSEUPDATESTAGE._serialized_end = 1949 - _ANSWERRESPONSEDONESTAGE._serialized_start = 1951 - _ANSWERRESPONSEDONESTAGE._serialized_end = 1976 - _ANSWERRESPONSEERRORSTAGE._serialized_start = 1978 - _ANSWERRESPONSEERRORSTAGE._serialized_end = 2048 - _ANSWERRESPONSE._serialized_start = 2051 - _ANSWERRESPONSE._serialized_end = 2377 - _OPTIONALWEBRTCCONFIGREQUEST._serialized_start = 2379 - _OPTIONALWEBRTCCONFIGREQUEST._serialized_end = 2408 - _OPTIONALWEBRTCCONFIGRESPONSE._serialized_start = 2410 - _OPTIONALWEBRTCCONFIGRESPONSE._serialized_end = 2499 - _SIGNALINGSERVICE._serialized_start = 2502 - _SIGNALINGSERVICE._serialized_end = 3020 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#proto/rpc/webrtc/v1/signaling.proto\x12\x13proto.rpc.webrtc.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x17google/rpc/status.proto"\xdf\x01\n\x0cICECandidate\x12\x1c\n\tcandidate\x18\x01 \x01(\tR\tcandidate\x12\x1c\n\x07sdp_mid\x18\x02 \x01(\tH\x00R\x06sdpMid\x88\x01\x01\x12+\n\x0fsdpm_line_index\x18\x03 \x01(\rH\x01R\rsdpmLineIndex\x88\x01\x01\x120\n\x11username_fragment\x18\x04 \x01(\tH\x02R\x10usernameFragment\x88\x01\x01B\n\n\x08_sdp_midB\x12\n\x10_sdpm_line_indexB\x14\n\x12_username_fragment"H\n\x0bCallRequest\x12\x10\n\x03sdp\x18\x01 \x01(\tR\x03sdp\x12\'\n\x0fdisable_trickle\x18\x02 \x01(\x08R\x0edisableTrickle")\n\x15CallResponseInitStage\x12\x10\n\x03sdp\x18\x01 \x01(\tR\x03sdp"Z\n\x17CallResponseUpdateStage\x12?\n\tcandidate\x18\x01 \x01(\x0b2!.proto.rpc.webrtc.v1.ICECandidateR\tcandidate"\xb5\x01\n\x0cCallResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12@\n\x04init\x18\x02 \x01(\x0b2*.proto.rpc.webrtc.v1.CallResponseInitStageH\x00R\x04init\x12F\n\x06update\x18\x03 \x01(\x0b2,.proto.rpc.webrtc.v1.CallResponseUpdateStageH\x00R\x06updateB\x07\n\x05stage"\xb6\x01\n\x11CallUpdateRequest\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12A\n\tcandidate\x18\x02 \x01(\x0b2!.proto.rpc.webrtc.v1.ICECandidateH\x00R\tcandidate\x12\x14\n\x04done\x18\x03 \x01(\x08H\x00R\x04done\x12*\n\x05error\x18\x04 \x01(\x0b2\x12.google.rpc.StatusH\x00R\x05errorB\x08\n\x06update"\x14\n\x12CallUpdateResponse"[\n\tICEServer\x12\x12\n\x04urls\x18\x01 \x03(\tR\x04urls\x12\x1a\n\x08username\x18\x02 \x01(\tR\x08username\x12\x1e\n\ncredential\x18\x03 \x01(\tR\ncredential"\x8d\x01\n\x0cWebRTCConfig\x12T\n\x16additional_ice_servers\x18\x01 \x03(\x0b2\x1e.proto.rpc.webrtc.v1.ICEServerR\x14additionalIceServers\x12\'\n\x0fdisable_trickle\x18\x02 \x01(\x08R\x0edisableTrickle"\xc0\x01\n\x16AnswerRequestInitStage\x12\x10\n\x03sdp\x18\x01 \x01(\tR\x03sdp\x12J\n\x0foptional_config\x18\x02 \x01(\x0b2!.proto.rpc.webrtc.v1.WebRTCConfigR\x0eoptionalConfig\x12;\n\x08deadline\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampH\x00R\x08deadline\x88\x01\x01B\x0b\n\t_deadline"[\n\x18AnswerRequestUpdateStage\x12?\n\tcandidate\x18\x01 \x01(\x0b2!.proto.rpc.webrtc.v1.ICECandidateR\tcandidate"\x18\n\x16AnswerRequestDoneStage"E\n\x17AnswerRequestErrorStage\x12*\n\x06status\x18\x01 \x01(\x0b2\x12.google.rpc.StatusR\x06status"\x1d\n\x1bAnswerRequestHeartbeatStage"\x93\x03\n\rAnswerRequest\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12A\n\x04init\x18\x02 \x01(\x0b2+.proto.rpc.webrtc.v1.AnswerRequestInitStageH\x00R\x04init\x12G\n\x06update\x18\x03 \x01(\x0b2-.proto.rpc.webrtc.v1.AnswerRequestUpdateStageH\x00R\x06update\x12A\n\x04done\x18\x04 \x01(\x0b2+.proto.rpc.webrtc.v1.AnswerRequestDoneStageH\x00R\x04done\x12D\n\x05error\x18\x05 \x01(\x0b2,.proto.rpc.webrtc.v1.AnswerRequestErrorStageH\x00R\x05error\x12P\n\theartbeat\x18\x06 \x01(\x0b20.proto.rpc.webrtc.v1.AnswerRequestHeartbeatStageH\x00R\theartbeatB\x07\n\x05stage"+\n\x17AnswerResponseInitStage\x12\x10\n\x03sdp\x18\x01 \x01(\tR\x03sdp"\\\n\x19AnswerResponseUpdateStage\x12?\n\tcandidate\x18\x01 \x01(\x0b2!.proto.rpc.webrtc.v1.ICECandidateR\tcandidate"\x19\n\x17AnswerResponseDoneStage"F\n\x18AnswerResponseErrorStage\x12*\n\x06status\x18\x01 \x01(\x0b2\x12.google.rpc.StatusR\x06status"\xc6\x02\n\x0eAnswerResponse\x12\x12\n\x04uuid\x18\x01 \x01(\tR\x04uuid\x12B\n\x04init\x18\x02 \x01(\x0b2,.proto.rpc.webrtc.v1.AnswerResponseInitStageH\x00R\x04init\x12H\n\x06update\x18\x03 \x01(\x0b2..proto.rpc.webrtc.v1.AnswerResponseUpdateStageH\x00R\x06update\x12B\n\x04done\x18\x04 \x01(\x0b2,.proto.rpc.webrtc.v1.AnswerResponseDoneStageH\x00R\x04done\x12E\n\x05error\x18\x05 \x01(\x0b2-.proto.rpc.webrtc.v1.AnswerResponseErrorStageH\x00R\x05errorB\x07\n\x05stage"\x1d\n\x1bOptionalWebRTCConfigRequest"Y\n\x1cOptionalWebRTCConfigResponse\x129\n\x06config\x18\x01 \x01(\x0b2!.proto.rpc.webrtc.v1.WebRTCConfigR\x06config2\x86\x04\n\x10SignalingService\x12j\n\x04Call\x12 .proto.rpc.webrtc.v1.CallRequest\x1a!.proto.rpc.webrtc.v1.CallResponse"\x1b\x82\xd3\xe4\x93\x02\x15"\x13/rpc/webrtc/v1/call0\x01\x12\x81\x01\n\nCallUpdate\x12&.proto.rpc.webrtc.v1.CallUpdateRequest\x1a\'.proto.rpc.webrtc.v1.CallUpdateResponse""\x82\xd3\xe4\x93\x02\x1c\x1a\x1a/rpc/webrtc/v1/call_update\x12U\n\x06Answer\x12#.proto.rpc.webrtc.v1.AnswerResponse\x1a".proto.rpc.webrtc.v1.AnswerRequest(\x010\x01\x12\xaa\x01\n\x14OptionalWebRTCConfig\x120.proto.rpc.webrtc.v1.OptionalWebRTCConfigRequest\x1a1.proto.rpc.webrtc.v1.OptionalWebRTCConfigResponse"-\x82\xd3\xe4\x93\x02\'\x12%/rpc/webrtc/v1/optional_webrtc_configB\'Z%go.viam.com/utils/proto/rpc/webrtc/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'proto.rpc.webrtc.v1.signaling_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z%go.viam.com/utils/proto/rpc/webrtc/v1' + _globals['_SIGNALINGSERVICE'].methods_by_name['Call']._loaded_options = None + _globals['_SIGNALINGSERVICE'].methods_by_name['Call']._serialized_options = b'\x82\xd3\xe4\x93\x02\x15"\x13/rpc/webrtc/v1/call' + _globals['_SIGNALINGSERVICE'].methods_by_name['CallUpdate']._loaded_options = None + _globals['_SIGNALINGSERVICE'].methods_by_name['CallUpdate']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1c\x1a\x1a/rpc/webrtc/v1/call_update' + _globals['_SIGNALINGSERVICE'].methods_by_name['OptionalWebRTCConfig']._loaded_options = None + _globals['_SIGNALINGSERVICE'].methods_by_name['OptionalWebRTCConfig']._serialized_options = b"\x82\xd3\xe4\x93\x02'\x12%/rpc/webrtc/v1/optional_webrtc_config" + _globals['_ICECANDIDATE']._serialized_start = 149 + _globals['_ICECANDIDATE']._serialized_end = 372 + _globals['_CALLREQUEST']._serialized_start = 374 + _globals['_CALLREQUEST']._serialized_end = 446 + _globals['_CALLRESPONSEINITSTAGE']._serialized_start = 448 + _globals['_CALLRESPONSEINITSTAGE']._serialized_end = 489 + _globals['_CALLRESPONSEUPDATESTAGE']._serialized_start = 491 + _globals['_CALLRESPONSEUPDATESTAGE']._serialized_end = 581 + _globals['_CALLRESPONSE']._serialized_start = 584 + _globals['_CALLRESPONSE']._serialized_end = 765 + _globals['_CALLUPDATEREQUEST']._serialized_start = 768 + _globals['_CALLUPDATEREQUEST']._serialized_end = 950 + _globals['_CALLUPDATERESPONSE']._serialized_start = 952 + _globals['_CALLUPDATERESPONSE']._serialized_end = 972 + _globals['_ICESERVER']._serialized_start = 974 + _globals['_ICESERVER']._serialized_end = 1065 + _globals['_WEBRTCCONFIG']._serialized_start = 1068 + _globals['_WEBRTCCONFIG']._serialized_end = 1209 + _globals['_ANSWERREQUESTINITSTAGE']._serialized_start = 1212 + _globals['_ANSWERREQUESTINITSTAGE']._serialized_end = 1404 + _globals['_ANSWERREQUESTUPDATESTAGE']._serialized_start = 1406 + _globals['_ANSWERREQUESTUPDATESTAGE']._serialized_end = 1497 + _globals['_ANSWERREQUESTDONESTAGE']._serialized_start = 1499 + _globals['_ANSWERREQUESTDONESTAGE']._serialized_end = 1523 + _globals['_ANSWERREQUESTERRORSTAGE']._serialized_start = 1525 + _globals['_ANSWERREQUESTERRORSTAGE']._serialized_end = 1594 + _globals['_ANSWERREQUESTHEARTBEATSTAGE']._serialized_start = 1596 + _globals['_ANSWERREQUESTHEARTBEATSTAGE']._serialized_end = 1625 + _globals['_ANSWERREQUEST']._serialized_start = 1628 + _globals['_ANSWERREQUEST']._serialized_end = 2031 + _globals['_ANSWERRESPONSEINITSTAGE']._serialized_start = 2033 + _globals['_ANSWERRESPONSEINITSTAGE']._serialized_end = 2076 + _globals['_ANSWERRESPONSEUPDATESTAGE']._serialized_start = 2078 + _globals['_ANSWERRESPONSEUPDATESTAGE']._serialized_end = 2170 + _globals['_ANSWERRESPONSEDONESTAGE']._serialized_start = 2172 + _globals['_ANSWERRESPONSEDONESTAGE']._serialized_end = 2197 + _globals['_ANSWERRESPONSEERRORSTAGE']._serialized_start = 2199 + _globals['_ANSWERRESPONSEERRORSTAGE']._serialized_end = 2269 + _globals['_ANSWERRESPONSE']._serialized_start = 2272 + _globals['_ANSWERRESPONSE']._serialized_end = 2598 + _globals['_OPTIONALWEBRTCCONFIGREQUEST']._serialized_start = 2600 + _globals['_OPTIONALWEBRTCCONFIGREQUEST']._serialized_end = 2629 + _globals['_OPTIONALWEBRTCCONFIGRESPONSE']._serialized_start = 2631 + _globals['_OPTIONALWEBRTCCONFIGRESPONSE']._serialized_end = 2720 + _globals['_SIGNALINGSERVICE']._serialized_start = 2723 + _globals['_SIGNALINGSERVICE']._serialized_end = 3241 \ No newline at end of file diff --git a/src/viam/gen/proto/rpc/webrtc/v1/signaling_pb2.pyi b/src/viam/gen/proto/rpc/webrtc/v1/signaling_pb2.pyi index 4b1e89a67..f46f22132 100644 --- a/src/viam/gen/proto/rpc/webrtc/v1/signaling_pb2.pyi +++ b/src/viam/gen/proto/rpc/webrtc/v1/signaling_pb2.pyi @@ -7,15 +7,12 @@ import collections.abc import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.message +import google.protobuf.timestamp_pb2 import google.rpc.status_pb2 -import sys import typing -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class ICECandidate(google.protobuf.message.Message): """ICECandidate represents an ICE candidate. From https://github.com/pion/webrtc/blob/5f6baf73255598a7b4a7c9400bb0381acc9aa3dc/icecandidateinit.go @@ -33,25 +30,26 @@ class ICECandidate(google.protobuf.message.Message): def __init__(self, *, candidate: builtins.str=..., sdp_mid: builtins.str | None=..., sdpm_line_index: builtins.int | None=..., username_fragment: builtins.str | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['_sdp_mid', b'_sdp_mid', '_sdpm_line_index', b'_sdpm_line_index', '_username_fragment', b'_username_fragment', 'sdp_mid', b'sdp_mid', 'sdpm_line_index', b'sdpm_line_index', 'username_fragment', b'username_fragment']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['_sdp_mid', b'_sdp_mid', '_sdpm_line_index', b'_sdpm_line_index', '_username_fragment', b'_username_fragment', 'sdp_mid', b'sdp_mid', 'sdpm_line_index', b'sdpm_line_index', 'username_fragment', b'username_fragment']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['_sdp_mid', b'_sdp_mid', '_sdpm_line_index', b'_sdpm_line_index', '_username_fragment', b'_username_fragment', 'candidate', b'candidate', 'sdp_mid', b'sdp_mid', 'sdpm_line_index', b'sdpm_line_index', 'username_fragment', b'username_fragment']) -> None: + def ClearField(self, field_name: typing.Literal['_sdp_mid', b'_sdp_mid', '_sdpm_line_index', b'_sdpm_line_index', '_username_fragment', b'_username_fragment', 'candidate', b'candidate', 'sdp_mid', b'sdp_mid', 'sdpm_line_index', b'sdpm_line_index', 'username_fragment', b'username_fragment']) -> None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal['_sdp_mid', b'_sdp_mid']) -> typing_extensions.Literal['sdp_mid'] | None: + def WhichOneof(self, oneof_group: typing.Literal['_sdp_mid', b'_sdp_mid']) -> typing.Literal['sdp_mid'] | None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal['_sdpm_line_index', b'_sdpm_line_index']) -> typing_extensions.Literal['sdpm_line_index'] | None: + def WhichOneof(self, oneof_group: typing.Literal['_sdpm_line_index', b'_sdpm_line_index']) -> typing.Literal['sdpm_line_index'] | None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal['_username_fragment', b'_username_fragment']) -> typing_extensions.Literal['username_fragment'] | None: + def WhichOneof(self, oneof_group: typing.Literal['_username_fragment', b'_username_fragment']) -> typing.Literal['username_fragment'] | None: ... global___ICECandidate = ICECandidate +@typing.final class CallRequest(google.protobuf.message.Message): """CallRequest is the SDP offer that the controlling side is making.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -64,10 +62,11 @@ class CallRequest(google.protobuf.message.Message): def __init__(self, *, sdp: builtins.str=..., disable_trickle: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['disable_trickle', b'disable_trickle', 'sdp', b'sdp']) -> None: + def ClearField(self, field_name: typing.Literal['disable_trickle', b'disable_trickle', 'sdp', b'sdp']) -> None: ... global___CallRequest = CallRequest +@typing.final class CallResponseInitStage(google.protobuf.message.Message): """CallResponseInitStage is the first and a one time stage that represents the initial response to starting a call. @@ -79,10 +78,11 @@ class CallResponseInitStage(google.protobuf.message.Message): def __init__(self, *, sdp: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['sdp', b'sdp']) -> None: + def ClearField(self, field_name: typing.Literal['sdp', b'sdp']) -> None: ... global___CallResponseInitStage = CallResponseInitStage +@typing.final class CallResponseUpdateStage(google.protobuf.message.Message): """CallResponseUpdateStage is multiply used to trickle in ICE candidates from the controlled (answering) side. @@ -97,13 +97,14 @@ class CallResponseUpdateStage(google.protobuf.message.Message): def __init__(self, *, candidate: global___ICECandidate | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['candidate', b'candidate']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['candidate', b'candidate']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['candidate', b'candidate']) -> None: + def ClearField(self, field_name: typing.Literal['candidate', b'candidate']) -> None: ... global___CallResponseUpdateStage = CallResponseUpdateStage +@typing.final class CallResponse(google.protobuf.message.Message): """CallResponse is the SDP answer that the controlled side responds with.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -123,16 +124,17 @@ class CallResponse(google.protobuf.message.Message): def __init__(self, *, uuid: builtins.str=..., init: global___CallResponseInitStage | None=..., update: global___CallResponseUpdateStage | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['init', b'init', 'stage', b'stage', 'update', b'update']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['init', b'init', 'stage', b'stage', 'update', b'update']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['init', b'init', 'stage', b'stage', 'update', b'update', 'uuid', b'uuid']) -> None: + def ClearField(self, field_name: typing.Literal['init', b'init', 'stage', b'stage', 'update', b'update', 'uuid', b'uuid']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['stage', b'stage']) -> typing_extensions.Literal['init', 'update'] | None: + def WhichOneof(self, oneof_group: typing.Literal['stage', b'stage']) -> typing.Literal['init', 'update'] | None: ... global___CallResponse = CallResponse +@typing.final class CallUpdateRequest(google.protobuf.message.Message): """CallUpdateRequest updates the call with additional info to the controlled side.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -141,11 +143,11 @@ class CallUpdateRequest(google.protobuf.message.Message): DONE_FIELD_NUMBER: builtins.int ERROR_FIELD_NUMBER: builtins.int uuid: builtins.str + done: builtins.bool @property def candidate(self) -> global___ICECandidate: ... - done: builtins.bool @property def error(self) -> google.rpc.status_pb2.Status: @@ -154,16 +156,17 @@ class CallUpdateRequest(google.protobuf.message.Message): def __init__(self, *, uuid: builtins.str=..., candidate: global___ICECandidate | None=..., done: builtins.bool=..., error: google.rpc.status_pb2.Status | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['candidate', b'candidate', 'done', b'done', 'error', b'error', 'update', b'update']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['candidate', b'candidate', 'done', b'done', 'error', b'error', 'update', b'update']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['candidate', b'candidate', 'done', b'done', 'error', b'error', 'update', b'update', 'uuid', b'uuid']) -> None: + def ClearField(self, field_name: typing.Literal['candidate', b'candidate', 'done', b'done', 'error', b'error', 'update', b'update', 'uuid', b'uuid']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['update', b'update']) -> typing_extensions.Literal['candidate', 'done', 'error'] | None: + def WhichOneof(self, oneof_group: typing.Literal['update', b'update']) -> typing.Literal['candidate', 'done', 'error'] | None: ... global___CallUpdateRequest = CallUpdateRequest +@typing.final class CallUpdateResponse(google.protobuf.message.Message): """CallUpdateResponse contains nothing in response to a call update.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -172,45 +175,48 @@ class CallUpdateResponse(google.protobuf.message.Message): ... global___CallUpdateResponse = CallUpdateResponse +@typing.final class ICEServer(google.protobuf.message.Message): """ICEServer describes an ICE server.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor URLS_FIELD_NUMBER: builtins.int USERNAME_FIELD_NUMBER: builtins.int CREDENTIAL_FIELD_NUMBER: builtins.int + username: builtins.str + credential: builtins.str @property def urls(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... - username: builtins.str - credential: builtins.str def __init__(self, *, urls: collections.abc.Iterable[builtins.str] | None=..., username: builtins.str=..., credential: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['credential', b'credential', 'urls', b'urls', 'username', b'username']) -> None: + def ClearField(self, field_name: typing.Literal['credential', b'credential', 'urls', b'urls', 'username', b'username']) -> None: ... global___ICEServer = ICEServer +@typing.final class WebRTCConfig(google.protobuf.message.Message): """WebRTCConfig represents parts of a WebRTC config.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor ADDITIONAL_ICE_SERVERS_FIELD_NUMBER: builtins.int DISABLE_TRICKLE_FIELD_NUMBER: builtins.int + disable_trickle: builtins.bool + 'disable_trickle indicates if Trickle ICE should be used. Currently, both\n sides must both respect this setting.\n ' @property def additional_ice_servers(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ICEServer]: ... - disable_trickle: builtins.bool - 'disable_trickle indicates if Trickle ICE should be used. Currently, both\n sides must both respect this setting.\n ' def __init__(self, *, additional_ice_servers: collections.abc.Iterable[global___ICEServer] | None=..., disable_trickle: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['additional_ice_servers', b'additional_ice_servers', 'disable_trickle', b'disable_trickle']) -> None: + def ClearField(self, field_name: typing.Literal['additional_ice_servers', b'additional_ice_servers', 'disable_trickle', b'disable_trickle']) -> None: ... global___WebRTCConfig = WebRTCConfig +@typing.final class AnswerRequestInitStage(google.protobuf.message.Message): """AnswerRequestInitStage is the first and a one time stage that represents the callers initial SDP request to the controlled (answerer) side. @@ -218,22 +224,31 @@ class AnswerRequestInitStage(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor SDP_FIELD_NUMBER: builtins.int OPTIONAL_CONFIG_FIELD_NUMBER: builtins.int + DEADLINE_FIELD_NUMBER: builtins.int sdp: builtins.str @property def optional_config(self) -> global___WebRTCConfig: ... - def __init__(self, *, sdp: builtins.str=..., optional_config: global___WebRTCConfig | None=...) -> None: + @property + def deadline(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, sdp: builtins.str=..., optional_config: global___WebRTCConfig | None=..., deadline: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_deadline', b'_deadline', 'deadline', b'deadline', 'optional_config', b'optional_config']) -> builtins.bool: ... - def HasField(self, field_name: typing_extensions.Literal['optional_config', b'optional_config']) -> builtins.bool: + def ClearField(self, field_name: typing.Literal['_deadline', b'_deadline', 'deadline', b'deadline', 'optional_config', b'optional_config', 'sdp', b'sdp']) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['optional_config', b'optional_config', 'sdp', b'sdp']) -> None: + def WhichOneof(self, oneof_group: typing.Literal['_deadline', b'_deadline']) -> typing.Literal['deadline'] | None: ... global___AnswerRequestInitStage = AnswerRequestInitStage +@typing.final class AnswerRequestUpdateStage(google.protobuf.message.Message): """AnswerRequestUpdateStage is multiply used to trickle in ICE candidates to the controlled (answerer) side. @@ -248,13 +263,14 @@ class AnswerRequestUpdateStage(google.protobuf.message.Message): def __init__(self, *, candidate: global___ICECandidate | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['candidate', b'candidate']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['candidate', b'candidate']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['candidate', b'candidate']) -> None: + def ClearField(self, field_name: typing.Literal['candidate', b'candidate']) -> None: ... global___AnswerRequestUpdateStage = AnswerRequestUpdateStage +@typing.final class AnswerRequestDoneStage(google.protobuf.message.Message): """AnswerRequestDoneStage indicates the controller is done responding with candidates.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -263,6 +279,7 @@ class AnswerRequestDoneStage(google.protobuf.message.Message): ... global___AnswerRequestDoneStage = AnswerRequestDoneStage +@typing.final class AnswerRequestErrorStage(google.protobuf.message.Message): """AnswerRequestErrorStage indicates the exchange has failed with an error.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -275,13 +292,23 @@ class AnswerRequestErrorStage(google.protobuf.message.Message): def __init__(self, *, status: google.rpc.status_pb2.Status | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['status', b'status']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['status', b'status']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['status', b'status']) -> None: + def ClearField(self, field_name: typing.Literal['status', b'status']) -> None: ... global___AnswerRequestErrorStage = AnswerRequestErrorStage +@typing.final +class AnswerRequestHeartbeatStage(google.protobuf.message.Message): + """AnswerRequestHeartbeatStage is sent periodically to verify liveness of answerer.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___AnswerRequestHeartbeatStage = AnswerRequestHeartbeatStage + +@typing.final class AnswerRequest(google.protobuf.message.Message): """AnswerRequest is the SDP offer that the controlling side is making via the answering stream. @@ -292,6 +319,7 @@ class AnswerRequest(google.protobuf.message.Message): UPDATE_FIELD_NUMBER: builtins.int DONE_FIELD_NUMBER: builtins.int ERROR_FIELD_NUMBER: builtins.int + HEARTBEAT_FIELD_NUMBER: builtins.int uuid: builtins.str @property @@ -310,19 +338,24 @@ class AnswerRequest(google.protobuf.message.Message): def error(self) -> global___AnswerRequestErrorStage: """error is sent any time before done""" - def __init__(self, *, uuid: builtins.str=..., init: global___AnswerRequestInitStage | None=..., update: global___AnswerRequestUpdateStage | None=..., done: global___AnswerRequestDoneStage | None=..., error: global___AnswerRequestErrorStage | None=...) -> None: + @property + def heartbeat(self) -> global___AnswerRequestHeartbeatStage: + """heartbeat is sent periodically to verify liveness of answerer""" + + def __init__(self, *, uuid: builtins.str=..., init: global___AnswerRequestInitStage | None=..., update: global___AnswerRequestUpdateStage | None=..., done: global___AnswerRequestDoneStage | None=..., error: global___AnswerRequestErrorStage | None=..., heartbeat: global___AnswerRequestHeartbeatStage | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['done', b'done', 'error', b'error', 'init', b'init', 'stage', b'stage', 'update', b'update']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['done', b'done', 'error', b'error', 'heartbeat', b'heartbeat', 'init', b'init', 'stage', b'stage', 'update', b'update']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['done', b'done', 'error', b'error', 'init', b'init', 'stage', b'stage', 'update', b'update', 'uuid', b'uuid']) -> None: + def ClearField(self, field_name: typing.Literal['done', b'done', 'error', b'error', 'heartbeat', b'heartbeat', 'init', b'init', 'stage', b'stage', 'update', b'update', 'uuid', b'uuid']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['stage', b'stage']) -> typing_extensions.Literal['init', 'update', 'done', 'error'] | None: + def WhichOneof(self, oneof_group: typing.Literal['stage', b'stage']) -> typing.Literal['init', 'update', 'done', 'error', 'heartbeat'] | None: ... global___AnswerRequest = AnswerRequest +@typing.final class AnswerResponseInitStage(google.protobuf.message.Message): """AnswerResponseInitStage is the first and a one time stage that represents the answerers initial SDP response to the controlling side. @@ -334,10 +367,11 @@ class AnswerResponseInitStage(google.protobuf.message.Message): def __init__(self, *, sdp: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['sdp', b'sdp']) -> None: + def ClearField(self, field_name: typing.Literal['sdp', b'sdp']) -> None: ... global___AnswerResponseInitStage = AnswerResponseInitStage +@typing.final class AnswerResponseUpdateStage(google.protobuf.message.Message): """AnswerResponseUpdateStage is multiply used to trickle in ICE candidates to the controlling side. @@ -352,13 +386,14 @@ class AnswerResponseUpdateStage(google.protobuf.message.Message): def __init__(self, *, candidate: global___ICECandidate | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['candidate', b'candidate']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['candidate', b'candidate']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['candidate', b'candidate']) -> None: + def ClearField(self, field_name: typing.Literal['candidate', b'candidate']) -> None: ... global___AnswerResponseUpdateStage = AnswerResponseUpdateStage +@typing.final class AnswerResponseDoneStage(google.protobuf.message.Message): """AnswerResponseDoneStage indicates the answerer is done responding with candidates.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -367,6 +402,7 @@ class AnswerResponseDoneStage(google.protobuf.message.Message): ... global___AnswerResponseDoneStage = AnswerResponseDoneStage +@typing.final class AnswerResponseErrorStage(google.protobuf.message.Message): """AnswerResponseErrorStage indicates the exchange has failed with an error.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -379,13 +415,14 @@ class AnswerResponseErrorStage(google.protobuf.message.Message): def __init__(self, *, status: google.rpc.status_pb2.Status | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['status', b'status']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['status', b'status']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['status', b'status']) -> None: + def ClearField(self, field_name: typing.Literal['status', b'status']) -> None: ... global___AnswerResponseErrorStage = AnswerResponseErrorStage +@typing.final class AnswerResponse(google.protobuf.message.Message): """AnswerResponse is the SDP answer that an answerer responds with.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -415,16 +452,17 @@ class AnswerResponse(google.protobuf.message.Message): def __init__(self, *, uuid: builtins.str=..., init: global___AnswerResponseInitStage | None=..., update: global___AnswerResponseUpdateStage | None=..., done: global___AnswerResponseDoneStage | None=..., error: global___AnswerResponseErrorStage | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['done', b'done', 'error', b'error', 'init', b'init', 'stage', b'stage', 'update', b'update']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['done', b'done', 'error', b'error', 'init', b'init', 'stage', b'stage', 'update', b'update']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['done', b'done', 'error', b'error', 'init', b'init', 'stage', b'stage', 'update', b'update', 'uuid', b'uuid']) -> None: + def ClearField(self, field_name: typing.Literal['done', b'done', 'error', b'error', 'init', b'init', 'stage', b'stage', 'update', b'update', 'uuid', b'uuid']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['stage', b'stage']) -> typing_extensions.Literal['init', 'update', 'done', 'error'] | None: + def WhichOneof(self, oneof_group: typing.Literal['stage', b'stage']) -> typing.Literal['init', 'update', 'done', 'error'] | None: ... global___AnswerResponse = AnswerResponse +@typing.final class OptionalWebRTCConfigRequest(google.protobuf.message.Message): """OptionalWebRTCConfigRequest is the request for getting an optional WebRTC config to use for the peer connection. @@ -435,6 +473,7 @@ class OptionalWebRTCConfigRequest(google.protobuf.message.Message): ... global___OptionalWebRTCConfigRequest = OptionalWebRTCConfigRequest +@typing.final class OptionalWebRTCConfigResponse(google.protobuf.message.Message): """OptionalWebRTCConfigResponse contains the optional WebRTC config to use for the peer connection. @@ -449,9 +488,9 @@ class OptionalWebRTCConfigResponse(google.protobuf.message.Message): def __init__(self, *, config: global___WebRTCConfig | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['config', b'config']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['config', b'config']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['config', b'config']) -> None: + def ClearField(self, field_name: typing.Literal['config', b'config']) -> None: ... global___OptionalWebRTCConfigResponse = OptionalWebRTCConfigResponse \ No newline at end of file diff --git a/src/viam/gen/provisioning/__init__.py b/src/viam/gen/provisioning/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/provisioning/v1/__init__.py b/src/viam/gen/provisioning/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/provisioning/v1/provisioning_grpc.py b/src/viam/gen/provisioning/v1/provisioning_grpc.py new file mode 100644 index 000000000..c0ea3514a --- /dev/null +++ b/src/viam/gen/provisioning/v1/provisioning_grpc.py @@ -0,0 +1,51 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from ... import provisioning + +class ProvisioningServiceBase(abc.ABC): + + @abc.abstractmethod + async def GetSmartMachineStatus(self, stream: 'grpclib.server.Stream[provisioning.v1.provisioning_pb2.GetSmartMachineStatusRequest, provisioning.v1.provisioning_pb2.GetSmartMachineStatusResponse]') -> None: + pass + + @abc.abstractmethod + async def SetNetworkCredentials(self, stream: 'grpclib.server.Stream[provisioning.v1.provisioning_pb2.SetNetworkCredentialsRequest, provisioning.v1.provisioning_pb2.SetNetworkCredentialsResponse]') -> None: + pass + + @abc.abstractmethod + async def SetSmartMachineCredentials(self, stream: 'grpclib.server.Stream[provisioning.v1.provisioning_pb2.SetSmartMachineCredentialsRequest, provisioning.v1.provisioning_pb2.SetSmartMachineCredentialsResponse]') -> None: + pass + + @abc.abstractmethod + async def GetNetworkList(self, stream: 'grpclib.server.Stream[provisioning.v1.provisioning_pb2.GetNetworkListRequest, provisioning.v1.provisioning_pb2.GetNetworkListResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.provisioning.v1.ProvisioningService/GetSmartMachineStatus': grpclib.const.Handler(self.GetSmartMachineStatus, grpclib.const.Cardinality.UNARY_UNARY, provisioning.v1.provisioning_pb2.GetSmartMachineStatusRequest, provisioning.v1.provisioning_pb2.GetSmartMachineStatusResponse), '/viam.provisioning.v1.ProvisioningService/SetNetworkCredentials': grpclib.const.Handler(self.SetNetworkCredentials, grpclib.const.Cardinality.UNARY_UNARY, provisioning.v1.provisioning_pb2.SetNetworkCredentialsRequest, provisioning.v1.provisioning_pb2.SetNetworkCredentialsResponse), '/viam.provisioning.v1.ProvisioningService/SetSmartMachineCredentials': grpclib.const.Handler(self.SetSmartMachineCredentials, grpclib.const.Cardinality.UNARY_UNARY, provisioning.v1.provisioning_pb2.SetSmartMachineCredentialsRequest, provisioning.v1.provisioning_pb2.SetSmartMachineCredentialsResponse), '/viam.provisioning.v1.ProvisioningService/GetNetworkList': grpclib.const.Handler(self.GetNetworkList, grpclib.const.Cardinality.UNARY_UNARY, provisioning.v1.provisioning_pb2.GetNetworkListRequest, provisioning.v1.provisioning_pb2.GetNetworkListResponse)} + +class UnimplementedProvisioningServiceBase(ProvisioningServiceBase): + + async def GetSmartMachineStatus(self, stream: 'grpclib.server.Stream[provisioning.v1.provisioning_pb2.GetSmartMachineStatusRequest, provisioning.v1.provisioning_pb2.GetSmartMachineStatusResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetNetworkCredentials(self, stream: 'grpclib.server.Stream[provisioning.v1.provisioning_pb2.SetNetworkCredentialsRequest, provisioning.v1.provisioning_pb2.SetNetworkCredentialsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetSmartMachineCredentials(self, stream: 'grpclib.server.Stream[provisioning.v1.provisioning_pb2.SetSmartMachineCredentialsRequest, provisioning.v1.provisioning_pb2.SetSmartMachineCredentialsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetNetworkList(self, stream: 'grpclib.server.Stream[provisioning.v1.provisioning_pb2.GetNetworkListRequest, provisioning.v1.provisioning_pb2.GetNetworkListResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class ProvisioningServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.GetSmartMachineStatus = grpclib.client.UnaryUnaryMethod(channel, '/viam.provisioning.v1.ProvisioningService/GetSmartMachineStatus', provisioning.v1.provisioning_pb2.GetSmartMachineStatusRequest, provisioning.v1.provisioning_pb2.GetSmartMachineStatusResponse) + self.SetNetworkCredentials = grpclib.client.UnaryUnaryMethod(channel, '/viam.provisioning.v1.ProvisioningService/SetNetworkCredentials', provisioning.v1.provisioning_pb2.SetNetworkCredentialsRequest, provisioning.v1.provisioning_pb2.SetNetworkCredentialsResponse) + self.SetSmartMachineCredentials = grpclib.client.UnaryUnaryMethod(channel, '/viam.provisioning.v1.ProvisioningService/SetSmartMachineCredentials', provisioning.v1.provisioning_pb2.SetSmartMachineCredentialsRequest, provisioning.v1.provisioning_pb2.SetSmartMachineCredentialsResponse) + self.GetNetworkList = grpclib.client.UnaryUnaryMethod(channel, '/viam.provisioning.v1.ProvisioningService/GetNetworkList', provisioning.v1.provisioning_pb2.GetNetworkListRequest, provisioning.v1.provisioning_pb2.GetNetworkListResponse) \ No newline at end of file diff --git a/src/viam/gen/provisioning/v1/provisioning_pb2.py b/src/viam/gen/provisioning/v1/provisioning_pb2.py new file mode 100644 index 000000000..e13d5e9c1 --- /dev/null +++ b/src/viam/gen/provisioning/v1/provisioning_pb2.py @@ -0,0 +1,39 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'provisioning/v1/provisioning.proto') +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n"provisioning/v1/provisioning.proto\x12\x14viam.provisioning.v1"\x1e\n\x1cGetSmartMachineStatusRequest"\xcb\x02\n\x1dGetSmartMachineStatusResponse\x12S\n\x11provisioning_info\x18\x01 \x01(\x0b2&.viam.provisioning.v1.ProvisioningInfoR\x10provisioningInfo\x12A\n\x1dhas_smart_machine_credentials\x18\x02 \x01(\x08R\x1ahasSmartMachineCredentials\x12\x1b\n\tis_online\x18\x03 \x01(\x08R\x08isOnline\x12]\n\x19latest_connection_attempt\x18\x04 \x01(\x0b2!.viam.provisioning.v1.NetworkInfoR\x17latestConnectionAttempt\x12\x16\n\x06errors\x18\x05 \x03(\tR\x06errors"X\n\x1cSetNetworkCredentialsRequest\x12\x12\n\x04type\x18\x01 \x01(\tR\x04type\x12\x12\n\x04ssid\x18\x02 \x01(\tR\x04ssid\x12\x10\n\x03psk\x18\x03 \x01(\tR\x03psk"\x1f\n\x1dSetNetworkCredentialsResponse"\\\n!SetSmartMachineCredentialsRequest\x127\n\x05cloud\x18\x01 \x01(\x0b2!.viam.provisioning.v1.CloudConfigR\x05cloud"$\n"SetSmartMachineCredentialsResponse"\x17\n\x15GetNetworkListRequest"W\n\x16GetNetworkListResponse\x12=\n\x08networks\x18\x01 \x03(\x0b2!.viam.provisioning.v1.NetworkInfoR\x08networks"m\n\x10ProvisioningInfo\x12\x1f\n\x0bfragment_id\x18\x01 \x01(\tR\nfragmentId\x12\x14\n\x05model\x18\x02 \x01(\tR\x05model\x12"\n\x0cmanufacturer\x18\x03 \x01(\tR\x0cmanufacturer"\xa6\x01\n\x0bNetworkInfo\x12\x12\n\x04type\x18\x01 \x01(\tR\x04type\x12\x12\n\x04ssid\x18\x02 \x01(\tR\x04ssid\x12\x1a\n\x08security\x18\x03 \x01(\tR\x08security\x12\x16\n\x06signal\x18\x04 \x01(\x05R\x06signal\x12\x1c\n\tconnected\x18\x05 \x01(\x08R\tconnected\x12\x1d\n\nlast_error\x18\x06 \x01(\tR\tlastError"V\n\x0bCloudConfig\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n\x06secret\x18\x02 \x01(\tR\x06secret\x12\x1f\n\x0bapp_address\x18\x03 \x01(\tR\nappAddress2\x9a\x04\n\x13ProvisioningService\x12\x80\x01\n\x15GetSmartMachineStatus\x122.viam.provisioning.v1.GetSmartMachineStatusRequest\x1a3.viam.provisioning.v1.GetSmartMachineStatusResponse\x12\x80\x01\n\x15SetNetworkCredentials\x122.viam.provisioning.v1.SetNetworkCredentialsRequest\x1a3.viam.provisioning.v1.SetNetworkCredentialsResponse\x12\x8f\x01\n\x1aSetSmartMachineCredentials\x127.viam.provisioning.v1.SetSmartMachineCredentialsRequest\x1a8.viam.provisioning.v1.SetSmartMachineCredentialsResponse\x12k\n\x0eGetNetworkList\x12+.viam.provisioning.v1.GetNetworkListRequest\x1a,.viam.provisioning.v1.GetNetworkListResponseB!Z\x1fgo.viam.com/api/provisioning/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'provisioning.v1.provisioning_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z\x1fgo.viam.com/api/provisioning/v1' + _globals['_GETSMARTMACHINESTATUSREQUEST']._serialized_start = 60 + _globals['_GETSMARTMACHINESTATUSREQUEST']._serialized_end = 90 + _globals['_GETSMARTMACHINESTATUSRESPONSE']._serialized_start = 93 + _globals['_GETSMARTMACHINESTATUSRESPONSE']._serialized_end = 424 + _globals['_SETNETWORKCREDENTIALSREQUEST']._serialized_start = 426 + _globals['_SETNETWORKCREDENTIALSREQUEST']._serialized_end = 514 + _globals['_SETNETWORKCREDENTIALSRESPONSE']._serialized_start = 516 + _globals['_SETNETWORKCREDENTIALSRESPONSE']._serialized_end = 547 + _globals['_SETSMARTMACHINECREDENTIALSREQUEST']._serialized_start = 549 + _globals['_SETSMARTMACHINECREDENTIALSREQUEST']._serialized_end = 641 + _globals['_SETSMARTMACHINECREDENTIALSRESPONSE']._serialized_start = 643 + _globals['_SETSMARTMACHINECREDENTIALSRESPONSE']._serialized_end = 679 + _globals['_GETNETWORKLISTREQUEST']._serialized_start = 681 + _globals['_GETNETWORKLISTREQUEST']._serialized_end = 704 + _globals['_GETNETWORKLISTRESPONSE']._serialized_start = 706 + _globals['_GETNETWORKLISTRESPONSE']._serialized_end = 793 + _globals['_PROVISIONINGINFO']._serialized_start = 795 + _globals['_PROVISIONINGINFO']._serialized_end = 904 + _globals['_NETWORKINFO']._serialized_start = 907 + _globals['_NETWORKINFO']._serialized_end = 1073 + _globals['_CLOUDCONFIG']._serialized_start = 1075 + _globals['_CLOUDCONFIG']._serialized_end = 1161 + _globals['_PROVISIONINGSERVICE']._serialized_start = 1164 + _globals['_PROVISIONINGSERVICE']._serialized_end = 1702 \ No newline at end of file diff --git a/src/viam/gen/provisioning/v1/provisioning_pb2.pyi b/src/viam/gen/provisioning/v1/provisioning_pb2.pyi new file mode 100644 index 000000000..1998bbebf --- /dev/null +++ b/src/viam/gen/provisioning/v1/provisioning_pb2.pyi @@ -0,0 +1,188 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class GetSmartMachineStatusRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___GetSmartMachineStatusRequest = GetSmartMachineStatusRequest + +@typing.final +class GetSmartMachineStatusResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PROVISIONING_INFO_FIELD_NUMBER: builtins.int + HAS_SMART_MACHINE_CREDENTIALS_FIELD_NUMBER: builtins.int + IS_ONLINE_FIELD_NUMBER: builtins.int + LATEST_CONNECTION_ATTEMPT_FIELD_NUMBER: builtins.int + ERRORS_FIELD_NUMBER: builtins.int + has_smart_machine_credentials: builtins.bool + is_online: builtins.bool + + @property + def provisioning_info(self) -> global___ProvisioningInfo: + ... + + @property + def latest_connection_attempt(self) -> global___NetworkInfo: + ... + + @property + def errors(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, provisioning_info: global___ProvisioningInfo | None=..., has_smart_machine_credentials: builtins.bool=..., is_online: builtins.bool=..., latest_connection_attempt: global___NetworkInfo | None=..., errors: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['latest_connection_attempt', b'latest_connection_attempt', 'provisioning_info', b'provisioning_info']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['errors', b'errors', 'has_smart_machine_credentials', b'has_smart_machine_credentials', 'is_online', b'is_online', 'latest_connection_attempt', b'latest_connection_attempt', 'provisioning_info', b'provisioning_info']) -> None: + ... +global___GetSmartMachineStatusResponse = GetSmartMachineStatusResponse + +@typing.final +class SetNetworkCredentialsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TYPE_FIELD_NUMBER: builtins.int + SSID_FIELD_NUMBER: builtins.int + PSK_FIELD_NUMBER: builtins.int + type: builtins.str + ssid: builtins.str + psk: builtins.str + + def __init__(self, *, type: builtins.str=..., ssid: builtins.str=..., psk: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['psk', b'psk', 'ssid', b'ssid', 'type', b'type']) -> None: + ... +global___SetNetworkCredentialsRequest = SetNetworkCredentialsRequest + +@typing.final +class SetNetworkCredentialsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___SetNetworkCredentialsResponse = SetNetworkCredentialsResponse + +@typing.final +class SetSmartMachineCredentialsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CLOUD_FIELD_NUMBER: builtins.int + + @property + def cloud(self) -> global___CloudConfig: + ... + + def __init__(self, *, cloud: global___CloudConfig | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['cloud', b'cloud']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['cloud', b'cloud']) -> None: + ... +global___SetSmartMachineCredentialsRequest = SetSmartMachineCredentialsRequest + +@typing.final +class SetSmartMachineCredentialsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___SetSmartMachineCredentialsResponse = SetSmartMachineCredentialsResponse + +@typing.final +class GetNetworkListRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___GetNetworkListRequest = GetNetworkListRequest + +@typing.final +class GetNetworkListResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NETWORKS_FIELD_NUMBER: builtins.int + + @property + def networks(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___NetworkInfo]: + ... + + def __init__(self, *, networks: collections.abc.Iterable[global___NetworkInfo] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['networks', b'networks']) -> None: + ... +global___GetNetworkListResponse = GetNetworkListResponse + +@typing.final +class ProvisioningInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAGMENT_ID_FIELD_NUMBER: builtins.int + MODEL_FIELD_NUMBER: builtins.int + MANUFACTURER_FIELD_NUMBER: builtins.int + fragment_id: builtins.str + model: builtins.str + manufacturer: builtins.str + + def __init__(self, *, fragment_id: builtins.str=..., model: builtins.str=..., manufacturer: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['fragment_id', b'fragment_id', 'manufacturer', b'manufacturer', 'model', b'model']) -> None: + ... +global___ProvisioningInfo = ProvisioningInfo + +@typing.final +class NetworkInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TYPE_FIELD_NUMBER: builtins.int + SSID_FIELD_NUMBER: builtins.int + SECURITY_FIELD_NUMBER: builtins.int + SIGNAL_FIELD_NUMBER: builtins.int + CONNECTED_FIELD_NUMBER: builtins.int + LAST_ERROR_FIELD_NUMBER: builtins.int + type: builtins.str + ssid: builtins.str + security: builtins.str + signal: builtins.int + connected: builtins.bool + last_error: builtins.str + + def __init__(self, *, type: builtins.str=..., ssid: builtins.str=..., security: builtins.str=..., signal: builtins.int=..., connected: builtins.bool=..., last_error: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['connected', b'connected', 'last_error', b'last_error', 'security', b'security', 'signal', b'signal', 'ssid', b'ssid', 'type', b'type']) -> None: + ... +global___NetworkInfo = NetworkInfo + +@typing.final +class CloudConfig(google.protobuf.message.Message): + """minimal CloudConfig to create /etc/viam.json""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + SECRET_FIELD_NUMBER: builtins.int + APP_ADDRESS_FIELD_NUMBER: builtins.int + id: builtins.str + 'SmartMachine part id' + secret: builtins.str + 'SmartMachine part secret' + app_address: builtins.str + + def __init__(self, *, id: builtins.str=..., secret: builtins.str=..., app_address: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['app_address', b'app_address', 'id', b'id', 'secret', b'secret']) -> None: + ... +global___CloudConfig = CloudConfig \ No newline at end of file diff --git a/src/viam/gen/robot/v1/robot_grpc.py b/src/viam/gen/robot/v1/robot_grpc.py index 87e673f67..a3acdeb35 100644 --- a/src/viam/gen/robot/v1/robot_grpc.py +++ b/src/viam/gen/robot/v1/robot_grpc.py @@ -2,6 +2,7 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from ... import common @@ -17,6 +18,10 @@ class RobotServiceBase(abc.ABC): async def GetOperations(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetOperationsRequest, robot.v1.robot_pb2.GetOperationsResponse]') -> None: pass + @abc.abstractmethod + async def GetSessions(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetSessionsRequest, robot.v1.robot_pb2.GetSessionsResponse]') -> None: + pass + @abc.abstractmethod async def ResourceNames(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.ResourceNamesRequest, robot.v1.robot_pb2.ResourceNamesResponse]') -> None: pass @@ -34,7 +39,7 @@ async def BlockForOperation(self, stream: 'grpclib.server.Stream[robot.v1.robot_ pass @abc.abstractmethod - async def DiscoverComponents(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.DiscoverComponentsRequest, robot.v1.robot_pb2.DiscoverComponentsResponse]') -> None: + async def GetModelsFromModules(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetModelsFromModulesRequest, robot.v1.robot_pb2.GetModelsFromModulesResponse]') -> None: pass @abc.abstractmethod @@ -45,6 +50,10 @@ async def FrameSystemConfig(self, stream: 'grpclib.server.Stream[robot.v1.robot_ async def TransformPose(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.TransformPoseRequest, robot.v1.robot_pb2.TransformPoseResponse]') -> None: pass + @abc.abstractmethod + async def TransformPCD(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.TransformPCDRequest, robot.v1.robot_pb2.TransformPCDResponse]') -> None: + pass + @abc.abstractmethod async def GetStatus(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetStatusRequest, robot.v1.robot_pb2.GetStatusResponse]') -> None: pass @@ -57,20 +66,143 @@ async def StreamStatus(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.S async def StopAll(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.StopAllRequest, robot.v1.robot_pb2.StopAllResponse]') -> None: pass + @abc.abstractmethod + async def StartSession(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.StartSessionRequest, robot.v1.robot_pb2.StartSessionResponse]') -> None: + pass + + @abc.abstractmethod + async def SendSessionHeartbeat(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.SendSessionHeartbeatRequest, robot.v1.robot_pb2.SendSessionHeartbeatResponse]') -> None: + pass + + @abc.abstractmethod + async def Log(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.LogRequest, robot.v1.robot_pb2.LogResponse]') -> None: + pass + + @abc.abstractmethod + async def GetCloudMetadata(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetCloudMetadataRequest, robot.v1.robot_pb2.GetCloudMetadataResponse]') -> None: + pass + + @abc.abstractmethod + async def RestartModule(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.RestartModuleRequest, robot.v1.robot_pb2.RestartModuleResponse]') -> None: + pass + + @abc.abstractmethod + async def Shutdown(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.ShutdownRequest, robot.v1.robot_pb2.ShutdownResponse]') -> None: + pass + + @abc.abstractmethod + async def GetMachineStatus(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetMachineStatusRequest, robot.v1.robot_pb2.GetMachineStatusResponse]') -> None: + pass + + @abc.abstractmethod + async def GetVersion(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetVersionRequest, robot.v1.robot_pb2.GetVersionResponse]') -> None: + pass + + @abc.abstractmethod + async def Tunnel(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.TunnelRequest, robot.v1.robot_pb2.TunnelResponse]') -> None: + pass + + @abc.abstractmethod + async def ListTunnels(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.ListTunnelsRequest, robot.v1.robot_pb2.ListTunnelsResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.robot.v1.RobotService/GetOperations': grpclib.const.Handler(self.GetOperations, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.GetOperationsRequest, robot.v1.robot_pb2.GetOperationsResponse), '/viam.robot.v1.RobotService/ResourceNames': grpclib.const.Handler(self.ResourceNames, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.ResourceNamesRequest, robot.v1.robot_pb2.ResourceNamesResponse), '/viam.robot.v1.RobotService/ResourceRPCSubtypes': grpclib.const.Handler(self.ResourceRPCSubtypes, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.ResourceRPCSubtypesRequest, robot.v1.robot_pb2.ResourceRPCSubtypesResponse), '/viam.robot.v1.RobotService/CancelOperation': grpclib.const.Handler(self.CancelOperation, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.CancelOperationRequest, robot.v1.robot_pb2.CancelOperationResponse), '/viam.robot.v1.RobotService/BlockForOperation': grpclib.const.Handler(self.BlockForOperation, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.BlockForOperationRequest, robot.v1.robot_pb2.BlockForOperationResponse), '/viam.robot.v1.RobotService/DiscoverComponents': grpclib.const.Handler(self.DiscoverComponents, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.DiscoverComponentsRequest, robot.v1.robot_pb2.DiscoverComponentsResponse), '/viam.robot.v1.RobotService/FrameSystemConfig': grpclib.const.Handler(self.FrameSystemConfig, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.FrameSystemConfigRequest, robot.v1.robot_pb2.FrameSystemConfigResponse), '/viam.robot.v1.RobotService/TransformPose': grpclib.const.Handler(self.TransformPose, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.TransformPoseRequest, robot.v1.robot_pb2.TransformPoseResponse), '/viam.robot.v1.RobotService/GetStatus': grpclib.const.Handler(self.GetStatus, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.GetStatusRequest, robot.v1.robot_pb2.GetStatusResponse), '/viam.robot.v1.RobotService/StreamStatus': grpclib.const.Handler(self.StreamStatus, grpclib.const.Cardinality.UNARY_STREAM, robot.v1.robot_pb2.StreamStatusRequest, robot.v1.robot_pb2.StreamStatusResponse), '/viam.robot.v1.RobotService/StopAll': grpclib.const.Handler(self.StopAll, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.StopAllRequest, robot.v1.robot_pb2.StopAllResponse)} + return {'/viam.robot.v1.RobotService/GetOperations': grpclib.const.Handler(self.GetOperations, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.GetOperationsRequest, robot.v1.robot_pb2.GetOperationsResponse), '/viam.robot.v1.RobotService/GetSessions': grpclib.const.Handler(self.GetSessions, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.GetSessionsRequest, robot.v1.robot_pb2.GetSessionsResponse), '/viam.robot.v1.RobotService/ResourceNames': grpclib.const.Handler(self.ResourceNames, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.ResourceNamesRequest, robot.v1.robot_pb2.ResourceNamesResponse), '/viam.robot.v1.RobotService/ResourceRPCSubtypes': grpclib.const.Handler(self.ResourceRPCSubtypes, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.ResourceRPCSubtypesRequest, robot.v1.robot_pb2.ResourceRPCSubtypesResponse), '/viam.robot.v1.RobotService/CancelOperation': grpclib.const.Handler(self.CancelOperation, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.CancelOperationRequest, robot.v1.robot_pb2.CancelOperationResponse), '/viam.robot.v1.RobotService/BlockForOperation': grpclib.const.Handler(self.BlockForOperation, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.BlockForOperationRequest, robot.v1.robot_pb2.BlockForOperationResponse), '/viam.robot.v1.RobotService/GetModelsFromModules': grpclib.const.Handler(self.GetModelsFromModules, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.GetModelsFromModulesRequest, robot.v1.robot_pb2.GetModelsFromModulesResponse), '/viam.robot.v1.RobotService/FrameSystemConfig': grpclib.const.Handler(self.FrameSystemConfig, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.FrameSystemConfigRequest, robot.v1.robot_pb2.FrameSystemConfigResponse), '/viam.robot.v1.RobotService/TransformPose': grpclib.const.Handler(self.TransformPose, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.TransformPoseRequest, robot.v1.robot_pb2.TransformPoseResponse), '/viam.robot.v1.RobotService/TransformPCD': grpclib.const.Handler(self.TransformPCD, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.TransformPCDRequest, robot.v1.robot_pb2.TransformPCDResponse), '/viam.robot.v1.RobotService/GetStatus': grpclib.const.Handler(self.GetStatus, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.GetStatusRequest, robot.v1.robot_pb2.GetStatusResponse), '/viam.robot.v1.RobotService/StreamStatus': grpclib.const.Handler(self.StreamStatus, grpclib.const.Cardinality.UNARY_STREAM, robot.v1.robot_pb2.StreamStatusRequest, robot.v1.robot_pb2.StreamStatusResponse), '/viam.robot.v1.RobotService/StopAll': grpclib.const.Handler(self.StopAll, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.StopAllRequest, robot.v1.robot_pb2.StopAllResponse), '/viam.robot.v1.RobotService/StartSession': grpclib.const.Handler(self.StartSession, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.StartSessionRequest, robot.v1.robot_pb2.StartSessionResponse), '/viam.robot.v1.RobotService/SendSessionHeartbeat': grpclib.const.Handler(self.SendSessionHeartbeat, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.SendSessionHeartbeatRequest, robot.v1.robot_pb2.SendSessionHeartbeatResponse), '/viam.robot.v1.RobotService/Log': grpclib.const.Handler(self.Log, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.LogRequest, robot.v1.robot_pb2.LogResponse), '/viam.robot.v1.RobotService/GetCloudMetadata': grpclib.const.Handler(self.GetCloudMetadata, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.GetCloudMetadataRequest, robot.v1.robot_pb2.GetCloudMetadataResponse), '/viam.robot.v1.RobotService/RestartModule': grpclib.const.Handler(self.RestartModule, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.RestartModuleRequest, robot.v1.robot_pb2.RestartModuleResponse), '/viam.robot.v1.RobotService/Shutdown': grpclib.const.Handler(self.Shutdown, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.ShutdownRequest, robot.v1.robot_pb2.ShutdownResponse), '/viam.robot.v1.RobotService/GetMachineStatus': grpclib.const.Handler(self.GetMachineStatus, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.GetMachineStatusRequest, robot.v1.robot_pb2.GetMachineStatusResponse), '/viam.robot.v1.RobotService/GetVersion': grpclib.const.Handler(self.GetVersion, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.GetVersionRequest, robot.v1.robot_pb2.GetVersionResponse), '/viam.robot.v1.RobotService/Tunnel': grpclib.const.Handler(self.Tunnel, grpclib.const.Cardinality.STREAM_STREAM, robot.v1.robot_pb2.TunnelRequest, robot.v1.robot_pb2.TunnelResponse), '/viam.robot.v1.RobotService/ListTunnels': grpclib.const.Handler(self.ListTunnels, grpclib.const.Cardinality.UNARY_UNARY, robot.v1.robot_pb2.ListTunnelsRequest, robot.v1.robot_pb2.ListTunnelsResponse)} + +class UnimplementedRobotServiceBase(RobotServiceBase): + + async def GetOperations(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetOperationsRequest, robot.v1.robot_pb2.GetOperationsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetSessions(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetSessionsRequest, robot.v1.robot_pb2.GetSessionsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ResourceNames(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.ResourceNamesRequest, robot.v1.robot_pb2.ResourceNamesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ResourceRPCSubtypes(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.ResourceRPCSubtypesRequest, robot.v1.robot_pb2.ResourceRPCSubtypesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CancelOperation(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.CancelOperationRequest, robot.v1.robot_pb2.CancelOperationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def BlockForOperation(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.BlockForOperationRequest, robot.v1.robot_pb2.BlockForOperationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetModelsFromModules(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetModelsFromModulesRequest, robot.v1.robot_pb2.GetModelsFromModulesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def FrameSystemConfig(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.FrameSystemConfigRequest, robot.v1.robot_pb2.FrameSystemConfigResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def TransformPose(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.TransformPoseRequest, robot.v1.robot_pb2.TransformPoseResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def TransformPCD(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.TransformPCDRequest, robot.v1.robot_pb2.TransformPCDResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetStatus(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetStatusRequest, robot.v1.robot_pb2.GetStatusResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def StreamStatus(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.StreamStatusRequest, robot.v1.robot_pb2.StreamStatusResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def StopAll(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.StopAllRequest, robot.v1.robot_pb2.StopAllResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def StartSession(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.StartSessionRequest, robot.v1.robot_pb2.StartSessionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SendSessionHeartbeat(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.SendSessionHeartbeatRequest, robot.v1.robot_pb2.SendSessionHeartbeatResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Log(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.LogRequest, robot.v1.robot_pb2.LogResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetCloudMetadata(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetCloudMetadataRequest, robot.v1.robot_pb2.GetCloudMetadataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RestartModule(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.RestartModuleRequest, robot.v1.robot_pb2.RestartModuleResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Shutdown(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.ShutdownRequest, robot.v1.robot_pb2.ShutdownResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetMachineStatus(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetMachineStatusRequest, robot.v1.robot_pb2.GetMachineStatusResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetVersion(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.GetVersionRequest, robot.v1.robot_pb2.GetVersionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Tunnel(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.TunnelRequest, robot.v1.robot_pb2.TunnelResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListTunnels(self, stream: 'grpclib.server.Stream[robot.v1.robot_pb2.ListTunnelsRequest, robot.v1.robot_pb2.ListTunnelsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class RobotServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: self.GetOperations = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/GetOperations', robot.v1.robot_pb2.GetOperationsRequest, robot.v1.robot_pb2.GetOperationsResponse) + self.GetSessions = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/GetSessions', robot.v1.robot_pb2.GetSessionsRequest, robot.v1.robot_pb2.GetSessionsResponse) self.ResourceNames = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/ResourceNames', robot.v1.robot_pb2.ResourceNamesRequest, robot.v1.robot_pb2.ResourceNamesResponse) self.ResourceRPCSubtypes = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/ResourceRPCSubtypes', robot.v1.robot_pb2.ResourceRPCSubtypesRequest, robot.v1.robot_pb2.ResourceRPCSubtypesResponse) self.CancelOperation = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/CancelOperation', robot.v1.robot_pb2.CancelOperationRequest, robot.v1.robot_pb2.CancelOperationResponse) self.BlockForOperation = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/BlockForOperation', robot.v1.robot_pb2.BlockForOperationRequest, robot.v1.robot_pb2.BlockForOperationResponse) - self.DiscoverComponents = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/DiscoverComponents', robot.v1.robot_pb2.DiscoverComponentsRequest, robot.v1.robot_pb2.DiscoverComponentsResponse) + self.GetModelsFromModules = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/GetModelsFromModules', robot.v1.robot_pb2.GetModelsFromModulesRequest, robot.v1.robot_pb2.GetModelsFromModulesResponse) self.FrameSystemConfig = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/FrameSystemConfig', robot.v1.robot_pb2.FrameSystemConfigRequest, robot.v1.robot_pb2.FrameSystemConfigResponse) self.TransformPose = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/TransformPose', robot.v1.robot_pb2.TransformPoseRequest, robot.v1.robot_pb2.TransformPoseResponse) + self.TransformPCD = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/TransformPCD', robot.v1.robot_pb2.TransformPCDRequest, robot.v1.robot_pb2.TransformPCDResponse) self.GetStatus = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/GetStatus', robot.v1.robot_pb2.GetStatusRequest, robot.v1.robot_pb2.GetStatusResponse) self.StreamStatus = grpclib.client.UnaryStreamMethod(channel, '/viam.robot.v1.RobotService/StreamStatus', robot.v1.robot_pb2.StreamStatusRequest, robot.v1.robot_pb2.StreamStatusResponse) - self.StopAll = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/StopAll', robot.v1.robot_pb2.StopAllRequest, robot.v1.robot_pb2.StopAllResponse) \ No newline at end of file + self.StopAll = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/StopAll', robot.v1.robot_pb2.StopAllRequest, robot.v1.robot_pb2.StopAllResponse) + self.StartSession = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/StartSession', robot.v1.robot_pb2.StartSessionRequest, robot.v1.robot_pb2.StartSessionResponse) + self.SendSessionHeartbeat = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/SendSessionHeartbeat', robot.v1.robot_pb2.SendSessionHeartbeatRequest, robot.v1.robot_pb2.SendSessionHeartbeatResponse) + self.Log = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/Log', robot.v1.robot_pb2.LogRequest, robot.v1.robot_pb2.LogResponse) + self.GetCloudMetadata = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/GetCloudMetadata', robot.v1.robot_pb2.GetCloudMetadataRequest, robot.v1.robot_pb2.GetCloudMetadataResponse) + self.RestartModule = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/RestartModule', robot.v1.robot_pb2.RestartModuleRequest, robot.v1.robot_pb2.RestartModuleResponse) + self.Shutdown = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/Shutdown', robot.v1.robot_pb2.ShutdownRequest, robot.v1.robot_pb2.ShutdownResponse) + self.GetMachineStatus = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/GetMachineStatus', robot.v1.robot_pb2.GetMachineStatusRequest, robot.v1.robot_pb2.GetMachineStatusResponse) + self.GetVersion = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/GetVersion', robot.v1.robot_pb2.GetVersionRequest, robot.v1.robot_pb2.GetVersionResponse) + self.Tunnel = grpclib.client.StreamStreamMethod(channel, '/viam.robot.v1.RobotService/Tunnel', robot.v1.robot_pb2.TunnelRequest, robot.v1.robot_pb2.TunnelResponse) + self.ListTunnels = grpclib.client.UnaryUnaryMethod(channel, '/viam.robot.v1.RobotService/ListTunnels', robot.v1.robot_pb2.ListTunnelsRequest, robot.v1.robot_pb2.ListTunnelsResponse) \ No newline at end of file diff --git a/src/viam/gen/robot/v1/robot_pb2.py b/src/viam/gen/robot/v1/robot_pb2.py index bff190f0c..62c9f2e35 100644 --- a/src/viam/gen/robot/v1/robot_pb2.py +++ b/src/viam/gen/robot/v1/robot_pb2.py @@ -1,99 +1,188 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'robot/v1/robot.proto') _sym_db = _symbol_database.Default() from ...common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14robot/v1/robot.proto\x12\rviam.robot.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\x94\x01\n\x11FrameSystemConfig\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12L\n\x14pose_in_parent_frame\x18\x02 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x11poseInParentFrame\x12\x1d\n\nmodel_json\x18\x03 \x01(\x0cR\tmodelJson"n\n\x18FrameSystemConfigRequest\x12R\n\x17supplemental_transforms\x18\x01 \x03(\x0b2\x19.viam.common.v1.TransformR\x16supplementalTransforms"o\n\x19FrameSystemConfigResponse\x12R\n\x14frame_system_configs\x18\x01 \x03(\x0b2 .viam.robot.v1.FrameSystemConfigR\x12frameSystemConfigs"\xc1\x01\n\x14TransformPoseRequest\x123\n\x06source\x18\x01 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x06source\x12 \n\x0bdestination\x18\x02 \x01(\tR\x0bdestination\x12R\n\x17supplemental_transforms\x18\x03 \x03(\x0b2\x19.viam.common.v1.TransformR\x16supplementalTransforms"H\n\x15TransformPoseResponse\x12/\n\x04pose\x18\x01 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x04pose"\x16\n\x14ResourceNamesRequest"S\n\x15ResourceNamesResponse\x12:\n\tresources\x18\x01 \x03(\x0b2\x1c.viam.common.v1.ResourceNameR\tresources"q\n\x12ResourceRPCSubtype\x126\n\x07subtype\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x07subtype\x12#\n\rproto_service\x18\x02 \x01(\tR\x0cprotoService"\x1c\n\x1aResourceRPCSubtypesRequest"t\n\x1bResourceRPCSubtypesResponse\x12U\n\x15resource_rpc_subtypes\x18\x01 \x03(\x0b2!.viam.robot.v1.ResourceRPCSubtypeR\x13resourceRpcSubtypes"\xa0\x01\n\tOperation\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n\x06method\x18\x02 \x01(\tR\x06method\x125\n\targuments\x18\x03 \x01(\x0b2\x17.google.protobuf.StructR\targuments\x124\n\x07started\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampR\x07started"\x16\n\x14GetOperationsRequest"Q\n\x15GetOperationsResponse\x128\n\noperations\x18\x01 \x03(\x0b2\x18.viam.robot.v1.OperationR\noperations"(\n\x16CancelOperationRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x19\n\x17CancelOperationResponse"*\n\x18BlockForOperationRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x1b\n\x19BlockForOperationResponse"@\n\x0eDiscoveryQuery\x12\x18\n\x07subtype\x18\x01 \x01(\tR\x07subtype\x12\x14\n\x05model\x18\x02 \x01(\tR\x05model"s\n\tDiscovery\x123\n\x05query\x18\x01 \x01(\x0b2\x1d.viam.robot.v1.DiscoveryQueryR\x05query\x121\n\x07results\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x07results"T\n\x19DiscoverComponentsRequest\x127\n\x07queries\x18\x01 \x03(\x0b2\x1d.viam.robot.v1.DiscoveryQueryR\x07queries"T\n\x1aDiscoverComponentsResponse\x126\n\tdiscovery\x18\x01 \x03(\x0b2\x18.viam.robot.v1.DiscoveryR\tdiscovery"k\n\x06Status\x120\n\x04name\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x04name\x12/\n\x06status\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x06status"W\n\x10GetStatusRequest\x12C\n\x0eresource_names\x18\x01 \x03(\x0b2\x1c.viam.common.v1.ResourceNameR\rresourceNames"B\n\x11GetStatusResponse\x12-\n\x06status\x18\x01 \x03(\x0b2\x15.viam.robot.v1.StatusR\x06status"\x8b\x01\n\x13StreamStatusRequest\x12C\n\x0eresource_names\x18\x01 \x03(\x0b2\x1c.viam.common.v1.ResourceNameR\rresourceNames\x12/\n\x05every\x18\x02 \x01(\x0b2\x19.google.protobuf.DurationR\x05every"E\n\x14StreamStatusResponse\x12-\n\x06status\x18\x01 \x03(\x0b2\x15.viam.robot.v1.StatusR\x06status"x\n\x13StopExtraParameters\x120\n\x04name\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x04name\x12/\n\x06params\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x06params"J\n\x0eStopAllRequest\x128\n\x05extra\x18c \x03(\x0b2".viam.robot.v1.StopExtraParametersR\x05extra"\x11\n\x0fStopAllResponse2\xdb\x0b\n\x0cRobotService\x12\x80\x01\n\rGetOperations\x12#.viam.robot.v1.GetOperationsRequest\x1a$.viam.robot.v1.GetOperationsResponse"$\x82\xd3\xe4\x93\x02\x1e\x12\x1c/viam/api/v1/operations/list\x12\x7f\n\rResourceNames\x12#.viam.robot.v1.ResourceNamesRequest\x1a$.viam.robot.v1.ResourceNamesResponse"#\x82\xd3\xe4\x93\x02\x1d\x12\x1b/viam/api/v1/resources/list\x12\x9d\x01\n\x13ResourceRPCSubtypes\x12).viam.robot.v1.ResourceRPCSubtypesRequest\x1a*.viam.robot.v1.ResourceRPCSubtypesResponse"/\x82\xd3\xe4\x93\x02)\x12\'/viam/api/v1/resource_rpc_subtypes/list\x12\x88\x01\n\x0fCancelOperation\x12%.viam.robot.v1.CancelOperationRequest\x1a&.viam.robot.v1.CancelOperationResponse"&\x82\xd3\xe4\x93\x02 "\x1e/viam/api/v1/operations/cancel\x12\x8d\x01\n\x11BlockForOperation\x12\'.viam.robot.v1.BlockForOperationRequest\x1a(.viam.robot.v1.BlockForOperationResponse"%\x82\xd3\xe4\x93\x02\x1f"\x1d/viam/api/v1/operations/block\x12\x94\x01\n\x12DiscoverComponents\x12(.viam.robot.v1.DiscoverComponentsRequest\x1a).viam.robot.v1.DiscoverComponentsResponse")\x82\xd3\xe4\x93\x02#\x12!/viam/api/v1/discovery/components\x12\x90\x01\n\x11FrameSystemConfig\x12\'.viam.robot.v1.FrameSystemConfigRequest\x1a(.viam.robot.v1.FrameSystemConfigResponse"(\x82\xd3\xe4\x93\x02"\x12 /viam/api/v1/frame_system/config\x12\x8c\x01\n\rTransformPose\x12#.viam.robot.v1.TransformPoseRequest\x1a$.viam.robot.v1.TransformPoseResponse"0\x82\xd3\xe4\x93\x02*\x12(/viam/api/v1/frame_system/transform_pose\x12k\n\tGetStatus\x12\x1f.viam.robot.v1.GetStatusRequest\x1a .viam.robot.v1.GetStatusResponse"\x1b\x82\xd3\xe4\x93\x02\x15\x12\x13/viam/api/v1/status\x12}\n\x0cStreamStatus\x12".viam.robot.v1.StreamStatusRequest\x1a#.viam.robot.v1.StreamStatusResponse""\x82\xd3\xe4\x93\x02\x1c\x12\x1a/viam/api/v1/status/stream0\x01\x12g\n\x07StopAll\x12\x1d.viam.robot.v1.StopAllRequest\x1a\x1e.viam.robot.v1.StopAllResponse"\x1d\x82\xd3\xe4\x93\x02\x17\x12\x15/viam/api/v1/stop_allB-\n\x11com.viam.robot.v1Z\x18go.viam.com/api/robot/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'robot.v1.robot_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x11com.viam.robot.v1Z\x18go.viam.com/api/robot/v1' - _ROBOTSERVICE.methods_by_name['GetOperations']._options = None - _ROBOTSERVICE.methods_by_name['GetOperations']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1e\x12\x1c/viam/api/v1/operations/list' - _ROBOTSERVICE.methods_by_name['ResourceNames']._options = None - _ROBOTSERVICE.methods_by_name['ResourceNames']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1d\x12\x1b/viam/api/v1/resources/list' - _ROBOTSERVICE.methods_by_name['ResourceRPCSubtypes']._options = None - _ROBOTSERVICE.methods_by_name['ResourceRPCSubtypes']._serialized_options = b"\x82\xd3\xe4\x93\x02)\x12'/viam/api/v1/resource_rpc_subtypes/list" - _ROBOTSERVICE.methods_by_name['CancelOperation']._options = None - _ROBOTSERVICE.methods_by_name['CancelOperation']._serialized_options = b'\x82\xd3\xe4\x93\x02 "\x1e/viam/api/v1/operations/cancel' - _ROBOTSERVICE.methods_by_name['BlockForOperation']._options = None - _ROBOTSERVICE.methods_by_name['BlockForOperation']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1f"\x1d/viam/api/v1/operations/block' - _ROBOTSERVICE.methods_by_name['DiscoverComponents']._options = None - _ROBOTSERVICE.methods_by_name['DiscoverComponents']._serialized_options = b'\x82\xd3\xe4\x93\x02#\x12!/viam/api/v1/discovery/components' - _ROBOTSERVICE.methods_by_name['FrameSystemConfig']._options = None - _ROBOTSERVICE.methods_by_name['FrameSystemConfig']._serialized_options = b'\x82\xd3\xe4\x93\x02"\x12 /viam/api/v1/frame_system/config' - _ROBOTSERVICE.methods_by_name['TransformPose']._options = None - _ROBOTSERVICE.methods_by_name['TransformPose']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x12(/viam/api/v1/frame_system/transform_pose' - _ROBOTSERVICE.methods_by_name['GetStatus']._options = None - _ROBOTSERVICE.methods_by_name['GetStatus']._serialized_options = b'\x82\xd3\xe4\x93\x02\x15\x12\x13/viam/api/v1/status' - _ROBOTSERVICE.methods_by_name['StreamStatus']._options = None - _ROBOTSERVICE.methods_by_name['StreamStatus']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1c\x12\x1a/viam/api/v1/status/stream' - _ROBOTSERVICE.methods_by_name['StopAll']._options = None - _ROBOTSERVICE.methods_by_name['StopAll']._serialized_options = b'\x82\xd3\xe4\x93\x02\x17\x12\x15/viam/api/v1/stop_all' - _FRAMESYSTEMCONFIG._serialized_start = 189 - _FRAMESYSTEMCONFIG._serialized_end = 337 - _FRAMESYSTEMCONFIGREQUEST._serialized_start = 339 - _FRAMESYSTEMCONFIGREQUEST._serialized_end = 449 - _FRAMESYSTEMCONFIGRESPONSE._serialized_start = 451 - _FRAMESYSTEMCONFIGRESPONSE._serialized_end = 562 - _TRANSFORMPOSEREQUEST._serialized_start = 565 - _TRANSFORMPOSEREQUEST._serialized_end = 758 - _TRANSFORMPOSERESPONSE._serialized_start = 760 - _TRANSFORMPOSERESPONSE._serialized_end = 832 - _RESOURCENAMESREQUEST._serialized_start = 834 - _RESOURCENAMESREQUEST._serialized_end = 856 - _RESOURCENAMESRESPONSE._serialized_start = 858 - _RESOURCENAMESRESPONSE._serialized_end = 941 - _RESOURCERPCSUBTYPE._serialized_start = 943 - _RESOURCERPCSUBTYPE._serialized_end = 1056 - _RESOURCERPCSUBTYPESREQUEST._serialized_start = 1058 - _RESOURCERPCSUBTYPESREQUEST._serialized_end = 1086 - _RESOURCERPCSUBTYPESRESPONSE._serialized_start = 1088 - _RESOURCERPCSUBTYPESRESPONSE._serialized_end = 1204 - _OPERATION._serialized_start = 1207 - _OPERATION._serialized_end = 1367 - _GETOPERATIONSREQUEST._serialized_start = 1369 - _GETOPERATIONSREQUEST._serialized_end = 1391 - _GETOPERATIONSRESPONSE._serialized_start = 1393 - _GETOPERATIONSRESPONSE._serialized_end = 1474 - _CANCELOPERATIONREQUEST._serialized_start = 1476 - _CANCELOPERATIONREQUEST._serialized_end = 1516 - _CANCELOPERATIONRESPONSE._serialized_start = 1518 - _CANCELOPERATIONRESPONSE._serialized_end = 1543 - _BLOCKFOROPERATIONREQUEST._serialized_start = 1545 - _BLOCKFOROPERATIONREQUEST._serialized_end = 1587 - _BLOCKFOROPERATIONRESPONSE._serialized_start = 1589 - _BLOCKFOROPERATIONRESPONSE._serialized_end = 1616 - _DISCOVERYQUERY._serialized_start = 1618 - _DISCOVERYQUERY._serialized_end = 1682 - _DISCOVERY._serialized_start = 1684 - _DISCOVERY._serialized_end = 1799 - _DISCOVERCOMPONENTSREQUEST._serialized_start = 1801 - _DISCOVERCOMPONENTSREQUEST._serialized_end = 1885 - _DISCOVERCOMPONENTSRESPONSE._serialized_start = 1887 - _DISCOVERCOMPONENTSRESPONSE._serialized_end = 1971 - _STATUS._serialized_start = 1973 - _STATUS._serialized_end = 2080 - _GETSTATUSREQUEST._serialized_start = 2082 - _GETSTATUSREQUEST._serialized_end = 2169 - _GETSTATUSRESPONSE._serialized_start = 2171 - _GETSTATUSRESPONSE._serialized_end = 2237 - _STREAMSTATUSREQUEST._serialized_start = 2240 - _STREAMSTATUSREQUEST._serialized_end = 2379 - _STREAMSTATUSRESPONSE._serialized_start = 2381 - _STREAMSTATUSRESPONSE._serialized_end = 2450 - _STOPEXTRAPARAMETERS._serialized_start = 2452 - _STOPEXTRAPARAMETERS._serialized_end = 2572 - _STOPALLREQUEST._serialized_start = 2574 - _STOPALLREQUEST._serialized_end = 2648 - _STOPALLRESPONSE._serialized_start = 2650 - _STOPALLRESPONSE._serialized_end = 2667 - _ROBOTSERVICE._serialized_start = 2670 - _ROBOTSERVICE._serialized_end = 4169 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14robot/v1/robot.proto\x12\rviam.robot.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"N\n\rTunnelRequest\x12)\n\x10destination_port\x18\x01 \x01(\rR\x0fdestinationPort\x12\x12\n\x04data\x18\x02 \x01(\x0cR\x04data"$\n\x0eTunnelResponse\x12\x12\n\x04data\x18\x01 \x01(\x0cR\x04data"\x14\n\x12ListTunnelsRequest"F\n\x13ListTunnelsResponse\x12/\n\x07tunnels\x18\x01 \x03(\x0b2\x15.viam.robot.v1.TunnelR\x07tunnels"f\n\x06Tunnel\x12\x12\n\x04port\x18\x01 \x01(\rR\x04port\x12H\n\x12connection_timeout\x18\x02 \x01(\x0b2\x19.google.protobuf.DurationR\x11connectionTimeout"}\n\x11FrameSystemConfig\x12/\n\x05frame\x18\x01 \x01(\x0b2\x19.viam.common.v1.TransformR\x05frame\x127\n\nkinematics\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\nkinematics"n\n\x18FrameSystemConfigRequest\x12R\n\x17supplemental_transforms\x18\x01 \x03(\x0b2\x19.viam.common.v1.TransformR\x16supplementalTransforms"o\n\x19FrameSystemConfigResponse\x12R\n\x14frame_system_configs\x18\x01 \x03(\x0b2 .viam.robot.v1.FrameSystemConfigR\x12frameSystemConfigs"\xc1\x01\n\x14TransformPoseRequest\x123\n\x06source\x18\x01 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x06source\x12 \n\x0bdestination\x18\x02 \x01(\tR\x0bdestination\x12R\n\x17supplemental_transforms\x18\x03 \x03(\x0b2\x19.viam.common.v1.TransformR\x16supplementalTransforms"H\n\x15TransformPoseResponse\x12/\n\x04pose\x18\x01 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x04pose"w\n\x13TransformPCDRequest\x12&\n\x0fpoint_cloud_pcd\x18\x01 \x01(\x0cR\rpointCloudPcd\x12\x16\n\x06source\x18\x02 \x01(\tR\x06source\x12 \n\x0bdestination\x18\x03 \x01(\tR\x0bdestination">\n\x14TransformPCDResponse\x12&\n\x0fpoint_cloud_pcd\x18\x01 \x01(\x0cR\rpointCloudPcd"\x16\n\x14ResourceNamesRequest"S\n\x15ResourceNamesResponse\x12:\n\tresources\x18\x01 \x03(\x0b2\x1c.viam.common.v1.ResourceNameR\tresources"q\n\x12ResourceRPCSubtype\x126\n\x07subtype\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x07subtype\x12#\n\rproto_service\x18\x02 \x01(\tR\x0cprotoService"\x1c\n\x1aResourceRPCSubtypesRequest"t\n\x1bResourceRPCSubtypesResponse\x12U\n\x15resource_rpc_subtypes\x18\x01 \x03(\x0b2!.viam.robot.v1.ResourceRPCSubtypeR\x13resourceRpcSubtypes"\xd3\x01\n\tOperation\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12\x16\n\x06method\x18\x02 \x01(\tR\x06method\x125\n\targuments\x18\x03 \x01(\x0b2\x17.google.protobuf.StructR\targuments\x124\n\x07started\x18\x04 \x01(\x0b2\x1a.google.protobuf.TimestampR\x07started\x12"\n\nsession_id\x18\x05 \x01(\tH\x00R\tsessionId\x88\x01\x01B\r\n\x0b_session_id"\x16\n\x14GetOperationsRequest"Q\n\x15GetOperationsResponse\x128\n\noperations\x18\x01 \x03(\x0b2\x18.viam.robot.v1.OperationR\noperations"(\n\x16CancelOperationRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x19\n\x17CancelOperationResponse"*\n\x18BlockForOperationRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x1b\n\x19BlockForOperationResponse"\xc6\x01\n\x12PeerConnectionInfo\x125\n\x04type\x18\x01 \x01(\x0e2!.viam.robot.v1.PeerConnectionTypeR\x04type\x12*\n\x0eremote_address\x18\x02 \x01(\tH\x00R\rremoteAddress\x88\x01\x01\x12(\n\rlocal_address\x18\x03 \x01(\tH\x01R\x0clocalAddress\x88\x01\x01B\x11\n\x0f_remote_addressB\x10\n\x0e_local_address"\x8c\x01\n\x07Session\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12X\n\x14peer_connection_info\x18\x02 \x01(\x0b2!.viam.robot.v1.PeerConnectionInfoH\x00R\x12peerConnectionInfo\x88\x01\x01B\x17\n\x15_peer_connection_info"\x14\n\x12GetSessionsRequest"I\n\x13GetSessionsResponse\x122\n\x08sessions\x18\x01 \x03(\x0b2\x16.viam.robot.v1.SessionR\x08sessions"\x82\x01\n\x0bModuleModel\x12\x1f\n\x0bmodule_name\x18\x01 \x01(\tR\nmoduleName\x12\x14\n\x05model\x18\x02 \x01(\tR\x05model\x12\x10\n\x03api\x18\x03 \x01(\tR\x03api\x12*\n\x11from_local_module\x18\x04 \x01(\x08R\x0ffromLocalModule"\x1d\n\x1bGetModelsFromModulesRequest"R\n\x1cGetModelsFromModulesResponse\x122\n\x06models\x18\x01 \x03(\x0b2\x1a.viam.robot.v1.ModuleModelR\x06models"\xb4\x01\n\x06Status\x120\n\x04name\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x04name\x12/\n\x06status\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x06status\x12G\n\x11last_reconfigured\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\x10lastReconfigured"W\n\x10GetStatusRequest\x12C\n\x0eresource_names\x18\x01 \x03(\x0b2\x1c.viam.common.v1.ResourceNameR\rresourceNames"B\n\x11GetStatusResponse\x12-\n\x06status\x18\x01 \x03(\x0b2\x15.viam.robot.v1.StatusR\x06status"\x8b\x01\n\x13StreamStatusRequest\x12C\n\x0eresource_names\x18\x01 \x03(\x0b2\x1c.viam.common.v1.ResourceNameR\rresourceNames\x12/\n\x05every\x18\x02 \x01(\x0b2\x19.google.protobuf.DurationR\x05every"E\n\x14StreamStatusResponse\x12-\n\x06status\x18\x01 \x03(\x0b2\x15.viam.robot.v1.StatusR\x06status"x\n\x13StopExtraParameters\x120\n\x04name\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x04name\x12/\n\x06params\x18\x02 \x01(\x0b2\x17.google.protobuf.StructR\x06params"J\n\x0eStopAllRequest\x128\n\x05extra\x18c \x03(\x0b2".viam.robot.v1.StopExtraParametersR\x05extra"\x11\n\x0fStopAllResponse"-\n\x13StartSessionRequest\x12\x16\n\x06resume\x18\x01 \x01(\tR\x06resume"l\n\x14StartSessionResponse\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12D\n\x10heartbeat_window\x18\x02 \x01(\x0b2\x19.google.protobuf.DurationR\x0fheartbeatWindow"-\n\x1bSendSessionHeartbeatRequest\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id"\x1e\n\x1cSendSessionHeartbeatResponse":\n\nLogRequest\x12,\n\x04logs\x18\x01 \x03(\x0b2\x18.viam.common.v1.LogEntryR\x04logs"\r\n\x0bLogResponse"\x19\n\x17GetCloudMetadataRequest"\xd0\x01\n\x18GetCloudMetadataResponse\x12&\n\rrobot_part_id\x18\x01 \x01(\tB\x02\x18\x01R\x0brobotPartId\x12$\n\x0eprimary_org_id\x18\x02 \x01(\tR\x0cprimaryOrgId\x12\x1f\n\x0blocation_id\x18\x03 \x01(\tR\nlocationId\x12\x1d\n\nmachine_id\x18\x04 \x01(\tR\tmachineId\x12&\n\x0fmachine_part_id\x18\x05 \x01(\tR\rmachinePartId"f\n\x14RestartModuleRequest\x12\x1d\n\tmodule_id\x18\x01 \x01(\tH\x00R\x08moduleId\x12!\n\x0bmodule_name\x18\x02 \x01(\tH\x00R\nmoduleNameB\x0c\n\nid_or_name"\x17\n\x15RestartModuleResponse"\x11\n\x0fShutdownRequest"\x12\n\x10ShutdownResponse"\x19\n\x17GetMachineStatusRequest"\x9c\x02\n\x18GetMachineStatusResponse\x12;\n\tresources\x18\x01 \x03(\x0b2\x1d.viam.robot.v1.ResourceStatusR\tresources\x123\n\x06config\x18\x02 \x01(\x0b2\x1b.viam.robot.v1.ConfigStatusR\x06config\x12C\n\x05state\x18\x03 \x01(\x0e2-.viam.robot.v1.GetMachineStatusResponse.StateR\x05state"I\n\x05State\x12\x15\n\x11STATE_UNSPECIFIED\x10\x00\x12\x16\n\x12STATE_INITIALIZING\x10\x01\x12\x11\n\rSTATE_RUNNING\x10\x02"\xe0\x03\n\x0eResourceStatus\x120\n\x04name\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x04name\x129\n\x05state\x18\x02 \x01(\x0e2#.viam.robot.v1.ResourceStatus.StateR\x05state\x12=\n\x0clast_updated\x18\x03 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0blastUpdated\x12\x1a\n\x08revision\x18\x04 \x01(\tR\x08revision\x12\x14\n\x05error\x18\x05 \x01(\tR\x05error\x12S\n\x0ecloud_metadata\x18\x06 \x01(\x0b2\'.viam.robot.v1.GetCloudMetadataResponseH\x00R\rcloudMetadata\x88\x01\x01"\x87\x01\n\x05State\x12\x15\n\x11STATE_UNSPECIFIED\x10\x00\x12\x16\n\x12STATE_UNCONFIGURED\x10\x01\x12\x15\n\x11STATE_CONFIGURING\x10\x02\x12\x0f\n\x0bSTATE_READY\x10\x03\x12\x12\n\x0eSTATE_REMOVING\x10\x04\x12\x13\n\x0fSTATE_UNHEALTHY\x10\x05B\x11\n\x0f_cloud_metadata"i\n\x0cConfigStatus\x12\x1a\n\x08revision\x18\x01 \x01(\tR\x08revision\x12=\n\x0clast_updated\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\x0blastUpdated"\x13\n\x11GetVersionRequest"k\n\x12GetVersionResponse\x12\x1a\n\x08platform\x18\x01 \x01(\tR\x08platform\x12\x18\n\x07version\x18\x02 \x01(\tR\x07version\x12\x1f\n\x0bapi_version\x18\x03 \x01(\tR\napiVersion*z\n\x12PeerConnectionType\x12$\n PEER_CONNECTION_TYPE_UNSPECIFIED\x10\x00\x12\x1d\n\x19PEER_CONNECTION_TYPE_GRPC\x10\x01\x12\x1f\n\x1bPEER_CONNECTION_TYPE_WEBRTC\x10\x022\x8e\x17\n\x0cRobotService\x12\x80\x01\n\rGetOperations\x12#.viam.robot.v1.GetOperationsRequest\x1a$.viam.robot.v1.GetOperationsResponse"$\x82\xd3\xe4\x93\x02\x1e\x12\x1c/viam/api/v1/operations/list\x12x\n\x0bGetSessions\x12!.viam.robot.v1.GetSessionsRequest\x1a".viam.robot.v1.GetSessionsResponse""\x82\xd3\xe4\x93\x02\x1c\x12\x1a/viam/api/v1/sessions/list\x12\x7f\n\rResourceNames\x12#.viam.robot.v1.ResourceNamesRequest\x1a$.viam.robot.v1.ResourceNamesResponse"#\x82\xd3\xe4\x93\x02\x1d\x12\x1b/viam/api/v1/resources/list\x12\x9d\x01\n\x13ResourceRPCSubtypes\x12).viam.robot.v1.ResourceRPCSubtypesRequest\x1a*.viam.robot.v1.ResourceRPCSubtypesResponse"/\x82\xd3\xe4\x93\x02)\x12\'/viam/api/v1/resource_rpc_subtypes/list\x12\x88\x01\n\x0fCancelOperation\x12%.viam.robot.v1.CancelOperationRequest\x1a&.viam.robot.v1.CancelOperationResponse"&\x82\xd3\xe4\x93\x02 "\x1e/viam/api/v1/operations/cancel\x12\x8d\x01\n\x11BlockForOperation\x12\'.viam.robot.v1.BlockForOperationRequest\x1a(.viam.robot.v1.BlockForOperationResponse"%\x82\xd3\xe4\x93\x02\x1f"\x1d/viam/api/v1/operations/block\x12\x93\x01\n\x14GetModelsFromModules\x12*.viam.robot.v1.GetModelsFromModulesRequest\x1a+.viam.robot.v1.GetModelsFromModulesResponse""\x82\xd3\xe4\x93\x02\x1c\x12\x1a/viam/api/v1/module_models\x12\x90\x01\n\x11FrameSystemConfig\x12\'.viam.robot.v1.FrameSystemConfigRequest\x1a(.viam.robot.v1.FrameSystemConfigResponse"(\x82\xd3\xe4\x93\x02"\x12 /viam/api/v1/frame_system/config\x12\x8c\x01\n\rTransformPose\x12#.viam.robot.v1.TransformPoseRequest\x1a$.viam.robot.v1.TransformPoseResponse"0\x82\xd3\xe4\x93\x02*\x12(/viam/api/v1/frame_system/transform_pose\x12\x88\x01\n\x0cTransformPCD\x12".viam.robot.v1.TransformPCDRequest\x1a#.viam.robot.v1.TransformPCDResponse"/\x82\xd3\xe4\x93\x02)\x12\'/viam/api/v1/frame_system/transform_pcd\x12n\n\tGetStatus\x12\x1f.viam.robot.v1.GetStatusRequest\x1a .viam.robot.v1.GetStatusResponse"\x1e\x88\x02\x01\x82\xd3\xe4\x93\x02\x15\x12\x13/viam/api/v1/status\x12\x80\x01\n\x0cStreamStatus\x12".viam.robot.v1.StreamStatusRequest\x1a#.viam.robot.v1.StreamStatusResponse"%\x88\x02\x01\x82\xd3\xe4\x93\x02\x1c\x12\x1a/viam/api/v1/status/stream0\x01\x12g\n\x07StopAll\x12\x1d.viam.robot.v1.StopAllRequest\x1a\x1e.viam.robot.v1.StopAllResponse"\x1d\x82\xd3\xe4\x93\x02\x17\x12\x15/viam/api/v1/stop_all\x12v\n\x0cStartSession\x12".viam.robot.v1.StartSessionRequest\x1a#.viam.robot.v1.StartSessionResponse"\x1d\x82\xd3\xe4\x93\x02\x17"\x15/viam/api/v1/sessions\x12\x9d\x01\n\x14SendSessionHeartbeat\x12*.viam.robot.v1.SendSessionHeartbeatRequest\x1a+.viam.robot.v1.SendSessionHeartbeatResponse",\x82\xd3\xe4\x93\x02&"$/viam/api/v1/sessions/{id}/heartbeat\x12V\n\x03Log\x12\x19.viam.robot.v1.LogRequest\x1a\x1a.viam.robot.v1.LogResponse"\x18\x82\xd3\xe4\x93\x02\x12"\x10/viam/api/v1/log\x12\x88\x01\n\x10GetCloudMetadata\x12&.viam.robot.v1.GetCloudMetadataRequest\x1a\'.viam.robot.v1.GetCloudMetadataResponse"#\x82\xd3\xe4\x93\x02\x1d\x12\x1b/viam/api/v1/cloud_metadata\x12\x7f\n\rRestartModule\x12#.viam.robot.v1.RestartModuleRequest\x1a$.viam.robot.v1.RestartModuleResponse"#\x82\xd3\xe4\x93\x02\x1d"\x1b/viam/api/v1/restart_module\x12j\n\x08Shutdown\x12\x1e.viam.robot.v1.ShutdownRequest\x1a\x1f.viam.robot.v1.ShutdownResponse"\x1d\x82\xd3\xe4\x93\x02\x17"\x15/viam/api/v1/shutdown\x12\x88\x01\n\x10GetMachineStatus\x12&.viam.robot.v1.GetMachineStatusRequest\x1a\'.viam.robot.v1.GetMachineStatusResponse"#\x82\xd3\xe4\x93\x02\x1d\x12\x1b/viam/api/v1/machine_status\x12o\n\nGetVersion\x12 .viam.robot.v1.GetVersionRequest\x1a!.viam.robot.v1.GetVersionResponse"\x1c\x82\xd3\xe4\x93\x02\x16\x12\x14/viam/api/v1/version\x12I\n\x06Tunnel\x12\x1c.viam.robot.v1.TunnelRequest\x1a\x1d.viam.robot.v1.TunnelResponse(\x010\x01\x12w\n\x0bListTunnels\x12!.viam.robot.v1.ListTunnelsRequest\x1a".viam.robot.v1.ListTunnelsResponse"!\x82\xd3\xe4\x93\x02\x1b\x12\x19/viam/api/v1/list_tunnelsB-\n\x11com.viam.robot.v1Z\x18go.viam.com/api/robot/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'robot.v1.robot_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x11com.viam.robot.v1Z\x18go.viam.com/api/robot/v1' + _globals['_GETCLOUDMETADATARESPONSE'].fields_by_name['robot_part_id']._loaded_options = None + _globals['_GETCLOUDMETADATARESPONSE'].fields_by_name['robot_part_id']._serialized_options = b'\x18\x01' + _globals['_ROBOTSERVICE'].methods_by_name['GetOperations']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['GetOperations']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1e\x12\x1c/viam/api/v1/operations/list' + _globals['_ROBOTSERVICE'].methods_by_name['GetSessions']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['GetSessions']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1c\x12\x1a/viam/api/v1/sessions/list' + _globals['_ROBOTSERVICE'].methods_by_name['ResourceNames']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['ResourceNames']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1d\x12\x1b/viam/api/v1/resources/list' + _globals['_ROBOTSERVICE'].methods_by_name['ResourceRPCSubtypes']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['ResourceRPCSubtypes']._serialized_options = b"\x82\xd3\xe4\x93\x02)\x12'/viam/api/v1/resource_rpc_subtypes/list" + _globals['_ROBOTSERVICE'].methods_by_name['CancelOperation']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['CancelOperation']._serialized_options = b'\x82\xd3\xe4\x93\x02 "\x1e/viam/api/v1/operations/cancel' + _globals['_ROBOTSERVICE'].methods_by_name['BlockForOperation']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['BlockForOperation']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1f"\x1d/viam/api/v1/operations/block' + _globals['_ROBOTSERVICE'].methods_by_name['GetModelsFromModules']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['GetModelsFromModules']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1c\x12\x1a/viam/api/v1/module_models' + _globals['_ROBOTSERVICE'].methods_by_name['FrameSystemConfig']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['FrameSystemConfig']._serialized_options = b'\x82\xd3\xe4\x93\x02"\x12 /viam/api/v1/frame_system/config' + _globals['_ROBOTSERVICE'].methods_by_name['TransformPose']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['TransformPose']._serialized_options = b'\x82\xd3\xe4\x93\x02*\x12(/viam/api/v1/frame_system/transform_pose' + _globals['_ROBOTSERVICE'].methods_by_name['TransformPCD']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['TransformPCD']._serialized_options = b"\x82\xd3\xe4\x93\x02)\x12'/viam/api/v1/frame_system/transform_pcd" + _globals['_ROBOTSERVICE'].methods_by_name['GetStatus']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['GetStatus']._serialized_options = b'\x88\x02\x01\x82\xd3\xe4\x93\x02\x15\x12\x13/viam/api/v1/status' + _globals['_ROBOTSERVICE'].methods_by_name['StreamStatus']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['StreamStatus']._serialized_options = b'\x88\x02\x01\x82\xd3\xe4\x93\x02\x1c\x12\x1a/viam/api/v1/status/stream' + _globals['_ROBOTSERVICE'].methods_by_name['StopAll']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['StopAll']._serialized_options = b'\x82\xd3\xe4\x93\x02\x17\x12\x15/viam/api/v1/stop_all' + _globals['_ROBOTSERVICE'].methods_by_name['StartSession']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['StartSession']._serialized_options = b'\x82\xd3\xe4\x93\x02\x17"\x15/viam/api/v1/sessions' + _globals['_ROBOTSERVICE'].methods_by_name['SendSessionHeartbeat']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['SendSessionHeartbeat']._serialized_options = b'\x82\xd3\xe4\x93\x02&"$/viam/api/v1/sessions/{id}/heartbeat' + _globals['_ROBOTSERVICE'].methods_by_name['Log']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['Log']._serialized_options = b'\x82\xd3\xe4\x93\x02\x12"\x10/viam/api/v1/log' + _globals['_ROBOTSERVICE'].methods_by_name['GetCloudMetadata']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['GetCloudMetadata']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1d\x12\x1b/viam/api/v1/cloud_metadata' + _globals['_ROBOTSERVICE'].methods_by_name['RestartModule']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['RestartModule']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1d"\x1b/viam/api/v1/restart_module' + _globals['_ROBOTSERVICE'].methods_by_name['Shutdown']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['Shutdown']._serialized_options = b'\x82\xd3\xe4\x93\x02\x17"\x15/viam/api/v1/shutdown' + _globals['_ROBOTSERVICE'].methods_by_name['GetMachineStatus']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['GetMachineStatus']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1d\x12\x1b/viam/api/v1/machine_status' + _globals['_ROBOTSERVICE'].methods_by_name['GetVersion']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['GetVersion']._serialized_options = b'\x82\xd3\xe4\x93\x02\x16\x12\x14/viam/api/v1/version' + _globals['_ROBOTSERVICE'].methods_by_name['ListTunnels']._loaded_options = None + _globals['_ROBOTSERVICE'].methods_by_name['ListTunnels']._serialized_options = b'\x82\xd3\xe4\x93\x02\x1b\x12\x19/viam/api/v1/list_tunnels' + _globals['_PEERCONNECTIONTYPE']._serialized_start = 5356 + _globals['_PEERCONNECTIONTYPE']._serialized_end = 5478 + _globals['_TUNNELREQUEST']._serialized_start = 188 + _globals['_TUNNELREQUEST']._serialized_end = 266 + _globals['_TUNNELRESPONSE']._serialized_start = 268 + _globals['_TUNNELRESPONSE']._serialized_end = 304 + _globals['_LISTTUNNELSREQUEST']._serialized_start = 306 + _globals['_LISTTUNNELSREQUEST']._serialized_end = 326 + _globals['_LISTTUNNELSRESPONSE']._serialized_start = 328 + _globals['_LISTTUNNELSRESPONSE']._serialized_end = 398 + _globals['_TUNNEL']._serialized_start = 400 + _globals['_TUNNEL']._serialized_end = 502 + _globals['_FRAMESYSTEMCONFIG']._serialized_start = 504 + _globals['_FRAMESYSTEMCONFIG']._serialized_end = 629 + _globals['_FRAMESYSTEMCONFIGREQUEST']._serialized_start = 631 + _globals['_FRAMESYSTEMCONFIGREQUEST']._serialized_end = 741 + _globals['_FRAMESYSTEMCONFIGRESPONSE']._serialized_start = 743 + _globals['_FRAMESYSTEMCONFIGRESPONSE']._serialized_end = 854 + _globals['_TRANSFORMPOSEREQUEST']._serialized_start = 857 + _globals['_TRANSFORMPOSEREQUEST']._serialized_end = 1050 + _globals['_TRANSFORMPOSERESPONSE']._serialized_start = 1052 + _globals['_TRANSFORMPOSERESPONSE']._serialized_end = 1124 + _globals['_TRANSFORMPCDREQUEST']._serialized_start = 1126 + _globals['_TRANSFORMPCDREQUEST']._serialized_end = 1245 + _globals['_TRANSFORMPCDRESPONSE']._serialized_start = 1247 + _globals['_TRANSFORMPCDRESPONSE']._serialized_end = 1309 + _globals['_RESOURCENAMESREQUEST']._serialized_start = 1311 + _globals['_RESOURCENAMESREQUEST']._serialized_end = 1333 + _globals['_RESOURCENAMESRESPONSE']._serialized_start = 1335 + _globals['_RESOURCENAMESRESPONSE']._serialized_end = 1418 + _globals['_RESOURCERPCSUBTYPE']._serialized_start = 1420 + _globals['_RESOURCERPCSUBTYPE']._serialized_end = 1533 + _globals['_RESOURCERPCSUBTYPESREQUEST']._serialized_start = 1535 + _globals['_RESOURCERPCSUBTYPESREQUEST']._serialized_end = 1563 + _globals['_RESOURCERPCSUBTYPESRESPONSE']._serialized_start = 1565 + _globals['_RESOURCERPCSUBTYPESRESPONSE']._serialized_end = 1681 + _globals['_OPERATION']._serialized_start = 1684 + _globals['_OPERATION']._serialized_end = 1895 + _globals['_GETOPERATIONSREQUEST']._serialized_start = 1897 + _globals['_GETOPERATIONSREQUEST']._serialized_end = 1919 + _globals['_GETOPERATIONSRESPONSE']._serialized_start = 1921 + _globals['_GETOPERATIONSRESPONSE']._serialized_end = 2002 + _globals['_CANCELOPERATIONREQUEST']._serialized_start = 2004 + _globals['_CANCELOPERATIONREQUEST']._serialized_end = 2044 + _globals['_CANCELOPERATIONRESPONSE']._serialized_start = 2046 + _globals['_CANCELOPERATIONRESPONSE']._serialized_end = 2071 + _globals['_BLOCKFOROPERATIONREQUEST']._serialized_start = 2073 + _globals['_BLOCKFOROPERATIONREQUEST']._serialized_end = 2115 + _globals['_BLOCKFOROPERATIONRESPONSE']._serialized_start = 2117 + _globals['_BLOCKFOROPERATIONRESPONSE']._serialized_end = 2144 + _globals['_PEERCONNECTIONINFO']._serialized_start = 2147 + _globals['_PEERCONNECTIONINFO']._serialized_end = 2345 + _globals['_SESSION']._serialized_start = 2348 + _globals['_SESSION']._serialized_end = 2488 + _globals['_GETSESSIONSREQUEST']._serialized_start = 2490 + _globals['_GETSESSIONSREQUEST']._serialized_end = 2510 + _globals['_GETSESSIONSRESPONSE']._serialized_start = 2512 + _globals['_GETSESSIONSRESPONSE']._serialized_end = 2585 + _globals['_MODULEMODEL']._serialized_start = 2588 + _globals['_MODULEMODEL']._serialized_end = 2718 + _globals['_GETMODELSFROMMODULESREQUEST']._serialized_start = 2720 + _globals['_GETMODELSFROMMODULESREQUEST']._serialized_end = 2749 + _globals['_GETMODELSFROMMODULESRESPONSE']._serialized_start = 2751 + _globals['_GETMODELSFROMMODULESRESPONSE']._serialized_end = 2833 + _globals['_STATUS']._serialized_start = 2836 + _globals['_STATUS']._serialized_end = 3016 + _globals['_GETSTATUSREQUEST']._serialized_start = 3018 + _globals['_GETSTATUSREQUEST']._serialized_end = 3105 + _globals['_GETSTATUSRESPONSE']._serialized_start = 3107 + _globals['_GETSTATUSRESPONSE']._serialized_end = 3173 + _globals['_STREAMSTATUSREQUEST']._serialized_start = 3176 + _globals['_STREAMSTATUSREQUEST']._serialized_end = 3315 + _globals['_STREAMSTATUSRESPONSE']._serialized_start = 3317 + _globals['_STREAMSTATUSRESPONSE']._serialized_end = 3386 + _globals['_STOPEXTRAPARAMETERS']._serialized_start = 3388 + _globals['_STOPEXTRAPARAMETERS']._serialized_end = 3508 + _globals['_STOPALLREQUEST']._serialized_start = 3510 + _globals['_STOPALLREQUEST']._serialized_end = 3584 + _globals['_STOPALLRESPONSE']._serialized_start = 3586 + _globals['_STOPALLRESPONSE']._serialized_end = 3603 + _globals['_STARTSESSIONREQUEST']._serialized_start = 3605 + _globals['_STARTSESSIONREQUEST']._serialized_end = 3650 + _globals['_STARTSESSIONRESPONSE']._serialized_start = 3652 + _globals['_STARTSESSIONRESPONSE']._serialized_end = 3760 + _globals['_SENDSESSIONHEARTBEATREQUEST']._serialized_start = 3762 + _globals['_SENDSESSIONHEARTBEATREQUEST']._serialized_end = 3807 + _globals['_SENDSESSIONHEARTBEATRESPONSE']._serialized_start = 3809 + _globals['_SENDSESSIONHEARTBEATRESPONSE']._serialized_end = 3839 + _globals['_LOGREQUEST']._serialized_start = 3841 + _globals['_LOGREQUEST']._serialized_end = 3899 + _globals['_LOGRESPONSE']._serialized_start = 3901 + _globals['_LOGRESPONSE']._serialized_end = 3914 + _globals['_GETCLOUDMETADATAREQUEST']._serialized_start = 3916 + _globals['_GETCLOUDMETADATAREQUEST']._serialized_end = 3941 + _globals['_GETCLOUDMETADATARESPONSE']._serialized_start = 3944 + _globals['_GETCLOUDMETADATARESPONSE']._serialized_end = 4152 + _globals['_RESTARTMODULEREQUEST']._serialized_start = 4154 + _globals['_RESTARTMODULEREQUEST']._serialized_end = 4256 + _globals['_RESTARTMODULERESPONSE']._serialized_start = 4258 + _globals['_RESTARTMODULERESPONSE']._serialized_end = 4281 + _globals['_SHUTDOWNREQUEST']._serialized_start = 4283 + _globals['_SHUTDOWNREQUEST']._serialized_end = 4300 + _globals['_SHUTDOWNRESPONSE']._serialized_start = 4302 + _globals['_SHUTDOWNRESPONSE']._serialized_end = 4320 + _globals['_GETMACHINESTATUSREQUEST']._serialized_start = 4322 + _globals['_GETMACHINESTATUSREQUEST']._serialized_end = 4347 + _globals['_GETMACHINESTATUSRESPONSE']._serialized_start = 4350 + _globals['_GETMACHINESTATUSRESPONSE']._serialized_end = 4634 + _globals['_GETMACHINESTATUSRESPONSE_STATE']._serialized_start = 4561 + _globals['_GETMACHINESTATUSRESPONSE_STATE']._serialized_end = 4634 + _globals['_RESOURCESTATUS']._serialized_start = 4637 + _globals['_RESOURCESTATUS']._serialized_end = 5117 + _globals['_RESOURCESTATUS_STATE']._serialized_start = 4963 + _globals['_RESOURCESTATUS_STATE']._serialized_end = 5098 + _globals['_CONFIGSTATUS']._serialized_start = 5119 + _globals['_CONFIGSTATUS']._serialized_end = 5224 + _globals['_GETVERSIONREQUEST']._serialized_start = 5226 + _globals['_GETVERSIONREQUEST']._serialized_end = 5245 + _globals['_GETVERSIONRESPONSE']._serialized_start = 5247 + _globals['_GETVERSIONRESPONSE']._serialized_end = 5354 + _globals['_ROBOTSERVICE']._serialized_start = 5481 + _globals['_ROBOTSERVICE']._serialized_end = 8439 \ No newline at end of file diff --git a/src/viam/gen/robot/v1/robot_pb2.pyi b/src/viam/gen/robot/v1/robot_pb2.pyi index ffcb7fbf9..b2ef3cbd3 100644 --- a/src/viam/gen/robot/v1/robot_pb2.pyi +++ b/src/viam/gen/robot/v1/robot_pb2.pyi @@ -8,38 +8,133 @@ from ... import common import google.protobuf.descriptor import google.protobuf.duration_pb2 import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import google.protobuf.struct_pb2 import google.protobuf.timestamp_pb2 import sys -if sys.version_info >= (3, 8): +import typing +if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _PeerConnectionType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PeerConnectionTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PeerConnectionType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PEER_CONNECTION_TYPE_UNSPECIFIED: _PeerConnectionType.ValueType + PEER_CONNECTION_TYPE_GRPC: _PeerConnectionType.ValueType + PEER_CONNECTION_TYPE_WEBRTC: _PeerConnectionType.ValueType + +class PeerConnectionType(_PeerConnectionType, metaclass=_PeerConnectionTypeEnumTypeWrapper): + ... +PEER_CONNECTION_TYPE_UNSPECIFIED: PeerConnectionType.ValueType +PEER_CONNECTION_TYPE_GRPC: PeerConnectionType.ValueType +PEER_CONNECTION_TYPE_WEBRTC: PeerConnectionType.ValueType +global___PeerConnectionType = PeerConnectionType + +@typing.final +class TunnelRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DESTINATION_PORT_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + destination_port: builtins.int + data: builtins.bytes + + def __init__(self, *, destination_port: builtins.int=..., data: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data', 'destination_port', b'destination_port']) -> None: + ... +global___TunnelRequest = TunnelRequest + +@typing.final +class TunnelResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + data: builtins.bytes + + def __init__(self, *, data: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___TunnelResponse = TunnelResponse + +@typing.final +class ListTunnelsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___ListTunnelsRequest = ListTunnelsRequest + +@typing.final +class ListTunnelsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + TUNNELS_FIELD_NUMBER: builtins.int + + @property + def tunnels(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Tunnel]: + ... + + def __init__(self, *, tunnels: collections.abc.Iterable[global___Tunnel] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['tunnels', b'tunnels']) -> None: + ... +global___ListTunnelsResponse = ListTunnelsResponse + +@typing.final +class Tunnel(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PORT_FIELD_NUMBER: builtins.int + CONNECTION_TIMEOUT_FIELD_NUMBER: builtins.int + port: builtins.int + + @property + def connection_timeout(self) -> google.protobuf.duration_pb2.Duration: + ... + + def __init__(self, *, port: builtins.int=..., connection_timeout: google.protobuf.duration_pb2.Duration | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['connection_timeout', b'connection_timeout']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['connection_timeout', b'connection_timeout', 'port', b'port']) -> None: + ... +global___Tunnel = Tunnel + +@typing.final class FrameSystemConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - POSE_IN_PARENT_FRAME_FIELD_NUMBER: builtins.int - MODEL_JSON_FIELD_NUMBER: builtins.int - name: builtins.str + FRAME_FIELD_NUMBER: builtins.int + KINEMATICS_FIELD_NUMBER: builtins.int @property - def pose_in_parent_frame(self) -> common.v1.common_pb2.PoseInFrame: + def frame(self) -> common.v1.common_pb2.Transform: + """this is an experimental API message""" + + @property + def kinematics(self) -> google.protobuf.struct_pb2.Struct: ... - model_json: builtins.bytes - def __init__(self, *, name: builtins.str=..., pose_in_parent_frame: common.v1.common_pb2.PoseInFrame | None=..., model_json: builtins.bytes=...) -> None: + def __init__(self, *, frame: common.v1.common_pb2.Transform | None=..., kinematics: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['pose_in_parent_frame', b'pose_in_parent_frame']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['frame', b'frame', 'kinematics', b'kinematics']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['model_json', b'model_json', 'name', b'name', 'pose_in_parent_frame', b'pose_in_parent_frame']) -> None: + def ClearField(self, field_name: typing.Literal['frame', b'frame', 'kinematics', b'kinematics']) -> None: ... global___FrameSystemConfig = FrameSystemConfig +@typing.final class FrameSystemConfigRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor SUPPLEMENTAL_TRANSFORMS_FIELD_NUMBER: builtins.int @@ -53,10 +148,11 @@ class FrameSystemConfigRequest(google.protobuf.message.Message): def __init__(self, *, supplemental_transforms: collections.abc.Iterable[common.v1.common_pb2.Transform] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['supplemental_transforms', b'supplemental_transforms']) -> None: + def ClearField(self, field_name: typing.Literal['supplemental_transforms', b'supplemental_transforms']) -> None: ... global___FrameSystemConfigRequest = FrameSystemConfigRequest +@typing.final class FrameSystemConfigResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor FRAME_SYSTEM_CONFIGS_FIELD_NUMBER: builtins.int @@ -68,23 +164,24 @@ class FrameSystemConfigResponse(google.protobuf.message.Message): def __init__(self, *, frame_system_configs: collections.abc.Iterable[global___FrameSystemConfig] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['frame_system_configs', b'frame_system_configs']) -> None: + def ClearField(self, field_name: typing.Literal['frame_system_configs', b'frame_system_configs']) -> None: ... global___FrameSystemConfigResponse = FrameSystemConfigResponse +@typing.final class TransformPoseRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor SOURCE_FIELD_NUMBER: builtins.int DESTINATION_FIELD_NUMBER: builtins.int SUPPLEMENTAL_TRANSFORMS_FIELD_NUMBER: builtins.int + destination: builtins.str + 'the reference frame into which the source pose should be transformed,\n if unset this defaults to the "world" reference frame\n ' @property def source(self) -> common.v1.common_pb2.PoseInFrame: """the original pose to transform along with the reference frame in which it was observed """ - destination: builtins.str - 'the reference frame into which the source pose should be transformed,\n if unset this defaults to the "world" reference frame\n ' @property def supplemental_transforms(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.Transform]: @@ -95,13 +192,14 @@ class TransformPoseRequest(google.protobuf.message.Message): def __init__(self, *, source: common.v1.common_pb2.PoseInFrame | None=..., destination: builtins.str=..., supplemental_transforms: collections.abc.Iterable[common.v1.common_pb2.Transform] | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['source', b'source']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['source', b'source']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['destination', b'destination', 'source', b'source', 'supplemental_transforms', b'supplemental_transforms']) -> None: + def ClearField(self, field_name: typing.Literal['destination', b'destination', 'source', b'source', 'supplemental_transforms', b'supplemental_transforms']) -> None: ... global___TransformPoseRequest = TransformPoseRequest +@typing.final class TransformPoseResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSE_FIELD_NUMBER: builtins.int @@ -113,13 +211,47 @@ class TransformPoseResponse(google.protobuf.message.Message): def __init__(self, *, pose: common.v1.common_pb2.PoseInFrame | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['pose', b'pose']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['pose', b'pose']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['pose', b'pose']) -> None: + def ClearField(self, field_name: typing.Literal['pose', b'pose']) -> None: ... global___TransformPoseResponse = TransformPoseResponse +@typing.final +class TransformPCDRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + POINT_CLOUD_PCD_FIELD_NUMBER: builtins.int + SOURCE_FIELD_NUMBER: builtins.int + DESTINATION_FIELD_NUMBER: builtins.int + point_cloud_pcd: builtins.bytes + 'the point clouds to transform. This should be in the PCD format\n encoded into bytes: https://pointclouds.org/documentation/tutorials/pcd_file_format.html\n ' + source: builtins.str + 'the reference frame of the point cloud.' + destination: builtins.str + 'the reference frame into which the source data should be transformed, if unset this defaults to the "world" reference frame.\n Do not move the robot between the generation of the initial pointcloud and the receipt\n of the transformed pointcloud because that will make the transformations inaccurate\n ' + + def __init__(self, *, point_cloud_pcd: builtins.bytes=..., source: builtins.str=..., destination: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['destination', b'destination', 'point_cloud_pcd', b'point_cloud_pcd', 'source', b'source']) -> None: + ... +global___TransformPCDRequest = TransformPCDRequest + +@typing.final +class TransformPCDResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + POINT_CLOUD_PCD_FIELD_NUMBER: builtins.int + point_cloud_pcd: builtins.bytes + + def __init__(self, *, point_cloud_pcd: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['point_cloud_pcd', b'point_cloud_pcd']) -> None: + ... +global___TransformPCDResponse = TransformPCDResponse + +@typing.final class ResourceNamesRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -127,6 +259,7 @@ class ResourceNamesRequest(google.protobuf.message.Message): ... global___ResourceNamesRequest = ResourceNamesRequest +@typing.final class ResourceNamesResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor RESOURCES_FIELD_NUMBER: builtins.int @@ -138,30 +271,32 @@ class ResourceNamesResponse(google.protobuf.message.Message): def __init__(self, *, resources: collections.abc.Iterable[common.v1.common_pb2.ResourceName] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['resources', b'resources']) -> None: + def ClearField(self, field_name: typing.Literal['resources', b'resources']) -> None: ... global___ResourceNamesResponse = ResourceNamesResponse +@typing.final class ResourceRPCSubtype(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor SUBTYPE_FIELD_NUMBER: builtins.int PROTO_SERVICE_FIELD_NUMBER: builtins.int + proto_service: builtins.str @property def subtype(self) -> common.v1.common_pb2.ResourceName: ... - proto_service: builtins.str def __init__(self, *, subtype: common.v1.common_pb2.ResourceName | None=..., proto_service: builtins.str=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['subtype', b'subtype']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['subtype', b'subtype']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['proto_service', b'proto_service', 'subtype', b'subtype']) -> None: + def ClearField(self, field_name: typing.Literal['proto_service', b'proto_service', 'subtype', b'subtype']) -> None: ... global___ResourceRPCSubtype = ResourceRPCSubtype +@typing.final class ResourceRPCSubtypesRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -169,6 +304,7 @@ class ResourceRPCSubtypesRequest(google.protobuf.message.Message): ... global___ResourceRPCSubtypesRequest = ResourceRPCSubtypesRequest +@typing.final class ResourceRPCSubtypesResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor RESOURCE_RPC_SUBTYPES_FIELD_NUMBER: builtins.int @@ -180,18 +316,21 @@ class ResourceRPCSubtypesResponse(google.protobuf.message.Message): def __init__(self, *, resource_rpc_subtypes: collections.abc.Iterable[global___ResourceRPCSubtype] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['resource_rpc_subtypes', b'resource_rpc_subtypes']) -> None: + def ClearField(self, field_name: typing.Literal['resource_rpc_subtypes', b'resource_rpc_subtypes']) -> None: ... global___ResourceRPCSubtypesResponse = ResourceRPCSubtypesResponse +@typing.final class Operation(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int METHOD_FIELD_NUMBER: builtins.int ARGUMENTS_FIELD_NUMBER: builtins.int STARTED_FIELD_NUMBER: builtins.int + SESSION_ID_FIELD_NUMBER: builtins.int id: builtins.str method: builtins.str + session_id: builtins.str @property def arguments(self) -> google.protobuf.struct_pb2.Struct: @@ -201,16 +340,20 @@ class Operation(google.protobuf.message.Message): def started(self) -> google.protobuf.timestamp_pb2.Timestamp: ... - def __init__(self, *, id: builtins.str=..., method: builtins.str=..., arguments: google.protobuf.struct_pb2.Struct | None=..., started: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + def __init__(self, *, id: builtins.str=..., method: builtins.str=..., arguments: google.protobuf.struct_pb2.Struct | None=..., started: google.protobuf.timestamp_pb2.Timestamp | None=..., session_id: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_session_id', b'_session_id', 'arguments', b'arguments', 'session_id', b'session_id', 'started', b'started']) -> builtins.bool: ... - def HasField(self, field_name: typing_extensions.Literal['arguments', b'arguments', 'started', b'started']) -> builtins.bool: + def ClearField(self, field_name: typing.Literal['_session_id', b'_session_id', 'arguments', b'arguments', 'id', b'id', 'method', b'method', 'session_id', b'session_id', 'started', b'started']) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['arguments', b'arguments', 'id', b'id', 'method', b'method', 'started', b'started']) -> None: + def WhichOneof(self, oneof_group: typing.Literal['_session_id', b'_session_id']) -> typing.Literal['session_id'] | None: ... global___Operation = Operation +@typing.final class GetOperationsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -218,6 +361,7 @@ class GetOperationsRequest(google.protobuf.message.Message): ... global___GetOperationsRequest = GetOperationsRequest +@typing.final class GetOperationsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor OPERATIONS_FIELD_NUMBER: builtins.int @@ -229,10 +373,11 @@ class GetOperationsResponse(google.protobuf.message.Message): def __init__(self, *, operations: collections.abc.Iterable[global___Operation] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['operations', b'operations']) -> None: + def ClearField(self, field_name: typing.Literal['operations', b'operations']) -> None: ... global___GetOperationsResponse = GetOperationsResponse +@typing.final class CancelOperationRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -241,10 +386,11 @@ class CancelOperationRequest(google.protobuf.message.Message): def __init__(self, *, id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: ... global___CancelOperationRequest = CancelOperationRequest +@typing.final class CancelOperationResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -252,6 +398,7 @@ class CancelOperationResponse(google.protobuf.message.Message): ... global___CancelOperationResponse = CancelOperationResponse +@typing.final class BlockForOperationRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -260,10 +407,11 @@ class BlockForOperationRequest(google.protobuf.message.Message): def __init__(self, *, id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: ... global___BlockForOperationRequest = BlockForOperationRequest +@typing.final class BlockForOperationResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -271,78 +419,131 @@ class BlockForOperationResponse(google.protobuf.message.Message): ... global___BlockForOperationResponse = BlockForOperationResponse -class DiscoveryQuery(google.protobuf.message.Message): - """Discovery""" +@typing.final +class PeerConnectionInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - SUBTYPE_FIELD_NUMBER: builtins.int - MODEL_FIELD_NUMBER: builtins.int - subtype: builtins.str - model: builtins.str + TYPE_FIELD_NUMBER: builtins.int + REMOTE_ADDRESS_FIELD_NUMBER: builtins.int + LOCAL_ADDRESS_FIELD_NUMBER: builtins.int + type: global___PeerConnectionType.ValueType + remote_address: builtins.str + local_address: builtins.str + + def __init__(self, *, type: global___PeerConnectionType.ValueType=..., remote_address: builtins.str | None=..., local_address: builtins.str | None=...) -> None: + ... - def __init__(self, *, subtype: builtins.str=..., model: builtins.str=...) -> None: + def HasField(self, field_name: typing.Literal['_local_address', b'_local_address', '_remote_address', b'_remote_address', 'local_address', b'local_address', 'remote_address', b'remote_address']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['model', b'model', 'subtype', b'subtype']) -> None: + def ClearField(self, field_name: typing.Literal['_local_address', b'_local_address', '_remote_address', b'_remote_address', 'local_address', b'local_address', 'remote_address', b'remote_address', 'type', b'type']) -> None: ... -global___DiscoveryQuery = DiscoveryQuery -class Discovery(google.protobuf.message.Message): + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_local_address', b'_local_address']) -> typing.Literal['local_address'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_remote_address', b'_remote_address']) -> typing.Literal['remote_address'] | None: + ... +global___PeerConnectionInfo = PeerConnectionInfo + +@typing.final +class Session(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - QUERY_FIELD_NUMBER: builtins.int - RESULTS_FIELD_NUMBER: builtins.int + ID_FIELD_NUMBER: builtins.int + PEER_CONNECTION_INFO_FIELD_NUMBER: builtins.int + id: builtins.str @property - def query(self) -> global___DiscoveryQuery: + def peer_connection_info(self) -> global___PeerConnectionInfo: ... - @property - def results(self) -> google.protobuf.struct_pb2.Struct: + def __init__(self, *, id: builtins.str=..., peer_connection_info: global___PeerConnectionInfo | None=...) -> None: ... - def __init__(self, *, query: global___DiscoveryQuery | None=..., results: google.protobuf.struct_pb2.Struct | None=...) -> None: + def HasField(self, field_name: typing.Literal['_peer_connection_info', b'_peer_connection_info', 'peer_connection_info', b'peer_connection_info']) -> builtins.bool: ... - def HasField(self, field_name: typing_extensions.Literal['query', b'query', 'results', b'results']) -> builtins.bool: + def ClearField(self, field_name: typing.Literal['_peer_connection_info', b'_peer_connection_info', 'id', b'id', 'peer_connection_info', b'peer_connection_info']) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['query', b'query', 'results', b'results']) -> None: + def WhichOneof(self, oneof_group: typing.Literal['_peer_connection_info', b'_peer_connection_info']) -> typing.Literal['peer_connection_info'] | None: ... -global___Discovery = Discovery +global___Session = Session -class DiscoverComponentsRequest(google.protobuf.message.Message): +@typing.final +class GetSessionsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - QUERIES_FIELD_NUMBER: builtins.int + + def __init__(self) -> None: + ... +global___GetSessionsRequest = GetSessionsRequest + +@typing.final +class GetSessionsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SESSIONS_FIELD_NUMBER: builtins.int @property - def queries(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___DiscoveryQuery]: + def sessions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Session]: + ... + + def __init__(self, *, sessions: collections.abc.Iterable[global___Session] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['sessions', b'sessions']) -> None: + ... +global___GetSessionsResponse = GetSessionsResponse + +@typing.final +class ModuleModel(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODULE_NAME_FIELD_NUMBER: builtins.int + MODEL_FIELD_NUMBER: builtins.int + API_FIELD_NUMBER: builtins.int + FROM_LOCAL_MODULE_FIELD_NUMBER: builtins.int + module_name: builtins.str + model: builtins.str + api: builtins.str + from_local_module: builtins.bool + + def __init__(self, *, module_name: builtins.str=..., model: builtins.str=..., api: builtins.str=..., from_local_module: builtins.bool=...) -> None: ... - def __init__(self, *, queries: collections.abc.Iterable[global___DiscoveryQuery] | None=...) -> None: + def ClearField(self, field_name: typing.Literal['api', b'api', 'from_local_module', b'from_local_module', 'model', b'model', 'module_name', b'module_name']) -> None: ... +global___ModuleModel = ModuleModel - def ClearField(self, field_name: typing_extensions.Literal['queries', b'queries']) -> None: +@typing.final +class GetModelsFromModulesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: ... -global___DiscoverComponentsRequest = DiscoverComponentsRequest +global___GetModelsFromModulesRequest = GetModelsFromModulesRequest -class DiscoverComponentsResponse(google.protobuf.message.Message): +@typing.final +class GetModelsFromModulesResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - DISCOVERY_FIELD_NUMBER: builtins.int + MODELS_FIELD_NUMBER: builtins.int @property - def discovery(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Discovery]: + def models(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ModuleModel]: ... - def __init__(self, *, discovery: collections.abc.Iterable[global___Discovery] | None=...) -> None: + def __init__(self, *, models: collections.abc.Iterable[global___ModuleModel] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['discovery', b'discovery']) -> None: + def ClearField(self, field_name: typing.Literal['models', b'models']) -> None: ... -global___DiscoverComponentsResponse = DiscoverComponentsResponse +global___GetModelsFromModulesResponse = GetModelsFromModulesResponse +@typing.final class Status(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int STATUS_FIELD_NUMBER: builtins.int + LAST_RECONFIGURED_FIELD_NUMBER: builtins.int @property def name(self) -> common.v1.common_pb2.ResourceName: @@ -352,16 +553,21 @@ class Status(google.protobuf.message.Message): def status(self) -> google.protobuf.struct_pb2.Struct: ... - def __init__(self, *, name: common.v1.common_pb2.ResourceName | None=..., status: google.protobuf.struct_pb2.Struct | None=...) -> None: + @property + def last_reconfigured(self) -> google.protobuf.timestamp_pb2.Timestamp: + ... + + def __init__(self, *, name: common.v1.common_pb2.ResourceName | None=..., status: google.protobuf.struct_pb2.Struct | None=..., last_reconfigured: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['name', b'name', 'status', b'status']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['last_reconfigured', b'last_reconfigured', 'name', b'name', 'status', b'status']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name', 'status', b'status']) -> None: + def ClearField(self, field_name: typing.Literal['last_reconfigured', b'last_reconfigured', 'name', b'name', 'status', b'status']) -> None: ... global___Status = Status +@typing.final class GetStatusRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor RESOURCE_NAMES_FIELD_NUMBER: builtins.int @@ -373,10 +579,11 @@ class GetStatusRequest(google.protobuf.message.Message): def __init__(self, *, resource_names: collections.abc.Iterable[common.v1.common_pb2.ResourceName] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['resource_names', b'resource_names']) -> None: + def ClearField(self, field_name: typing.Literal['resource_names', b'resource_names']) -> None: ... global___GetStatusRequest = GetStatusRequest +@typing.final class GetStatusResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor STATUS_FIELD_NUMBER: builtins.int @@ -388,10 +595,11 @@ class GetStatusResponse(google.protobuf.message.Message): def __init__(self, *, status: collections.abc.Iterable[global___Status] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['status', b'status']) -> None: + def ClearField(self, field_name: typing.Literal['status', b'status']) -> None: ... global___GetStatusResponse = GetStatusResponse +@typing.final class StreamStatusRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor RESOURCE_NAMES_FIELD_NUMBER: builtins.int @@ -408,13 +616,14 @@ class StreamStatusRequest(google.protobuf.message.Message): def __init__(self, *, resource_names: collections.abc.Iterable[common.v1.common_pb2.ResourceName] | None=..., every: google.protobuf.duration_pb2.Duration | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['every', b'every']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['every', b'every']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['every', b'every', 'resource_names', b'resource_names']) -> None: + def ClearField(self, field_name: typing.Literal['every', b'every', 'resource_names', b'resource_names']) -> None: ... global___StreamStatusRequest = StreamStatusRequest +@typing.final class StreamStatusResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor STATUS_FIELD_NUMBER: builtins.int @@ -426,10 +635,11 @@ class StreamStatusResponse(google.protobuf.message.Message): def __init__(self, *, status: collections.abc.Iterable[global___Status] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['status', b'status']) -> None: + def ClearField(self, field_name: typing.Literal['status', b'status']) -> None: ... global___StreamStatusResponse = StreamStatusResponse +@typing.final class StopExtraParameters(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -446,13 +656,14 @@ class StopExtraParameters(google.protobuf.message.Message): def __init__(self, *, name: common.v1.common_pb2.ResourceName | None=..., params: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['name', b'name', 'params', b'params']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['name', b'name', 'params', b'params']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name', 'params', b'params']) -> None: + def ClearField(self, field_name: typing.Literal['name', b'name', 'params', b'params']) -> None: ... global___StopExtraParameters = StopExtraParameters +@typing.final class StopAllRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor EXTRA_FIELD_NUMBER: builtins.int @@ -464,13 +675,346 @@ class StopAllRequest(google.protobuf.message.Message): def __init__(self, *, extra: collections.abc.Iterable[global___StopExtraParameters] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['extra', b'extra']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra']) -> None: ... global___StopAllRequest = StopAllRequest +@typing.final class StopAllResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor def __init__(self) -> None: ... -global___StopAllResponse = StopAllResponse \ No newline at end of file +global___StopAllResponse = StopAllResponse + +@typing.final +class StartSessionRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESUME_FIELD_NUMBER: builtins.int + resume: builtins.str + 'resume can be used to attempt to continue a stream after a disconnection event. If\n a session is not found, a new one will be created and returned.\n ' + + def __init__(self, *, resume: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['resume', b'resume']) -> None: + ... +global___StartSessionRequest = StartSessionRequest + +@typing.final +class StartSessionResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + HEARTBEAT_WINDOW_FIELD_NUMBER: builtins.int + id: builtins.str + + @property + def heartbeat_window(self) -> google.protobuf.duration_pb2.Duration: + ... + + def __init__(self, *, id: builtins.str=..., heartbeat_window: google.protobuf.duration_pb2.Duration | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['heartbeat_window', b'heartbeat_window']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['heartbeat_window', b'heartbeat_window', 'id', b'id']) -> None: + ... +global___StartSessionResponse = StartSessionResponse + +@typing.final +class SendSessionHeartbeatRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + id: builtins.str + + def __init__(self, *, id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['id', b'id']) -> None: + ... +global___SendSessionHeartbeatRequest = SendSessionHeartbeatRequest + +@typing.final +class SendSessionHeartbeatResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___SendSessionHeartbeatResponse = SendSessionHeartbeatResponse + +@typing.final +class LogRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LOGS_FIELD_NUMBER: builtins.int + + @property + def logs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.LogEntry]: + ... + + def __init__(self, *, logs: collections.abc.Iterable[common.v1.common_pb2.LogEntry] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['logs', b'logs']) -> None: + ... +global___LogRequest = LogRequest + +@typing.final +class LogResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___LogResponse = LogResponse + +@typing.final +class GetCloudMetadataRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___GetCloudMetadataRequest = GetCloudMetadataRequest + +@typing.final +class GetCloudMetadataResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ROBOT_PART_ID_FIELD_NUMBER: builtins.int + PRIMARY_ORG_ID_FIELD_NUMBER: builtins.int + LOCATION_ID_FIELD_NUMBER: builtins.int + MACHINE_ID_FIELD_NUMBER: builtins.int + MACHINE_PART_ID_FIELD_NUMBER: builtins.int + robot_part_id: builtins.str + 'Deprecated: use machine_part_id field.' + primary_org_id: builtins.str + location_id: builtins.str + machine_id: builtins.str + machine_part_id: builtins.str + + def __init__(self, *, robot_part_id: builtins.str=..., primary_org_id: builtins.str=..., location_id: builtins.str=..., machine_id: builtins.str=..., machine_part_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['location_id', b'location_id', 'machine_id', b'machine_id', 'machine_part_id', b'machine_part_id', 'primary_org_id', b'primary_org_id', 'robot_part_id', b'robot_part_id']) -> None: + ... +global___GetCloudMetadataResponse = GetCloudMetadataResponse + +@typing.final +class RestartModuleRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MODULE_ID_FIELD_NUMBER: builtins.int + MODULE_NAME_FIELD_NUMBER: builtins.int + module_id: builtins.str + 'ID is for registry modules, name for local modules' + module_name: builtins.str + + def __init__(self, *, module_id: builtins.str=..., module_name: builtins.str=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['id_or_name', b'id_or_name', 'module_id', b'module_id', 'module_name', b'module_name']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['id_or_name', b'id_or_name', 'module_id', b'module_id', 'module_name', b'module_name']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['id_or_name', b'id_or_name']) -> typing.Literal['module_id', 'module_name'] | None: + ... +global___RestartModuleRequest = RestartModuleRequest + +@typing.final +class RestartModuleResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___RestartModuleResponse = RestartModuleResponse + +@typing.final +class ShutdownRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___ShutdownRequest = ShutdownRequest + +@typing.final +class ShutdownResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___ShutdownResponse = ShutdownResponse + +@typing.final +class GetMachineStatusRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___GetMachineStatusRequest = GetMachineStatusRequest + +@typing.final +class GetMachineStatusResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _State: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _StateEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[GetMachineStatusResponse._State.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + STATE_UNSPECIFIED: GetMachineStatusResponse._State.ValueType + STATE_INITIALIZING: GetMachineStatusResponse._State.ValueType + 'the machine is reachable but still in the process of configuring initial\n modules and resources.\n ' + STATE_RUNNING: GetMachineStatusResponse._State.ValueType + 'the machine has finished initializing.' + + class State(_State, metaclass=_StateEnumTypeWrapper): + ... + STATE_UNSPECIFIED: GetMachineStatusResponse.State.ValueType + STATE_INITIALIZING: GetMachineStatusResponse.State.ValueType + 'the machine is reachable but still in the process of configuring initial\n modules and resources.\n ' + STATE_RUNNING: GetMachineStatusResponse.State.ValueType + 'the machine has finished initializing.' + RESOURCES_FIELD_NUMBER: builtins.int + CONFIG_FIELD_NUMBER: builtins.int + STATE_FIELD_NUMBER: builtins.int + state: global___GetMachineStatusResponse.State.ValueType + + @property + def resources(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ResourceStatus]: + ... + + @property + def config(self) -> global___ConfigStatus: + ... + + def __init__(self, *, resources: collections.abc.Iterable[global___ResourceStatus] | None=..., config: global___ConfigStatus | None=..., state: global___GetMachineStatusResponse.State.ValueType=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['config', b'config']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['config', b'config', 'resources', b'resources', 'state', b'state']) -> None: + ... +global___GetMachineStatusResponse = GetMachineStatusResponse + +@typing.final +class ResourceStatus(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _State: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _StateEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[ResourceStatus._State.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + STATE_UNSPECIFIED: ResourceStatus._State.ValueType + STATE_UNCONFIGURED: ResourceStatus._State.ValueType + 'a newly created resource.' + STATE_CONFIGURING: ResourceStatus._State.ValueType + 'a resource that is being configured.' + STATE_READY: ResourceStatus._State.ValueType + 'a resource that has been successfully configured once, and is not re-configuring,\n being removed, or unhealthy.\n ' + STATE_REMOVING: ResourceStatus._State.ValueType + 'a resource that is being removed from the robot.' + STATE_UNHEALTHY: ResourceStatus._State.ValueType + 'a resource that is in an unhealthy state.' + + class State(_State, metaclass=_StateEnumTypeWrapper): + ... + STATE_UNSPECIFIED: ResourceStatus.State.ValueType + STATE_UNCONFIGURED: ResourceStatus.State.ValueType + 'a newly created resource.' + STATE_CONFIGURING: ResourceStatus.State.ValueType + 'a resource that is being configured.' + STATE_READY: ResourceStatus.State.ValueType + 'a resource that has been successfully configured once, and is not re-configuring,\n being removed, or unhealthy.\n ' + STATE_REMOVING: ResourceStatus.State.ValueType + 'a resource that is being removed from the robot.' + STATE_UNHEALTHY: ResourceStatus.State.ValueType + 'a resource that is in an unhealthy state.' + NAME_FIELD_NUMBER: builtins.int + STATE_FIELD_NUMBER: builtins.int + LAST_UPDATED_FIELD_NUMBER: builtins.int + REVISION_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + CLOUD_METADATA_FIELD_NUMBER: builtins.int + state: global___ResourceStatus.State.ValueType + 'current state.' + revision: builtins.str + 'revision of the last config that successfully updated this resource.' + error: builtins.str + 'error details for a resource. This is guaranteed to be null if the\n resource is ready and non-null if the resource unhealthy.\n ' + + @property + def name(self) -> common.v1.common_pb2.ResourceName: + """resource name.""" + + @property + def last_updated(self) -> google.protobuf.timestamp_pb2.Timestamp: + """state transition timestamp.""" + + @property + def cloud_metadata(self) -> global___GetCloudMetadataResponse: + """infomation about resource orgID, locationID and partID""" + + def __init__(self, *, name: common.v1.common_pb2.ResourceName | None=..., state: global___ResourceStatus.State.ValueType=..., last_updated: google.protobuf.timestamp_pb2.Timestamp | None=..., revision: builtins.str=..., error: builtins.str=..., cloud_metadata: global___GetCloudMetadataResponse | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_cloud_metadata', b'_cloud_metadata', 'cloud_metadata', b'cloud_metadata', 'last_updated', b'last_updated', 'name', b'name']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_cloud_metadata', b'_cloud_metadata', 'cloud_metadata', b'cloud_metadata', 'error', b'error', 'last_updated', b'last_updated', 'name', b'name', 'revision', b'revision', 'state', b'state']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_cloud_metadata', b'_cloud_metadata']) -> typing.Literal['cloud_metadata'] | None: + ... +global___ResourceStatus = ResourceStatus + +@typing.final +class ConfigStatus(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + REVISION_FIELD_NUMBER: builtins.int + LAST_UPDATED_FIELD_NUMBER: builtins.int + revision: builtins.str + 'revision of the last config that the machine successfully ingested.' + + @property + def last_updated(self) -> google.protobuf.timestamp_pb2.Timestamp: + """config ingestion timestamp.""" + + def __init__(self, *, revision: builtins.str=..., last_updated: google.protobuf.timestamp_pb2.Timestamp | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['last_updated', b'last_updated']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['last_updated', b'last_updated', 'revision', b'revision']) -> None: + ... +global___ConfigStatus = ConfigStatus + +@typing.final +class GetVersionRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___GetVersionRequest = GetVersionRequest + +@typing.final +class GetVersionResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PLATFORM_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + API_VERSION_FIELD_NUMBER: builtins.int + platform: builtins.str + 'platform type of viam-server (ie. `rdk` or `micro-rdk`).' + version: builtins.str + 'version of viam-server. If built without a version, it will be dev-.' + api_version: builtins.str + + def __init__(self, *, platform: builtins.str=..., version: builtins.str=..., api_version: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['api_version', b'api_version', 'platform', b'platform', 'version', b'version']) -> None: + ... +global___GetVersionResponse = GetVersionResponse \ No newline at end of file diff --git a/src/viam/gen/service/datamanager/v1/data_manager_grpc.py b/src/viam/gen/service/datamanager/v1/data_manager_grpc.py index 4b89f77ab..030c22154 100644 --- a/src/viam/gen/service/datamanager/v1/data_manager_grpc.py +++ b/src/viam/gen/service/datamanager/v1/data_manager_grpc.py @@ -2,9 +2,12 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import common import google.api.annotations_pb2 +import google.protobuf.struct_pb2 from .... import service class DataManagerServiceBase(abc.ABC): @@ -13,10 +16,23 @@ class DataManagerServiceBase(abc.ABC): async def Sync(self, stream: 'grpclib.server.Stream[service.datamanager.v1.data_manager_pb2.SyncRequest, service.datamanager.v1.data_manager_pb2.SyncResponse]') -> None: pass + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.service.datamanager.v1.DataManagerService/Sync': grpclib.const.Handler(self.Sync, grpclib.const.Cardinality.UNARY_UNARY, service.datamanager.v1.data_manager_pb2.SyncRequest, service.datamanager.v1.data_manager_pb2.SyncResponse)} + return {'/viam.service.datamanager.v1.DataManagerService/Sync': grpclib.const.Handler(self.Sync, grpclib.const.Cardinality.UNARY_UNARY, service.datamanager.v1.data_manager_pb2.SyncRequest, service.datamanager.v1.data_manager_pb2.SyncResponse), '/viam.service.datamanager.v1.DataManagerService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedDataManagerServiceBase(DataManagerServiceBase): + + async def Sync(self, stream: 'grpclib.server.Stream[service.datamanager.v1.data_manager_pb2.SyncRequest, service.datamanager.v1.data_manager_pb2.SyncResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class DataManagerServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: - self.Sync = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.datamanager.v1.DataManagerService/Sync', service.datamanager.v1.data_manager_pb2.SyncRequest, service.datamanager.v1.data_manager_pb2.SyncResponse) \ No newline at end of file + self.Sync = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.datamanager.v1.DataManagerService/Sync', service.datamanager.v1.data_manager_pb2.SyncRequest, service.datamanager.v1.data_manager_pb2.SyncResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.datamanager.v1.DataManagerService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/service/datamanager/v1/data_manager_pb2.py b/src/viam/gen/service/datamanager/v1/data_manager_pb2.py index 7fc2c28d0..2407dfcd0 100644 --- a/src/viam/gen/service/datamanager/v1/data_manager_pb2.py +++ b/src/viam/gen/service/datamanager/v1/data_manager_pb2.py @@ -1,21 +1,28 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'service/datamanager/v1/data_manager.proto') _sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)service/datamanager/v1/data_manager.proto\x12\x1bviam.service.datamanager.v1\x1a\x1cgoogle/api/annotations.proto"!\n\x0bSyncRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x0e\n\x0cSyncResponse2\xac\x01\n\x12DataManagerService\x12\x95\x01\n\x04Sync\x12(.viam.service.datamanager.v1.SyncRequest\x1a).viam.service.datamanager.v1.SyncResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/datamanager/{name}/datasyncBI\n\x1fcom.viam.service.datamanager.v1Z&go.viam.com/api/service/datamanager/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.datamanager.v1.data_manager_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1fcom.viam.service.datamanager.v1Z&go.viam.com/api/service/datamanager/v1' - _DATAMANAGERSERVICE.methods_by_name['Sync']._options = None - _DATAMANAGERSERVICE.methods_by_name['Sync']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/datamanager/{name}/datasync' - _SYNCREQUEST._serialized_start = 104 - _SYNCREQUEST._serialized_end = 137 - _SYNCRESPONSE._serialized_start = 139 - _SYNCRESPONSE._serialized_end = 153 - _DATAMANAGERSERVICE._serialized_start = 156 - _DATAMANAGERSERVICE._serialized_end = 328 \ No newline at end of file +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n)service/datamanager/v1/data_manager.proto\x12\x1bviam.service.datamanager.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"P\n\x0bSyncRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x0e\n\x0cSyncResponse2\xbb\x02\n\x12DataManagerService\x12\x95\x01\n\x04Sync\x12(.viam.service.datamanager.v1.SyncRequest\x1a).viam.service.datamanager.v1.SyncResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/datamanager/{name}/datasync\x12\x8c\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse":\x82\xd3\xe4\x93\x024"2/viam/api/v1/service/datamanager/{name}/do_commandBI\n\x1fcom.viam.service.datamanager.v1Z&go.viam.com/api/service/datamanager/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.datamanager.v1.data_manager_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1fcom.viam.service.datamanager.v1Z&go.viam.com/api/service/datamanager/v1' + _globals['_DATAMANAGERSERVICE'].methods_by_name['Sync']._loaded_options = None + _globals['_DATAMANAGERSERVICE'].methods_by_name['Sync']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/datamanager/{name}/datasync' + _globals['_DATAMANAGERSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_DATAMANAGERSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x024"2/viam/api/v1/service/datamanager/{name}/do_command' + _globals['_SYNCREQUEST']._serialized_start = 158 + _globals['_SYNCREQUEST']._serialized_end = 238 + _globals['_SYNCRESPONSE']._serialized_start = 240 + _globals['_SYNCRESPONSE']._serialized_end = 254 + _globals['_DATAMANAGERSERVICE']._serialized_start = 257 + _globals['_DATAMANAGERSERVICE']._serialized_end = 572 \ No newline at end of file diff --git a/src/viam/gen/service/datamanager/v1/data_manager_pb2.pyi b/src/viam/gen/service/datamanager/v1/data_manager_pb2.pyi index 9d078f66a..8c6538abb 100644 --- a/src/viam/gen/service/datamanager/v1/data_manager_pb2.pyi +++ b/src/viam/gen/service/datamanager/v1/data_manager_pb2.pyi @@ -5,25 +5,32 @@ isort:skip_file import builtins import google.protobuf.descriptor import google.protobuf.message -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import google.protobuf.struct_pb2 +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class SyncRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___SyncRequest = SyncRequest +@typing.final class SyncResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor diff --git a/src/viam/gen/service/discovery/__init__.py b/src/viam/gen/service/discovery/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/service/discovery/v1/__init__.py b/src/viam/gen/service/discovery/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/service/discovery/v1/discovery_grpc.py b/src/viam/gen/service/discovery/v1/discovery_grpc.py new file mode 100644 index 000000000..18437dfb4 --- /dev/null +++ b/src/viam/gen/service/discovery/v1/discovery_grpc.py @@ -0,0 +1,39 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from .... import app +from .... import common +import google.api.annotations_pb2 +import google.protobuf.struct_pb2 +from .... import service + +class DiscoveryServiceBase(abc.ABC): + + @abc.abstractmethod + async def DiscoverResources(self, stream: 'grpclib.server.Stream[service.discovery.v1.discovery_pb2.DiscoverResourcesRequest, service.discovery.v1.discovery_pb2.DiscoverResourcesResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.service.discovery.v1.DiscoveryService/DiscoverResources': grpclib.const.Handler(self.DiscoverResources, grpclib.const.Cardinality.UNARY_UNARY, service.discovery.v1.discovery_pb2.DiscoverResourcesRequest, service.discovery.v1.discovery_pb2.DiscoverResourcesResponse), '/viam.service.discovery.v1.DiscoveryService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedDiscoveryServiceBase(DiscoveryServiceBase): + + async def DiscoverResources(self, stream: 'grpclib.server.Stream[service.discovery.v1.discovery_pb2.DiscoverResourcesRequest, service.discovery.v1.discovery_pb2.DiscoverResourcesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class DiscoveryServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.DiscoverResources = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.discovery.v1.DiscoveryService/DiscoverResources', service.discovery.v1.discovery_pb2.DiscoverResourcesRequest, service.discovery.v1.discovery_pb2.DiscoverResourcesResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.discovery.v1.DiscoveryService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/service/discovery/v1/discovery_pb2.py b/src/viam/gen/service/discovery/v1/discovery_pb2.py new file mode 100644 index 000000000..f9ede9887 --- /dev/null +++ b/src/viam/gen/service/discovery/v1/discovery_pb2.py @@ -0,0 +1,29 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'service/discovery/v1/discovery.proto') +_sym_db = _symbol_database.Default() +from ....app.v1 import robot_pb2 as app_dot_v1_dot_robot__pb2 +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n$service/discovery/v1/discovery.proto\x12\x19viam.service.discovery.v1\x1a\x12app/v1/robot.proto\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"]\n\x18DiscoverResourcesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"[\n\x19DiscoverResourcesResponse\x12>\n\x0bdiscoveries\x18\x01 \x03(\x0b2\x1c.viam.app.v1.ComponentConfigR\x0bdiscoveries2\xcf\x02\n\x10DiscoveryService\x12\xad\x01\n\x11DiscoverResources\x123.viam.service.discovery.v1.DiscoverResourcesRequest\x1a4.viam.service.discovery.v1.DiscoverResourcesResponse"-\x82\xd3\xe4\x93\x02\'\x12%/viam/api/v1/service/{name}/discovery\x12\x8a\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/discovery/{name}/do_commandBD\n\x1dcom.viam.service.discovery.v1Z#go.viam.com/api/service/discovey/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.discovery.v1.discovery_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1dcom.viam.service.discovery.v1Z#go.viam.com/api/service/discovey/v1' + _globals['_DISCOVERYSERVICE'].methods_by_name['DiscoverResources']._loaded_options = None + _globals['_DISCOVERYSERVICE'].methods_by_name['DiscoverResources']._serialized_options = b"\x82\xd3\xe4\x93\x02'\x12%/viam/api/v1/service/{name}/discovery" + _globals['_DISCOVERYSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_DISCOVERYSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/discovery/{name}/do_command' + _globals['_DISCOVERRESOURCESREQUEST']._serialized_start = 171 + _globals['_DISCOVERRESOURCESREQUEST']._serialized_end = 264 + _globals['_DISCOVERRESOURCESRESPONSE']._serialized_start = 266 + _globals['_DISCOVERRESOURCESRESPONSE']._serialized_end = 357 + _globals['_DISCOVERYSERVICE']._serialized_start = 360 + _globals['_DISCOVERYSERVICE']._serialized_end = 695 \ No newline at end of file diff --git a/src/viam/gen/service/discovery/v1/discovery_pb2.pyi b/src/viam/gen/service/discovery/v1/discovery_pb2.pyi new file mode 100644 index 000000000..fad274caf --- /dev/null +++ b/src/viam/gen/service/discovery/v1/discovery_pb2.pyi @@ -0,0 +1,51 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +from .... import app +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import google.protobuf.struct_pb2 +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class DiscoverResourcesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'name of the discover service' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___DiscoverResourcesRequest = DiscoverResourcesRequest + +@typing.final +class DiscoverResourcesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DISCOVERIES_FIELD_NUMBER: builtins.int + + @property + def discoveries(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[app.v1.robot_pb2.ComponentConfig]: + """list of ComponentConfigs that describe the components found by a discover service.""" + + def __init__(self, *, discoveries: collections.abc.Iterable[app.v1.robot_pb2.ComponentConfig] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['discoveries', b'discoveries']) -> None: + ... +global___DiscoverResourcesResponse = DiscoverResourcesResponse \ No newline at end of file diff --git a/src/viam/gen/service/generic/__init__.py b/src/viam/gen/service/generic/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/service/generic/v1/__init__.py b/src/viam/gen/service/generic/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/service/generic/v1/generic_grpc.py b/src/viam/gen/service/generic/v1/generic_grpc.py new file mode 100644 index 000000000..114a51746 --- /dev/null +++ b/src/viam/gen/service/generic/v1/generic_grpc.py @@ -0,0 +1,29 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from .... import common +import google.api.annotations_pb2 +from .... import service + +class GenericServiceBase(abc.ABC): + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.service.generic.v1.GenericService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedGenericServiceBase(GenericServiceBase): + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class GenericServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.generic.v1.GenericService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/service/generic/v1/generic_pb2.py b/src/viam/gen/service/generic/v1/generic_pb2.py new file mode 100644 index 000000000..53d25c6e0 --- /dev/null +++ b/src/viam/gen/service/generic/v1/generic_pb2.py @@ -0,0 +1,21 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'service/generic/v1/generic.proto') +_sym_db = _symbol_database.Default() +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n service/generic/v1/generic.proto\x12\x17viam.service.generic.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto2\x9b\x01\n\x0eGenericService\x12\x88\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"6\x82\xd3\xe4\x93\x020"./viam/api/v1/service/generic/{name}/do_commandBA\n\x1bcom.viam.service.generic.v1Z"go.viam.com/api/service/generic/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.generic.v1.generic_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1bcom.viam.service.generic.v1Z"go.viam.com/api/service/generic/v1' + _globals['_GENERICSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_GENERICSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x020"./viam/api/v1/service/generic/{name}/do_command' + _globals['_GENERICSERVICE']._serialized_start = 116 + _globals['_GENERICSERVICE']._serialized_end = 271 \ No newline at end of file diff --git a/src/viam/gen/service/generic/v1/generic_pb2.pyi b/src/viam/gen/service/generic/v1/generic_pb2.pyi new file mode 100644 index 000000000..ab0716881 --- /dev/null +++ b/src/viam/gen/service/generic/v1/generic_pb2.pyi @@ -0,0 +1,6 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import google.protobuf.descriptor +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor \ No newline at end of file diff --git a/src/viam/gen/service/mlmodel/__init__.py b/src/viam/gen/service/mlmodel/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/service/mlmodel/v1/__init__.py b/src/viam/gen/service/mlmodel/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/service/mlmodel/v1/mlmodel_grpc.py b/src/viam/gen/service/mlmodel/v1/mlmodel_grpc.py new file mode 100644 index 000000000..65e94f9df --- /dev/null +++ b/src/viam/gen/service/mlmodel/v1/mlmodel_grpc.py @@ -0,0 +1,37 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +import google.api.annotations_pb2 +import google.protobuf.struct_pb2 +from .... import service + +class MLModelServiceBase(abc.ABC): + + @abc.abstractmethod + async def Infer(self, stream: 'grpclib.server.Stream[service.mlmodel.v1.mlmodel_pb2.InferRequest, service.mlmodel.v1.mlmodel_pb2.InferResponse]') -> None: + pass + + @abc.abstractmethod + async def Metadata(self, stream: 'grpclib.server.Stream[service.mlmodel.v1.mlmodel_pb2.MetadataRequest, service.mlmodel.v1.mlmodel_pb2.MetadataResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.service.mlmodel.v1.MLModelService/Infer': grpclib.const.Handler(self.Infer, grpclib.const.Cardinality.UNARY_UNARY, service.mlmodel.v1.mlmodel_pb2.InferRequest, service.mlmodel.v1.mlmodel_pb2.InferResponse), '/viam.service.mlmodel.v1.MLModelService/Metadata': grpclib.const.Handler(self.Metadata, grpclib.const.Cardinality.UNARY_UNARY, service.mlmodel.v1.mlmodel_pb2.MetadataRequest, service.mlmodel.v1.mlmodel_pb2.MetadataResponse)} + +class UnimplementedMLModelServiceBase(MLModelServiceBase): + + async def Infer(self, stream: 'grpclib.server.Stream[service.mlmodel.v1.mlmodel_pb2.InferRequest, service.mlmodel.v1.mlmodel_pb2.InferResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def Metadata(self, stream: 'grpclib.server.Stream[service.mlmodel.v1.mlmodel_pb2.MetadataRequest, service.mlmodel.v1.mlmodel_pb2.MetadataResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class MLModelServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.Infer = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.mlmodel.v1.MLModelService/Infer', service.mlmodel.v1.mlmodel_pb2.InferRequest, service.mlmodel.v1.mlmodel_pb2.InferResponse) + self.Metadata = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.mlmodel.v1.MLModelService/Metadata', service.mlmodel.v1.mlmodel_pb2.MetadataRequest, service.mlmodel.v1.mlmodel_pb2.MetadataResponse) \ No newline at end of file diff --git a/src/viam/gen/service/mlmodel/v1/mlmodel_pb2.py b/src/viam/gen/service/mlmodel/v1/mlmodel_pb2.py new file mode 100644 index 000000000..e01012ca2 --- /dev/null +++ b/src/viam/gen/service/mlmodel/v1/mlmodel_pb2.py @@ -0,0 +1,83 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'service/mlmodel/v1/mlmodel.proto') +_sym_db = _symbol_database.Default() +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n service/mlmodel/v1/mlmodel.proto\x12\x17viam.service.mlmodel.v1\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"\xae\x01\n\x0cInferRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12I\n\rinput_tensors\x18\x03 \x01(\x0b2$.viam.service.mlmodel.v1.FlatTensorsR\x0cinputTensors\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraJ\x04\x08\x02\x10\x03R\ninput_data"{\n\rInferResponse\x12K\n\x0eoutput_tensors\x18\x03 \x01(\x0b2$.viam.service.mlmodel.v1.FlatTensorsR\routputTensorsJ\x04\x08\x01\x10\x02J\x04\x08\x02\x10\x03R\x04nameR\x0boutput_data"T\n\x0fMetadataRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"Q\n\x10MetadataResponse\x12=\n\x08metadata\x18\x01 \x01(\x0b2!.viam.service.mlmodel.v1.MetadataR\x08metadata"\xde\x01\n\x08Metadata\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04type\x18\x02 \x01(\tR\x04type\x12 \n\x0bdescription\x18\x03 \x01(\tR\x0bdescription\x12B\n\ninput_info\x18\x04 \x03(\x0b2#.viam.service.mlmodel.v1.TensorInfoR\tinputInfo\x12D\n\x0boutput_info\x18\x05 \x03(\x0b2#.viam.service.mlmodel.v1.TensorInfoR\noutputInfo"\xee\x01\n\nTensorInfo\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12 \n\x0bdescription\x18\x02 \x01(\tR\x0bdescription\x12\x1b\n\tdata_type\x18\x03 \x01(\tR\x08dataType\x12\x14\n\x05shape\x18\x04 \x03(\x05R\x05shape\x12H\n\x10associated_files\x18\x05 \x03(\x0b2\x1d.viam.service.mlmodel.v1.FileR\x0fassociatedFiles\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x7f\n\x04File\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12 \n\x0bdescription\x18\x02 \x01(\tR\x0bdescription\x12A\n\nlabel_type\x18\x03 \x01(\x0e2".viam.service.mlmodel.v1.LabelTypeR\tlabelType"(\n\x12FlatTensorDataInt8\x12\x12\n\x04data\x18\x01 \x01(\x0cR\x04data")\n\x13FlatTensorDataUInt8\x12\x12\n\x04data\x18\x01 \x01(\x0cR\x04data"-\n\x13FlatTensorDataInt16\x12\x16\n\x04data\x18\x01 \x03(\x07B\x02\x10\x01R\x04data".\n\x14FlatTensorDataUInt16\x12\x16\n\x04data\x18\x01 \x03(\x07B\x02\x10\x01R\x04data"-\n\x13FlatTensorDataInt32\x12\x16\n\x04data\x18\x01 \x03(\x0fB\x02\x10\x01R\x04data".\n\x14FlatTensorDataUInt32\x12\x16\n\x04data\x18\x01 \x03(\x07B\x02\x10\x01R\x04data"-\n\x13FlatTensorDataInt64\x12\x16\n\x04data\x18\x01 \x03(\x10B\x02\x10\x01R\x04data".\n\x14FlatTensorDataUInt64\x12\x16\n\x04data\x18\x01 \x03(\x06B\x02\x10\x01R\x04data"-\n\x13FlatTensorDataFloat\x12\x16\n\x04data\x18\x01 \x03(\x02B\x02\x10\x01R\x04data".\n\x14FlatTensorDataDouble\x12\x16\n\x04data\x18\x01 \x03(\x01B\x02\x10\x01R\x04data"\xf3\x06\n\nFlatTensor\x12\x14\n\x05shape\x18\x01 \x03(\x06R\x05shape\x12N\n\x0bint8_tensor\x18\x02 \x01(\x0b2+.viam.service.mlmodel.v1.FlatTensorDataInt8H\x00R\nint8Tensor\x12Q\n\x0cuint8_tensor\x18\x03 \x01(\x0b2,.viam.service.mlmodel.v1.FlatTensorDataUInt8H\x00R\x0buint8Tensor\x12Q\n\x0cint16_tensor\x18\x04 \x01(\x0b2,.viam.service.mlmodel.v1.FlatTensorDataInt16H\x00R\x0bint16Tensor\x12T\n\ruint16_tensor\x18\x05 \x01(\x0b2-.viam.service.mlmodel.v1.FlatTensorDataUInt16H\x00R\x0cuint16Tensor\x12Q\n\x0cint32_tensor\x18\x06 \x01(\x0b2,.viam.service.mlmodel.v1.FlatTensorDataInt32H\x00R\x0bint32Tensor\x12T\n\ruint32_tensor\x18\x07 \x01(\x0b2-.viam.service.mlmodel.v1.FlatTensorDataUInt32H\x00R\x0cuint32Tensor\x12Q\n\x0cint64_tensor\x18\x08 \x01(\x0b2,.viam.service.mlmodel.v1.FlatTensorDataInt64H\x00R\x0bint64Tensor\x12T\n\ruint64_tensor\x18\t \x01(\x0b2-.viam.service.mlmodel.v1.FlatTensorDataUInt64H\x00R\x0cuint64Tensor\x12Q\n\x0cfloat_tensor\x18\n \x01(\x0b2,.viam.service.mlmodel.v1.FlatTensorDataFloatH\x00R\x0bfloatTensor\x12T\n\rdouble_tensor\x18\x0b \x01(\x0b2-.viam.service.mlmodel.v1.FlatTensorDataDoubleH\x00R\x0cdoubleTensorB\x08\n\x06tensor"\xbb\x01\n\x0bFlatTensors\x12K\n\x07tensors\x18\x01 \x03(\x0b21.viam.service.mlmodel.v1.FlatTensors.TensorsEntryR\x07tensors\x1a_\n\x0cTensorsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x129\n\x05value\x18\x02 \x01(\x0b2#.viam.service.mlmodel.v1.FlatTensorR\x05value:\x028\x01*`\n\tLabelType\x12\x1a\n\x16LABEL_TYPE_UNSPECIFIED\x10\x00\x12\x1b\n\x17LABEL_TYPE_TENSOR_VALUE\x10\x01\x12\x1a\n\x16LABEL_TYPE_TENSOR_AXIS\x10\x022\xb4\x02\n\x0eMLModelService\x12\x89\x01\n\x05Infer\x12%.viam.service.mlmodel.v1.InferRequest\x1a&.viam.service.mlmodel.v1.InferResponse"1\x82\xd3\xe4\x93\x02+")/viam/api/v1/service/mlmodel/{name}/infer\x12\x95\x01\n\x08Metadata\x12(.viam.service.mlmodel.v1.MetadataRequest\x1a).viam.service.mlmodel.v1.MetadataResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/service/mlmodel/{name}/metadataBA\n\x1bcom.viam.service.mlmodel.v1Z"go.viam.com/api/service/mlmodel/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.mlmodel.v1.mlmodel_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1bcom.viam.service.mlmodel.v1Z"go.viam.com/api/service/mlmodel/v1' + _globals['_FLATTENSORDATAINT16'].fields_by_name['data']._loaded_options = None + _globals['_FLATTENSORDATAINT16'].fields_by_name['data']._serialized_options = b'\x10\x01' + _globals['_FLATTENSORDATAUINT16'].fields_by_name['data']._loaded_options = None + _globals['_FLATTENSORDATAUINT16'].fields_by_name['data']._serialized_options = b'\x10\x01' + _globals['_FLATTENSORDATAINT32'].fields_by_name['data']._loaded_options = None + _globals['_FLATTENSORDATAINT32'].fields_by_name['data']._serialized_options = b'\x10\x01' + _globals['_FLATTENSORDATAUINT32'].fields_by_name['data']._loaded_options = None + _globals['_FLATTENSORDATAUINT32'].fields_by_name['data']._serialized_options = b'\x10\x01' + _globals['_FLATTENSORDATAINT64'].fields_by_name['data']._loaded_options = None + _globals['_FLATTENSORDATAINT64'].fields_by_name['data']._serialized_options = b'\x10\x01' + _globals['_FLATTENSORDATAUINT64'].fields_by_name['data']._loaded_options = None + _globals['_FLATTENSORDATAUINT64'].fields_by_name['data']._serialized_options = b'\x10\x01' + _globals['_FLATTENSORDATAFLOAT'].fields_by_name['data']._loaded_options = None + _globals['_FLATTENSORDATAFLOAT'].fields_by_name['data']._serialized_options = b'\x10\x01' + _globals['_FLATTENSORDATADOUBLE'].fields_by_name['data']._loaded_options = None + _globals['_FLATTENSORDATADOUBLE'].fields_by_name['data']._serialized_options = b'\x10\x01' + _globals['_FLATTENSORS_TENSORSENTRY']._loaded_options = None + _globals['_FLATTENSORS_TENSORSENTRY']._serialized_options = b'8\x01' + _globals['_MLMODELSERVICE'].methods_by_name['Infer']._loaded_options = None + _globals['_MLMODELSERVICE'].methods_by_name['Infer']._serialized_options = b'\x82\xd3\xe4\x93\x02+")/viam/api/v1/service/mlmodel/{name}/infer' + _globals['_MLMODELSERVICE'].methods_by_name['Metadata']._loaded_options = None + _globals['_MLMODELSERVICE'].methods_by_name['Metadata']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/service/mlmodel/{name}/metadata' + _globals['_LABELTYPE']._serialized_start = 2728 + _globals['_LABELTYPE']._serialized_end = 2824 + _globals['_INFERREQUEST']._serialized_start = 122 + _globals['_INFERREQUEST']._serialized_end = 296 + _globals['_INFERRESPONSE']._serialized_start = 298 + _globals['_INFERRESPONSE']._serialized_end = 421 + _globals['_METADATAREQUEST']._serialized_start = 423 + _globals['_METADATAREQUEST']._serialized_end = 507 + _globals['_METADATARESPONSE']._serialized_start = 509 + _globals['_METADATARESPONSE']._serialized_end = 590 + _globals['_METADATA']._serialized_start = 593 + _globals['_METADATA']._serialized_end = 815 + _globals['_TENSORINFO']._serialized_start = 818 + _globals['_TENSORINFO']._serialized_end = 1056 + _globals['_FILE']._serialized_start = 1058 + _globals['_FILE']._serialized_end = 1185 + _globals['_FLATTENSORDATAINT8']._serialized_start = 1187 + _globals['_FLATTENSORDATAINT8']._serialized_end = 1227 + _globals['_FLATTENSORDATAUINT8']._serialized_start = 1229 + _globals['_FLATTENSORDATAUINT8']._serialized_end = 1270 + _globals['_FLATTENSORDATAINT16']._serialized_start = 1272 + _globals['_FLATTENSORDATAINT16']._serialized_end = 1317 + _globals['_FLATTENSORDATAUINT16']._serialized_start = 1319 + _globals['_FLATTENSORDATAUINT16']._serialized_end = 1365 + _globals['_FLATTENSORDATAINT32']._serialized_start = 1367 + _globals['_FLATTENSORDATAINT32']._serialized_end = 1412 + _globals['_FLATTENSORDATAUINT32']._serialized_start = 1414 + _globals['_FLATTENSORDATAUINT32']._serialized_end = 1460 + _globals['_FLATTENSORDATAINT64']._serialized_start = 1462 + _globals['_FLATTENSORDATAINT64']._serialized_end = 1507 + _globals['_FLATTENSORDATAUINT64']._serialized_start = 1509 + _globals['_FLATTENSORDATAUINT64']._serialized_end = 1555 + _globals['_FLATTENSORDATAFLOAT']._serialized_start = 1557 + _globals['_FLATTENSORDATAFLOAT']._serialized_end = 1602 + _globals['_FLATTENSORDATADOUBLE']._serialized_start = 1604 + _globals['_FLATTENSORDATADOUBLE']._serialized_end = 1650 + _globals['_FLATTENSOR']._serialized_start = 1653 + _globals['_FLATTENSOR']._serialized_end = 2536 + _globals['_FLATTENSORS']._serialized_start = 2539 + _globals['_FLATTENSORS']._serialized_end = 2726 + _globals['_FLATTENSORS_TENSORSENTRY']._serialized_start = 2631 + _globals['_FLATTENSORS_TENSORSENTRY']._serialized_end = 2726 + _globals['_MLMODELSERVICE']._serialized_start = 2827 + _globals['_MLMODELSERVICE']._serialized_end = 3135 \ No newline at end of file diff --git a/src/viam/gen/service/mlmodel/v1/mlmodel_pb2.pyi b/src/viam/gen/service/mlmodel/v1/mlmodel_pb2.pyi new file mode 100644 index 000000000..e3ee307ab --- /dev/null +++ b/src/viam/gen/service/mlmodel/v1/mlmodel_pb2.pyi @@ -0,0 +1,480 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import google.protobuf.struct_pb2 +import sys +import typing +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _LabelType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _LabelTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_LabelType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LABEL_TYPE_UNSPECIFIED: _LabelType.ValueType + LABEL_TYPE_TENSOR_VALUE: _LabelType.ValueType + 'the value of the arrays/tensor is the label index' + LABEL_TYPE_TENSOR_AXIS: _LabelType.ValueType + 'the position of the tensor value in the axis is the label index' + +class LabelType(_LabelType, metaclass=_LabelTypeEnumTypeWrapper): + ... +LABEL_TYPE_UNSPECIFIED: LabelType.ValueType +LABEL_TYPE_TENSOR_VALUE: LabelType.ValueType +'the value of the arrays/tensor is the label index' +LABEL_TYPE_TENSOR_AXIS: LabelType.ValueType +'the position of the tensor value in the axis is the label index' +global___LabelType = LabelType + +@typing.final +class InferRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + INPUT_TENSORS_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'name of the model service' + + @property + def input_tensors(self) -> global___FlatTensors: + """the input data is provided as set of named flat tensors""" + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., input_tensors: global___FlatTensors | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra', 'input_tensors', b'input_tensors']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'input_tensors', b'input_tensors', 'name', b'name']) -> None: + ... +global___InferRequest = InferRequest + +@typing.final +class InferResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + OUTPUT_TENSORS_FIELD_NUMBER: builtins.int + + @property + def output_tensors(self) -> global___FlatTensors: + """the output data is provided as a set of named flat tensors""" + + def __init__(self, *, output_tensors: global___FlatTensors | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['output_tensors', b'output_tensors']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['output_tensors', b'output_tensors']) -> None: + ... +global___InferResponse = InferResponse + +@typing.final +class MetadataRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'name of the model service' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___MetadataRequest = MetadataRequest + +@typing.final +class MetadataResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + METADATA_FIELD_NUMBER: builtins.int + + @property + def metadata(self) -> global___Metadata: + """this is the metadata associated with the ML model""" + + def __init__(self, *, metadata: global___Metadata | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['metadata', b'metadata']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['metadata', b'metadata']) -> None: + ... +global___MetadataResponse = MetadataResponse + +@typing.final +class Metadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + INPUT_INFO_FIELD_NUMBER: builtins.int + OUTPUT_INFO_FIELD_NUMBER: builtins.int + name: builtins.str + 'name of the model' + type: builtins.str + 'type of model e.g. object_detector, text_classifier' + description: builtins.str + 'description of the model' + + @property + def input_info(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TensorInfo]: + """the necessary input arrays/tensors for an inference, order matters""" + + @property + def output_info(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TensorInfo]: + """the output arrays/tensors of the model, order matters""" + + def __init__(self, *, name: builtins.str=..., type: builtins.str=..., description: builtins.str=..., input_info: collections.abc.Iterable[global___TensorInfo] | None=..., output_info: collections.abc.Iterable[global___TensorInfo] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['description', b'description', 'input_info', b'input_info', 'name', b'name', 'output_info', b'output_info', 'type', b'type']) -> None: + ... +global___Metadata = Metadata + +@typing.final +class TensorInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + DATA_TYPE_FIELD_NUMBER: builtins.int + SHAPE_FIELD_NUMBER: builtins.int + ASSOCIATED_FILES_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'name of the data in the array/tensor' + description: builtins.str + 'description of the data in the array/tensor' + data_type: builtins.str + 'data type of the array/tensor, e.g. float32, float64, uint8' + + @property + def shape(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """shape of the array/tensor (-1 for unknown)""" + + @property + def associated_files(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___File]: + """files associated with the array/tensor, like for category labels""" + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """anything else you want to say""" + + def __init__(self, *, name: builtins.str=..., description: builtins.str=..., data_type: builtins.str=..., shape: collections.abc.Iterable[builtins.int] | None=..., associated_files: collections.abc.Iterable[global___File] | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['associated_files', b'associated_files', 'data_type', b'data_type', 'description', b'description', 'extra', b'extra', 'name', b'name', 'shape', b'shape']) -> None: + ... +global___TensorInfo = TensorInfo + +@typing.final +class File(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + LABEL_TYPE_FIELD_NUMBER: builtins.int + name: builtins.str + 'name of the file, with file extension' + description: builtins.str + 'description of what the file contains' + label_type: global___LabelType.ValueType + 'How to associate the arrays/tensors to the labels in the file' + + def __init__(self, *, name: builtins.str=..., description: builtins.str=..., label_type: global___LabelType.ValueType=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['description', b'description', 'label_type', b'label_type', 'name', b'name']) -> None: + ... +global___File = File + +@typing.final +class FlatTensorDataInt8(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + data: builtins.bytes + + def __init__(self, *, data: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___FlatTensorDataInt8 = FlatTensorDataInt8 + +@typing.final +class FlatTensorDataUInt8(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + data: builtins.bytes + + def __init__(self, *, data: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___FlatTensorDataUInt8 = FlatTensorDataUInt8 + +@typing.final +class FlatTensorDataInt16(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """packs two 16-bit numbers per entry - explicitly little-endian + so big-endian producers/consumers must compensate + """ + + def __init__(self, *, data: collections.abc.Iterable[builtins.int] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___FlatTensorDataInt16 = FlatTensorDataInt16 + +@typing.final +class FlatTensorDataUInt16(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """packs two 16-bit numbers per entry - explicitly little-endian + so big-endian producers/consumers must compensate + """ + + def __init__(self, *, data: collections.abc.Iterable[builtins.int] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___FlatTensorDataUInt16 = FlatTensorDataUInt16 + +@typing.final +class FlatTensorDataInt32(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + ... + + def __init__(self, *, data: collections.abc.Iterable[builtins.int] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___FlatTensorDataInt32 = FlatTensorDataInt32 + +@typing.final +class FlatTensorDataUInt32(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + ... + + def __init__(self, *, data: collections.abc.Iterable[builtins.int] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___FlatTensorDataUInt32 = FlatTensorDataUInt32 + +@typing.final +class FlatTensorDataInt64(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + ... + + def __init__(self, *, data: collections.abc.Iterable[builtins.int] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___FlatTensorDataInt64 = FlatTensorDataInt64 + +@typing.final +class FlatTensorDataUInt64(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + ... + + def __init__(self, *, data: collections.abc.Iterable[builtins.int] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___FlatTensorDataUInt64 = FlatTensorDataUInt64 + +@typing.final +class FlatTensorDataFloat(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: + ... + + def __init__(self, *, data: collections.abc.Iterable[builtins.float] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___FlatTensorDataFloat = FlatTensorDataFloat + +@typing.final +class FlatTensorDataDouble(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DATA_FIELD_NUMBER: builtins.int + + @property + def data(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: + ... + + def __init__(self, *, data: collections.abc.Iterable[builtins.float] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['data', b'data']) -> None: + ... +global___FlatTensorDataDouble = FlatTensorDataDouble + +@typing.final +class FlatTensor(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SHAPE_FIELD_NUMBER: builtins.int + INT8_TENSOR_FIELD_NUMBER: builtins.int + UINT8_TENSOR_FIELD_NUMBER: builtins.int + INT16_TENSOR_FIELD_NUMBER: builtins.int + UINT16_TENSOR_FIELD_NUMBER: builtins.int + INT32_TENSOR_FIELD_NUMBER: builtins.int + UINT32_TENSOR_FIELD_NUMBER: builtins.int + INT64_TENSOR_FIELD_NUMBER: builtins.int + UINT64_TENSOR_FIELD_NUMBER: builtins.int + FLOAT_TENSOR_FIELD_NUMBER: builtins.int + DOUBLE_TENSOR_FIELD_NUMBER: builtins.int + + @property + def shape(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.int]: + """the shape of the provided tensor as a list of integer extents""" + + @property + def int8_tensor(self) -> global___FlatTensorDataInt8: + ... + + @property + def uint8_tensor(self) -> global___FlatTensorDataUInt8: + ... + + @property + def int16_tensor(self) -> global___FlatTensorDataInt16: + ... + + @property + def uint16_tensor(self) -> global___FlatTensorDataUInt16: + ... + + @property + def int32_tensor(self) -> global___FlatTensorDataInt32: + ... + + @property + def uint32_tensor(self) -> global___FlatTensorDataUInt32: + ... + + @property + def int64_tensor(self) -> global___FlatTensorDataInt64: + ... + + @property + def uint64_tensor(self) -> global___FlatTensorDataUInt64: + ... + + @property + def float_tensor(self) -> global___FlatTensorDataFloat: + ... + + @property + def double_tensor(self) -> global___FlatTensorDataDouble: + ... + + def __init__(self, *, shape: collections.abc.Iterable[builtins.int] | None=..., int8_tensor: global___FlatTensorDataInt8 | None=..., uint8_tensor: global___FlatTensorDataUInt8 | None=..., int16_tensor: global___FlatTensorDataInt16 | None=..., uint16_tensor: global___FlatTensorDataUInt16 | None=..., int32_tensor: global___FlatTensorDataInt32 | None=..., uint32_tensor: global___FlatTensorDataUInt32 | None=..., int64_tensor: global___FlatTensorDataInt64 | None=..., uint64_tensor: global___FlatTensorDataUInt64 | None=..., float_tensor: global___FlatTensorDataFloat | None=..., double_tensor: global___FlatTensorDataDouble | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['double_tensor', b'double_tensor', 'float_tensor', b'float_tensor', 'int16_tensor', b'int16_tensor', 'int32_tensor', b'int32_tensor', 'int64_tensor', b'int64_tensor', 'int8_tensor', b'int8_tensor', 'tensor', b'tensor', 'uint16_tensor', b'uint16_tensor', 'uint32_tensor', b'uint32_tensor', 'uint64_tensor', b'uint64_tensor', 'uint8_tensor', b'uint8_tensor']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['double_tensor', b'double_tensor', 'float_tensor', b'float_tensor', 'int16_tensor', b'int16_tensor', 'int32_tensor', b'int32_tensor', 'int64_tensor', b'int64_tensor', 'int8_tensor', b'int8_tensor', 'shape', b'shape', 'tensor', b'tensor', 'uint16_tensor', b'uint16_tensor', 'uint32_tensor', b'uint32_tensor', 'uint64_tensor', b'uint64_tensor', 'uint8_tensor', b'uint8_tensor']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['tensor', b'tensor']) -> typing.Literal['int8_tensor', 'uint8_tensor', 'int16_tensor', 'uint16_tensor', 'int32_tensor', 'uint32_tensor', 'int64_tensor', 'uint64_tensor', 'float_tensor', 'double_tensor'] | None: + ... +global___FlatTensor = FlatTensor + +@typing.final +class FlatTensors(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class TensorsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + + @property + def value(self) -> global___FlatTensor: + ... + + def __init__(self, *, key: builtins.str=..., value: global___FlatTensor | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['value', b'value']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... + TENSORS_FIELD_NUMBER: builtins.int + + @property + def tensors(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___FlatTensor]: + """A name-indexed collection of flat tensor objects""" + + def __init__(self, *, tensors: collections.abc.Mapping[builtins.str, global___FlatTensor] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['tensors', b'tensors']) -> None: + ... +global___FlatTensors = FlatTensors \ No newline at end of file diff --git a/src/viam/gen/service/motion/v1/motion_grpc.py b/src/viam/gen/service/motion/v1/motion_grpc.py index c3da893db..4a268282b 100644 --- a/src/viam/gen/service/motion/v1/motion_grpc.py +++ b/src/viam/gen/service/motion/v1/motion_grpc.py @@ -2,11 +2,13 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common import google.api.annotations_pb2 import google.protobuf.struct_pb2 +import google.protobuf.timestamp_pb2 from .... import service class MotionServiceBase(abc.ABC): @@ -16,19 +18,70 @@ async def Move(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2 pass @abc.abstractmethod - async def MoveSingleComponent(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.MoveSingleComponentRequest, service.motion.v1.motion_pb2.MoveSingleComponentResponse]') -> None: + async def MoveOnMap(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.MoveOnMapRequest, service.motion.v1.motion_pb2.MoveOnMapResponse]') -> None: + pass + + @abc.abstractmethod + async def MoveOnGlobe(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.MoveOnGlobeRequest, service.motion.v1.motion_pb2.MoveOnGlobeResponse]') -> None: pass @abc.abstractmethod async def GetPose(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.GetPoseRequest, service.motion.v1.motion_pb2.GetPoseResponse]') -> None: pass + @abc.abstractmethod + async def StopPlan(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.StopPlanRequest, service.motion.v1.motion_pb2.StopPlanResponse]') -> None: + pass + + @abc.abstractmethod + async def ListPlanStatuses(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.ListPlanStatusesRequest, service.motion.v1.motion_pb2.ListPlanStatusesResponse]') -> None: + pass + + @abc.abstractmethod + async def GetPlan(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.GetPlanRequest, service.motion.v1.motion_pb2.GetPlanResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.service.motion.v1.MotionService/Move': grpclib.const.Handler(self.Move, grpclib.const.Cardinality.UNARY_UNARY, service.motion.v1.motion_pb2.MoveRequest, service.motion.v1.motion_pb2.MoveResponse), '/viam.service.motion.v1.MotionService/MoveSingleComponent': grpclib.const.Handler(self.MoveSingleComponent, grpclib.const.Cardinality.UNARY_UNARY, service.motion.v1.motion_pb2.MoveSingleComponentRequest, service.motion.v1.motion_pb2.MoveSingleComponentResponse), '/viam.service.motion.v1.MotionService/GetPose': grpclib.const.Handler(self.GetPose, grpclib.const.Cardinality.UNARY_UNARY, service.motion.v1.motion_pb2.GetPoseRequest, service.motion.v1.motion_pb2.GetPoseResponse)} + return {'/viam.service.motion.v1.MotionService/Move': grpclib.const.Handler(self.Move, grpclib.const.Cardinality.UNARY_UNARY, service.motion.v1.motion_pb2.MoveRequest, service.motion.v1.motion_pb2.MoveResponse), '/viam.service.motion.v1.MotionService/MoveOnMap': grpclib.const.Handler(self.MoveOnMap, grpclib.const.Cardinality.UNARY_UNARY, service.motion.v1.motion_pb2.MoveOnMapRequest, service.motion.v1.motion_pb2.MoveOnMapResponse), '/viam.service.motion.v1.MotionService/MoveOnGlobe': grpclib.const.Handler(self.MoveOnGlobe, grpclib.const.Cardinality.UNARY_UNARY, service.motion.v1.motion_pb2.MoveOnGlobeRequest, service.motion.v1.motion_pb2.MoveOnGlobeResponse), '/viam.service.motion.v1.MotionService/GetPose': grpclib.const.Handler(self.GetPose, grpclib.const.Cardinality.UNARY_UNARY, service.motion.v1.motion_pb2.GetPoseRequest, service.motion.v1.motion_pb2.GetPoseResponse), '/viam.service.motion.v1.MotionService/StopPlan': grpclib.const.Handler(self.StopPlan, grpclib.const.Cardinality.UNARY_UNARY, service.motion.v1.motion_pb2.StopPlanRequest, service.motion.v1.motion_pb2.StopPlanResponse), '/viam.service.motion.v1.MotionService/ListPlanStatuses': grpclib.const.Handler(self.ListPlanStatuses, grpclib.const.Cardinality.UNARY_UNARY, service.motion.v1.motion_pb2.ListPlanStatusesRequest, service.motion.v1.motion_pb2.ListPlanStatusesResponse), '/viam.service.motion.v1.MotionService/GetPlan': grpclib.const.Handler(self.GetPlan, grpclib.const.Cardinality.UNARY_UNARY, service.motion.v1.motion_pb2.GetPlanRequest, service.motion.v1.motion_pb2.GetPlanResponse), '/viam.service.motion.v1.MotionService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedMotionServiceBase(MotionServiceBase): + + async def Move(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.MoveRequest, service.motion.v1.motion_pb2.MoveResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def MoveOnMap(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.MoveOnMapRequest, service.motion.v1.motion_pb2.MoveOnMapResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def MoveOnGlobe(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.MoveOnGlobeRequest, service.motion.v1.motion_pb2.MoveOnGlobeResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPose(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.GetPoseRequest, service.motion.v1.motion_pb2.GetPoseResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def StopPlan(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.StopPlanRequest, service.motion.v1.motion_pb2.StopPlanResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def ListPlanStatuses(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.ListPlanStatusesRequest, service.motion.v1.motion_pb2.ListPlanStatusesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPlan(self, stream: 'grpclib.server.Stream[service.motion.v1.motion_pb2.GetPlanRequest, service.motion.v1.motion_pb2.GetPlanResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class MotionServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: self.Move = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.motion.v1.MotionService/Move', service.motion.v1.motion_pb2.MoveRequest, service.motion.v1.motion_pb2.MoveResponse) - self.MoveSingleComponent = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.motion.v1.MotionService/MoveSingleComponent', service.motion.v1.motion_pb2.MoveSingleComponentRequest, service.motion.v1.motion_pb2.MoveSingleComponentResponse) - self.GetPose = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.motion.v1.MotionService/GetPose', service.motion.v1.motion_pb2.GetPoseRequest, service.motion.v1.motion_pb2.GetPoseResponse) \ No newline at end of file + self.MoveOnMap = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.motion.v1.MotionService/MoveOnMap', service.motion.v1.motion_pb2.MoveOnMapRequest, service.motion.v1.motion_pb2.MoveOnMapResponse) + self.MoveOnGlobe = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.motion.v1.MotionService/MoveOnGlobe', service.motion.v1.motion_pb2.MoveOnGlobeRequest, service.motion.v1.motion_pb2.MoveOnGlobeResponse) + self.GetPose = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.motion.v1.MotionService/GetPose', service.motion.v1.motion_pb2.GetPoseRequest, service.motion.v1.motion_pb2.GetPoseResponse) + self.StopPlan = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.motion.v1.MotionService/StopPlan', service.motion.v1.motion_pb2.StopPlanRequest, service.motion.v1.motion_pb2.StopPlanResponse) + self.ListPlanStatuses = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.motion.v1.MotionService/ListPlanStatuses', service.motion.v1.motion_pb2.ListPlanStatusesRequest, service.motion.v1.motion_pb2.ListPlanStatusesResponse) + self.GetPlan = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.motion.v1.MotionService/GetPlan', service.motion.v1.motion_pb2.GetPlanRequest, service.motion.v1.motion_pb2.GetPlanResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.motion.v1.MotionService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/service/motion/v1/motion_pb2.py b/src/viam/gen/service/motion/v1/motion_pb2.py index 8b59b6944..b5bb4d437 100644 --- a/src/viam/gen/service/motion/v1/motion_pb2.py +++ b/src/viam/gen/service/motion/v1/motion_pb2.py @@ -1,35 +1,97 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'service/motion/v1/motion.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eservice/motion/v1/motion.proto\x12\x16viam.service.motion.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"\xa6\x02\n\x0bMoveRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12=\n\x0bdestination\x18\x02 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x0bdestination\x12C\n\x0ecomponent_name\x18\x03 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12@\n\x0bworld_state\x18\x04 \x01(\x0b2\x1a.viam.common.v1.WorldStateH\x00R\nworldState\x88\x01\x01\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\x0e\n\x0c_world_state"(\n\x0cMoveResponse\x12\x18\n\x07success\x18\x01 \x01(\x08R\x07success"\xb5\x02\n\x1aMoveSingleComponentRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12=\n\x0bdestination\x18\x02 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x0bdestination\x12C\n\x0ecomponent_name\x18\x03 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12@\n\x0bworld_state\x18\x04 \x01(\x0b2\x1a.viam.common.v1.WorldStateH\x00R\nworldState\x88\x01\x01\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\x0e\n\x0c_world_state"7\n\x1bMoveSingleComponentResponse\x12\x18\n\x07success\x18\x01 \x01(\x08R\x07success"\x99\x02\n\x0eGetPoseRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12C\n\x0ecomponent_name\x18\x02 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12+\n\x11destination_frame\x18\x03 \x01(\tR\x10destinationFrame\x12R\n\x17supplemental_transforms\x18\x04 \x03(\x0b2\x19.viam.common.v1.TransformR\x16supplementalTransforms\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"B\n\x0fGetPoseResponse\x12/\n\x04pose\x18\x01 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x04pose2\xdc\x03\n\rMotionService\x12\x82\x01\n\x04Move\x12#.viam.service.motion.v1.MoveRequest\x1a$.viam.service.motion.v1.MoveResponse"/\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/service/motion/{name}/move\x12\xb7\x01\n\x13MoveSingleComponent\x122.viam.service.motion.v1.MoveSingleComponentRequest\x1a3.viam.service.motion.v1.MoveSingleComponentResponse"7\x82\xd3\xe4\x93\x021"//viam/api/v1/service/motion/movesinglecomponent\x12\x8b\x01\n\x07GetPose\x12&.viam.service.motion.v1.GetPoseRequest\x1a\'.viam.service.motion.v1.GetPoseResponse"/\x82\xd3\xe4\x93\x02)\x12\'/viam/api/v1/service/motion/{name}/poseB?\n\x1acom.viam.service.motion.v1Z!go.viam.com/api/service/motion/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.motion.v1.motion_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1acom.viam.service.motion.v1Z!go.viam.com/api/service/motion/v1' - _MOTIONSERVICE.methods_by_name['Move']._options = None - _MOTIONSERVICE.methods_by_name['Move']._serialized_options = b'\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/service/motion/{name}/move' - _MOTIONSERVICE.methods_by_name['MoveSingleComponent']._options = None - _MOTIONSERVICE.methods_by_name['MoveSingleComponent']._serialized_options = b'\x82\xd3\xe4\x93\x021"//viam/api/v1/service/motion/movesinglecomponent' - _MOTIONSERVICE.methods_by_name['GetPose']._options = None - _MOTIONSERVICE.methods_by_name['GetPose']._serialized_options = b"\x82\xd3\xe4\x93\x02)\x12'/viam/api/v1/service/motion/{name}/pose" - _MOVEREQUEST._serialized_start = 143 - _MOVEREQUEST._serialized_end = 437 - _MOVERESPONSE._serialized_start = 439 - _MOVERESPONSE._serialized_end = 479 - _MOVESINGLECOMPONENTREQUEST._serialized_start = 482 - _MOVESINGLECOMPONENTREQUEST._serialized_end = 791 - _MOVESINGLECOMPONENTRESPONSE._serialized_start = 793 - _MOVESINGLECOMPONENTRESPONSE._serialized_end = 848 - _GETPOSEREQUEST._serialized_start = 851 - _GETPOSEREQUEST._serialized_end = 1132 - _GETPOSERESPONSE._serialized_start = 1134 - _GETPOSERESPONSE._serialized_end = 1200 - _MOTIONSERVICE._serialized_start = 1203 - _MOTIONSERVICE._serialized_end = 1679 \ No newline at end of file +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eservice/motion/v1/motion.proto\x12\x16viam.service.motion.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"\x82\x03\n\x0bMoveRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12=\n\x0bdestination\x18\x02 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x0bdestination\x12C\n\x0ecomponent_name\x18\x03 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12@\n\x0bworld_state\x18\x04 \x01(\x0b2\x1a.viam.common.v1.WorldStateH\x00R\nworldState\x88\x01\x01\x12J\n\x0bconstraints\x18\x05 \x01(\x0b2#.viam.service.motion.v1.ConstraintsH\x01R\x0bconstraints\x88\x01\x01\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\x0e\n\x0c_world_stateB\x0e\n\x0c_constraints"(\n\x0cMoveResponse\x12\x18\n\x07success\x18\x01 \x01(\x08R\x07success"\xd2\x03\n\x10MoveOnMapRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x126\n\x0bdestination\x18\x02 \x01(\x0b2\x14.viam.common.v1.PoseR\x0bdestination\x12C\n\x0ecomponent_name\x18\x03 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12H\n\x11slam_service_name\x18\x04 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x0fslamServiceName\x12c\n\x14motion_configuration\x18\x05 \x01(\x0b2+.viam.service.motion.v1.MotionConfigurationH\x00R\x13motionConfiguration\x88\x01\x01\x126\n\tobstacles\x18\x06 \x03(\x0b2\x18.viam.common.v1.GeometryR\tobstacles\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\x17\n\x15_motion_configuration"6\n\x11MoveOnMapResponse\x12!\n\x0cexecution_id\x18\x01 \x01(\tR\x0bexecutionId"\x8d\x01\n\x10ObstacleDetector\x12C\n\x0evision_service\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rvisionService\x124\n\x06camera\x18\x02 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x06camera"\x98\x04\n\x13MotionConfiguration\x12W\n\x12obstacle_detectors\x18\x01 \x03(\x0b2(.viam.service.motion.v1.ObstacleDetectorR\x11obstacleDetectors\x12F\n\x1dposition_polling_frequency_hz\x18\x02 \x01(\x01H\x00R\x1apositionPollingFrequencyHz\x88\x01\x01\x12F\n\x1dobstacle_polling_frequency_hz\x18\x03 \x01(\x01H\x01R\x1aobstaclePollingFrequencyHz\x88\x01\x01\x12-\n\x10plan_deviation_m\x18\x04 \x01(\x01H\x02R\x0eplanDeviationM\x88\x01\x01\x12,\n\x10linear_m_per_sec\x18\x05 \x01(\x01H\x03R\rlinearMPerSec\x88\x01\x01\x124\n\x14angular_degs_per_sec\x18\x06 \x01(\x01H\x04R\x11angularDegsPerSec\x88\x01\x01B \n\x1e_position_polling_frequency_hzB \n\x1e_obstacle_polling_frequency_hzB\x13\n\x11_plan_deviation_mB\x13\n\x11_linear_m_per_secB\x17\n\x15_angular_degs_per_sec"\xd4\x04\n\x12MoveOnGlobeRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12:\n\x0bdestination\x18\x02 \x01(\x0b2\x18.viam.common.v1.GeoPointR\x0bdestination\x12\x1d\n\x07heading\x18\x03 \x01(\x01H\x00R\x07heading\x88\x01\x01\x12C\n\x0ecomponent_name\x18\x04 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12N\n\x14movement_sensor_name\x18\x05 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x12movementSensorName\x129\n\tobstacles\x18\x06 \x03(\x0b2\x1b.viam.common.v1.GeoGeometryR\tobstacles\x12c\n\x14motion_configuration\x18\x07 \x01(\x0b2+.viam.service.motion.v1.MotionConfigurationH\x01R\x13motionConfiguration\x88\x01\x01\x12F\n\x10bounding_regions\x18\x08 \x03(\x0b2\x1b.viam.common.v1.GeoGeometryR\x0fboundingRegions\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\n\n\x08_headingB\x17\n\x15_motion_configuration"8\n\x13MoveOnGlobeResponse\x12!\n\x0cexecution_id\x18\x01 \x01(\tR\x0bexecutionId"\x99\x02\n\x0eGetPoseRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12C\n\x0ecomponent_name\x18\x02 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12+\n\x11destination_frame\x18\x03 \x01(\tR\x10destinationFrame\x12R\n\x17supplemental_transforms\x18\x04 \x03(\x0b2\x19.viam.common.v1.TransformR\x16supplementalTransforms\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"B\n\x0fGetPoseResponse\x12/\n\x04pose\x18\x01 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x04pose"\x99\x01\n\x0fStopPlanRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12C\n\x0ecomponent_name\x18\x02 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x12\n\x10StopPlanResponse"\x88\x01\n\x17ListPlanStatusesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12*\n\x11only_active_plans\x18\x02 \x01(\x08R\x0fonlyActivePlans\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"y\n\x18ListPlanStatusesResponse\x12]\n\x16plan_statuses_with_ids\x18\x01 \x03(\x0b2(.viam.service.motion.v1.PlanStatusWithIDR\x13planStatusesWithIds"\xf7\x01\n\x0eGetPlanRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12C\n\x0ecomponent_name\x18\x02 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12$\n\x0elast_plan_only\x18\x03 \x01(\x08R\x0clastPlanOnly\x12&\n\x0cexecution_id\x18\x04 \x01(\tH\x00R\x0bexecutionId\x88\x01\x01\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extraB\x0f\n\r_execution_id"\xc1\x01\n\x0fGetPlanResponse\x12_\n\x18current_plan_with_status\x18\x01 \x01(\x0b2&.viam.service.motion.v1.PlanWithStatusR\x15currentPlanWithStatus\x12M\n\x0ereplan_history\x18\x02 \x03(\x0b2&.viam.service.motion.v1.PlanWithStatusR\rreplanHistory"\xb3\x02\n\x0bConstraints\x12U\n\x11linear_constraint\x18\x01 \x03(\x0b2(.viam.service.motion.v1.LinearConstraintR\x10linearConstraint\x12d\n\x16orientation_constraint\x18\x02 \x03(\x0b2-.viam.service.motion.v1.OrientationConstraintR\x15orientationConstraint\x12g\n\x17collision_specification\x18\x03 \x03(\x0b2..viam.service.motion.v1.CollisionSpecificationR\x16collisionSpecification"\xbb\x01\n\x10LinearConstraint\x12/\n\x11line_tolerance_mm\x18\x01 \x01(\x02H\x00R\x0flineToleranceMm\x88\x01\x01\x12A\n\x1aorientation_tolerance_degs\x18\x02 \x01(\x02H\x01R\x18orientationToleranceDegs\x88\x01\x01B\x14\n\x12_line_tolerance_mmB\x1d\n\x1b_orientation_tolerance_degs"y\n\x15OrientationConstraint\x12A\n\x1aorientation_tolerance_degs\x18\x01 \x01(\x02H\x00R\x18orientationToleranceDegs\x88\x01\x01B\x1d\n\x1b_orientation_tolerance_degs"\xc1\x01\n\x16CollisionSpecification\x12]\n\x06allows\x18\x01 \x03(\x0b2E.viam.service.motion.v1.CollisionSpecification.AllowedFrameCollisionsR\x06allows\x1aH\n\x16AllowedFrameCollisions\x12\x16\n\x06frame1\x18\x01 \x01(\tR\x06frame1\x12\x16\n\x06frame2\x18\x02 \x01(\tR\x06frame2"\xc9\x01\n\x0ePlanWithStatus\x120\n\x04plan\x18\x01 \x01(\x0b2\x1c.viam.service.motion.v1.PlanR\x04plan\x12:\n\x06status\x18\x02 \x01(\x0b2".viam.service.motion.v1.PlanStatusR\x06status\x12I\n\x0estatus_history\x18\x03 \x03(\x0b2".viam.service.motion.v1.PlanStatusR\rstatusHistory"\xcf\x01\n\x10PlanStatusWithID\x12\x17\n\x07plan_id\x18\x01 \x01(\tR\x06planId\x12C\n\x0ecomponent_name\x18\x02 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12!\n\x0cexecution_id\x18\x03 \x01(\tR\x0bexecutionId\x12:\n\x06status\x18\x04 \x01(\x0b2".viam.service.motion.v1.PlanStatusR\x06status"\xa7\x01\n\nPlanStatus\x127\n\x05state\x18\x01 \x01(\x0e2!.viam.service.motion.v1.PlanStateR\x05state\x128\n\ttimestamp\x18\x02 \x01(\x0b2\x1a.google.protobuf.TimestampR\ttimestamp\x12\x1b\n\x06reason\x18\x03 \x01(\tH\x00R\x06reason\x88\x01\x01B\t\n\x07_reason"\xb6\x01\n\x04Plan\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x12C\n\x0ecomponent_name\x18\x02 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\rcomponentName\x12!\n\x0cexecution_id\x18\x03 \x01(\tR\x0bexecutionId\x126\n\x05steps\x18\x04 \x03(\x0b2 .viam.service.motion.v1.PlanStepR\x05steps"\xab\x01\n\x08PlanStep\x12>\n\x04step\x18\x01 \x03(\x0b2*.viam.service.motion.v1.PlanStep.StepEntryR\x04step\x1a_\n\tStepEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12<\n\x05value\x18\x02 \x01(\x0b2&.viam.service.motion.v1.ComponentStateR\x05value:\x028\x01":\n\x0eComponentState\x12(\n\x04pose\x18\x01 \x01(\x0b2\x14.viam.common.v1.PoseR\x04pose*\x8c\x01\n\tPlanState\x12\x1a\n\x16PLAN_STATE_UNSPECIFIED\x10\x00\x12\x1a\n\x16PLAN_STATE_IN_PROGRESS\x10\x01\x12\x16\n\x12PLAN_STATE_STOPPED\x10\x02\x12\x18\n\x14PLAN_STATE_SUCCEEDED\x10\x03\x12\x15\n\x11PLAN_STATE_FAILED\x10\x042\xc9\t\n\rMotionService\x12\x82\x01\n\x04Move\x12#.viam.service.motion.v1.MoveRequest\x1a$.viam.service.motion.v1.MoveResponse"/\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/service/motion/{name}/move\x12\x98\x01\n\tMoveOnMap\x12(.viam.service.motion.v1.MoveOnMapRequest\x1a).viam.service.motion.v1.MoveOnMapResponse"6\x82\xd3\xe4\x93\x020"./viam/api/v1/service/motion/{name}/move_on_map\x12\xa0\x01\n\x0bMoveOnGlobe\x12*.viam.service.motion.v1.MoveOnGlobeRequest\x1a+.viam.service.motion.v1.MoveOnGlobeResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/motion/{name}/move_on_globe\x12\x8b\x01\n\x07GetPose\x12&.viam.service.motion.v1.GetPoseRequest\x1a\'.viam.service.motion.v1.GetPoseResponse"/\x82\xd3\xe4\x93\x02)\x12\'/viam/api/v1/service/motion/{name}/pose\x12\x93\x01\n\x08StopPlan\x12\'.viam.service.motion.v1.StopPlanRequest\x1a(.viam.service.motion.v1.StopPlanResponse"4\x82\xd3\xe4\x93\x02.\x1a,/viam/api/v1/service/motion/{name}/stop_plan\x12\xb4\x01\n\x10ListPlanStatuses\x12/.viam.service.motion.v1.ListPlanStatusesRequest\x1a0.viam.service.motion.v1.ListPlanStatusesResponse"=\x82\xd3\xe4\x93\x027\x125/viam/api/v1/service/motion/{name}/list_plan_statuses\x12\x8f\x01\n\x07GetPlan\x12&.viam.service.motion.v1.GetPlanRequest\x1a\'.viam.service.motion.v1.GetPlanResponse"3\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/service/motion/{name}/get_plan\x12\x87\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"5\x82\xd3\xe4\x93\x02/"-/viam/api/v1/service/motion/{name}/do_commandB?\n\x1acom.viam.service.motion.v1Z!go.viam.com/api/service/motion/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.motion.v1.motion_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1acom.viam.service.motion.v1Z!go.viam.com/api/service/motion/v1' + _globals['_PLANSTEP_STEPENTRY']._loaded_options = None + _globals['_PLANSTEP_STEPENTRY']._serialized_options = b'8\x01' + _globals['_MOTIONSERVICE'].methods_by_name['Move']._loaded_options = None + _globals['_MOTIONSERVICE'].methods_by_name['Move']._serialized_options = b'\x82\xd3\xe4\x93\x02)"\'/viam/api/v1/service/motion/{name}/move' + _globals['_MOTIONSERVICE'].methods_by_name['MoveOnMap']._loaded_options = None + _globals['_MOTIONSERVICE'].methods_by_name['MoveOnMap']._serialized_options = b'\x82\xd3\xe4\x93\x020"./viam/api/v1/service/motion/{name}/move_on_map' + _globals['_MOTIONSERVICE'].methods_by_name['MoveOnGlobe']._loaded_options = None + _globals['_MOTIONSERVICE'].methods_by_name['MoveOnGlobe']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/motion/{name}/move_on_globe' + _globals['_MOTIONSERVICE'].methods_by_name['GetPose']._loaded_options = None + _globals['_MOTIONSERVICE'].methods_by_name['GetPose']._serialized_options = b"\x82\xd3\xe4\x93\x02)\x12'/viam/api/v1/service/motion/{name}/pose" + _globals['_MOTIONSERVICE'].methods_by_name['StopPlan']._loaded_options = None + _globals['_MOTIONSERVICE'].methods_by_name['StopPlan']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x1a,/viam/api/v1/service/motion/{name}/stop_plan' + _globals['_MOTIONSERVICE'].methods_by_name['ListPlanStatuses']._loaded_options = None + _globals['_MOTIONSERVICE'].methods_by_name['ListPlanStatuses']._serialized_options = b'\x82\xd3\xe4\x93\x027\x125/viam/api/v1/service/motion/{name}/list_plan_statuses' + _globals['_MOTIONSERVICE'].methods_by_name['GetPlan']._loaded_options = None + _globals['_MOTIONSERVICE'].methods_by_name['GetPlan']._serialized_options = b'\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/service/motion/{name}/get_plan' + _globals['_MOTIONSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_MOTIONSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x02/"-/viam/api/v1/service/motion/{name}/do_command' + _globals['_PLANSTATE']._serialized_start = 5530 + _globals['_PLANSTATE']._serialized_end = 5670 + _globals['_MOVEREQUEST']._serialized_start = 176 + _globals['_MOVEREQUEST']._serialized_end = 562 + _globals['_MOVERESPONSE']._serialized_start = 564 + _globals['_MOVERESPONSE']._serialized_end = 604 + _globals['_MOVEONMAPREQUEST']._serialized_start = 607 + _globals['_MOVEONMAPREQUEST']._serialized_end = 1073 + _globals['_MOVEONMAPRESPONSE']._serialized_start = 1075 + _globals['_MOVEONMAPRESPONSE']._serialized_end = 1129 + _globals['_OBSTACLEDETECTOR']._serialized_start = 1132 + _globals['_OBSTACLEDETECTOR']._serialized_end = 1273 + _globals['_MOTIONCONFIGURATION']._serialized_start = 1276 + _globals['_MOTIONCONFIGURATION']._serialized_end = 1812 + _globals['_MOVEONGLOBEREQUEST']._serialized_start = 1815 + _globals['_MOVEONGLOBEREQUEST']._serialized_end = 2411 + _globals['_MOVEONGLOBERESPONSE']._serialized_start = 2413 + _globals['_MOVEONGLOBERESPONSE']._serialized_end = 2469 + _globals['_GETPOSEREQUEST']._serialized_start = 2472 + _globals['_GETPOSEREQUEST']._serialized_end = 2753 + _globals['_GETPOSERESPONSE']._serialized_start = 2755 + _globals['_GETPOSERESPONSE']._serialized_end = 2821 + _globals['_STOPPLANREQUEST']._serialized_start = 2824 + _globals['_STOPPLANREQUEST']._serialized_end = 2977 + _globals['_STOPPLANRESPONSE']._serialized_start = 2979 + _globals['_STOPPLANRESPONSE']._serialized_end = 2997 + _globals['_LISTPLANSTATUSESREQUEST']._serialized_start = 3000 + _globals['_LISTPLANSTATUSESREQUEST']._serialized_end = 3136 + _globals['_LISTPLANSTATUSESRESPONSE']._serialized_start = 3138 + _globals['_LISTPLANSTATUSESRESPONSE']._serialized_end = 3259 + _globals['_GETPLANREQUEST']._serialized_start = 3262 + _globals['_GETPLANREQUEST']._serialized_end = 3509 + _globals['_GETPLANRESPONSE']._serialized_start = 3512 + _globals['_GETPLANRESPONSE']._serialized_end = 3705 + _globals['_CONSTRAINTS']._serialized_start = 3708 + _globals['_CONSTRAINTS']._serialized_end = 4015 + _globals['_LINEARCONSTRAINT']._serialized_start = 4018 + _globals['_LINEARCONSTRAINT']._serialized_end = 4205 + _globals['_ORIENTATIONCONSTRAINT']._serialized_start = 4207 + _globals['_ORIENTATIONCONSTRAINT']._serialized_end = 4328 + _globals['_COLLISIONSPECIFICATION']._serialized_start = 4331 + _globals['_COLLISIONSPECIFICATION']._serialized_end = 4524 + _globals['_COLLISIONSPECIFICATION_ALLOWEDFRAMECOLLISIONS']._serialized_start = 4452 + _globals['_COLLISIONSPECIFICATION_ALLOWEDFRAMECOLLISIONS']._serialized_end = 4524 + _globals['_PLANWITHSTATUS']._serialized_start = 4527 + _globals['_PLANWITHSTATUS']._serialized_end = 4728 + _globals['_PLANSTATUSWITHID']._serialized_start = 4731 + _globals['_PLANSTATUSWITHID']._serialized_end = 4938 + _globals['_PLANSTATUS']._serialized_start = 4941 + _globals['_PLANSTATUS']._serialized_end = 5108 + _globals['_PLAN']._serialized_start = 5111 + _globals['_PLAN']._serialized_end = 5293 + _globals['_PLANSTEP']._serialized_start = 5296 + _globals['_PLANSTEP']._serialized_end = 5467 + _globals['_PLANSTEP_STEPENTRY']._serialized_start = 5372 + _globals['_PLANSTEP_STEPENTRY']._serialized_end = 5467 + _globals['_COMPONENTSTATE']._serialized_start = 5469 + _globals['_COMPONENTSTATE']._serialized_end = 5527 + _globals['_MOTIONSERVICE']._serialized_start = 5673 + _globals['_MOTIONSERVICE']._serialized_end = 6898 \ No newline at end of file diff --git a/src/viam/gen/service/motion/v1/motion_pb2.pyi b/src/viam/gen/service/motion/v1/motion_pb2.pyi index f452159fa..464397635 100644 --- a/src/viam/gen/service/motion/v1/motion_pb2.pyi +++ b/src/viam/gen/service/motion/v1/motion_pb2.pyi @@ -7,53 +7,98 @@ import collections.abc from .... import common import google.protobuf.descriptor import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import google.protobuf.struct_pb2 +import google.protobuf.timestamp_pb2 import sys -if sys.version_info >= (3, 8): +import typing +if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _PlanState: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PlanStateEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PlanState.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PLAN_STATE_UNSPECIFIED: _PlanState.ValueType + PLAN_STATE_IN_PROGRESS: _PlanState.ValueType + PLAN_STATE_STOPPED: _PlanState.ValueType + PLAN_STATE_SUCCEEDED: _PlanState.ValueType + PLAN_STATE_FAILED: _PlanState.ValueType + +class PlanState(_PlanState, metaclass=_PlanStateEnumTypeWrapper): + """The states that a plan can be in. + InProgress if the plan is executing. + Stopped if the plan was stopped. + Suceeded if the robot reached its destination successfully. + Failed if the robot did not reach its destination. + """ +PLAN_STATE_UNSPECIFIED: PlanState.ValueType +PLAN_STATE_IN_PROGRESS: PlanState.ValueType +PLAN_STATE_STOPPED: PlanState.ValueType +PLAN_STATE_SUCCEEDED: PlanState.ValueType +PLAN_STATE_FAILED: PlanState.ValueType +global___PlanState = PlanState + +@typing.final class MoveRequest(google.protobuf.message.Message): + """Moves any component on the robot to a specified destination which can be from the reference frame of any other component on the robot.""" DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int DESTINATION_FIELD_NUMBER: builtins.int COMPONENT_NAME_FIELD_NUMBER: builtins.int WORLD_STATE_FIELD_NUMBER: builtins.int + CONSTRAINTS_FIELD_NUMBER: builtins.int EXTRA_FIELD_NUMBER: builtins.int name: builtins.str + 'Name of the motion service' @property def destination(self) -> common.v1.common_pb2.PoseInFrame: - ... + """Destination to move to, which can a pose in the reference frame of any frame in the robot's frame system""" @property def component_name(self) -> common.v1.common_pb2.ResourceName: - ... + """Component on the robot to move to the specified destination""" @property def world_state(self) -> common.v1.common_pb2.WorldState: - ... + """Avoid obstacles by specifying their geometries in the world state + Augment the frame system of the robot by specifying additional transforms to add to it for the duration of the Move + """ + + @property + def constraints(self) -> global___Constraints: + """Constrain the way the robot will move""" @property def extra(self) -> google.protobuf.struct_pb2.Struct: """Additional arguments to the method""" - def __init__(self, *, name: builtins.str=..., destination: common.v1.common_pb2.PoseInFrame | None=..., component_name: common.v1.common_pb2.ResourceName | None=..., world_state: common.v1.common_pb2.WorldState | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + def __init__(self, *, name: builtins.str=..., destination: common.v1.common_pb2.PoseInFrame | None=..., component_name: common.v1.common_pb2.ResourceName | None=..., world_state: common.v1.common_pb2.WorldState | None=..., constraints: global___Constraints | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_constraints', b'_constraints', '_world_state', b'_world_state', 'component_name', b'component_name', 'constraints', b'constraints', 'destination', b'destination', 'extra', b'extra', 'world_state', b'world_state']) -> builtins.bool: ... - def HasField(self, field_name: typing_extensions.Literal['_world_state', b'_world_state', 'component_name', b'component_name', 'destination', b'destination', 'extra', b'extra', 'world_state', b'world_state']) -> builtins.bool: + def ClearField(self, field_name: typing.Literal['_constraints', b'_constraints', '_world_state', b'_world_state', 'component_name', b'component_name', 'constraints', b'constraints', 'destination', b'destination', 'extra', b'extra', 'name', b'name', 'world_state', b'world_state']) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['_world_state', b'_world_state', 'component_name', b'component_name', 'destination', b'destination', 'extra', b'extra', 'name', b'name', 'world_state', b'world_state']) -> None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_constraints', b'_constraints']) -> typing.Literal['constraints'] | None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['_world_state', b'_world_state']) -> typing_extensions.Literal['world_state'] | None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_world_state', b'_world_state']) -> typing.Literal['world_state'] | None: ... global___MoveRequest = MoveRequest +@typing.final class MoveResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor SUCCESS_FIELD_NUMBER: builtins.int @@ -62,60 +107,231 @@ class MoveResponse(google.protobuf.message.Message): def __init__(self, *, success: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['success', b'success']) -> None: + def ClearField(self, field_name: typing.Literal['success', b'success']) -> None: ... global___MoveResponse = MoveResponse -class MoveSingleComponentRequest(google.protobuf.message.Message): +@typing.final +class MoveOnMapRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int DESTINATION_FIELD_NUMBER: builtins.int COMPONENT_NAME_FIELD_NUMBER: builtins.int - WORLD_STATE_FIELD_NUMBER: builtins.int + SLAM_SERVICE_NAME_FIELD_NUMBER: builtins.int + MOTION_CONFIGURATION_FIELD_NUMBER: builtins.int + OBSTACLES_FIELD_NUMBER: builtins.int EXTRA_FIELD_NUMBER: builtins.int name: builtins.str + 'Name of the motion service' @property - def destination(self) -> common.v1.common_pb2.PoseInFrame: - ... + def destination(self) -> common.v1.common_pb2.Pose: + """Specify a destination to, which can be any pose with respect to the SLAM map's origin""" @property def component_name(self) -> common.v1.common_pb2.ResourceName: + """Component on the robot to move to the specified destination""" + + @property + def slam_service_name(self) -> common.v1.common_pb2.ResourceName: + """Name of the slam service from which the SLAM map is requested""" + + @property + def motion_configuration(self) -> global___MotionConfiguration: + """Optional set of motion configuration options""" + + @property + def obstacles(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.Geometry]: + """Obstacles to be considered for motion planning""" + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., destination: common.v1.common_pb2.Pose | None=..., component_name: common.v1.common_pb2.ResourceName | None=..., slam_service_name: common.v1.common_pb2.ResourceName | None=..., motion_configuration: global___MotionConfiguration | None=..., obstacles: collections.abc.Iterable[common.v1.common_pb2.Geometry] | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_motion_configuration', b'_motion_configuration', 'component_name', b'component_name', 'destination', b'destination', 'extra', b'extra', 'motion_configuration', b'motion_configuration', 'slam_service_name', b'slam_service_name']) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal['_motion_configuration', b'_motion_configuration', 'component_name', b'component_name', 'destination', b'destination', 'extra', b'extra', 'motion_configuration', b'motion_configuration', 'name', b'name', 'obstacles', b'obstacles', 'slam_service_name', b'slam_service_name']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_motion_configuration', b'_motion_configuration']) -> typing.Literal['motion_configuration'] | None: + ... +global___MoveOnMapRequest = MoveOnMapRequest + +@typing.final +class MoveOnMapResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + EXECUTION_ID_FIELD_NUMBER: builtins.int + execution_id: builtins.str + 'The unique ID which identifies the execution.\n Multiple plans will share the same execution_id if they were\n generated due to replanning.\n ' + + def __init__(self, *, execution_id: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['execution_id', b'execution_id']) -> None: + ... +global___MoveOnMapResponse = MoveOnMapResponse + +@typing.final +class ObstacleDetector(google.protobuf.message.Message): + """Pairs a vision service with a camera, informing the service about which camera it may use""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + VISION_SERVICE_FIELD_NUMBER: builtins.int + CAMERA_FIELD_NUMBER: builtins.int + @property - def world_state(self) -> common.v1.common_pb2.WorldState: + def vision_service(self) -> common.v1.common_pb2.ResourceName: + ... + + @property + def camera(self) -> common.v1.common_pb2.ResourceName: + ... + + def __init__(self, *, vision_service: common.v1.common_pb2.ResourceName | None=..., camera: common.v1.common_pb2.ResourceName | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['camera', b'camera', 'vision_service', b'vision_service']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['camera', b'camera', 'vision_service', b'vision_service']) -> None: + ... +global___ObstacleDetector = ObstacleDetector + +@typing.final +class MotionConfiguration(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + OBSTACLE_DETECTORS_FIELD_NUMBER: builtins.int + POSITION_POLLING_FREQUENCY_HZ_FIELD_NUMBER: builtins.int + OBSTACLE_POLLING_FREQUENCY_HZ_FIELD_NUMBER: builtins.int + PLAN_DEVIATION_M_FIELD_NUMBER: builtins.int + LINEAR_M_PER_SEC_FIELD_NUMBER: builtins.int + ANGULAR_DEGS_PER_SEC_FIELD_NUMBER: builtins.int + position_polling_frequency_hz: builtins.float + 'Sets the frequency to poll for the position of the robot' + obstacle_polling_frequency_hz: builtins.float + 'Sets the frequency to poll the vision service(s) for new obstacles' + plan_deviation_m: builtins.float + 'Sets the distance in meters that a robot is allowed to deviate from the motion plan' + linear_m_per_sec: builtins.float + 'Optional linear velocity to target when moving' + angular_degs_per_sec: builtins.float + 'Optional angular velocity to target when turning' + + @property + def obstacle_detectors(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ObstacleDetector]: + """The ObstacleDetectors that will be used for transient obstacle avoidance""" + + def __init__(self, *, obstacle_detectors: collections.abc.Iterable[global___ObstacleDetector] | None=..., position_polling_frequency_hz: builtins.float | None=..., obstacle_polling_frequency_hz: builtins.float | None=..., plan_deviation_m: builtins.float | None=..., linear_m_per_sec: builtins.float | None=..., angular_degs_per_sec: builtins.float | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_angular_degs_per_sec', b'_angular_degs_per_sec', '_linear_m_per_sec', b'_linear_m_per_sec', '_obstacle_polling_frequency_hz', b'_obstacle_polling_frequency_hz', '_plan_deviation_m', b'_plan_deviation_m', '_position_polling_frequency_hz', b'_position_polling_frequency_hz', 'angular_degs_per_sec', b'angular_degs_per_sec', 'linear_m_per_sec', b'linear_m_per_sec', 'obstacle_polling_frequency_hz', b'obstacle_polling_frequency_hz', 'plan_deviation_m', b'plan_deviation_m', 'position_polling_frequency_hz', b'position_polling_frequency_hz']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_angular_degs_per_sec', b'_angular_degs_per_sec', '_linear_m_per_sec', b'_linear_m_per_sec', '_obstacle_polling_frequency_hz', b'_obstacle_polling_frequency_hz', '_plan_deviation_m', b'_plan_deviation_m', '_position_polling_frequency_hz', b'_position_polling_frequency_hz', 'angular_degs_per_sec', b'angular_degs_per_sec', 'linear_m_per_sec', b'linear_m_per_sec', 'obstacle_detectors', b'obstacle_detectors', 'obstacle_polling_frequency_hz', b'obstacle_polling_frequency_hz', 'plan_deviation_m', b'plan_deviation_m', 'position_polling_frequency_hz', b'position_polling_frequency_hz']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_angular_degs_per_sec', b'_angular_degs_per_sec']) -> typing.Literal['angular_degs_per_sec'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_linear_m_per_sec', b'_linear_m_per_sec']) -> typing.Literal['linear_m_per_sec'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_obstacle_polling_frequency_hz', b'_obstacle_polling_frequency_hz']) -> typing.Literal['obstacle_polling_frequency_hz'] | None: ... + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_plan_deviation_m', b'_plan_deviation_m']) -> typing.Literal['plan_deviation_m'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_position_polling_frequency_hz', b'_position_polling_frequency_hz']) -> typing.Literal['position_polling_frequency_hz'] | None: + ... +global___MotionConfiguration = MotionConfiguration + +@typing.final +class MoveOnGlobeRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + DESTINATION_FIELD_NUMBER: builtins.int + HEADING_FIELD_NUMBER: builtins.int + COMPONENT_NAME_FIELD_NUMBER: builtins.int + MOVEMENT_SENSOR_NAME_FIELD_NUMBER: builtins.int + OBSTACLES_FIELD_NUMBER: builtins.int + MOTION_CONFIGURATION_FIELD_NUMBER: builtins.int + BOUNDING_REGIONS_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of the motion service' + heading: builtins.float + 'Optional compass heading to achieve at the destination, in degrees [0-360)' + + @property + def destination(self) -> common.v1.common_pb2.GeoPoint: + """Destination, encoded as a GeoPoint""" + + @property + def component_name(self) -> common.v1.common_pb2.ResourceName: + """Component on the robot to move to the specified destination""" + + @property + def movement_sensor_name(self) -> common.v1.common_pb2.ResourceName: + """Name of the movement sensor which will be used to check robot location""" + + @property + def obstacles(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.GeoGeometry]: + """Obstacles to be considered for motion planning""" + + @property + def motion_configuration(self) -> global___MotionConfiguration: + """Optional set of motion configuration options""" + + @property + def bounding_regions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.GeoGeometry]: + """Set of obstacles which the robot must remain within while navigating""" + @property def extra(self) -> google.protobuf.struct_pb2.Struct: """Additional arguments to the method""" - def __init__(self, *, name: builtins.str=..., destination: common.v1.common_pb2.PoseInFrame | None=..., component_name: common.v1.common_pb2.ResourceName | None=..., world_state: common.v1.common_pb2.WorldState | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + def __init__(self, *, name: builtins.str=..., destination: common.v1.common_pb2.GeoPoint | None=..., heading: builtins.float | None=..., component_name: common.v1.common_pb2.ResourceName | None=..., movement_sensor_name: common.v1.common_pb2.ResourceName | None=..., obstacles: collections.abc.Iterable[common.v1.common_pb2.GeoGeometry] | None=..., motion_configuration: global___MotionConfiguration | None=..., bounding_regions: collections.abc.Iterable[common.v1.common_pb2.GeoGeometry] | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['_world_state', b'_world_state', 'component_name', b'component_name', 'destination', b'destination', 'extra', b'extra', 'world_state', b'world_state']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['_heading', b'_heading', '_motion_configuration', b'_motion_configuration', 'component_name', b'component_name', 'destination', b'destination', 'extra', b'extra', 'heading', b'heading', 'motion_configuration', b'motion_configuration', 'movement_sensor_name', b'movement_sensor_name']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['_world_state', b'_world_state', 'component_name', b'component_name', 'destination', b'destination', 'extra', b'extra', 'name', b'name', 'world_state', b'world_state']) -> None: + def ClearField(self, field_name: typing.Literal['_heading', b'_heading', '_motion_configuration', b'_motion_configuration', 'bounding_regions', b'bounding_regions', 'component_name', b'component_name', 'destination', b'destination', 'extra', b'extra', 'heading', b'heading', 'motion_configuration', b'motion_configuration', 'movement_sensor_name', b'movement_sensor_name', 'name', b'name', 'obstacles', b'obstacles']) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['_world_state', b'_world_state']) -> typing_extensions.Literal['world_state'] | None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_heading', b'_heading']) -> typing.Literal['heading'] | None: ... -global___MoveSingleComponentRequest = MoveSingleComponentRequest -class MoveSingleComponentResponse(google.protobuf.message.Message): + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_motion_configuration', b'_motion_configuration']) -> typing.Literal['motion_configuration'] | None: + ... +global___MoveOnGlobeRequest = MoveOnGlobeRequest + +@typing.final +class MoveOnGlobeResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - SUCCESS_FIELD_NUMBER: builtins.int - success: builtins.bool + EXECUTION_ID_FIELD_NUMBER: builtins.int + execution_id: builtins.str + 'The unique ID which identifies the execution.\n Multiple plans will share the same execution_id if they were\n generated due to replanning.\n ' - def __init__(self, *, success: builtins.bool=...) -> None: + def __init__(self, *, execution_id: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['success', b'success']) -> None: + def ClearField(self, field_name: typing.Literal['execution_id', b'execution_id']) -> None: ... -global___MoveSingleComponentResponse = MoveSingleComponentResponse +global___MoveOnGlobeResponse = MoveOnGlobeResponse +@typing.final class GetPoseRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -124,12 +340,12 @@ class GetPoseRequest(google.protobuf.message.Message): SUPPLEMENTAL_TRANSFORMS_FIELD_NUMBER: builtins.int EXTRA_FIELD_NUMBER: builtins.int name: builtins.str + destination_frame: builtins.str + 'the reference frame in which the component\'s pose\n should be provided, if unset this defaults\n to the "world" reference frame\n ' @property def component_name(self) -> common.v1.common_pb2.ResourceName: """the component whose pose is being requested""" - destination_frame: builtins.str - 'the reference frame in which the component\'s pose\n should be provided, if unset this defaults\n to the "world" reference frame\n ' @property def supplemental_transforms(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.Transform]: @@ -144,13 +360,14 @@ class GetPoseRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=..., component_name: common.v1.common_pb2.ResourceName | None=..., destination_frame: builtins.str=..., supplemental_transforms: collections.abc.Iterable[common.v1.common_pb2.Transform] | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['component_name', b'component_name', 'extra', b'extra']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['component_name', b'component_name', 'extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['component_name', b'component_name', 'destination_frame', b'destination_frame', 'extra', b'extra', 'name', b'name', 'supplemental_transforms', b'supplemental_transforms']) -> None: + def ClearField(self, field_name: typing.Literal['component_name', b'component_name', 'destination_frame', b'destination_frame', 'extra', b'extra', 'name', b'name', 'supplemental_transforms', b'supplemental_transforms']) -> None: ... global___GetPoseRequest = GetPoseRequest +@typing.final class GetPoseResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSE_FIELD_NUMBER: builtins.int @@ -162,9 +379,460 @@ class GetPoseResponse(google.protobuf.message.Message): def __init__(self, *, pose: common.v1.common_pb2.PoseInFrame | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['pose', b'pose']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['pose', b'pose']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['pose', b'pose']) -> None: + ... +global___GetPoseResponse = GetPoseResponse + +@typing.final +class StopPlanRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + COMPONENT_NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'The name of the motion service' + + @property + def component_name(self) -> common.v1.common_pb2.ResourceName: + """The component of the currently executing plan to stop""" + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., component_name: common.v1.common_pb2.ResourceName | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['component_name', b'component_name', 'extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['component_name', b'component_name', 'extra', b'extra', 'name', b'name']) -> None: + ... +global___StopPlanRequest = StopPlanRequest + +@typing.final +class StopPlanResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___StopPlanResponse = StopPlanResponse + +@typing.final +class ListPlanStatusesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + ONLY_ACTIVE_PLANS_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'The name of the motion service' + only_active_plans: builtins.bool + 'If supplied, the response will filter the\n plan results for the supplied state\n ' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., only_active_plans: builtins.bool=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'only_active_plans', b'only_active_plans']) -> None: + ... +global___ListPlanStatusesRequest = ListPlanStatusesRequest + +@typing.final +class ListPlanStatusesResponse(google.protobuf.message.Message): + """Status of all executed / executing plan statuses with associated IDs within the 24 hour TTL""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PLAN_STATUSES_WITH_IDS_FIELD_NUMBER: builtins.int + + @property + def plan_statuses_with_ids(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PlanStatusWithID]: + """List of last known statuses with the associated IDs of all plans within the TTL + ordered by timestamp in ascending order + """ + + def __init__(self, *, plan_statuses_with_ids: collections.abc.Iterable[global___PlanStatusWithID] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['plan_statuses_with_ids', b'plan_statuses_with_ids']) -> None: + ... +global___ListPlanStatusesResponse = ListPlanStatusesResponse + +@typing.final +class GetPlanRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + COMPONENT_NAME_FIELD_NUMBER: builtins.int + LAST_PLAN_ONLY_FIELD_NUMBER: builtins.int + EXECUTION_ID_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'The name of the motion service' + last_plan_only: builtins.bool + 'If supplied, the response will only return\n the the last plan for the component / execution\n ' + execution_id: builtins.str + 'If you want to know about the plans of a previous execution' + + @property + def component_name(self) -> common.v1.common_pb2.ResourceName: + """The name of the component which was requested to be moved.""" + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., component_name: common.v1.common_pb2.ResourceName | None=..., last_plan_only: builtins.bool=..., execution_id: builtins.str | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_execution_id', b'_execution_id', 'component_name', b'component_name', 'execution_id', b'execution_id', 'extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_execution_id', b'_execution_id', 'component_name', b'component_name', 'execution_id', b'execution_id', 'extra', b'extra', 'last_plan_only', b'last_plan_only', 'name', b'name']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_execution_id', b'_execution_id']) -> typing.Literal['execution_id'] | None: + ... +global___GetPlanRequest = GetPlanRequest + +@typing.final +class GetPlanResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CURRENT_PLAN_WITH_STATUS_FIELD_NUMBER: builtins.int + REPLAN_HISTORY_FIELD_NUMBER: builtins.int + + @property + def current_plan_with_status(self) -> global___PlanWithStatus: + """The current plan and status that matches the request query""" + + @property + def replan_history(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PlanWithStatus]: + """Returns the history of all previous plans that were + generated in ascending order. + This field will be empty if the motion service + did not need to re-plan. + """ + + def __init__(self, *, current_plan_with_status: global___PlanWithStatus | None=..., replan_history: collections.abc.Iterable[global___PlanWithStatus] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['current_plan_with_status', b'current_plan_with_status']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['current_plan_with_status', b'current_plan_with_status', 'replan_history', b'replan_history']) -> None: + ... +global___GetPlanResponse = GetPlanResponse + +@typing.final +class Constraints(google.protobuf.message.Message): + """Constraints specifies all enumerated constraints to be passed to Viam's motion planning, along with any optional parameters""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LINEAR_CONSTRAINT_FIELD_NUMBER: builtins.int + ORIENTATION_CONSTRAINT_FIELD_NUMBER: builtins.int + COLLISION_SPECIFICATION_FIELD_NUMBER: builtins.int + + @property + def linear_constraint(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___LinearConstraint]: + """Typed message for a specific constraint""" + + @property + def orientation_constraint(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___OrientationConstraint]: + ... + + @property + def collision_specification(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___CollisionSpecification]: + """Arc constraint, Time constraint, and others will be added here when they are supported""" + + def __init__(self, *, linear_constraint: collections.abc.Iterable[global___LinearConstraint] | None=..., orientation_constraint: collections.abc.Iterable[global___OrientationConstraint] | None=..., collision_specification: collections.abc.Iterable[global___CollisionSpecification] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['collision_specification', b'collision_specification', 'linear_constraint', b'linear_constraint', 'orientation_constraint', b'orientation_constraint']) -> None: + ... +global___Constraints = Constraints + +@typing.final +class LinearConstraint(google.protobuf.message.Message): + """LinearConstraint specifies that the component being moved should move linearly relative to its goal. + It does not constrain the motion of components other than the `component_name` specified in motion.Move + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + LINE_TOLERANCE_MM_FIELD_NUMBER: builtins.int + ORIENTATION_TOLERANCE_DEGS_FIELD_NUMBER: builtins.int + line_tolerance_mm: builtins.float + 'Max linear deviation from straight-line between start and goal, in mm.' + orientation_tolerance_degs: builtins.float + 'Max allowable orientation deviation, in degrees, while on the shortest path between start / goal states' + + def __init__(self, *, line_tolerance_mm: builtins.float | None=..., orientation_tolerance_degs: builtins.float | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_line_tolerance_mm', b'_line_tolerance_mm', '_orientation_tolerance_degs', b'_orientation_tolerance_degs', 'line_tolerance_mm', b'line_tolerance_mm', 'orientation_tolerance_degs', b'orientation_tolerance_degs']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_line_tolerance_mm', b'_line_tolerance_mm', '_orientation_tolerance_degs', b'_orientation_tolerance_degs', 'line_tolerance_mm', b'line_tolerance_mm', 'orientation_tolerance_degs', b'orientation_tolerance_degs']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_line_tolerance_mm', b'_line_tolerance_mm']) -> typing.Literal['line_tolerance_mm'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_orientation_tolerance_degs', b'_orientation_tolerance_degs']) -> typing.Literal['orientation_tolerance_degs'] | None: + ... +global___LinearConstraint = LinearConstraint + +@typing.final +class OrientationConstraint(google.protobuf.message.Message): + """OrientationConstraint specifies that the component being moved will not deviate its orientation beyond some threshold relative + to the goal. It does not constrain the motion of components other than the `component_name` specified in motion.Move + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ORIENTATION_TOLERANCE_DEGS_FIELD_NUMBER: builtins.int + orientation_tolerance_degs: builtins.float + 'Max allowable orientation deviation, in degrees, while on the shortest path between start / goal states' + + def __init__(self, *, orientation_tolerance_degs: builtins.float | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_orientation_tolerance_degs', b'_orientation_tolerance_degs', 'orientation_tolerance_degs', b'orientation_tolerance_degs']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_orientation_tolerance_degs', b'_orientation_tolerance_degs', 'orientation_tolerance_degs', b'orientation_tolerance_degs']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_orientation_tolerance_degs', b'_orientation_tolerance_degs']) -> typing.Literal['orientation_tolerance_degs'] | None: + ... +global___OrientationConstraint = OrientationConstraint + +@typing.final +class CollisionSpecification(google.protobuf.message.Message): + """CollisionSpecification is used to selectively apply obstacle avoidance to specific parts of the robot""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class AllowedFrameCollisions(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + FRAME1_FIELD_NUMBER: builtins.int + FRAME2_FIELD_NUMBER: builtins.int + frame1: builtins.str + frame2: builtins.str + + def __init__(self, *, frame1: builtins.str=..., frame2: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['frame1', b'frame1', 'frame2', b'frame2']) -> None: + ... + ALLOWS_FIELD_NUMBER: builtins.int + + @property + def allows(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___CollisionSpecification.AllowedFrameCollisions]: + """Pairs of frame which should be allowed to collide with one another""" + + def __init__(self, *, allows: collections.abc.Iterable[global___CollisionSpecification.AllowedFrameCollisions] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['allows', b'allows']) -> None: + ... +global___CollisionSpecification = CollisionSpecification + +@typing.final +class PlanWithStatus(google.protobuf.message.Message): + """Describes a plan, its current status & all status changes + that have occured previously on that plan + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PLAN_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + STATUS_HISTORY_FIELD_NUMBER: builtins.int + + @property + def plan(self) -> global___Plan: + """The plan""" + + @property + def status(self) -> global___PlanStatus: + """The current status of the plan""" + + @property + def status_history(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PlanStatus]: + """The prior status changes that have happened during plan execution""" + + def __init__(self, *, plan: global___Plan | None=..., status: global___PlanStatus | None=..., status_history: collections.abc.Iterable[global___PlanStatus] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['plan', b'plan', 'status', b'status']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['plan', b'plan', 'status', b'status', 'status_history', b'status_history']) -> None: + ... +global___PlanWithStatus = PlanWithStatus + +@typing.final +class PlanStatusWithID(google.protobuf.message.Message): + """PlanStatusWithID describes the state of a given plan at a + point in time plus the plan_id, component_name and execution_id + the status is associated with + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PLAN_ID_FIELD_NUMBER: builtins.int + COMPONENT_NAME_FIELD_NUMBER: builtins.int + EXECUTION_ID_FIELD_NUMBER: builtins.int + STATUS_FIELD_NUMBER: builtins.int + plan_id: builtins.str + 'The unique ID of the plan' + execution_id: builtins.str + 'The unique ID which identifies the plan execution.\n Multiple plans will share the same execution_id if they were\n generated due to replanning.\n ' + + @property + def component_name(self) -> common.v1.common_pb2.ResourceName: + """The component to be moved. + Used for tracking & stopping. + NOTE: A plan may move more components than just the + component_name. + """ + + @property + def status(self) -> global___PlanStatus: + ... + + def __init__(self, *, plan_id: builtins.str=..., component_name: common.v1.common_pb2.ResourceName | None=..., execution_id: builtins.str=..., status: global___PlanStatus | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['component_name', b'component_name', 'status', b'status']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['component_name', b'component_name', 'execution_id', b'execution_id', 'plan_id', b'plan_id', 'status', b'status']) -> None: + ... +global___PlanStatusWithID = PlanStatusWithID + +@typing.final +class PlanStatus(google.protobuf.message.Message): + """Plan status describes the state of a given plan at a + point in time + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + STATE_FIELD_NUMBER: builtins.int + TIMESTAMP_FIELD_NUMBER: builtins.int + REASON_FIELD_NUMBER: builtins.int + state: global___PlanState.ValueType + 'The state of the plan execution' + reason: builtins.str + 'The reason for the state change. If motion plan failed\n this will return the error message.\n If motion needed to re-plan, this will return\n the re-plan reason.\n ' + + @property + def timestamp(self) -> google.protobuf.timestamp_pb2.Timestamp: + """The time the executing plan transtioned to the state""" + + def __init__(self, *, state: global___PlanState.ValueType=..., timestamp: google.protobuf.timestamp_pb2.Timestamp | None=..., reason: builtins.str | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_reason', b'_reason', 'reason', b'reason', 'timestamp', b'timestamp']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_reason', b'_reason', 'reason', b'reason', 'state', b'state', 'timestamp', b'timestamp']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_reason', b'_reason']) -> typing.Literal['reason'] | None: + ... +global___PlanStatus = PlanStatus + +@typing.final +class Plan(google.protobuf.message.Message): + """A plan describes a motion plan""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ID_FIELD_NUMBER: builtins.int + COMPONENT_NAME_FIELD_NUMBER: builtins.int + EXECUTION_ID_FIELD_NUMBER: builtins.int + STEPS_FIELD_NUMBER: builtins.int + id: builtins.str + "The plan's unique ID" + execution_id: builtins.str + 'The unique ID which identifies the execution.\n Multiple plans will share the same execution_id if they were\n generated due to replanning\n ' + + @property + def component_name(self) -> common.v1.common_pb2.ResourceName: + """The component requested to be moved. + Used for tracking & stopping. + NOTE: A plan may move more components than just the + root component. + """ + + @property + def steps(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___PlanStep]: + """The steps of a plan is an ordered list of plan steps""" + + def __init__(self, *, id: builtins.str=..., component_name: common.v1.common_pb2.ResourceName | None=..., execution_id: builtins.str=..., steps: collections.abc.Iterable[global___PlanStep] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['component_name', b'component_name']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['component_name', b'component_name', 'execution_id', b'execution_id', 'id', b'id', 'steps', b'steps']) -> None: + ... +global___Plan = Plan + +@typing.final +class PlanStep(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class StepEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + + @property + def value(self) -> global___ComponentState: + ... + + def __init__(self, *, key: builtins.str=..., value: global___ComponentState | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['value', b'value']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: + ... + STEP_FIELD_NUMBER: builtins.int + + @property + def step(self) -> google.protobuf.internal.containers.MessageMap[builtins.str, global___ComponentState]: + """A step is the component state each + component resource should reach while executing + that step of the plan. + Keys are the fully qualified component name. + """ + + def __init__(self, *, step: collections.abc.Mapping[builtins.str, global___ComponentState] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['step', b'step']) -> None: + ... +global___PlanStep = PlanStep + +@typing.final +class ComponentState(google.protobuf.message.Message): + """A pose""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + POSE_FIELD_NUMBER: builtins.int + + @property + def pose(self) -> common.v1.common_pb2.Pose: + ... + + def __init__(self, *, pose: common.v1.common_pb2.Pose | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['pose', b'pose']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['pose', b'pose']) -> None: + def ClearField(self, field_name: typing.Literal['pose', b'pose']) -> None: ... -global___GetPoseResponse = GetPoseResponse \ No newline at end of file +global___ComponentState = ComponentState \ No newline at end of file diff --git a/src/viam/gen/service/navigation/v1/navigation_grpc.py b/src/viam/gen/service/navigation/v1/navigation_grpc.py index 9019fa8ff..04cf85164 100644 --- a/src/viam/gen/service/navigation/v1/navigation_grpc.py +++ b/src/viam/gen/service/navigation/v1/navigation_grpc.py @@ -2,10 +2,12 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common import google.api.annotations_pb2 +import google.protobuf.struct_pb2 from .... import service class NavigationServiceBase(abc.ABC): @@ -34,8 +36,56 @@ async def AddWaypoint(self, stream: 'grpclib.server.Stream[service.navigation.v1 async def RemoveWaypoint(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.RemoveWaypointRequest, service.navigation.v1.navigation_pb2.RemoveWaypointResponse]') -> None: pass + @abc.abstractmethod + async def GetObstacles(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.GetObstaclesRequest, service.navigation.v1.navigation_pb2.GetObstaclesResponse]') -> None: + pass + + @abc.abstractmethod + async def GetPaths(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.GetPathsRequest, service.navigation.v1.navigation_pb2.GetPathsResponse]') -> None: + pass + + @abc.abstractmethod + async def GetProperties(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.GetPropertiesRequest, service.navigation.v1.navigation_pb2.GetPropertiesResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.service.navigation.v1.NavigationService/GetMode': grpclib.const.Handler(self.GetMode, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.GetModeRequest, service.navigation.v1.navigation_pb2.GetModeResponse), '/viam.service.navigation.v1.NavigationService/SetMode': grpclib.const.Handler(self.SetMode, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.SetModeRequest, service.navigation.v1.navigation_pb2.SetModeResponse), '/viam.service.navigation.v1.NavigationService/GetLocation': grpclib.const.Handler(self.GetLocation, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.GetLocationRequest, service.navigation.v1.navigation_pb2.GetLocationResponse), '/viam.service.navigation.v1.NavigationService/GetWaypoints': grpclib.const.Handler(self.GetWaypoints, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.GetWaypointsRequest, service.navigation.v1.navigation_pb2.GetWaypointsResponse), '/viam.service.navigation.v1.NavigationService/AddWaypoint': grpclib.const.Handler(self.AddWaypoint, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.AddWaypointRequest, service.navigation.v1.navigation_pb2.AddWaypointResponse), '/viam.service.navigation.v1.NavigationService/RemoveWaypoint': grpclib.const.Handler(self.RemoveWaypoint, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.RemoveWaypointRequest, service.navigation.v1.navigation_pb2.RemoveWaypointResponse)} + return {'/viam.service.navigation.v1.NavigationService/GetMode': grpclib.const.Handler(self.GetMode, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.GetModeRequest, service.navigation.v1.navigation_pb2.GetModeResponse), '/viam.service.navigation.v1.NavigationService/SetMode': grpclib.const.Handler(self.SetMode, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.SetModeRequest, service.navigation.v1.navigation_pb2.SetModeResponse), '/viam.service.navigation.v1.NavigationService/GetLocation': grpclib.const.Handler(self.GetLocation, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.GetLocationRequest, service.navigation.v1.navigation_pb2.GetLocationResponse), '/viam.service.navigation.v1.NavigationService/GetWaypoints': grpclib.const.Handler(self.GetWaypoints, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.GetWaypointsRequest, service.navigation.v1.navigation_pb2.GetWaypointsResponse), '/viam.service.navigation.v1.NavigationService/AddWaypoint': grpclib.const.Handler(self.AddWaypoint, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.AddWaypointRequest, service.navigation.v1.navigation_pb2.AddWaypointResponse), '/viam.service.navigation.v1.NavigationService/RemoveWaypoint': grpclib.const.Handler(self.RemoveWaypoint, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.RemoveWaypointRequest, service.navigation.v1.navigation_pb2.RemoveWaypointResponse), '/viam.service.navigation.v1.NavigationService/GetObstacles': grpclib.const.Handler(self.GetObstacles, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.GetObstaclesRequest, service.navigation.v1.navigation_pb2.GetObstaclesResponse), '/viam.service.navigation.v1.NavigationService/GetPaths': grpclib.const.Handler(self.GetPaths, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.GetPathsRequest, service.navigation.v1.navigation_pb2.GetPathsResponse), '/viam.service.navigation.v1.NavigationService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, service.navigation.v1.navigation_pb2.GetPropertiesRequest, service.navigation.v1.navigation_pb2.GetPropertiesResponse), '/viam.service.navigation.v1.NavigationService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedNavigationServiceBase(NavigationServiceBase): + + async def GetMode(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.GetModeRequest, service.navigation.v1.navigation_pb2.GetModeResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetMode(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.SetModeRequest, service.navigation.v1.navigation_pb2.SetModeResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetLocation(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.GetLocationRequest, service.navigation.v1.navigation_pb2.GetLocationResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetWaypoints(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.GetWaypointsRequest, service.navigation.v1.navigation_pb2.GetWaypointsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def AddWaypoint(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.AddWaypointRequest, service.navigation.v1.navigation_pb2.AddWaypointResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RemoveWaypoint(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.RemoveWaypointRequest, service.navigation.v1.navigation_pb2.RemoveWaypointResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetObstacles(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.GetObstaclesRequest, service.navigation.v1.navigation_pb2.GetObstaclesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPaths(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.GetPathsRequest, service.navigation.v1.navigation_pb2.GetPathsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetProperties(self, stream: 'grpclib.server.Stream[service.navigation.v1.navigation_pb2.GetPropertiesRequest, service.navigation.v1.navigation_pb2.GetPropertiesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class NavigationServiceStub: @@ -45,4 +95,8 @@ def __init__(self, channel: grpclib.client.Channel) -> None: self.GetLocation = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.navigation.v1.NavigationService/GetLocation', service.navigation.v1.navigation_pb2.GetLocationRequest, service.navigation.v1.navigation_pb2.GetLocationResponse) self.GetWaypoints = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.navigation.v1.NavigationService/GetWaypoints', service.navigation.v1.navigation_pb2.GetWaypointsRequest, service.navigation.v1.navigation_pb2.GetWaypointsResponse) self.AddWaypoint = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.navigation.v1.NavigationService/AddWaypoint', service.navigation.v1.navigation_pb2.AddWaypointRequest, service.navigation.v1.navigation_pb2.AddWaypointResponse) - self.RemoveWaypoint = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.navigation.v1.NavigationService/RemoveWaypoint', service.navigation.v1.navigation_pb2.RemoveWaypointRequest, service.navigation.v1.navigation_pb2.RemoveWaypointResponse) \ No newline at end of file + self.RemoveWaypoint = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.navigation.v1.NavigationService/RemoveWaypoint', service.navigation.v1.navigation_pb2.RemoveWaypointRequest, service.navigation.v1.navigation_pb2.RemoveWaypointResponse) + self.GetObstacles = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.navigation.v1.NavigationService/GetObstacles', service.navigation.v1.navigation_pb2.GetObstaclesRequest, service.navigation.v1.navigation_pb2.GetObstaclesResponse) + self.GetPaths = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.navigation.v1.NavigationService/GetPaths', service.navigation.v1.navigation_pb2.GetPathsRequest, service.navigation.v1.navigation_pb2.GetPathsResponse) + self.GetProperties = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.navigation.v1.NavigationService/GetProperties', service.navigation.v1.navigation_pb2.GetPropertiesRequest, service.navigation.v1.navigation_pb2.GetPropertiesResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.navigation.v1.NavigationService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/service/navigation/v1/navigation_pb2.py b/src/viam/gen/service/navigation/v1/navigation_pb2.py index 36bff0016..738baa15d 100644 --- a/src/viam/gen/service/navigation/v1/navigation_pb2.py +++ b/src/viam/gen/service/navigation/v1/navigation_pb2.py @@ -1,56 +1,84 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'service/navigation/v1/navigation.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&service/navigation/v1/navigation.proto\x12\x1aviam.service.navigation.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto"$\n\x0eGetModeRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"G\n\x0fGetModeResponse\x124\n\x04mode\x18\x01 \x01(\x0e2 .viam.service.navigation.v1.ModeR\x04mode"Z\n\x0eSetModeRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x124\n\x04mode\x18\x02 \x01(\x0e2 .viam.service.navigation.v1.ModeR\x04mode"\x11\n\x0fSetModeResponse"P\n\x08Waypoint\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x124\n\x08location\x18\x02 \x01(\x0b2\x18.viam.common.v1.GeoPointR\x08location"(\n\x12GetLocationRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"K\n\x13GetLocationResponse\x124\n\x08location\x18\x01 \x01(\x0b2\x18.viam.common.v1.GeoPointR\x08location")\n\x13GetWaypointsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"Z\n\x14GetWaypointsResponse\x12B\n\twaypoints\x18\x01 \x03(\x0b2$.viam.service.navigation.v1.WaypointR\twaypoints"^\n\x12AddWaypointRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x124\n\x08location\x18\x02 \x01(\x0b2\x18.viam.common.v1.GeoPointR\x08location"\x15\n\x13AddWaypointResponse";\n\x15RemoveWaypointRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x0e\n\x02id\x18\x02 \x01(\tR\x02id"\x18\n\x16RemoveWaypointResponse*@\n\x04Mode\x12\x14\n\x10MODE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bMODE_MANUAL\x10\x01\x12\x11\n\rMODE_WAYPOINT\x10\x022\x83\x08\n\x11NavigationService\x12\x97\x01\n\x07GetMode\x12*.viam.service.navigation.v1.GetModeRequest\x1a+.viam.service.navigation.v1.GetModeResponse"3\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/service/navigation/{name}/mode\x12\x97\x01\n\x07SetMode\x12*.viam.service.navigation.v1.SetModeRequest\x1a+.viam.service.navigation.v1.SetModeResponse"3\x82\xd3\xe4\x93\x02-\x1a+/viam/api/v1/service/navigation/{name}/mode\x12\xa7\x01\n\x0bGetLocation\x12..viam.service.navigation.v1.GetLocationRequest\x1a/.viam.service.navigation.v1.GetLocationResponse"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/service/navigation/{name}/location\x12\xab\x01\n\x0cGetWaypoints\x12/.viam.service.navigation.v1.GetWaypointsRequest\x1a0.viam.service.navigation.v1.GetWaypointsResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/service/navigation/{name}/waypoints\x12\xa8\x01\n\x0bAddWaypoint\x12..viam.service.navigation.v1.AddWaypointRequest\x1a/.viam.service.navigation.v1.AddWaypointResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/navigation/{name}/waypoints\x12\xb6\x01\n\x0eRemoveWaypoint\x121.viam.service.navigation.v1.RemoveWaypointRequest\x1a2.viam.service.navigation.v1.RemoveWaypointResponse"=\x82\xd3\xe4\x93\x027*5/viam/api/v1/service/navigation/{name}/waypoints/{id}BG\n\x1ecom.viam.service.navigation.v1Z%go.viam.com/api/service/navigation/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.navigation.v1.navigation_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1ecom.viam.service.navigation.v1Z%go.viam.com/api/service/navigation/v1' - _NAVIGATIONSERVICE.methods_by_name['GetMode']._options = None - _NAVIGATIONSERVICE.methods_by_name['GetMode']._serialized_options = b'\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/service/navigation/{name}/mode' - _NAVIGATIONSERVICE.methods_by_name['SetMode']._options = None - _NAVIGATIONSERVICE.methods_by_name['SetMode']._serialized_options = b'\x82\xd3\xe4\x93\x02-\x1a+/viam/api/v1/service/navigation/{name}/mode' - _NAVIGATIONSERVICE.methods_by_name['GetLocation']._options = None - _NAVIGATIONSERVICE.methods_by_name['GetLocation']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/service/navigation/{name}/location' - _NAVIGATIONSERVICE.methods_by_name['GetWaypoints']._options = None - _NAVIGATIONSERVICE.methods_by_name['GetWaypoints']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/service/navigation/{name}/waypoints' - _NAVIGATIONSERVICE.methods_by_name['AddWaypoint']._options = None - _NAVIGATIONSERVICE.methods_by_name['AddWaypoint']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/navigation/{name}/waypoints' - _NAVIGATIONSERVICE.methods_by_name['RemoveWaypoint']._options = None - _NAVIGATIONSERVICE.methods_by_name['RemoveWaypoint']._serialized_options = b'\x82\xd3\xe4\x93\x027*5/viam/api/v1/service/navigation/{name}/waypoints/{id}' - _MODE._serialized_start = 888 - _MODE._serialized_end = 952 - _GETMODEREQUEST._serialized_start = 124 - _GETMODEREQUEST._serialized_end = 160 - _GETMODERESPONSE._serialized_start = 162 - _GETMODERESPONSE._serialized_end = 233 - _SETMODEREQUEST._serialized_start = 235 - _SETMODEREQUEST._serialized_end = 325 - _SETMODERESPONSE._serialized_start = 327 - _SETMODERESPONSE._serialized_end = 344 - _WAYPOINT._serialized_start = 346 - _WAYPOINT._serialized_end = 426 - _GETLOCATIONREQUEST._serialized_start = 428 - _GETLOCATIONREQUEST._serialized_end = 468 - _GETLOCATIONRESPONSE._serialized_start = 470 - _GETLOCATIONRESPONSE._serialized_end = 545 - _GETWAYPOINTSREQUEST._serialized_start = 547 - _GETWAYPOINTSREQUEST._serialized_end = 588 - _GETWAYPOINTSRESPONSE._serialized_start = 590 - _GETWAYPOINTSRESPONSE._serialized_end = 680 - _ADDWAYPOINTREQUEST._serialized_start = 682 - _ADDWAYPOINTREQUEST._serialized_end = 776 - _ADDWAYPOINTRESPONSE._serialized_start = 778 - _ADDWAYPOINTRESPONSE._serialized_end = 799 - _REMOVEWAYPOINTREQUEST._serialized_start = 801 - _REMOVEWAYPOINTREQUEST._serialized_end = 860 - _REMOVEWAYPOINTRESPONSE._serialized_start = 862 - _REMOVEWAYPOINTRESPONSE._serialized_end = 886 - _NAVIGATIONSERVICE._serialized_start = 955 - _NAVIGATIONSERVICE._serialized_end = 1982 \ No newline at end of file +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n&service/navigation/v1/navigation.proto\x12\x1aviam.service.navigation.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"S\n\x0eGetModeRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"G\n\x0fGetModeResponse\x124\n\x04mode\x18\x01 \x01(\x0e2 .viam.service.navigation.v1.ModeR\x04mode"\x89\x01\n\x0eSetModeRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x124\n\x04mode\x18\x02 \x01(\x0e2 .viam.service.navigation.v1.ModeR\x04mode\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x11\n\x0fSetModeResponse"P\n\x08Waypoint\x12\x0e\n\x02id\x18\x01 \x01(\tR\x02id\x124\n\x08location\x18\x02 \x01(\x0b2\x18.viam.common.v1.GeoPointR\x08location"W\n\x12GetLocationRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"t\n\x13GetLocationResponse\x124\n\x08location\x18\x01 \x01(\x0b2\x18.viam.common.v1.GeoPointR\x08location\x12\'\n\x0fcompass_heading\x18\x02 \x01(\x01R\x0ecompassHeading"X\n\x13GetWaypointsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"Z\n\x14GetWaypointsResponse\x12B\n\twaypoints\x18\x01 \x03(\x0b2$.viam.service.navigation.v1.WaypointR\twaypoints"\x8d\x01\n\x12AddWaypointRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x124\n\x08location\x18\x02 \x01(\x0b2\x18.viam.common.v1.GeoPointR\x08location\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x15\n\x13AddWaypointResponse"j\n\x15RemoveWaypointRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x0e\n\x02id\x18\x02 \x01(\tR\x02id\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\x18\n\x16RemoveWaypointResponse"X\n\x13GetObstaclesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"Q\n\x14GetObstaclesResponse\x129\n\tobstacles\x18\x01 \x03(\x0b2\x1b.viam.common.v1.GeoGeometryR\tobstacles"v\n\x04Path\x126\n\x17destination_waypoint_id\x18\x01 \x01(\tR\x15destinationWaypointId\x126\n\tgeopoints\x18\x02 \x03(\x0b2\x18.viam.common.v1.GeoPointR\tgeopoints"T\n\x0fGetPathsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"J\n\x10GetPathsResponse\x126\n\x05paths\x18\x01 \x03(\x0b2 .viam.service.navigation.v1.PathR\x05paths"*\n\x14GetPropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"W\n\x15GetPropertiesResponse\x12>\n\x08map_type\x18\x01 \x01(\x0e2#.viam.service.navigation.v1.MapTypeR\x07mapType*H\n\x07MapType\x12\x18\n\x14MAP_TYPE_UNSPECIFIED\x10\x00\x12\x11\n\rMAP_TYPE_NONE\x10\x01\x12\x10\n\x0cMAP_TYPE_GPS\x10\x02*R\n\x04Mode\x12\x14\n\x10MODE_UNSPECIFIED\x10\x00\x12\x0f\n\x0bMODE_MANUAL\x10\x01\x12\x11\n\rMODE_WAYPOINT\x10\x02\x12\x10\n\x0cMODE_EXPLORE\x10\x032\x97\r\n\x11NavigationService\x12\x97\x01\n\x07GetMode\x12*.viam.service.navigation.v1.GetModeRequest\x1a+.viam.service.navigation.v1.GetModeResponse"3\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/service/navigation/{name}/mode\x12\x97\x01\n\x07SetMode\x12*.viam.service.navigation.v1.SetModeRequest\x1a+.viam.service.navigation.v1.SetModeResponse"3\x82\xd3\xe4\x93\x02-\x1a+/viam/api/v1/service/navigation/{name}/mode\x12\xa7\x01\n\x0bGetLocation\x12..viam.service.navigation.v1.GetLocationRequest\x1a/.viam.service.navigation.v1.GetLocationResponse"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/service/navigation/{name}/location\x12\xab\x01\n\x0cGetWaypoints\x12/.viam.service.navigation.v1.GetWaypointsRequest\x1a0.viam.service.navigation.v1.GetWaypointsResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/service/navigation/{name}/waypoints\x12\xa8\x01\n\x0bAddWaypoint\x12..viam.service.navigation.v1.AddWaypointRequest\x1a/.viam.service.navigation.v1.AddWaypointResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/navigation/{name}/waypoints\x12\xb6\x01\n\x0eRemoveWaypoint\x121.viam.service.navigation.v1.RemoveWaypointRequest\x1a2.viam.service.navigation.v1.RemoveWaypointResponse"=\x82\xd3\xe4\x93\x027*5/viam/api/v1/service/navigation/{name}/waypoints/{id}\x12\xaf\x01\n\x0cGetObstacles\x12/.viam.service.navigation.v1.GetObstaclesRequest\x1a0.viam.service.navigation.v1.GetObstaclesResponse"<\x82\xd3\xe4\x93\x026\x124/viam/api/v1/service/navigation/{name}/get_obstacles\x12\x9f\x01\n\x08GetPaths\x12+.viam.service.navigation.v1.GetPathsRequest\x1a,.viam.service.navigation.v1.GetPathsResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/service/navigation/{name}/get_paths\x12\xaf\x01\n\rGetProperties\x120.viam.service.navigation.v1.GetPropertiesRequest\x1a1.viam.service.navigation.v1.GetPropertiesResponse"9\x82\xd3\xe4\x93\x023\x121/viam/api/v1/service/navigation/{name}/properties\x12\x8b\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"9\x82\xd3\xe4\x93\x023"1/viam/api/v1/service/navigation/{name}/do_commandBG\n\x1ecom.viam.service.navigation.v1Z%go.viam.com/api/service/navigation/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.navigation.v1.navigation_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1ecom.viam.service.navigation.v1Z%go.viam.com/api/service/navigation/v1' + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetMode']._loaded_options = None + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetMode']._serialized_options = b'\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/service/navigation/{name}/mode' + _globals['_NAVIGATIONSERVICE'].methods_by_name['SetMode']._loaded_options = None + _globals['_NAVIGATIONSERVICE'].methods_by_name['SetMode']._serialized_options = b'\x82\xd3\xe4\x93\x02-\x1a+/viam/api/v1/service/navigation/{name}/mode' + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetLocation']._loaded_options = None + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetLocation']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/service/navigation/{name}/location' + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetWaypoints']._loaded_options = None + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetWaypoints']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/service/navigation/{name}/waypoints' + _globals['_NAVIGATIONSERVICE'].methods_by_name['AddWaypoint']._loaded_options = None + _globals['_NAVIGATIONSERVICE'].methods_by_name['AddWaypoint']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/navigation/{name}/waypoints' + _globals['_NAVIGATIONSERVICE'].methods_by_name['RemoveWaypoint']._loaded_options = None + _globals['_NAVIGATIONSERVICE'].methods_by_name['RemoveWaypoint']._serialized_options = b'\x82\xd3\xe4\x93\x027*5/viam/api/v1/service/navigation/{name}/waypoints/{id}' + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetObstacles']._loaded_options = None + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetObstacles']._serialized_options = b'\x82\xd3\xe4\x93\x026\x124/viam/api/v1/service/navigation/{name}/get_obstacles' + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetPaths']._loaded_options = None + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetPaths']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/service/navigation/{name}/get_paths' + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetProperties']._loaded_options = None + _globals['_NAVIGATIONSERVICE'].methods_by_name['GetProperties']._serialized_options = b'\x82\xd3\xe4\x93\x023\x121/viam/api/v1/service/navigation/{name}/properties' + _globals['_NAVIGATIONSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_NAVIGATIONSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x023"1/viam/api/v1/service/navigation/{name}/do_command' + _globals['_MAPTYPE']._serialized_start = 1831 + _globals['_MAPTYPE']._serialized_end = 1903 + _globals['_MODE']._serialized_start = 1905 + _globals['_MODE']._serialized_end = 1987 + _globals['_GETMODEREQUEST']._serialized_start = 154 + _globals['_GETMODEREQUEST']._serialized_end = 237 + _globals['_GETMODERESPONSE']._serialized_start = 239 + _globals['_GETMODERESPONSE']._serialized_end = 310 + _globals['_SETMODEREQUEST']._serialized_start = 313 + _globals['_SETMODEREQUEST']._serialized_end = 450 + _globals['_SETMODERESPONSE']._serialized_start = 452 + _globals['_SETMODERESPONSE']._serialized_end = 469 + _globals['_WAYPOINT']._serialized_start = 471 + _globals['_WAYPOINT']._serialized_end = 551 + _globals['_GETLOCATIONREQUEST']._serialized_start = 553 + _globals['_GETLOCATIONREQUEST']._serialized_end = 640 + _globals['_GETLOCATIONRESPONSE']._serialized_start = 642 + _globals['_GETLOCATIONRESPONSE']._serialized_end = 758 + _globals['_GETWAYPOINTSREQUEST']._serialized_start = 760 + _globals['_GETWAYPOINTSREQUEST']._serialized_end = 848 + _globals['_GETWAYPOINTSRESPONSE']._serialized_start = 850 + _globals['_GETWAYPOINTSRESPONSE']._serialized_end = 940 + _globals['_ADDWAYPOINTREQUEST']._serialized_start = 943 + _globals['_ADDWAYPOINTREQUEST']._serialized_end = 1084 + _globals['_ADDWAYPOINTRESPONSE']._serialized_start = 1086 + _globals['_ADDWAYPOINTRESPONSE']._serialized_end = 1107 + _globals['_REMOVEWAYPOINTREQUEST']._serialized_start = 1109 + _globals['_REMOVEWAYPOINTREQUEST']._serialized_end = 1215 + _globals['_REMOVEWAYPOINTRESPONSE']._serialized_start = 1217 + _globals['_REMOVEWAYPOINTRESPONSE']._serialized_end = 1241 + _globals['_GETOBSTACLESREQUEST']._serialized_start = 1243 + _globals['_GETOBSTACLESREQUEST']._serialized_end = 1331 + _globals['_GETOBSTACLESRESPONSE']._serialized_start = 1333 + _globals['_GETOBSTACLESRESPONSE']._serialized_end = 1414 + _globals['_PATH']._serialized_start = 1416 + _globals['_PATH']._serialized_end = 1534 + _globals['_GETPATHSREQUEST']._serialized_start = 1536 + _globals['_GETPATHSREQUEST']._serialized_end = 1620 + _globals['_GETPATHSRESPONSE']._serialized_start = 1622 + _globals['_GETPATHSRESPONSE']._serialized_end = 1696 + _globals['_GETPROPERTIESREQUEST']._serialized_start = 1698 + _globals['_GETPROPERTIESREQUEST']._serialized_end = 1740 + _globals['_GETPROPERTIESRESPONSE']._serialized_start = 1742 + _globals['_GETPROPERTIESRESPONSE']._serialized_end = 1829 + _globals['_NAVIGATIONSERVICE']._serialized_start = 1990 + _globals['_NAVIGATIONSERVICE']._serialized_end = 3677 \ No newline at end of file diff --git a/src/viam/gen/service/navigation/v1/navigation_pb2.pyi b/src/viam/gen/service/navigation/v1/navigation_pb2.pyi index 0a8fca870..9f9de8298 100644 --- a/src/viam/gen/service/navigation/v1/navigation_pb2.pyi +++ b/src/viam/gen/service/navigation/v1/navigation_pb2.pyi @@ -9,6 +9,7 @@ import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message +import google.protobuf.struct_pb2 import sys import typing if sys.version_info >= (3, 10): @@ -17,6 +18,23 @@ else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _MapType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _MapTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MapType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + MAP_TYPE_UNSPECIFIED: _MapType.ValueType + MAP_TYPE_NONE: _MapType.ValueType + MAP_TYPE_GPS: _MapType.ValueType + +class MapType(_MapType, metaclass=_MapTypeEnumTypeWrapper): + """MapType represents the various types of maps the navigation service can ingest.""" +MAP_TYPE_UNSPECIFIED: MapType.ValueType +MAP_TYPE_NONE: MapType.ValueType +MAP_TYPE_GPS: MapType.ValueType +global___MapType = MapType + class _Mode: ValueType = typing.NewType('ValueType', builtins.int) V: typing_extensions.TypeAlias = ValueType @@ -26,26 +44,38 @@ class _ModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeW MODE_UNSPECIFIED: _Mode.ValueType MODE_MANUAL: _Mode.ValueType MODE_WAYPOINT: _Mode.ValueType + MODE_EXPLORE: _Mode.ValueType class Mode(_Mode, metaclass=_ModeEnumTypeWrapper): ... MODE_UNSPECIFIED: Mode.ValueType MODE_MANUAL: Mode.ValueType MODE_WAYPOINT: Mode.ValueType +MODE_EXPLORE: Mode.ValueType global___Mode = Mode +@typing.final class GetModeRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetModeRequest = GetModeRequest +@typing.final class GetModeResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor MODE_FIELD_NUMBER: builtins.int @@ -54,24 +84,34 @@ class GetModeResponse(google.protobuf.message.Message): def __init__(self, *, mode: global___Mode.ValueType=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['mode', b'mode']) -> None: + def ClearField(self, field_name: typing.Literal['mode', b'mode']) -> None: ... global___GetModeResponse = GetModeResponse +@typing.final class SetModeRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int MODE_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str mode: global___Mode.ValueType - def __init__(self, *, name: builtins.str=..., mode: global___Mode.ValueType=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., mode: global___Mode.ValueType=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['mode', b'mode', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'mode', b'mode', 'name', b'name']) -> None: ... global___SetModeRequest = SetModeRequest +@typing.final class SetModeResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -79,6 +119,7 @@ class SetModeResponse(google.protobuf.message.Message): ... global___SetModeResponse = SetModeResponse +@typing.final class Waypoint(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ID_FIELD_NUMBER: builtins.int @@ -92,55 +133,78 @@ class Waypoint(google.protobuf.message.Message): def __init__(self, *, id: builtins.str=..., location: common.v1.common_pb2.GeoPoint | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['location', b'location']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['location', b'location']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id', 'location', b'location']) -> None: + def ClearField(self, field_name: typing.Literal['id', b'id', 'location', b'location']) -> None: ... global___Waypoint = Waypoint +@typing.final class GetLocationRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetLocationRequest = GetLocationRequest +@typing.final class GetLocationResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor LOCATION_FIELD_NUMBER: builtins.int + COMPASS_HEADING_FIELD_NUMBER: builtins.int + compass_heading: builtins.float + 'A number from [0-360) where 0 is north\n 90 is east, 180 is south, 270 is west\n ' @property def location(self) -> common.v1.common_pb2.GeoPoint: ... - def __init__(self, *, location: common.v1.common_pb2.GeoPoint | None=...) -> None: + def __init__(self, *, location: common.v1.common_pb2.GeoPoint | None=..., compass_heading: builtins.float=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['location', b'location']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['location', b'location']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['location', b'location']) -> None: + def ClearField(self, field_name: typing.Literal['compass_heading', b'compass_heading', 'location', b'location']) -> None: ... global___GetLocationResponse = GetLocationResponse +@typing.final class GetWaypointsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetWaypointsRequest = GetWaypointsRequest +@typing.final class GetWaypointsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor WAYPOINTS_FIELD_NUMBER: builtins.int @@ -152,30 +216,37 @@ class GetWaypointsResponse(google.protobuf.message.Message): def __init__(self, *, waypoints: collections.abc.Iterable[global___Waypoint] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['waypoints', b'waypoints']) -> None: + def ClearField(self, field_name: typing.Literal['waypoints', b'waypoints']) -> None: ... global___GetWaypointsResponse = GetWaypointsResponse +@typing.final class AddWaypointRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int LOCATION_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str @property def location(self) -> common.v1.common_pb2.GeoPoint: ... - def __init__(self, *, name: builtins.str=..., location: common.v1.common_pb2.GeoPoint | None=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., location: common.v1.common_pb2.GeoPoint | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['location', b'location']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra', 'location', b'location']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['location', b'location', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'location', b'location', 'name', b'name']) -> None: ... global___AddWaypointRequest = AddWaypointRequest +@typing.final class AddWaypointResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -183,23 +254,166 @@ class AddWaypointResponse(google.protobuf.message.Message): ... global___AddWaypointResponse = AddWaypointResponse +@typing.final class RemoveWaypointRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int ID_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str id: builtins.str - def __init__(self, *, name: builtins.str=..., id: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., id: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['id', b'id', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'id', b'id', 'name', b'name']) -> None: ... global___RemoveWaypointRequest = RemoveWaypointRequest +@typing.final class RemoveWaypointResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor def __init__(self) -> None: ... -global___RemoveWaypointResponse = RemoveWaypointResponse \ No newline at end of file +global___RemoveWaypointResponse = RemoveWaypointResponse + +@typing.final +class GetObstaclesRequest(google.protobuf.message.Message): + """GetObstacles will return the geopoint location and geometry of all + known obstacles on the navigation map. Obstacles that are detected + through the vision service will only be returned if this endpoint is called + when the robot is sensing the obstacle + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetObstaclesRequest = GetObstaclesRequest + +@typing.final +class GetObstaclesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + OBSTACLES_FIELD_NUMBER: builtins.int + + @property + def obstacles(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.GeoGeometry]: + """List of all known geometries""" + + def __init__(self, *, obstacles: collections.abc.Iterable[common.v1.common_pb2.GeoGeometry] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['obstacles', b'obstacles']) -> None: + ... +global___GetObstaclesResponse = GetObstaclesResponse + +@typing.final +class Path(google.protobuf.message.Message): + """A user provided destination and the set of geopoints that + the robot is expected to take to get there + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + DESTINATION_WAYPOINT_ID_FIELD_NUMBER: builtins.int + GEOPOINTS_FIELD_NUMBER: builtins.int + destination_waypoint_id: builtins.str + 'The id of the user specified waypoint' + + @property + def geopoints(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.GeoPoint]: + """List of geopoints that the motion planner output to reach the destination + The first geopoint is the starting position of the robot for that path + """ + + def __init__(self, *, destination_waypoint_id: builtins.str=..., geopoints: collections.abc.Iterable[common.v1.common_pb2.GeoPoint] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['destination_waypoint_id', b'destination_waypoint_id', 'geopoints', b'geopoints']) -> None: + ... +global___Path = Path + +@typing.final +class GetPathsRequest(google.protobuf.message.Message): + """Returns all the paths known to the navigation service""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of the navigation service' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: + ... +global___GetPathsRequest = GetPathsRequest + +@typing.final +class GetPathsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + PATHS_FIELD_NUMBER: builtins.int + + @property + def paths(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Path]: + ... + + def __init__(self, *, paths: collections.abc.Iterable[global___Path] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['paths', b'paths']) -> None: + ... +global___GetPathsResponse = GetPathsResponse + +@typing.final +class GetPropertiesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of the navigation service' + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___GetPropertiesRequest = GetPropertiesRequest + +@typing.final +class GetPropertiesResponse(google.protobuf.message.Message): + """Returns properties information for the named navigation service""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + MAP_TYPE_FIELD_NUMBER: builtins.int + map_type: global___MapType.ValueType + + def __init__(self, *, map_type: global___MapType.ValueType=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['map_type', b'map_type']) -> None: + ... +global___GetPropertiesResponse = GetPropertiesResponse \ No newline at end of file diff --git a/src/viam/gen/service/sensors/v1/sensors_grpc.py b/src/viam/gen/service/sensors/v1/sensors_grpc.py index a4651b0ce..cd60cbe1c 100644 --- a/src/viam/gen/service/sensors/v1/sensors_grpc.py +++ b/src/viam/gen/service/sensors/v1/sensors_grpc.py @@ -2,6 +2,7 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common @@ -19,11 +20,27 @@ async def GetSensors(self, stream: 'grpclib.server.Stream[service.sensors.v1.sen async def GetReadings(self, stream: 'grpclib.server.Stream[service.sensors.v1.sensors_pb2.GetReadingsRequest, service.sensors.v1.sensors_pb2.GetReadingsResponse]') -> None: pass + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.service.sensors.v1.SensorsService/GetSensors': grpclib.const.Handler(self.GetSensors, grpclib.const.Cardinality.UNARY_UNARY, service.sensors.v1.sensors_pb2.GetSensorsRequest, service.sensors.v1.sensors_pb2.GetSensorsResponse), '/viam.service.sensors.v1.SensorsService/GetReadings': grpclib.const.Handler(self.GetReadings, grpclib.const.Cardinality.UNARY_UNARY, service.sensors.v1.sensors_pb2.GetReadingsRequest, service.sensors.v1.sensors_pb2.GetReadingsResponse)} + return {'/viam.service.sensors.v1.SensorsService/GetSensors': grpclib.const.Handler(self.GetSensors, grpclib.const.Cardinality.UNARY_UNARY, service.sensors.v1.sensors_pb2.GetSensorsRequest, service.sensors.v1.sensors_pb2.GetSensorsResponse), '/viam.service.sensors.v1.SensorsService/GetReadings': grpclib.const.Handler(self.GetReadings, grpclib.const.Cardinality.UNARY_UNARY, service.sensors.v1.sensors_pb2.GetReadingsRequest, service.sensors.v1.sensors_pb2.GetReadingsResponse), '/viam.service.sensors.v1.SensorsService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedSensorsServiceBase(SensorsServiceBase): + + async def GetSensors(self, stream: 'grpclib.server.Stream[service.sensors.v1.sensors_pb2.GetSensorsRequest, service.sensors.v1.sensors_pb2.GetSensorsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetReadings(self, stream: 'grpclib.server.Stream[service.sensors.v1.sensors_pb2.GetReadingsRequest, service.sensors.v1.sensors_pb2.GetReadingsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class SensorsServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: self.GetSensors = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.sensors.v1.SensorsService/GetSensors', service.sensors.v1.sensors_pb2.GetSensorsRequest, service.sensors.v1.sensors_pb2.GetSensorsResponse) - self.GetReadings = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.sensors.v1.SensorsService/GetReadings', service.sensors.v1.sensors_pb2.GetReadingsRequest, service.sensors.v1.sensors_pb2.GetReadingsResponse) \ No newline at end of file + self.GetReadings = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.sensors.v1.SensorsService/GetReadings', service.sensors.v1.sensors_pb2.GetReadingsRequest, service.sensors.v1.sensors_pb2.GetReadingsResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.sensors.v1.SensorsService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/service/sensors/v1/sensors_pb2.py b/src/viam/gen/service/sensors/v1/sensors_pb2.py index f5dad03ff..9a304eb65 100644 --- a/src/viam/gen/service/sensors/v1/sensors_pb2.py +++ b/src/viam/gen/service/sensors/v1/sensors_pb2.py @@ -1,35 +1,68 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'service/sensors/v1/sensors.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n service/sensors/v1/sensors.proto\x12\x17viam.service.sensors.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"\'\n\x11GetSensorsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"U\n\x12GetSensorsResponse\x12?\n\x0csensor_names\x18\x01 \x03(\x0b2\x1c.viam.common.v1.ResourceNameR\x0bsensorNames"i\n\x12GetReadingsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12?\n\x0csensor_names\x18\x02 \x03(\x0b2\x1c.viam.common.v1.ResourceNameR\x0bsensorNames"\xde\x01\n\x08Readings\x120\n\x04name\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameR\x04name\x12K\n\x08readings\x18\x02 \x03(\x0b2/.viam.service.sensors.v1.Readings.ReadingsEntryR\x08readings\x1aS\n\rReadingsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12,\n\x05value\x18\x02 \x01(\x0b2\x16.google.protobuf.ValueR\x05value:\x028\x01"T\n\x13GetReadingsResponse\x12=\n\x08readings\x18\x01 \x03(\x0b2!.viam.service.sensors.v1.ReadingsR\x08readings2\xc6\x02\n\x0eSensorsService\x12\x92\x01\n\nGetSensors\x12*.viam.service.sensors.v1.GetSensorsRequest\x1a+.viam.service.sensors.v1.GetSensorsResponse"+\x82\xd3\xe4\x93\x02%\x12#/viam/api/v1/service/{name}/sensors\x12\x9e\x01\n\x0bGetReadings\x12+.viam.service.sensors.v1.GetReadingsRequest\x1a,.viam.service.sensors.v1.GetReadingsResponse"4\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/service/sensors/{name}/readingsBA\n\x1bcom.viam.service.sensors.v1Z"go.viam.com/api/service/sensors/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.sensors.v1.sensors_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1bcom.viam.service.sensors.v1Z"go.viam.com/api/service/sensors/v1' - _READINGS_READINGSENTRY._options = None - _READINGS_READINGSENTRY._serialized_options = b'8\x01' - _SENSORSSERVICE.methods_by_name['GetSensors']._options = None - _SENSORSSERVICE.methods_by_name['GetSensors']._serialized_options = b'\x82\xd3\xe4\x93\x02%\x12#/viam/api/v1/service/{name}/sensors' - _SENSORSSERVICE.methods_by_name['GetReadings']._options = None - _SENSORSSERVICE.methods_by_name['GetReadings']._serialized_options = b'\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/service/sensors/{name}/readings' - _GETSENSORSREQUEST._serialized_start = 145 - _GETSENSORSREQUEST._serialized_end = 184 - _GETSENSORSRESPONSE._serialized_start = 186 - _GETSENSORSRESPONSE._serialized_end = 271 - _GETREADINGSREQUEST._serialized_start = 273 - _GETREADINGSREQUEST._serialized_end = 378 - _READINGS._serialized_start = 381 - _READINGS._serialized_end = 603 - _READINGS_READINGSENTRY._serialized_start = 520 - _READINGS_READINGSENTRY._serialized_end = 603 - _GETREADINGSRESPONSE._serialized_start = 605 - _GETREADINGSRESPONSE._serialized_end = 689 - _SENSORSSERVICE._serialized_start = 692 - _SENSORSSERVICE._serialized_end = 1018 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n service/sensors/v1/sensors.proto\x12\x17viam.service.sensors.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"b\n\x11GetSensorsRequest\x12\x16\n\x04name\x18\x01 \x01(\tB\x02\x18\x01R\x04name\x121\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructB\x02\x18\x01R\x05extra:\x02\x18\x01"]\n\x12GetSensorsResponse\x12C\n\x0csensor_names\x18\x01 \x03(\x0b2\x1c.viam.common.v1.ResourceNameB\x02\x18\x01R\x0bsensorNames:\x02\x18\x01"\xa8\x01\n\x12GetReadingsRequest\x12\x16\n\x04name\x18\x01 \x01(\tB\x02\x18\x01R\x04name\x12C\n\x0csensor_names\x18\x02 \x03(\x0b2\x1c.viam.common.v1.ResourceNameB\x02\x18\x01R\x0bsensorNames\x121\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructB\x02\x18\x01R\x05extra:\x02\x18\x01"\xea\x01\n\x08Readings\x124\n\x04name\x18\x01 \x01(\x0b2\x1c.viam.common.v1.ResourceNameB\x02\x18\x01R\x04name\x12O\n\x08readings\x18\x02 \x03(\x0b2/.viam.service.sensors.v1.Readings.ReadingsEntryB\x02\x18\x01R\x08readings\x1aS\n\rReadingsEntry\x12\x10\n\x03key\x18\x01 \x01(\tR\x03key\x12,\n\x05value\x18\x02 \x01(\x0b2\x16.google.protobuf.ValueR\x05value:\x028\x01:\x02\x18\x01"\\\n\x13GetReadingsResponse\x12A\n\x08readings\x18\x01 \x03(\x0b2!.viam.service.sensors.v1.ReadingsB\x02\x18\x01R\x08readings:\x02\x18\x012\xda\x03\n\x0eSensorsService\x12\x95\x01\n\nGetSensors\x12*.viam.service.sensors.v1.GetSensorsRequest\x1a+.viam.service.sensors.v1.GetSensorsResponse".\x88\x02\x01\x82\xd3\xe4\x93\x02%\x12#/viam/api/v1/service/{name}/sensors\x12\xa1\x01\n\x0bGetReadings\x12+.viam.service.sensors.v1.GetReadingsRequest\x1a,.viam.service.sensors.v1.GetReadingsResponse"7\x88\x02\x01\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/service/sensors/{name}/readings\x12\x8b\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"9\x88\x02\x01\x82\xd3\xe4\x93\x020"./viam/api/v1/service/sensors/{name}/do_commandBD\n\x1bcom.viam.service.sensors.v1Z"go.viam.com/api/service/sensors/v1\xb8\x01\x01b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.sensors.v1.sensors_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1bcom.viam.service.sensors.v1Z"go.viam.com/api/service/sensors/v1\xb8\x01\x01' + _globals['_GETSENSORSREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_GETSENSORSREQUEST'].fields_by_name['name']._serialized_options = b'\x18\x01' + _globals['_GETSENSORSREQUEST'].fields_by_name['extra']._loaded_options = None + _globals['_GETSENSORSREQUEST'].fields_by_name['extra']._serialized_options = b'\x18\x01' + _globals['_GETSENSORSREQUEST']._loaded_options = None + _globals['_GETSENSORSREQUEST']._serialized_options = b'\x18\x01' + _globals['_GETSENSORSRESPONSE'].fields_by_name['sensor_names']._loaded_options = None + _globals['_GETSENSORSRESPONSE'].fields_by_name['sensor_names']._serialized_options = b'\x18\x01' + _globals['_GETSENSORSRESPONSE']._loaded_options = None + _globals['_GETSENSORSRESPONSE']._serialized_options = b'\x18\x01' + _globals['_GETREADINGSREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_GETREADINGSREQUEST'].fields_by_name['name']._serialized_options = b'\x18\x01' + _globals['_GETREADINGSREQUEST'].fields_by_name['sensor_names']._loaded_options = None + _globals['_GETREADINGSREQUEST'].fields_by_name['sensor_names']._serialized_options = b'\x18\x01' + _globals['_GETREADINGSREQUEST'].fields_by_name['extra']._loaded_options = None + _globals['_GETREADINGSREQUEST'].fields_by_name['extra']._serialized_options = b'\x18\x01' + _globals['_GETREADINGSREQUEST']._loaded_options = None + _globals['_GETREADINGSREQUEST']._serialized_options = b'\x18\x01' + _globals['_READINGS_READINGSENTRY']._loaded_options = None + _globals['_READINGS_READINGSENTRY']._serialized_options = b'8\x01' + _globals['_READINGS'].fields_by_name['name']._loaded_options = None + _globals['_READINGS'].fields_by_name['name']._serialized_options = b'\x18\x01' + _globals['_READINGS'].fields_by_name['readings']._loaded_options = None + _globals['_READINGS'].fields_by_name['readings']._serialized_options = b'\x18\x01' + _globals['_READINGS']._loaded_options = None + _globals['_READINGS']._serialized_options = b'\x18\x01' + _globals['_GETREADINGSRESPONSE'].fields_by_name['readings']._loaded_options = None + _globals['_GETREADINGSRESPONSE'].fields_by_name['readings']._serialized_options = b'\x18\x01' + _globals['_GETREADINGSRESPONSE']._loaded_options = None + _globals['_GETREADINGSRESPONSE']._serialized_options = b'\x18\x01' + _globals['_SENSORSSERVICE'].methods_by_name['GetSensors']._loaded_options = None + _globals['_SENSORSSERVICE'].methods_by_name['GetSensors']._serialized_options = b'\x88\x02\x01\x82\xd3\xe4\x93\x02%\x12#/viam/api/v1/service/{name}/sensors' + _globals['_SENSORSSERVICE'].methods_by_name['GetReadings']._loaded_options = None + _globals['_SENSORSSERVICE'].methods_by_name['GetReadings']._serialized_options = b'\x88\x02\x01\x82\xd3\xe4\x93\x02.\x12,/viam/api/v1/service/sensors/{name}/readings' + _globals['_SENSORSSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_SENSORSSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x88\x02\x01\x82\xd3\xe4\x93\x020"./viam/api/v1/service/sensors/{name}/do_command' + _globals['_GETSENSORSREQUEST']._serialized_start = 145 + _globals['_GETSENSORSREQUEST']._serialized_end = 243 + _globals['_GETSENSORSRESPONSE']._serialized_start = 245 + _globals['_GETSENSORSRESPONSE']._serialized_end = 338 + _globals['_GETREADINGSREQUEST']._serialized_start = 341 + _globals['_GETREADINGSREQUEST']._serialized_end = 509 + _globals['_READINGS']._serialized_start = 512 + _globals['_READINGS']._serialized_end = 746 + _globals['_READINGS_READINGSENTRY']._serialized_start = 659 + _globals['_READINGS_READINGSENTRY']._serialized_end = 742 + _globals['_GETREADINGSRESPONSE']._serialized_start = 748 + _globals['_GETREADINGSRESPONSE']._serialized_end = 840 + _globals['_SENSORSSERVICE']._serialized_start = 843 + _globals['_SENSORSSERVICE']._serialized_end = 1317 \ No newline at end of file diff --git a/src/viam/gen/service/sensors/v1/sensors_pb2.pyi b/src/viam/gen/service/sensors/v1/sensors_pb2.pyi index dad158db8..ac654840b 100644 --- a/src/viam/gen/service/sensors/v1/sensors_pb2.pyi +++ b/src/viam/gen/service/sensors/v1/sensors_pb2.pyi @@ -9,25 +9,32 @@ import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.message import google.protobuf.struct_pb2 -import sys -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +@typing.final class GetSensorsRequest(google.protobuf.message.Message): + """The sensors service messages are deprecated""" DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str - def __init__(self, *, name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... global___GetSensorsRequest = GetSensorsRequest +@typing.final class GetSensorsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor SENSOR_NAMES_FIELD_NUMBER: builtins.int @@ -39,30 +46,41 @@ class GetSensorsResponse(google.protobuf.message.Message): def __init__(self, *, sensor_names: collections.abc.Iterable[common.v1.common_pb2.ResourceName] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['sensor_names', b'sensor_names']) -> None: + def ClearField(self, field_name: typing.Literal['sensor_names', b'sensor_names']) -> None: ... global___GetSensorsResponse = GetSensorsResponse +@typing.final class GetReadingsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int SENSOR_NAMES_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str @property def sensor_names(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.ResourceName]: ... - def __init__(self, *, name: builtins.str=..., sensor_names: collections.abc.Iterable[common.v1.common_pb2.ResourceName] | None=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., sensor_names: collections.abc.Iterable[common.v1.common_pb2.ResourceName] | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name', 'sensor_names', b'sensor_names']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name', 'sensor_names', b'sensor_names']) -> None: ... global___GetReadingsRequest = GetReadingsRequest +@typing.final class Readings(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor + @typing.final class ReadingsEntry(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor KEY_FIELD_NUMBER: builtins.int @@ -76,10 +94,10 @@ class Readings(google.protobuf.message.Message): def __init__(self, *, key: builtins.str=..., value: google.protobuf.struct_pb2.Value | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['value', b'value']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['value', b'value']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['key', b'key', 'value', b'value']) -> None: + def ClearField(self, field_name: typing.Literal['key', b'key', 'value', b'value']) -> None: ... NAME_FIELD_NUMBER: builtins.int READINGS_FIELD_NUMBER: builtins.int @@ -95,13 +113,14 @@ class Readings(google.protobuf.message.Message): def __init__(self, *, name: common.v1.common_pb2.ResourceName | None=..., readings: collections.abc.Mapping[builtins.str, google.protobuf.struct_pb2.Value] | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['name', b'name']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['name', b'name']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name', 'readings', b'readings']) -> None: + def ClearField(self, field_name: typing.Literal['name', b'name', 'readings', b'readings']) -> None: ... global___Readings = Readings +@typing.final class GetReadingsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor READINGS_FIELD_NUMBER: builtins.int @@ -113,6 +132,6 @@ class GetReadingsResponse(google.protobuf.message.Message): def __init__(self, *, readings: collections.abc.Iterable[global___Readings] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['readings', b'readings']) -> None: + def ClearField(self, field_name: typing.Literal['readings', b'readings']) -> None: ... global___GetReadingsResponse = GetReadingsResponse \ No newline at end of file diff --git a/src/viam/gen/service/shell/v1/shell_grpc.py b/src/viam/gen/service/shell/v1/shell_grpc.py index 248dce9c2..96b1f5fdb 100644 --- a/src/viam/gen/service/shell/v1/shell_grpc.py +++ b/src/viam/gen/service/shell/v1/shell_grpc.py @@ -2,8 +2,13 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server +from .... import common +import google.api.annotations_pb2 +import google.protobuf.struct_pb2 +import google.protobuf.timestamp_pb2 from .... import service class ShellServiceBase(abc.ABC): @@ -12,10 +17,39 @@ class ShellServiceBase(abc.ABC): async def Shell(self, stream: 'grpclib.server.Stream[service.shell.v1.shell_pb2.ShellRequest, service.shell.v1.shell_pb2.ShellResponse]') -> None: pass + @abc.abstractmethod + async def CopyFilesToMachine(self, stream: 'grpclib.server.Stream[service.shell.v1.shell_pb2.CopyFilesToMachineRequest, service.shell.v1.shell_pb2.CopyFilesToMachineResponse]') -> None: + pass + + @abc.abstractmethod + async def CopyFilesFromMachine(self, stream: 'grpclib.server.Stream[service.shell.v1.shell_pb2.CopyFilesFromMachineRequest, service.shell.v1.shell_pb2.CopyFilesFromMachineResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.service.shell.v1.ShellService/Shell': grpclib.const.Handler(self.Shell, grpclib.const.Cardinality.STREAM_STREAM, service.shell.v1.shell_pb2.ShellRequest, service.shell.v1.shell_pb2.ShellResponse)} + return {'/viam.service.shell.v1.ShellService/Shell': grpclib.const.Handler(self.Shell, grpclib.const.Cardinality.STREAM_STREAM, service.shell.v1.shell_pb2.ShellRequest, service.shell.v1.shell_pb2.ShellResponse), '/viam.service.shell.v1.ShellService/CopyFilesToMachine': grpclib.const.Handler(self.CopyFilesToMachine, grpclib.const.Cardinality.STREAM_STREAM, service.shell.v1.shell_pb2.CopyFilesToMachineRequest, service.shell.v1.shell_pb2.CopyFilesToMachineResponse), '/viam.service.shell.v1.ShellService/CopyFilesFromMachine': grpclib.const.Handler(self.CopyFilesFromMachine, grpclib.const.Cardinality.STREAM_STREAM, service.shell.v1.shell_pb2.CopyFilesFromMachineRequest, service.shell.v1.shell_pb2.CopyFilesFromMachineResponse), '/viam.service.shell.v1.ShellService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedShellServiceBase(ShellServiceBase): + + async def Shell(self, stream: 'grpclib.server.Stream[service.shell.v1.shell_pb2.ShellRequest, service.shell.v1.shell_pb2.ShellResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CopyFilesToMachine(self, stream: 'grpclib.server.Stream[service.shell.v1.shell_pb2.CopyFilesToMachineRequest, service.shell.v1.shell_pb2.CopyFilesToMachineResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CopyFilesFromMachine(self, stream: 'grpclib.server.Stream[service.shell.v1.shell_pb2.CopyFilesFromMachineRequest, service.shell.v1.shell_pb2.CopyFilesFromMachineResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class ShellServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: - self.Shell = grpclib.client.StreamStreamMethod(channel, '/viam.service.shell.v1.ShellService/Shell', service.shell.v1.shell_pb2.ShellRequest, service.shell.v1.shell_pb2.ShellResponse) \ No newline at end of file + self.Shell = grpclib.client.StreamStreamMethod(channel, '/viam.service.shell.v1.ShellService/Shell', service.shell.v1.shell_pb2.ShellRequest, service.shell.v1.shell_pb2.ShellResponse) + self.CopyFilesToMachine = grpclib.client.StreamStreamMethod(channel, '/viam.service.shell.v1.ShellService/CopyFilesToMachine', service.shell.v1.shell_pb2.CopyFilesToMachineRequest, service.shell.v1.shell_pb2.CopyFilesToMachineResponse) + self.CopyFilesFromMachine = grpclib.client.StreamStreamMethod(channel, '/viam.service.shell.v1.ShellService/CopyFilesFromMachine', service.shell.v1.shell_pb2.CopyFilesFromMachineRequest, service.shell.v1.shell_pb2.CopyFilesFromMachineResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.shell.v1.ShellService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/service/shell/v1/shell_pb2.py b/src/viam/gen/service/shell/v1/shell_pb2.py index eeb7ba619..f1fbd6403 100644 --- a/src/viam/gen/service/shell/v1/shell_pb2.py +++ b/src/viam/gen/service/shell/v1/shell_pb2.py @@ -1,18 +1,45 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'service/shell/v1/shell.proto') _sym_db = _symbol_database.Default() -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cservice/shell/v1/shell.proto\x12\x15viam.service.shell.v1";\n\x0cShellRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x17\n\x07data_in\x18\x02 \x01(\tR\x06dataIn"W\n\rShellResponse\x12\x19\n\x08data_out\x18\x01 \x01(\tR\x07dataOut\x12\x19\n\x08data_err\x18\x02 \x01(\tR\x07dataErr\x12\x10\n\x03eof\x18\x03 \x01(\x08R\x03eof2f\n\x0cShellService\x12V\n\x05Shell\x12#.viam.service.shell.v1.ShellRequest\x1a$.viam.service.shell.v1.ShellResponse(\x010\x01B=\n\x19com.viam.service.shell.v1Z go.viam.com/api/service/shell/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.shell.v1.shell_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x19com.viam.service.shell.v1Z go.viam.com/api/service/shell/v1' - _SHELLREQUEST._serialized_start = 55 - _SHELLREQUEST._serialized_end = 114 - _SHELLRESPONSE._serialized_start = 116 - _SHELLRESPONSE._serialized_end = 203 - _SHELLSERVICE._serialized_start = 205 - _SHELLSERVICE._serialized_end = 307 \ No newline at end of file +from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1cservice/shell/v1/shell.proto\x12\x15viam.service.shell.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto"j\n\x0cShellRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x17\n\x07data_in\x18\x02 \x01(\tR\x06dataIn\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"W\n\rShellResponse\x12\x19\n\x08data_out\x18\x01 \x01(\tR\x07dataOut\x12\x19\n\x08data_err\x18\x02 \x01(\tR\x07dataErr\x12\x10\n\x03eof\x18\x03 \x01(\x08R\x03eof"\xda\x01\n\x08FileData\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04size\x18\x02 \x01(\x03R\x04size\x12\x15\n\x06is_dir\x18\x03 \x01(\x08R\x05isDir\x12\x12\n\x04data\x18\x04 \x01(\x0cR\x04data\x12\x10\n\x03eof\x18\x05 \x01(\x08R\x03eof\x12:\n\x08mod_time\x18\x06 \x01(\x0b2\x1a.google.protobuf.TimestampH\x00R\x07modTime\x88\x01\x01\x12\x17\n\x04mode\x18\x07 \x01(\rH\x01R\x04mode\x88\x01\x01B\x0b\n\t_mod_timeB\x07\n\x05_mode"\xf1\x01\n!CopyFilesToMachineRequestMetadata\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12K\n\x0bsource_type\x18\x02 \x01(\x0e2*.viam.service.shell.v1.CopyFilesSourceTypeR\nsourceType\x12 \n\x0bdestination\x18\x03 \x01(\tR\x0bdestination\x12\x1a\n\x08preserve\x18\x04 \x01(\x08R\x08preserve\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\xbe\x01\n\x19CopyFilesToMachineRequest\x12V\n\x08metadata\x18\x01 \x01(\x0b28.viam.service.shell.v1.CopyFilesToMachineRequestMetadataH\x00R\x08metadata\x12>\n\tfile_data\x18\x02 \x01(\x0b2\x1f.viam.service.shell.v1.FileDataH\x00R\x08fileDataB\t\n\x07request"@\n\x1aCopyFilesToMachineResponse\x12"\n\rack_last_file\x18\x01 \x01(\x08R\x0backLastFile"\xc3\x01\n#CopyFilesFromMachineRequestMetadata\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n\x05paths\x18\x02 \x03(\tR\x05paths\x12\'\n\x0fallow_recursion\x18\x03 \x01(\x08R\x0eallowRecursion\x12\x1a\n\x08preserve\x18\x04 \x01(\x08R\x08preserve\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\xa8\x01\n\x1bCopyFilesFromMachineRequest\x12X\n\x08metadata\x18\x01 \x01(\x0b2:.viam.service.shell.v1.CopyFilesFromMachineRequestMetadataH\x00R\x08metadata\x12$\n\rack_last_file\x18\x02 \x01(\x08H\x00R\x0backLastFileB\t\n\x07request"s\n$CopyFilesFromMachineResponseMetadata\x12K\n\x0bsource_type\x18\x01 \x01(\x0e2*.viam.service.shell.v1.CopyFilesSourceTypeR\nsourceType"\xc5\x01\n\x1cCopyFilesFromMachineResponse\x12Y\n\x08metadata\x18\x01 \x01(\x0b2;.viam.service.shell.v1.CopyFilesFromMachineResponseMetadataH\x00R\x08metadata\x12>\n\tfile_data\x18\x02 \x01(\x0b2\x1f.viam.service.shell.v1.FileDataH\x00R\x08fileDataB\n\n\x08response*\xbd\x01\n\x13CopyFilesSourceType\x12&\n"COPY_FILES_SOURCE_TYPE_UNSPECIFIED\x10\x00\x12&\n"COPY_FILES_SOURCE_TYPE_SINGLE_FILE\x10\x01\x12+\n\'COPY_FILES_SOURCE_TYPE_SINGLE_DIRECTORY\x10\x02\x12)\n%COPY_FILES_SOURCE_TYPE_MULTIPLE_FILES\x10\x032\xf4\x03\n\x0cShellService\x12V\n\x05Shell\x12#.viam.service.shell.v1.ShellRequest\x1a$.viam.service.shell.v1.ShellResponse(\x010\x01\x12}\n\x12CopyFilesToMachine\x120.viam.service.shell.v1.CopyFilesToMachineRequest\x1a1.viam.service.shell.v1.CopyFilesToMachineResponse(\x010\x01\x12\x83\x01\n\x14CopyFilesFromMachine\x122.viam.service.shell.v1.CopyFilesFromMachineRequest\x1a3.viam.service.shell.v1.CopyFilesFromMachineResponse(\x010\x01\x12\x86\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"4\x82\xd3\xe4\x93\x02.",/viam/api/v1/service/shell/{name}/do_commandB=\n\x19com.viam.service.shell.v1Z go.viam.com/api/service/shell/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.shell.v1.shell_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x19com.viam.service.shell.v1Z go.viam.com/api/service/shell/v1' + _globals['_SHELLSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_SHELLSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x02.",/viam/api/v1/service/shell/{name}/do_command' + _globals['_COPYFILESSOURCETYPE']._serialized_start = 1780 + _globals['_COPYFILESSOURCETYPE']._serialized_end = 1969 + _globals['_SHELLREQUEST']._serialized_start = 172 + _globals['_SHELLREQUEST']._serialized_end = 278 + _globals['_SHELLRESPONSE']._serialized_start = 280 + _globals['_SHELLRESPONSE']._serialized_end = 367 + _globals['_FILEDATA']._serialized_start = 370 + _globals['_FILEDATA']._serialized_end = 588 + _globals['_COPYFILESTOMACHINEREQUESTMETADATA']._serialized_start = 591 + _globals['_COPYFILESTOMACHINEREQUESTMETADATA']._serialized_end = 832 + _globals['_COPYFILESTOMACHINEREQUEST']._serialized_start = 835 + _globals['_COPYFILESTOMACHINEREQUEST']._serialized_end = 1025 + _globals['_COPYFILESTOMACHINERESPONSE']._serialized_start = 1027 + _globals['_COPYFILESTOMACHINERESPONSE']._serialized_end = 1091 + _globals['_COPYFILESFROMMACHINEREQUESTMETADATA']._serialized_start = 1094 + _globals['_COPYFILESFROMMACHINEREQUESTMETADATA']._serialized_end = 1289 + _globals['_COPYFILESFROMMACHINEREQUEST']._serialized_start = 1292 + _globals['_COPYFILESFROMMACHINEREQUEST']._serialized_end = 1460 + _globals['_COPYFILESFROMMACHINERESPONSEMETADATA']._serialized_start = 1462 + _globals['_COPYFILESFROMMACHINERESPONSEMETADATA']._serialized_end = 1577 + _globals['_COPYFILESFROMMACHINERESPONSE']._serialized_start = 1580 + _globals['_COPYFILESFROMMACHINERESPONSE']._serialized_end = 1777 + _globals['_SHELLSERVICE']._serialized_start = 1972 + _globals['_SHELLSERVICE']._serialized_end = 2472 \ No newline at end of file diff --git a/src/viam/gen/service/shell/v1/shell_pb2.pyi b/src/viam/gen/service/shell/v1/shell_pb2.pyi index 7864da676..ef580b567 100644 --- a/src/viam/gen/service/shell/v1/shell_pb2.pyi +++ b/src/viam/gen/service/shell/v1/shell_pb2.pyi @@ -3,29 +3,69 @@ isort:skip_file """ import builtins +import collections.abc import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper import google.protobuf.message +import google.protobuf.struct_pb2 +import google.protobuf.timestamp_pb2 import sys -if sys.version_info >= (3, 8): +import typing +if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _CopyFilesSourceType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _CopyFilesSourceTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_CopyFilesSourceType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + COPY_FILES_SOURCE_TYPE_UNSPECIFIED: _CopyFilesSourceType.ValueType + COPY_FILES_SOURCE_TYPE_SINGLE_FILE: _CopyFilesSourceType.ValueType + COPY_FILES_SOURCE_TYPE_SINGLE_DIRECTORY: _CopyFilesSourceType.ValueType + COPY_FILES_SOURCE_TYPE_MULTIPLE_FILES: _CopyFilesSourceType.ValueType + +class CopyFilesSourceType(_CopyFilesSourceType, metaclass=_CopyFilesSourceTypeEnumTypeWrapper): + """CopyFilesSourceType indicates what will be copied. It's important + to disambiguate the single directory case from the multiple files + case in order to indicate that the user's intent is to copy a directory + into a single location which may result in a new top-level directory versus + the cause of multiples files that always go into the existing target destination. + """ +COPY_FILES_SOURCE_TYPE_UNSPECIFIED: CopyFilesSourceType.ValueType +COPY_FILES_SOURCE_TYPE_SINGLE_FILE: CopyFilesSourceType.ValueType +COPY_FILES_SOURCE_TYPE_SINGLE_DIRECTORY: CopyFilesSourceType.ValueType +COPY_FILES_SOURCE_TYPE_MULTIPLE_FILES: CopyFilesSourceType.ValueType +global___CopyFilesSourceType = CopyFilesSourceType + +@typing.final class ShellRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int DATA_IN_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str data_in: builtins.str - def __init__(self, *, name: builtins.str=..., data_in: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., data_in: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['data_in', b'data_in', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['data_in', b'data_in', 'extra', b'extra', 'name', b'name']) -> None: ... global___ShellRequest = ShellRequest +@typing.final class ShellResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor DATA_OUT_FIELD_NUMBER: builtins.int @@ -38,6 +78,230 @@ class ShellResponse(google.protobuf.message.Message): def __init__(self, *, data_out: builtins.str=..., data_err: builtins.str=..., eof: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['data_err', b'data_err', 'data_out', b'data_out', 'eof', b'eof']) -> None: + def ClearField(self, field_name: typing.Literal['data_err', b'data_err', 'data_out', b'data_out', 'eof', b'eof']) -> None: + ... +global___ShellResponse = ShellResponse + +@typing.final +class FileData(google.protobuf.message.Message): + """FileData contains partial (sometimes complete) information about a File. + When transmitting FileData with CopyFilesToMachine and CopyFilesFromMachine, + it MUST initially contain its name, size, and is_dir. Depending on whether + preservation is in use, the mod_time and mode fields may be initially set + as well. On all transmissions, data and eof must be set. Because files are + sent one-by-one, it is currently permitted to exclude the initially set fields. + If this ever changes, a new scheme should be used for identifying files (like a number) + in order to reduce data transmission while allowing out-of-order transfers. + eof must be true and its own message once no more data is to be sent for this file. + """ + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + SIZE_FIELD_NUMBER: builtins.int + IS_DIR_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + EOF_FIELD_NUMBER: builtins.int + MOD_TIME_FIELD_NUMBER: builtins.int + MODE_FIELD_NUMBER: builtins.int + name: builtins.str + size: builtins.int + is_dir: builtins.bool + data: builtins.bytes + eof: builtins.bool + mode: builtins.int + + @property + def mod_time(self) -> google.protobuf.timestamp_pb2.Timestamp: + """Note(erd): maybe support access time in the future if needed""" + + def __init__(self, *, name: builtins.str=..., size: builtins.int=..., is_dir: builtins.bool=..., data: builtins.bytes=..., eof: builtins.bool=..., mod_time: google.protobuf.timestamp_pb2.Timestamp | None=..., mode: builtins.int | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_mod_time', b'_mod_time', '_mode', b'_mode', 'mod_time', b'mod_time', 'mode', b'mode']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_mod_time', b'_mod_time', '_mode', b'_mode', 'data', b'data', 'eof', b'eof', 'is_dir', b'is_dir', 'mod_time', b'mod_time', 'mode', b'mode', 'name', b'name', 'size', b'size']) -> None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_mod_time', b'_mod_time']) -> typing.Literal['mod_time'] | None: + ... + + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_mode', b'_mode']) -> typing.Literal['mode'] | None: + ... +global___FileData = FileData + +@typing.final +class CopyFilesToMachineRequestMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + SOURCE_TYPE_FIELD_NUMBER: builtins.int + DESTINATION_FIELD_NUMBER: builtins.int + PRESERVE_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'name is the service name.' + source_type: global___CopyFilesSourceType.ValueType + 'source_type is the type of files that will be transmitted in this request stream.' + destination: builtins.str + 'destination is where the files should be placed. The receiver can choose to\n reasonably modify this destination based on its implementation semantics.\n ' + preserve: builtins.bool + 'preserve indicates the the receiver should use the metadata in the file to reflect\n the same state in its filesystem as applicable.\n ' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., source_type: global___CopyFilesSourceType.ValueType=..., destination: builtins.str=..., preserve: builtins.bool=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['destination', b'destination', 'extra', b'extra', 'name', b'name', 'preserve', b'preserve', 'source_type', b'source_type']) -> None: + ... +global___CopyFilesToMachineRequestMetadata = CopyFilesToMachineRequestMetadata + +@typing.final +class CopyFilesToMachineRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + METADATA_FIELD_NUMBER: builtins.int + FILE_DATA_FIELD_NUMBER: builtins.int + + @property + def metadata(self) -> global___CopyFilesToMachineRequestMetadata: + """metadata is sent first and only once.""" + + @property + def file_data(self) -> global___FileData: + """file_data is sent only after metadata. All data MUST be sent + in order per-file. + """ + + def __init__(self, *, metadata: global___CopyFilesToMachineRequestMetadata | None=..., file_data: global___FileData | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['file_data', b'file_data', 'metadata', b'metadata', 'request', b'request']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['file_data', b'file_data', 'metadata', b'metadata', 'request', b'request']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['request', b'request']) -> typing.Literal['metadata', 'file_data'] | None: + ... +global___CopyFilesToMachineRequest = CopyFilesToMachineRequest + +@typing.final +class CopyFilesToMachineResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + ACK_LAST_FILE_FIELD_NUMBER: builtins.int + ack_last_file: builtins.bool + 'value does not matter here but responses must be sent after every\n file has been received.\n ' + + def __init__(self, *, ack_last_file: builtins.bool=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['ack_last_file', b'ack_last_file']) -> None: + ... +global___CopyFilesToMachineResponse = CopyFilesToMachineResponse + +@typing.final +class CopyFilesFromMachineRequestMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + PATHS_FIELD_NUMBER: builtins.int + ALLOW_RECURSION_FIELD_NUMBER: builtins.int + PRESERVE_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int + name: builtins.str + 'name is the service name.' + allow_recursion: builtins.bool + 'allow_recursion indicates if directories should be recursed into. If\n a directory is encountered and this is false, an error MUST occur.\n ' + preserve: builtins.bool + "preserve indicates the the receiver should provide the metadata in the file\n to reflect the same state in the sender's filesystem as applicable.\n " + + @property + def paths(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """paths are the paths to copy from and send back over the wire.""" + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., paths: collections.abc.Iterable[builtins.str] | None=..., allow_recursion: builtins.bool=..., preserve: builtins.bool=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['allow_recursion', b'allow_recursion', 'extra', b'extra', 'name', b'name', 'paths', b'paths', 'preserve', b'preserve']) -> None: + ... +global___CopyFilesFromMachineRequestMetadata = CopyFilesFromMachineRequestMetadata + +@typing.final +class CopyFilesFromMachineRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + METADATA_FIELD_NUMBER: builtins.int + ACK_LAST_FILE_FIELD_NUMBER: builtins.int + ack_last_file: builtins.bool + 'ack_last_file is sent only after metadata and after each file has been received.\n The value does not matter.\n ' + + @property + def metadata(self) -> global___CopyFilesFromMachineRequestMetadata: + """metadata is sent first and only once.""" + + def __init__(self, *, metadata: global___CopyFilesFromMachineRequestMetadata | None=..., ack_last_file: builtins.bool=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['ack_last_file', b'ack_last_file', 'metadata', b'metadata', 'request', b'request']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['ack_last_file', b'ack_last_file', 'metadata', b'metadata', 'request', b'request']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['request', b'request']) -> typing.Literal['metadata', 'ack_last_file'] | None: + ... +global___CopyFilesFromMachineRequest = CopyFilesFromMachineRequest + +@typing.final +class CopyFilesFromMachineResponseMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + SOURCE_TYPE_FIELD_NUMBER: builtins.int + source_type: global___CopyFilesSourceType.ValueType + 'source_type is the type of files that will be transmitted in this response stream.' + + def __init__(self, *, source_type: global___CopyFilesSourceType.ValueType=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['source_type', b'source_type']) -> None: + ... +global___CopyFilesFromMachineResponseMetadata = CopyFilesFromMachineResponseMetadata + +@typing.final +class CopyFilesFromMachineResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + METADATA_FIELD_NUMBER: builtins.int + FILE_DATA_FIELD_NUMBER: builtins.int + + @property + def metadata(self) -> global___CopyFilesFromMachineResponseMetadata: + """metadata is sent first and only once.""" + + @property + def file_data(self) -> global___FileData: + """file_data is sent only after metadata. All data MUST be sent + in order per-file. + """ + + def __init__(self, *, metadata: global___CopyFilesFromMachineResponseMetadata | None=..., file_data: global___FileData | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['file_data', b'file_data', 'metadata', b'metadata', 'response', b'response']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['file_data', b'file_data', 'metadata', b'metadata', 'response', b'response']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['response', b'response']) -> typing.Literal['metadata', 'file_data'] | None: ... -global___ShellResponse = ShellResponse \ No newline at end of file +global___CopyFilesFromMachineResponse = CopyFilesFromMachineResponse \ No newline at end of file diff --git a/src/viam/gen/service/slam/v1/slam_grpc.py b/src/viam/gen/service/slam/v1/slam_grpc.py index b6ceb2ce4..ee0378c05 100644 --- a/src/viam/gen/service/slam/v1/slam_grpc.py +++ b/src/viam/gen/service/slam/v1/slam_grpc.py @@ -2,6 +2,7 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common @@ -15,14 +16,46 @@ async def GetPosition(self, stream: 'grpclib.server.Stream[service.slam.v1.slam_ pass @abc.abstractmethod - async def GetMap(self, stream: 'grpclib.server.Stream[service.slam.v1.slam_pb2.GetMapRequest, service.slam.v1.slam_pb2.GetMapResponse]') -> None: + async def GetPointCloudMap(self, stream: 'grpclib.server.Stream[service.slam.v1.slam_pb2.GetPointCloudMapRequest, service.slam.v1.slam_pb2.GetPointCloudMapResponse]') -> None: + pass + + @abc.abstractmethod + async def GetInternalState(self, stream: 'grpclib.server.Stream[service.slam.v1.slam_pb2.GetInternalStateRequest, service.slam.v1.slam_pb2.GetInternalStateResponse]') -> None: + pass + + @abc.abstractmethod + async def GetProperties(self, stream: 'grpclib.server.Stream[service.slam.v1.slam_pb2.GetPropertiesRequest, service.slam.v1.slam_pb2.GetPropertiesResponse]') -> None: + pass + + @abc.abstractmethod + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: pass def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.service.slam.v1.SLAMService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, service.slam.v1.slam_pb2.GetPositionRequest, service.slam.v1.slam_pb2.GetPositionResponse), '/viam.service.slam.v1.SLAMService/GetMap': grpclib.const.Handler(self.GetMap, grpclib.const.Cardinality.UNARY_UNARY, service.slam.v1.slam_pb2.GetMapRequest, service.slam.v1.slam_pb2.GetMapResponse)} + return {'/viam.service.slam.v1.SLAMService/GetPosition': grpclib.const.Handler(self.GetPosition, grpclib.const.Cardinality.UNARY_UNARY, service.slam.v1.slam_pb2.GetPositionRequest, service.slam.v1.slam_pb2.GetPositionResponse), '/viam.service.slam.v1.SLAMService/GetPointCloudMap': grpclib.const.Handler(self.GetPointCloudMap, grpclib.const.Cardinality.UNARY_STREAM, service.slam.v1.slam_pb2.GetPointCloudMapRequest, service.slam.v1.slam_pb2.GetPointCloudMapResponse), '/viam.service.slam.v1.SLAMService/GetInternalState': grpclib.const.Handler(self.GetInternalState, grpclib.const.Cardinality.UNARY_STREAM, service.slam.v1.slam_pb2.GetInternalStateRequest, service.slam.v1.slam_pb2.GetInternalStateResponse), '/viam.service.slam.v1.SLAMService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, service.slam.v1.slam_pb2.GetPropertiesRequest, service.slam.v1.slam_pb2.GetPropertiesResponse), '/viam.service.slam.v1.SLAMService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} + +class UnimplementedSLAMServiceBase(SLAMServiceBase): + + async def GetPosition(self, stream: 'grpclib.server.Stream[service.slam.v1.slam_pb2.GetPositionRequest, service.slam.v1.slam_pb2.GetPositionResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetPointCloudMap(self, stream: 'grpclib.server.Stream[service.slam.v1.slam_pb2.GetPointCloudMapRequest, service.slam.v1.slam_pb2.GetPointCloudMapResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetInternalState(self, stream: 'grpclib.server.Stream[service.slam.v1.slam_pb2.GetInternalStateRequest, service.slam.v1.slam_pb2.GetInternalStateResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetProperties(self, stream: 'grpclib.server.Stream[service.slam.v1.slam_pb2.GetPropertiesRequest, service.slam.v1.slam_pb2.GetPropertiesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class SLAMServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: self.GetPosition = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.slam.v1.SLAMService/GetPosition', service.slam.v1.slam_pb2.GetPositionRequest, service.slam.v1.slam_pb2.GetPositionResponse) - self.GetMap = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.slam.v1.SLAMService/GetMap', service.slam.v1.slam_pb2.GetMapRequest, service.slam.v1.slam_pb2.GetMapResponse) \ No newline at end of file + self.GetPointCloudMap = grpclib.client.UnaryStreamMethod(channel, '/viam.service.slam.v1.SLAMService/GetPointCloudMap', service.slam.v1.slam_pb2.GetPointCloudMapRequest, service.slam.v1.slam_pb2.GetPointCloudMapResponse) + self.GetInternalState = grpclib.client.UnaryStreamMethod(channel, '/viam.service.slam.v1.SLAMService/GetInternalState', service.slam.v1.slam_pb2.GetInternalStateRequest, service.slam.v1.slam_pb2.GetInternalStateResponse) + self.GetProperties = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.slam.v1.SLAMService/GetProperties', service.slam.v1.slam_pb2.GetPropertiesRequest, service.slam.v1.slam_pb2.GetPropertiesResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.slam.v1.SLAMService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/service/slam/v1/slam_pb2.py b/src/viam/gen/service/slam/v1/slam_pb2.py index 75041d8dd..99fad7b09 100644 --- a/src/viam/gen/service/slam/v1/slam_pb2.py +++ b/src/viam/gen/service/slam/v1/slam_pb2.py @@ -1,28 +1,51 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'service/slam/v1/slam.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aservice/slam/v1/slam.proto\x12\x14viam.service.slam.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto"(\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"F\n\x13GetPositionResponse\x12/\n\x04pose\x18\x01 \x01(\x0b2\x1b.viam.common.v1.PoseInFrameR\x04pose"\xca\x01\n\rGetMapRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1b\n\tmime_type\x18\x02 \x01(\tR\x08mimeType\x12B\n\x0fcamera_position\x18\x03 \x01(\x0b2\x14.viam.common.v1.PoseH\x00R\x0ecameraPosition\x88\x01\x01\x120\n\x14include_robot_marker\x18\x04 \x01(\x08R\x12includeRobotMarkerB\x12\n\x10_camera_position"\x91\x01\n\x0eGetMapResponse\x12C\n\x0bpoint_cloud\x18\x01 \x01(\x0b2 .viam.common.v1.PointCloudObjectH\x00R\npointCloud\x12\x16\n\x05image\x18\x02 \x01(\x0cH\x00R\x05image\x12\x1b\n\tmime_type\x18\x03 \x01(\tR\x08mimeTypeB\x05\n\x03map2\xa9\x02\n\x0bSLAMService\x12\x95\x01\n\x0bGetPosition\x12(.viam.service.slam.v1.GetPositionRequest\x1a).viam.service.slam.v1.GetPositionResponse"1\x82\xd3\xe4\x93\x02+\x12)/viam/api/v1/service/slam/{name}/position\x12\x81\x01\n\x06GetMap\x12#.viam.service.slam.v1.GetMapRequest\x1a$.viam.service.slam.v1.GetMapResponse",\x82\xd3\xe4\x93\x02&\x12$/viam/api/v1/service/slam/{name}/mapB;\n\x18com.viam.service.slam.v1Z\x1fgo.viam.com/api/service/slam/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.slam.v1.slam_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x18com.viam.service.slam.v1Z\x1fgo.viam.com/api/service/slam/v1' - _SLAMSERVICE.methods_by_name['GetPosition']._options = None - _SLAMSERVICE.methods_by_name['GetPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02+\x12)/viam/api/v1/service/slam/{name}/position' - _SLAMSERVICE.methods_by_name['GetMap']._options = None - _SLAMSERVICE.methods_by_name['GetMap']._serialized_options = b'\x82\xd3\xe4\x93\x02&\x12$/viam/api/v1/service/slam/{name}/map' - _GETPOSITIONREQUEST._serialized_start = 106 - _GETPOSITIONREQUEST._serialized_end = 146 - _GETPOSITIONRESPONSE._serialized_start = 148 - _GETPOSITIONRESPONSE._serialized_end = 218 - _GETMAPREQUEST._serialized_start = 221 - _GETMAPREQUEST._serialized_end = 423 - _GETMAPRESPONSE._serialized_start = 426 - _GETMAPRESPONSE._serialized_end = 571 - _SLAMSERVICE._serialized_start = 574 - _SLAMSERVICE._serialized_end = 871 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1aservice/slam/v1/slam.proto\x12\x14viam.service.slam.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto"(\n\x12GetPositionRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"?\n\x13GetPositionResponse\x12(\n\x04pose\x18\x01 \x01(\x0b2\x14.viam.common.v1.PoseR\x04pose"t\n\x17GetPointCloudMapRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12/\n\x11return_edited_map\x18\x02 \x01(\x08H\x00R\x0freturnEditedMap\x88\x01\x01B\x14\n\x12_return_edited_map"M\n\x18GetPointCloudMapResponse\x121\n\x15point_cloud_pcd_chunk\x18\x01 \x01(\x0cR\x12pointCloudPcdChunk"-\n\x17GetInternalStateRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"L\n\x18GetInternalStateResponse\x120\n\x14internal_state_chunk\x18\x01 \x01(\x0cR\x12internalStateChunk"*\n\x14GetPropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x9a\x02\n\x15GetPropertiesResponse\x12\x1d\n\ncloud_slam\x18\x01 \x01(\x08R\tcloudSlam\x12D\n\x0cmapping_mode\x18\x02 \x01(\x0e2!.viam.service.slam.v1.MappingModeR\x0bmappingMode\x12<\n\x18internal_state_file_type\x18\x03 \x01(\tH\x00R\x15internalStateFileType\x88\x01\x01\x12A\n\x0bsensor_info\x18\x04 \x03(\x0b2 .viam.service.slam.v1.SensorInfoR\nsensorInfoB\x1b\n\x19_internal_state_file_type"V\n\nSensorInfo\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x124\n\x04type\x18\x02 \x01(\x0e2 .viam.service.slam.v1.SensorTypeR\x04type*\x92\x01\n\x0bMappingMode\x12\x1c\n\x18MAPPING_MODE_UNSPECIFIED\x10\x00\x12\x1f\n\x1bMAPPING_MODE_CREATE_NEW_MAP\x10\x01\x12\x1e\n\x1aMAPPING_MODE_LOCALIZE_ONLY\x10\x02\x12$\n MAPPING_MODE_UPDATE_EXISTING_MAP\x10\x03*b\n\nSensorType\x12\x1b\n\x17SENSOR_TYPE_UNSPECIFIED\x10\x00\x12\x16\n\x12SENSOR_TYPE_CAMERA\x10\x01\x12\x1f\n\x1bSENSOR_TYPE_MOVEMENT_SENSOR\x10\x022\xac\x06\n\x0bSLAMService\x12\x95\x01\n\x0bGetPosition\x12(.viam.service.slam.v1.GetPositionRequest\x1a).viam.service.slam.v1.GetPositionResponse"1\x82\xd3\xe4\x93\x02+\x12)/viam/api/v1/service/slam/{name}/position\x12\xad\x01\n\x10GetPointCloudMap\x12-.viam.service.slam.v1.GetPointCloudMapRequest\x1a..viam.service.slam.v1.GetPointCloudMapResponse"8\x82\xd3\xe4\x93\x022\x120/viam/api/v1/service/slam/{name}/point_cloud_map0\x01\x12\xac\x01\n\x10GetInternalState\x12-.viam.service.slam.v1.GetInternalStateRequest\x1a..viam.service.slam.v1.GetInternalStateResponse"7\x82\xd3\xe4\x93\x021\x12//viam/api/v1/service/slam/{name}/internal_state0\x01\x12\x9d\x01\n\rGetProperties\x12*.viam.service.slam.v1.GetPropertiesRequest\x1a+.viam.service.slam.v1.GetPropertiesResponse"3\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/service/slam/{name}/properties\x12\x85\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"3\x82\xd3\xe4\x93\x02-"+/viam/api/v1/service/slam/{name}/do_commandB;\n\x18com.viam.service.slam.v1Z\x1fgo.viam.com/api/service/slam/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.slam.v1.slam_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x18com.viam.service.slam.v1Z\x1fgo.viam.com/api/service/slam/v1' + _globals['_SLAMSERVICE'].methods_by_name['GetPosition']._loaded_options = None + _globals['_SLAMSERVICE'].methods_by_name['GetPosition']._serialized_options = b'\x82\xd3\xe4\x93\x02+\x12)/viam/api/v1/service/slam/{name}/position' + _globals['_SLAMSERVICE'].methods_by_name['GetPointCloudMap']._loaded_options = None + _globals['_SLAMSERVICE'].methods_by_name['GetPointCloudMap']._serialized_options = b'\x82\xd3\xe4\x93\x022\x120/viam/api/v1/service/slam/{name}/point_cloud_map' + _globals['_SLAMSERVICE'].methods_by_name['GetInternalState']._loaded_options = None + _globals['_SLAMSERVICE'].methods_by_name['GetInternalState']._serialized_options = b'\x82\xd3\xe4\x93\x021\x12//viam/api/v1/service/slam/{name}/internal_state' + _globals['_SLAMSERVICE'].methods_by_name['GetProperties']._loaded_options = None + _globals['_SLAMSERVICE'].methods_by_name['GetProperties']._serialized_options = b'\x82\xd3\xe4\x93\x02-\x12+/viam/api/v1/service/slam/{name}/properties' + _globals['_SLAMSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_SLAMSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x02-"+/viam/api/v1/service/slam/{name}/do_command' + _globals['_MAPPINGMODE']._serialized_start = 953 + _globals['_MAPPINGMODE']._serialized_end = 1099 + _globals['_SENSORTYPE']._serialized_start = 1101 + _globals['_SENSORTYPE']._serialized_end = 1199 + _globals['_GETPOSITIONREQUEST']._serialized_start = 106 + _globals['_GETPOSITIONREQUEST']._serialized_end = 146 + _globals['_GETPOSITIONRESPONSE']._serialized_start = 148 + _globals['_GETPOSITIONRESPONSE']._serialized_end = 211 + _globals['_GETPOINTCLOUDMAPREQUEST']._serialized_start = 213 + _globals['_GETPOINTCLOUDMAPREQUEST']._serialized_end = 329 + _globals['_GETPOINTCLOUDMAPRESPONSE']._serialized_start = 331 + _globals['_GETPOINTCLOUDMAPRESPONSE']._serialized_end = 408 + _globals['_GETINTERNALSTATEREQUEST']._serialized_start = 410 + _globals['_GETINTERNALSTATEREQUEST']._serialized_end = 455 + _globals['_GETINTERNALSTATERESPONSE']._serialized_start = 457 + _globals['_GETINTERNALSTATERESPONSE']._serialized_end = 533 + _globals['_GETPROPERTIESREQUEST']._serialized_start = 535 + _globals['_GETPROPERTIESREQUEST']._serialized_end = 577 + _globals['_GETPROPERTIESRESPONSE']._serialized_start = 580 + _globals['_GETPROPERTIESRESPONSE']._serialized_end = 862 + _globals['_SENSORINFO']._serialized_start = 864 + _globals['_SENSORINFO']._serialized_end = 950 + _globals['_SLAMSERVICE']._serialized_start = 1202 + _globals['_SLAMSERVICE']._serialized_end = 2014 \ No newline at end of file diff --git a/src/viam/gen/service/slam/v1/slam_pb2.pyi b/src/viam/gen/service/slam/v1/slam_pb2.pyi index dc70118f0..e28371aaa 100644 --- a/src/viam/gen/service/slam/v1/slam_pb2.pyi +++ b/src/viam/gen/service/slam/v1/slam_pb2.pyi @@ -3,16 +3,60 @@ isort:skip_file """ import builtins +import collections.abc from .... import common import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper import google.protobuf.message import sys -if sys.version_info >= (3, 8): +import typing +if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _MappingMode: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _MappingModeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_MappingMode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + MAPPING_MODE_UNSPECIFIED: _MappingMode.ValueType + MAPPING_MODE_CREATE_NEW_MAP: _MappingMode.ValueType + MAPPING_MODE_LOCALIZE_ONLY: _MappingMode.ValueType + MAPPING_MODE_UPDATE_EXISTING_MAP: _MappingMode.ValueType + +class MappingMode(_MappingMode, metaclass=_MappingModeEnumTypeWrapper): + """MappingMode represnts the various form of mapping and localizing SLAM can perform. + These include, creating a new map, localizing on an existiing map and updating an + exisiting map. + """ +MAPPING_MODE_UNSPECIFIED: MappingMode.ValueType +MAPPING_MODE_CREATE_NEW_MAP: MappingMode.ValueType +MAPPING_MODE_LOCALIZE_ONLY: MappingMode.ValueType +MAPPING_MODE_UPDATE_EXISTING_MAP: MappingMode.ValueType +global___MappingMode = MappingMode + +class _SensorType: + ValueType = typing.NewType('ValueType', builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _SensorTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_SensorType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SENSOR_TYPE_UNSPECIFIED: _SensorType.ValueType + SENSOR_TYPE_CAMERA: _SensorType.ValueType + SENSOR_TYPE_MOVEMENT_SENSOR: _SensorType.ValueType + +class SensorType(_SensorType, metaclass=_SensorTypeEnumTypeWrapper): + ... +SENSOR_TYPE_UNSPECIFIED: SensorType.ValueType +SENSOR_TYPE_CAMERA: SensorType.ValueType +SENSOR_TYPE_MOVEMENT_SENSOR: SensorType.ValueType +global___SensorType = SensorType + +@typing.final class GetPositionRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -22,82 +66,148 @@ class GetPositionRequest(google.protobuf.message.Message): def __init__(self, *, name: builtins.str=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: ... global___GetPositionRequest = GetPositionRequest +@typing.final class GetPositionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor POSE_FIELD_NUMBER: builtins.int @property - def pose(self) -> common.v1.common_pb2.PoseInFrame: - """Current position of the robot within the World frame.""" + def pose(self) -> common.v1.common_pb2.Pose: + """Current position of the specified component in the SLAM Map""" - def __init__(self, *, pose: common.v1.common_pb2.PoseInFrame | None=...) -> None: + def __init__(self, *, pose: common.v1.common_pb2.Pose | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['pose', b'pose']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['pose', b'pose']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['pose', b'pose']) -> None: + def ClearField(self, field_name: typing.Literal['pose', b'pose']) -> None: ... global___GetPositionResponse = GetPositionResponse -class GetMapRequest(google.protobuf.message.Message): +@typing.final +class GetPointCloudMapRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int - MIME_TYPE_FIELD_NUMBER: builtins.int - CAMERA_POSITION_FIELD_NUMBER: builtins.int - INCLUDE_ROBOT_MARKER_FIELD_NUMBER: builtins.int + RETURN_EDITED_MAP_FIELD_NUMBER: builtins.int name: builtins.str 'Name of slam service' - mime_type: builtins.str - 'Requested MIME type of response (image/jpeg or image/pcd)' + return_edited_map: builtins.bool + 'For SLAM services that implement handling an edited map, this boolean\n should indicate whether to return that edited map. If the SLAM service\n does not handle edited maps, the unedited map will be returned instead.\n ' - @property - def camera_position(self) -> common.v1.common_pb2.Pose: - """Optional parameter for image/jpeg mime_type, used to project point - cloud into a 2D image. - """ - include_robot_marker: builtins.bool - 'Optional parameter for image/jpeg mime_type, defaults to false.\n Tells us whether to include the robot position on the 2D image.\n ' + def __init__(self, *, name: builtins.str=..., return_edited_map: builtins.bool | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_return_edited_map', b'_return_edited_map', 'return_edited_map', b'return_edited_map']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['_return_edited_map', b'_return_edited_map', 'name', b'name', 'return_edited_map', b'return_edited_map']) -> None: + ... + + def WhichOneof(self, oneof_group: typing.Literal['_return_edited_map', b'_return_edited_map']) -> typing.Literal['return_edited_map'] | None: + ... +global___GetPointCloudMapRequest = GetPointCloudMapRequest + +@typing.final +class GetPointCloudMapResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + POINT_CLOUD_PCD_CHUNK_FIELD_NUMBER: builtins.int + point_cloud_pcd_chunk: builtins.bytes + 'One chunk of the PointCloud.\n For a given GetPointCloudMap request, concatenating all\n GetPointCloudMapResponse.point_cloud_pcd_chunk values in the\n order received result in the complete pointcloud in standard PCD\n format where XY is the ground plane and positive Z is up, following\n the Right Hand Rule.\n\n Read more about the pointcloud format here:\n https://pointclouds.org/documentation/tutorials/pcd_file_format.html\n\n Viam expects pointcloud data with fields "x y z" or "x y z rgb", and for\n this to be specified in the pointcloud header in the FIELDS entry. If color\n data is included in the pointcloud, Viam\'s services assume that the color\n value encodes a confidence score for that data point. Viam expects the\n confidence score to be encoded in the blue parameter of the RGB value, on a\n scale from 1-100.\n\n Pointclouds are little endian encoded.\n ' - def __init__(self, *, name: builtins.str=..., mime_type: builtins.str=..., camera_position: common.v1.common_pb2.Pose | None=..., include_robot_marker: builtins.bool=...) -> None: + def __init__(self, *, point_cloud_pcd_chunk: builtins.bytes=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['_camera_position', b'_camera_position', 'camera_position', b'camera_position']) -> builtins.bool: + def ClearField(self, field_name: typing.Literal['point_cloud_pcd_chunk', b'point_cloud_pcd_chunk']) -> None: ... +global___GetPointCloudMapResponse = GetPointCloudMapResponse - def ClearField(self, field_name: typing_extensions.Literal['_camera_position', b'_camera_position', 'camera_position', b'camera_position', 'include_robot_marker', b'include_robot_marker', 'mime_type', b'mime_type', 'name', b'name']) -> None: +@typing.final +class GetInternalStateRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of slam service' + + def __init__(self, *, name: builtins.str=...) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['_camera_position', b'_camera_position']) -> typing_extensions.Literal['camera_position'] | None: + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: ... -global___GetMapRequest = GetMapRequest +global___GetInternalStateRequest = GetInternalStateRequest -class GetMapResponse(google.protobuf.message.Message): +@typing.final +class GetInternalStateResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - POINT_CLOUD_FIELD_NUMBER: builtins.int - IMAGE_FIELD_NUMBER: builtins.int - MIME_TYPE_FIELD_NUMBER: builtins.int + INTERNAL_STATE_CHUNK_FIELD_NUMBER: builtins.int + internal_state_chunk: builtins.bytes + 'Chunk of the internal state of the SLAM algorithm required to continue\n mapping/localization\n ' + + def __init__(self, *, internal_state_chunk: builtins.bytes=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['internal_state_chunk', b'internal_state_chunk']) -> None: + ... +global___GetInternalStateResponse = GetInternalStateResponse + +@typing.final +class GetPropertiesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + 'Name of the slam service' + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___GetPropertiesRequest = GetPropertiesRequest + +@typing.final +class GetPropertiesResponse(google.protobuf.message.Message): + """Returns properties information for the named slam service""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CLOUD_SLAM_FIELD_NUMBER: builtins.int + MAPPING_MODE_FIELD_NUMBER: builtins.int + INTERNAL_STATE_FILE_TYPE_FIELD_NUMBER: builtins.int + SENSOR_INFO_FIELD_NUMBER: builtins.int + cloud_slam: builtins.bool + mapping_mode: global___MappingMode.ValueType + internal_state_file_type: builtins.str @property - def point_cloud(self) -> common.v1.common_pb2.PointCloudObject: + def sensor_info(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___SensorInfo]: + ... + + def __init__(self, *, cloud_slam: builtins.bool=..., mapping_mode: global___MappingMode.ValueType=..., internal_state_file_type: builtins.str | None=..., sensor_info: collections.abc.Iterable[global___SensorInfo] | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['_internal_state_file_type', b'_internal_state_file_type', 'internal_state_file_type', b'internal_state_file_type']) -> builtins.bool: ... - image: builtins.bytes - mime_type: builtins.str - 'Actual MIME type of response (image/jpeg or image/pcd)' - def __init__(self, *, point_cloud: common.v1.common_pb2.PointCloudObject | None=..., image: builtins.bytes=..., mime_type: builtins.str=...) -> None: + def ClearField(self, field_name: typing.Literal['_internal_state_file_type', b'_internal_state_file_type', 'cloud_slam', b'cloud_slam', 'internal_state_file_type', b'internal_state_file_type', 'mapping_mode', b'mapping_mode', 'sensor_info', b'sensor_info']) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['image', b'image', 'map', b'map', 'point_cloud', b'point_cloud']) -> builtins.bool: + def WhichOneof(self, oneof_group: typing.Literal['_internal_state_file_type', b'_internal_state_file_type']) -> typing.Literal['internal_state_file_type'] | None: ... +global___GetPropertiesResponse = GetPropertiesResponse + +@typing.final +class SensorInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + name: builtins.str + type: global___SensorType.ValueType - def ClearField(self, field_name: typing_extensions.Literal['image', b'image', 'map', b'map', 'mime_type', b'mime_type', 'point_cloud', b'point_cloud']) -> None: + def __init__(self, *, name: builtins.str=..., type: global___SensorType.ValueType=...) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal['map', b'map']) -> typing_extensions.Literal['point_cloud', 'image'] | None: + def ClearField(self, field_name: typing.Literal['name', b'name', 'type', b'type']) -> None: ... -global___GetMapResponse = GetMapResponse \ No newline at end of file +global___SensorInfo = SensorInfo \ No newline at end of file diff --git a/src/viam/gen/service/vision/v1/vision_grpc.py b/src/viam/gen/service/vision/v1/vision_grpc.py index d3f922c39..474d1ae2e 100644 --- a/src/viam/gen/service/vision/v1/vision_grpc.py +++ b/src/viam/gen/service/vision/v1/vision_grpc.py @@ -2,9 +2,11 @@ import typing import grpclib.const import grpclib.client +import grpclib.exceptions if typing.TYPE_CHECKING: import grpclib.server from .... import common +from .... import component import google.api.annotations_pb2 import google.protobuf.struct_pb2 from .... import service @@ -12,83 +14,74 @@ class VisionServiceBase(abc.ABC): @abc.abstractmethod - async def GetModelParameterSchema(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetModelParameterSchemaRequest, service.vision.v1.vision_pb2.GetModelParameterSchemaResponse]') -> None: + async def GetDetectionsFromCamera(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetDetectionsFromCameraRequest, service.vision.v1.vision_pb2.GetDetectionsFromCameraResponse]') -> None: pass @abc.abstractmethod - async def GetDetectorNames(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetDetectorNamesRequest, service.vision.v1.vision_pb2.GetDetectorNamesResponse]') -> None: + async def GetDetections(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetDetectionsRequest, service.vision.v1.vision_pb2.GetDetectionsResponse]') -> None: pass @abc.abstractmethod - async def AddDetector(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.AddDetectorRequest, service.vision.v1.vision_pb2.AddDetectorResponse]') -> None: + async def GetClassificationsFromCamera(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetClassificationsFromCameraRequest, service.vision.v1.vision_pb2.GetClassificationsFromCameraResponse]') -> None: pass @abc.abstractmethod - async def RemoveDetector(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.RemoveDetectorRequest, service.vision.v1.vision_pb2.RemoveDetectorResponse]') -> None: + async def GetClassifications(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetClassificationsRequest, service.vision.v1.vision_pb2.GetClassificationsResponse]') -> None: pass @abc.abstractmethod - async def GetDetectionsFromCamera(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetDetectionsFromCameraRequest, service.vision.v1.vision_pb2.GetDetectionsFromCameraResponse]') -> None: + async def GetObjectPointClouds(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetObjectPointCloudsRequest, service.vision.v1.vision_pb2.GetObjectPointCloudsResponse]') -> None: pass @abc.abstractmethod - async def GetDetections(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetDetectionsRequest, service.vision.v1.vision_pb2.GetDetectionsResponse]') -> None: + async def GetProperties(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetPropertiesRequest, service.vision.v1.vision_pb2.GetPropertiesResponse]') -> None: pass @abc.abstractmethod - async def GetClassifierNames(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetClassifierNamesRequest, service.vision.v1.vision_pb2.GetClassifierNamesResponse]') -> None: + async def CaptureAllFromCamera(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.CaptureAllFromCameraRequest, service.vision.v1.vision_pb2.CaptureAllFromCameraResponse]') -> None: pass @abc.abstractmethod - async def AddClassifier(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.AddClassifierRequest, service.vision.v1.vision_pb2.AddClassifierResponse]') -> None: + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: pass - @abc.abstractmethod - async def RemoveClassifier(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.RemoveClassifierRequest, service.vision.v1.vision_pb2.RemoveClassifierResponse]') -> None: - pass + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/viam.service.vision.v1.VisionService/GetDetectionsFromCamera': grpclib.const.Handler(self.GetDetectionsFromCamera, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetDetectionsFromCameraRequest, service.vision.v1.vision_pb2.GetDetectionsFromCameraResponse), '/viam.service.vision.v1.VisionService/GetDetections': grpclib.const.Handler(self.GetDetections, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetDetectionsRequest, service.vision.v1.vision_pb2.GetDetectionsResponse), '/viam.service.vision.v1.VisionService/GetClassificationsFromCamera': grpclib.const.Handler(self.GetClassificationsFromCamera, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetClassificationsFromCameraRequest, service.vision.v1.vision_pb2.GetClassificationsFromCameraResponse), '/viam.service.vision.v1.VisionService/GetClassifications': grpclib.const.Handler(self.GetClassifications, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetClassificationsRequest, service.vision.v1.vision_pb2.GetClassificationsResponse), '/viam.service.vision.v1.VisionService/GetObjectPointClouds': grpclib.const.Handler(self.GetObjectPointClouds, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetObjectPointCloudsRequest, service.vision.v1.vision_pb2.GetObjectPointCloudsResponse), '/viam.service.vision.v1.VisionService/GetProperties': grpclib.const.Handler(self.GetProperties, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetPropertiesRequest, service.vision.v1.vision_pb2.GetPropertiesResponse), '/viam.service.vision.v1.VisionService/CaptureAllFromCamera': grpclib.const.Handler(self.CaptureAllFromCamera, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.CaptureAllFromCameraRequest, service.vision.v1.vision_pb2.CaptureAllFromCameraResponse), '/viam.service.vision.v1.VisionService/DoCommand': grpclib.const.Handler(self.DoCommand, grpclib.const.Cardinality.UNARY_UNARY, common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse)} - @abc.abstractmethod - async def GetClassificationsFromCamera(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetClassificationsFromCameraRequest, service.vision.v1.vision_pb2.GetClassificationsFromCameraResponse]') -> None: - pass +class UnimplementedVisionServiceBase(VisionServiceBase): - @abc.abstractmethod - async def GetClassifications(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetClassificationsRequest, service.vision.v1.vision_pb2.GetClassificationsResponse]') -> None: - pass + async def GetDetectionsFromCamera(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetDetectionsFromCameraRequest, service.vision.v1.vision_pb2.GetDetectionsFromCameraResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) - @abc.abstractmethod - async def GetSegmenterNames(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetSegmenterNamesRequest, service.vision.v1.vision_pb2.GetSegmenterNamesResponse]') -> None: - pass + async def GetDetections(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetDetectionsRequest, service.vision.v1.vision_pb2.GetDetectionsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) - @abc.abstractmethod - async def AddSegmenter(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.AddSegmenterRequest, service.vision.v1.vision_pb2.AddSegmenterResponse]') -> None: - pass + async def GetClassificationsFromCamera(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetClassificationsFromCameraRequest, service.vision.v1.vision_pb2.GetClassificationsFromCameraResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) - @abc.abstractmethod - async def RemoveSegmenter(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.RemoveSegmenterRequest, service.vision.v1.vision_pb2.RemoveSegmenterResponse]') -> None: - pass + async def GetClassifications(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetClassificationsRequest, service.vision.v1.vision_pb2.GetClassificationsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) - @abc.abstractmethod async def GetObjectPointClouds(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetObjectPointCloudsRequest, service.vision.v1.vision_pb2.GetObjectPointCloudsResponse]') -> None: - pass + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) - def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: - return {'/viam.service.vision.v1.VisionService/GetModelParameterSchema': grpclib.const.Handler(self.GetModelParameterSchema, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetModelParameterSchemaRequest, service.vision.v1.vision_pb2.GetModelParameterSchemaResponse), '/viam.service.vision.v1.VisionService/GetDetectorNames': grpclib.const.Handler(self.GetDetectorNames, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetDetectorNamesRequest, service.vision.v1.vision_pb2.GetDetectorNamesResponse), '/viam.service.vision.v1.VisionService/AddDetector': grpclib.const.Handler(self.AddDetector, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.AddDetectorRequest, service.vision.v1.vision_pb2.AddDetectorResponse), '/viam.service.vision.v1.VisionService/RemoveDetector': grpclib.const.Handler(self.RemoveDetector, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.RemoveDetectorRequest, service.vision.v1.vision_pb2.RemoveDetectorResponse), '/viam.service.vision.v1.VisionService/GetDetectionsFromCamera': grpclib.const.Handler(self.GetDetectionsFromCamera, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetDetectionsFromCameraRequest, service.vision.v1.vision_pb2.GetDetectionsFromCameraResponse), '/viam.service.vision.v1.VisionService/GetDetections': grpclib.const.Handler(self.GetDetections, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetDetectionsRequest, service.vision.v1.vision_pb2.GetDetectionsResponse), '/viam.service.vision.v1.VisionService/GetClassifierNames': grpclib.const.Handler(self.GetClassifierNames, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetClassifierNamesRequest, service.vision.v1.vision_pb2.GetClassifierNamesResponse), '/viam.service.vision.v1.VisionService/AddClassifier': grpclib.const.Handler(self.AddClassifier, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.AddClassifierRequest, service.vision.v1.vision_pb2.AddClassifierResponse), '/viam.service.vision.v1.VisionService/RemoveClassifier': grpclib.const.Handler(self.RemoveClassifier, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.RemoveClassifierRequest, service.vision.v1.vision_pb2.RemoveClassifierResponse), '/viam.service.vision.v1.VisionService/GetClassificationsFromCamera': grpclib.const.Handler(self.GetClassificationsFromCamera, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetClassificationsFromCameraRequest, service.vision.v1.vision_pb2.GetClassificationsFromCameraResponse), '/viam.service.vision.v1.VisionService/GetClassifications': grpclib.const.Handler(self.GetClassifications, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetClassificationsRequest, service.vision.v1.vision_pb2.GetClassificationsResponse), '/viam.service.vision.v1.VisionService/GetSegmenterNames': grpclib.const.Handler(self.GetSegmenterNames, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetSegmenterNamesRequest, service.vision.v1.vision_pb2.GetSegmenterNamesResponse), '/viam.service.vision.v1.VisionService/AddSegmenter': grpclib.const.Handler(self.AddSegmenter, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.AddSegmenterRequest, service.vision.v1.vision_pb2.AddSegmenterResponse), '/viam.service.vision.v1.VisionService/RemoveSegmenter': grpclib.const.Handler(self.RemoveSegmenter, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.RemoveSegmenterRequest, service.vision.v1.vision_pb2.RemoveSegmenterResponse), '/viam.service.vision.v1.VisionService/GetObjectPointClouds': grpclib.const.Handler(self.GetObjectPointClouds, grpclib.const.Cardinality.UNARY_UNARY, service.vision.v1.vision_pb2.GetObjectPointCloudsRequest, service.vision.v1.vision_pb2.GetObjectPointCloudsResponse)} + async def GetProperties(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.GetPropertiesRequest, service.vision.v1.vision_pb2.GetPropertiesResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def CaptureAllFromCamera(self, stream: 'grpclib.server.Stream[service.vision.v1.vision_pb2.CaptureAllFromCameraRequest, service.vision.v1.vision_pb2.CaptureAllFromCameraResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def DoCommand(self, stream: 'grpclib.server.Stream[common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) class VisionServiceStub: def __init__(self, channel: grpclib.client.Channel) -> None: - self.GetModelParameterSchema = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetModelParameterSchema', service.vision.v1.vision_pb2.GetModelParameterSchemaRequest, service.vision.v1.vision_pb2.GetModelParameterSchemaResponse) - self.GetDetectorNames = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetDetectorNames', service.vision.v1.vision_pb2.GetDetectorNamesRequest, service.vision.v1.vision_pb2.GetDetectorNamesResponse) - self.AddDetector = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/AddDetector', service.vision.v1.vision_pb2.AddDetectorRequest, service.vision.v1.vision_pb2.AddDetectorResponse) - self.RemoveDetector = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/RemoveDetector', service.vision.v1.vision_pb2.RemoveDetectorRequest, service.vision.v1.vision_pb2.RemoveDetectorResponse) self.GetDetectionsFromCamera = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetDetectionsFromCamera', service.vision.v1.vision_pb2.GetDetectionsFromCameraRequest, service.vision.v1.vision_pb2.GetDetectionsFromCameraResponse) self.GetDetections = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetDetections', service.vision.v1.vision_pb2.GetDetectionsRequest, service.vision.v1.vision_pb2.GetDetectionsResponse) - self.GetClassifierNames = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetClassifierNames', service.vision.v1.vision_pb2.GetClassifierNamesRequest, service.vision.v1.vision_pb2.GetClassifierNamesResponse) - self.AddClassifier = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/AddClassifier', service.vision.v1.vision_pb2.AddClassifierRequest, service.vision.v1.vision_pb2.AddClassifierResponse) - self.RemoveClassifier = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/RemoveClassifier', service.vision.v1.vision_pb2.RemoveClassifierRequest, service.vision.v1.vision_pb2.RemoveClassifierResponse) self.GetClassificationsFromCamera = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetClassificationsFromCamera', service.vision.v1.vision_pb2.GetClassificationsFromCameraRequest, service.vision.v1.vision_pb2.GetClassificationsFromCameraResponse) self.GetClassifications = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetClassifications', service.vision.v1.vision_pb2.GetClassificationsRequest, service.vision.v1.vision_pb2.GetClassificationsResponse) - self.GetSegmenterNames = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetSegmenterNames', service.vision.v1.vision_pb2.GetSegmenterNamesRequest, service.vision.v1.vision_pb2.GetSegmenterNamesResponse) - self.AddSegmenter = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/AddSegmenter', service.vision.v1.vision_pb2.AddSegmenterRequest, service.vision.v1.vision_pb2.AddSegmenterResponse) - self.RemoveSegmenter = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/RemoveSegmenter', service.vision.v1.vision_pb2.RemoveSegmenterRequest, service.vision.v1.vision_pb2.RemoveSegmenterResponse) - self.GetObjectPointClouds = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetObjectPointClouds', service.vision.v1.vision_pb2.GetObjectPointCloudsRequest, service.vision.v1.vision_pb2.GetObjectPointCloudsResponse) \ No newline at end of file + self.GetObjectPointClouds = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetObjectPointClouds', service.vision.v1.vision_pb2.GetObjectPointCloudsRequest, service.vision.v1.vision_pb2.GetObjectPointCloudsResponse) + self.GetProperties = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/GetProperties', service.vision.v1.vision_pb2.GetPropertiesRequest, service.vision.v1.vision_pb2.GetPropertiesResponse) + self.CaptureAllFromCamera = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/CaptureAllFromCamera', service.vision.v1.vision_pb2.CaptureAllFromCameraRequest, service.vision.v1.vision_pb2.CaptureAllFromCameraResponse) + self.DoCommand = grpclib.client.UnaryUnaryMethod(channel, '/viam.service.vision.v1.VisionService/DoCommand', common.v1.common_pb2.DoCommandRequest, common.v1.common_pb2.DoCommandResponse) \ No newline at end of file diff --git a/src/viam/gen/service/vision/v1/vision_pb2.py b/src/viam/gen/service/vision/v1/vision_pb2.py index 6c367f6a4..a6fc4c074 100644 --- a/src/viam/gen/service/vision/v1/vision_pb2.py +++ b/src/viam/gen/service/vision/v1/vision_pb2.py @@ -1,111 +1,69 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'service/vision/v1/vision.proto') _sym_db = _symbol_database.Default() from ....common.v1 import common_pb2 as common_dot_v1_dot_common__pb2 +from ....component.camera.v1 import camera_pb2 as component_dot_camera_dot_v1_dot_camera__pb2 from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eservice/vision/v1/vision.proto\x12\x16viam.service.vision.v1\x1a\x16common/v1/common.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"S\n\x1eGetModelParameterSchemaRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n\nmodel_type\x18\x02 \x01(\tR\tmodelType"W\n\x1fGetModelParameterSchemaResponse\x124\n\x16model_parameter_schema\x18\x01 \x01(\x0cR\x14modelParameterSchema"-\n\x17GetDetectorNamesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"A\n\x18GetDetectorNamesResponse\x12%\n\x0edetector_names\x18\x01 \x03(\tR\rdetectorNames"\xc7\x01\n\x12AddDetectorRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12#\n\rdetector_name\x18\x02 \x01(\tR\x0cdetectorName\x12.\n\x13detector_model_type\x18\x03 \x01(\tR\x11detectorModelType\x12H\n\x13detector_parameters\x18\x04 \x01(\x0b2\x17.google.protobuf.StructR\x12detectorParameters"\x15\n\x13AddDetectorResponse"P\n\x15RemoveDetectorRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12#\n\rdetector_name\x18\x02 \x01(\tR\x0cdetectorName"\x18\n\x16RemoveDetectorResponse"\xb0\x01\n\x14GetDetectionsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n\x05image\x18\x02 \x01(\x0cR\x05image\x12\x14\n\x05width\x18\x03 \x01(\x03R\x05width\x12\x16\n\x06height\x18\x04 \x01(\x03R\x06height\x12\x1b\n\tmime_type\x18\x05 \x01(\tR\x08mimeType\x12#\n\rdetector_name\x18\x06 \x01(\tR\x0cdetectorName"Z\n\x15GetDetectionsResponse\x12A\n\ndetections\x18\x01 \x03(\x0b2!.viam.service.vision.v1.DetectionR\ndetections"z\n\x1eGetDetectionsFromCameraRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n\x0bcamera_name\x18\x02 \x01(\tR\ncameraName\x12#\n\rdetector_name\x18\x03 \x01(\tR\x0cdetectorName"d\n\x1fGetDetectionsFromCameraResponse\x12A\n\ndetections\x18\x01 \x03(\x0b2!.viam.service.vision.v1.DetectionR\ndetections"\xda\x01\n\tDetection\x12\x18\n\x05x_min\x18\x01 \x01(\x03H\x00R\x04xMin\x88\x01\x01\x12\x18\n\x05y_min\x18\x02 \x01(\x03H\x01R\x04yMin\x88\x01\x01\x12\x18\n\x05x_max\x18\x03 \x01(\x03H\x02R\x04xMax\x88\x01\x01\x12\x18\n\x05y_max\x18\x04 \x01(\x03H\x03R\x04yMax\x88\x01\x01\x12\x1e\n\nconfidence\x18\x05 \x01(\x01R\nconfidence\x12\x1d\n\nclass_name\x18\x06 \x01(\tR\tclassNameB\x08\n\x06_x_minB\x08\n\x06_y_minB\x08\n\x06_x_maxB\x08\n\x06_y_max"/\n\x19GetClassifierNamesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"G\n\x1aGetClassifierNamesResponse\x12)\n\x10classifier_names\x18\x01 \x03(\tR\x0fclassifierNames"\xd5\x01\n\x14AddClassifierRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\'\n\x0fclassifier_name\x18\x02 \x01(\tR\x0eclassifierName\x122\n\x15classifier_model_type\x18\x03 \x01(\tR\x13classifierModelType\x12L\n\x15classifier_parameters\x18\x04 \x01(\x0b2\x17.google.protobuf.StructR\x14classifierParameters"\x17\n\x15AddClassifierResponse"V\n\x17RemoveClassifierRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\'\n\x0fclassifier_name\x18\x02 \x01(\tR\x0eclassifierName"\x1a\n\x18RemoveClassifierResponse"\xc7\x01\n\x19GetClassificationsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n\x05image\x18\x02 \x01(\x0cR\x05image\x12\x14\n\x05width\x18\x03 \x01(\x05R\x05width\x12\x16\n\x06height\x18\x04 \x01(\x05R\x06height\x12\x1b\n\tmime_type\x18\x05 \x01(\tR\x08mimeType\x12\'\n\x0fclassifier_name\x18\x06 \x01(\tR\x0eclassifierName\x12\x0c\n\x01n\x18\x07 \x01(\x05R\x01n"n\n\x1aGetClassificationsResponse\x12P\n\x0fclassifications\x18\x01 \x03(\x0b2&.viam.service.vision.v1.ClassificationR\x0fclassifications"\x91\x01\n#GetClassificationsFromCameraRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n\x0bcamera_name\x18\x02 \x01(\tR\ncameraName\x12\'\n\x0fclassifier_name\x18\x03 \x01(\tR\x0eclassifierName\x12\x0c\n\x01n\x18\x04 \x01(\x05R\x01n"x\n$GetClassificationsFromCameraResponse\x12P\n\x0fclassifications\x18\x01 \x03(\x0b2&.viam.service.vision.v1.ClassificationR\x0fclassifications"O\n\x0eClassification\x12\x1d\n\nclass_name\x18\x01 \x01(\tR\tclassName\x12\x1e\n\nconfidence\x18\x02 \x01(\x01R\nconfidence".\n\x18GetSegmenterNamesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"D\n\x19GetSegmenterNamesResponse\x12\'\n\x0fsegmenter_names\x18\x01 \x03(\tR\x0esegmenterNames"\xce\x01\n\x13AddSegmenterRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12%\n\x0esegmenter_name\x18\x02 \x01(\tR\rsegmenterName\x120\n\x14segmenter_model_type\x18\x03 \x01(\tR\x12segmenterModelType\x12J\n\x14segmenter_parameters\x18\x04 \x01(\x0b2\x17.google.protobuf.StructR\x13segmenterParameters"\x16\n\x14AddSegmenterResponse"S\n\x16RemoveSegmenterRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12%\n\x0esegmenter_name\x18\x02 \x01(\tR\rsegmenterName"\x19\n\x17RemoveSegmenterResponse"\x96\x01\n\x1bGetObjectPointCloudsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n\x0bcamera_name\x18\x02 \x01(\tR\ncameraName\x12%\n\x0esegmenter_name\x18\x03 \x01(\tR\rsegmenterName\x12\x1b\n\tmime_type\x18\x04 \x01(\tR\x08mimeType"w\n\x1cGetObjectPointCloudsResponse\x12\x1b\n\tmime_type\x18\x01 \x01(\tR\x08mimeType\x12:\n\x07objects\x18\x02 \x03(\x0b2 .viam.common.v1.PointCloudObjectR\x07objects2\xda\x15\n\rVisionService\x12\xcd\x01\n\x17GetModelParameterSchema\x126.viam.service.vision.v1.GetModelParameterSchemaRequest\x1a7.viam.service.vision.v1.GetModelParameterSchemaResponse"A\x82\xd3\xe4\x93\x02;\x129/viam/api/v1/service/vision/{name}/model_parameter_schema\x12\xb0\x01\n\x10GetDetectorNames\x12/.viam.service.vision.v1.GetDetectorNamesRequest\x1a0.viam.service.vision.v1.GetDetectorNamesResponse"9\x82\xd3\xe4\x93\x023\x121/viam/api/v1/service/vision/{name}/detector_names\x12\x9f\x01\n\x0bAddDetector\x12*.viam.service.vision.v1.AddDetectorRequest\x1a+.viam.service.vision.v1.AddDetectorResponse"7\x82\xd3\xe4\x93\x021"//viam/api/v1/service/vision/{name}/add_detector\x12\xab\x01\n\x0eRemoveDetector\x12-.viam.service.vision.v1.RemoveDetectorRequest\x1a..viam.service.vision.v1.RemoveDetectorResponse":\x82\xd3\xe4\x93\x024"2/viam/api/v1/service/vision/{name}/remove_detector\x12\xc8\x01\n\x17GetDetectionsFromCamera\x126.viam.service.vision.v1.GetDetectionsFromCameraRequest\x1a7.viam.service.vision.v1.GetDetectionsFromCameraResponse"<\x82\xd3\xe4\x93\x026"4/viam/api/v1/service/vision/{name}/camera_detections\x12\xa3\x01\n\rGetDetections\x12,.viam.service.vision.v1.GetDetectionsRequest\x1a-.viam.service.vision.v1.GetDetectionsResponse"5\x82\xd3\xe4\x93\x02/"-/viam/api/v1/service/vision/{name}/detections\x12\xb8\x01\n\x12GetClassifierNames\x121.viam.service.vision.v1.GetClassifierNamesRequest\x1a2.viam.service.vision.v1.GetClassifierNamesResponse";\x82\xd3\xe4\x93\x025\x123/viam/api/v1/service/vision/{name}/classifier_names\x12\xa7\x01\n\rAddClassifier\x12,.viam.service.vision.v1.AddClassifierRequest\x1a-.viam.service.vision.v1.AddClassifierResponse"9\x82\xd3\xe4\x93\x023"1/viam/api/v1/service/vision/{name}/add_classifier\x12\xb3\x01\n\x10RemoveClassifier\x12/.viam.service.vision.v1.RemoveClassifierRequest\x1a0.viam.service.vision.v1.RemoveClassifierResponse"<\x82\xd3\xe4\x93\x026"4/viam/api/v1/service/vision/{name}/remove_classifier\x12\xdc\x01\n\x1cGetClassificationsFromCamera\x12;.viam.service.vision.v1.GetClassificationsFromCameraRequest\x1a<.viam.service.vision.v1.GetClassificationsFromCameraResponse"A\x82\xd3\xe4\x93\x02;"9/viam/api/v1/service/vision/{name}/camera_classifications\x12\xb7\x01\n\x12GetClassifications\x121.viam.service.vision.v1.GetClassificationsRequest\x1a2.viam.service.vision.v1.GetClassificationsResponse":\x82\xd3\xe4\x93\x024"2/viam/api/v1/service/vision/{name}/classifications\x12\xb4\x01\n\x11GetSegmenterNames\x120.viam.service.vision.v1.GetSegmenterNamesRequest\x1a1.viam.service.vision.v1.GetSegmenterNamesResponse":\x82\xd3\xe4\x93\x024\x122/viam/api/v1/service/vision/{name}/segmenter_names\x12\xa3\x01\n\x0cAddSegmenter\x12+.viam.service.vision.v1.AddSegmenterRequest\x1a,.viam.service.vision.v1.AddSegmenterResponse"8\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/vision/{name}/add_segmenter\x12\xaf\x01\n\x0fRemoveSegmenter\x12..viam.service.vision.v1.RemoveSegmenterRequest\x1a/.viam.service.vision.v1.RemoveSegmenterResponse";\x82\xd3\xe4\x93\x025"3/viam/api/v1/service/vision/{name}/remove_segmenter\x12\xc1\x01\n\x14GetObjectPointClouds\x123.viam.service.vision.v1.GetObjectPointCloudsRequest\x1a4.viam.service.vision.v1.GetObjectPointCloudsResponse">\x82\xd3\xe4\x93\x028"6/viam/api/v1/service/vision/{name}/object_point_cloudsB?\n\x1acom.viam.service.vision.v1Z!go.viam.com/api/service/vision/v1b\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.vision.v1.vision_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'\n\x1acom.viam.service.vision.v1Z!go.viam.com/api/service/vision/v1' - _VISIONSERVICE.methods_by_name['GetModelParameterSchema']._options = None - _VISIONSERVICE.methods_by_name['GetModelParameterSchema']._serialized_options = b'\x82\xd3\xe4\x93\x02;\x129/viam/api/v1/service/vision/{name}/model_parameter_schema' - _VISIONSERVICE.methods_by_name['GetDetectorNames']._options = None - _VISIONSERVICE.methods_by_name['GetDetectorNames']._serialized_options = b'\x82\xd3\xe4\x93\x023\x121/viam/api/v1/service/vision/{name}/detector_names' - _VISIONSERVICE.methods_by_name['AddDetector']._options = None - _VISIONSERVICE.methods_by_name['AddDetector']._serialized_options = b'\x82\xd3\xe4\x93\x021"//viam/api/v1/service/vision/{name}/add_detector' - _VISIONSERVICE.methods_by_name['RemoveDetector']._options = None - _VISIONSERVICE.methods_by_name['RemoveDetector']._serialized_options = b'\x82\xd3\xe4\x93\x024"2/viam/api/v1/service/vision/{name}/remove_detector' - _VISIONSERVICE.methods_by_name['GetDetectionsFromCamera']._options = None - _VISIONSERVICE.methods_by_name['GetDetectionsFromCamera']._serialized_options = b'\x82\xd3\xe4\x93\x026"4/viam/api/v1/service/vision/{name}/camera_detections' - _VISIONSERVICE.methods_by_name['GetDetections']._options = None - _VISIONSERVICE.methods_by_name['GetDetections']._serialized_options = b'\x82\xd3\xe4\x93\x02/"-/viam/api/v1/service/vision/{name}/detections' - _VISIONSERVICE.methods_by_name['GetClassifierNames']._options = None - _VISIONSERVICE.methods_by_name['GetClassifierNames']._serialized_options = b'\x82\xd3\xe4\x93\x025\x123/viam/api/v1/service/vision/{name}/classifier_names' - _VISIONSERVICE.methods_by_name['AddClassifier']._options = None - _VISIONSERVICE.methods_by_name['AddClassifier']._serialized_options = b'\x82\xd3\xe4\x93\x023"1/viam/api/v1/service/vision/{name}/add_classifier' - _VISIONSERVICE.methods_by_name['RemoveClassifier']._options = None - _VISIONSERVICE.methods_by_name['RemoveClassifier']._serialized_options = b'\x82\xd3\xe4\x93\x026"4/viam/api/v1/service/vision/{name}/remove_classifier' - _VISIONSERVICE.methods_by_name['GetClassificationsFromCamera']._options = None - _VISIONSERVICE.methods_by_name['GetClassificationsFromCamera']._serialized_options = b'\x82\xd3\xe4\x93\x02;"9/viam/api/v1/service/vision/{name}/camera_classifications' - _VISIONSERVICE.methods_by_name['GetClassifications']._options = None - _VISIONSERVICE.methods_by_name['GetClassifications']._serialized_options = b'\x82\xd3\xe4\x93\x024"2/viam/api/v1/service/vision/{name}/classifications' - _VISIONSERVICE.methods_by_name['GetSegmenterNames']._options = None - _VISIONSERVICE.methods_by_name['GetSegmenterNames']._serialized_options = b'\x82\xd3\xe4\x93\x024\x122/viam/api/v1/service/vision/{name}/segmenter_names' - _VISIONSERVICE.methods_by_name['AddSegmenter']._options = None - _VISIONSERVICE.methods_by_name['AddSegmenter']._serialized_options = b'\x82\xd3\xe4\x93\x022"0/viam/api/v1/service/vision/{name}/add_segmenter' - _VISIONSERVICE.methods_by_name['RemoveSegmenter']._options = None - _VISIONSERVICE.methods_by_name['RemoveSegmenter']._serialized_options = b'\x82\xd3\xe4\x93\x025"3/viam/api/v1/service/vision/{name}/remove_segmenter' - _VISIONSERVICE.methods_by_name['GetObjectPointClouds']._options = None - _VISIONSERVICE.methods_by_name['GetObjectPointClouds']._serialized_options = b'\x82\xd3\xe4\x93\x028"6/viam/api/v1/service/vision/{name}/object_point_clouds' - _GETMODELPARAMETERSCHEMAREQUEST._serialized_start = 142 - _GETMODELPARAMETERSCHEMAREQUEST._serialized_end = 225 - _GETMODELPARAMETERSCHEMARESPONSE._serialized_start = 227 - _GETMODELPARAMETERSCHEMARESPONSE._serialized_end = 314 - _GETDETECTORNAMESREQUEST._serialized_start = 316 - _GETDETECTORNAMESREQUEST._serialized_end = 361 - _GETDETECTORNAMESRESPONSE._serialized_start = 363 - _GETDETECTORNAMESRESPONSE._serialized_end = 428 - _ADDDETECTORREQUEST._serialized_start = 431 - _ADDDETECTORREQUEST._serialized_end = 630 - _ADDDETECTORRESPONSE._serialized_start = 632 - _ADDDETECTORRESPONSE._serialized_end = 653 - _REMOVEDETECTORREQUEST._serialized_start = 655 - _REMOVEDETECTORREQUEST._serialized_end = 735 - _REMOVEDETECTORRESPONSE._serialized_start = 737 - _REMOVEDETECTORRESPONSE._serialized_end = 761 - _GETDETECTIONSREQUEST._serialized_start = 764 - _GETDETECTIONSREQUEST._serialized_end = 940 - _GETDETECTIONSRESPONSE._serialized_start = 942 - _GETDETECTIONSRESPONSE._serialized_end = 1032 - _GETDETECTIONSFROMCAMERAREQUEST._serialized_start = 1034 - _GETDETECTIONSFROMCAMERAREQUEST._serialized_end = 1156 - _GETDETECTIONSFROMCAMERARESPONSE._serialized_start = 1158 - _GETDETECTIONSFROMCAMERARESPONSE._serialized_end = 1258 - _DETECTION._serialized_start = 1261 - _DETECTION._serialized_end = 1479 - _GETCLASSIFIERNAMESREQUEST._serialized_start = 1481 - _GETCLASSIFIERNAMESREQUEST._serialized_end = 1528 - _GETCLASSIFIERNAMESRESPONSE._serialized_start = 1530 - _GETCLASSIFIERNAMESRESPONSE._serialized_end = 1601 - _ADDCLASSIFIERREQUEST._serialized_start = 1604 - _ADDCLASSIFIERREQUEST._serialized_end = 1817 - _ADDCLASSIFIERRESPONSE._serialized_start = 1819 - _ADDCLASSIFIERRESPONSE._serialized_end = 1842 - _REMOVECLASSIFIERREQUEST._serialized_start = 1844 - _REMOVECLASSIFIERREQUEST._serialized_end = 1930 - _REMOVECLASSIFIERRESPONSE._serialized_start = 1932 - _REMOVECLASSIFIERRESPONSE._serialized_end = 1958 - _GETCLASSIFICATIONSREQUEST._serialized_start = 1961 - _GETCLASSIFICATIONSREQUEST._serialized_end = 2160 - _GETCLASSIFICATIONSRESPONSE._serialized_start = 2162 - _GETCLASSIFICATIONSRESPONSE._serialized_end = 2272 - _GETCLASSIFICATIONSFROMCAMERAREQUEST._serialized_start = 2275 - _GETCLASSIFICATIONSFROMCAMERAREQUEST._serialized_end = 2420 - _GETCLASSIFICATIONSFROMCAMERARESPONSE._serialized_start = 2422 - _GETCLASSIFICATIONSFROMCAMERARESPONSE._serialized_end = 2542 - _CLASSIFICATION._serialized_start = 2544 - _CLASSIFICATION._serialized_end = 2623 - _GETSEGMENTERNAMESREQUEST._serialized_start = 2625 - _GETSEGMENTERNAMESREQUEST._serialized_end = 2671 - _GETSEGMENTERNAMESRESPONSE._serialized_start = 2673 - _GETSEGMENTERNAMESRESPONSE._serialized_end = 2741 - _ADDSEGMENTERREQUEST._serialized_start = 2744 - _ADDSEGMENTERREQUEST._serialized_end = 2950 - _ADDSEGMENTERRESPONSE._serialized_start = 2952 - _ADDSEGMENTERRESPONSE._serialized_end = 2974 - _REMOVESEGMENTERREQUEST._serialized_start = 2976 - _REMOVESEGMENTERREQUEST._serialized_end = 3059 - _REMOVESEGMENTERRESPONSE._serialized_start = 3061 - _REMOVESEGMENTERRESPONSE._serialized_end = 3086 - _GETOBJECTPOINTCLOUDSREQUEST._serialized_start = 3089 - _GETOBJECTPOINTCLOUDSREQUEST._serialized_end = 3239 - _GETOBJECTPOINTCLOUDSRESPONSE._serialized_start = 3241 - _GETOBJECTPOINTCLOUDSRESPONSE._serialized_end = 3360 - _VISIONSERVICE._serialized_start = 3363 - _VISIONSERVICE._serialized_end = 6141 \ No newline at end of file +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1eservice/vision/v1/vision.proto\x12\x16viam.service.vision.v1\x1a\x16common/v1/common.proto\x1a component/camera/v1/camera.proto\x1a\x1cgoogle/api/annotations.proto\x1a\x1cgoogle/protobuf/struct.proto"\xba\x01\n\x14GetDetectionsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n\x05image\x18\x02 \x01(\x0cR\x05image\x12\x14\n\x05width\x18\x03 \x01(\x03R\x05width\x12\x16\n\x06height\x18\x04 \x01(\x03R\x06height\x12\x1b\n\tmime_type\x18\x05 \x01(\tR\x08mimeType\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"Z\n\x15GetDetectionsResponse\x12A\n\ndetections\x18\x01 \x03(\x0b2!.viam.service.vision.v1.DetectionR\ndetections"\x84\x01\n\x1eGetDetectionsFromCameraRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n\x0bcamera_name\x18\x02 \x01(\tR\ncameraName\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"d\n\x1fGetDetectionsFromCameraResponse\x12A\n\ndetections\x18\x01 \x03(\x0b2!.viam.service.vision.v1.DetectionR\ndetections"\xea\x03\n\tDetection\x12\x18\n\x05x_min\x18\x01 \x01(\x03H\x00R\x04xMin\x88\x01\x01\x12\x18\n\x05y_min\x18\x02 \x01(\x03H\x01R\x04yMin\x88\x01\x01\x12\x18\n\x05x_max\x18\x03 \x01(\x03H\x02R\x04xMax\x88\x01\x01\x12\x18\n\x05y_max\x18\x04 \x01(\x03H\x03R\x04yMax\x88\x01\x01\x12\x1e\n\nconfidence\x18\x05 \x01(\x01R\nconfidence\x12\x1d\n\nclass_name\x18\x06 \x01(\tR\tclassName\x12-\n\x10x_min_normalized\x18\x07 \x01(\x01H\x04R\x0exMinNormalized\x88\x01\x01\x12-\n\x10y_min_normalized\x18\x08 \x01(\x01H\x05R\x0eyMinNormalized\x88\x01\x01\x12-\n\x10x_max_normalized\x18\t \x01(\x01H\x06R\x0exMaxNormalized\x88\x01\x01\x12-\n\x10y_max_normalized\x18\n \x01(\x01H\x07R\x0eyMaxNormalized\x88\x01\x01B\x08\n\x06_x_minB\x08\n\x06_y_minB\x08\n\x06_x_maxB\x08\n\x06_y_maxB\x13\n\x11_x_min_normalizedB\x13\n\x11_y_min_normalizedB\x13\n\x11_x_max_normalizedB\x13\n\x11_y_max_normalized"\xcd\x01\n\x19GetClassificationsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n\x05image\x18\x02 \x01(\x0cR\x05image\x12\x14\n\x05width\x18\x03 \x01(\x05R\x05width\x12\x16\n\x06height\x18\x04 \x01(\x05R\x06height\x12\x1b\n\tmime_type\x18\x05 \x01(\tR\x08mimeType\x12\x0c\n\x01n\x18\x06 \x01(\x05R\x01n\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"n\n\x1aGetClassificationsResponse\x12P\n\x0fclassifications\x18\x01 \x03(\x0b2&.viam.service.vision.v1.ClassificationR\x0fclassifications"\x97\x01\n#GetClassificationsFromCameraRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n\x0bcamera_name\x18\x02 \x01(\tR\ncameraName\x12\x0c\n\x01n\x18\x03 \x01(\x05R\x01n\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"x\n$GetClassificationsFromCameraResponse\x12P\n\x0fclassifications\x18\x01 \x03(\x0b2&.viam.service.vision.v1.ClassificationR\x0fclassifications"O\n\x0eClassification\x12\x1d\n\nclass_name\x18\x01 \x01(\tR\tclassName\x12\x1e\n\nconfidence\x18\x02 \x01(\x01R\nconfidence"\x9e\x01\n\x1bGetObjectPointCloudsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n\x0bcamera_name\x18\x02 \x01(\tR\ncameraName\x12\x1b\n\tmime_type\x18\x03 \x01(\tR\x08mimeType\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"w\n\x1cGetObjectPointCloudsResponse\x12\x1b\n\tmime_type\x18\x01 \x01(\tR\x08mimeType\x12:\n\x07objects\x18\x02 \x03(\x0b2 .viam.common.v1.PointCloudObjectR\x07objects"Y\n\x14GetPropertiesRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\xc5\x02\n\x1bCaptureAllFromCameraRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x1f\n\x0bcamera_name\x18\x02 \x01(\tR\ncameraName\x12!\n\x0creturn_image\x18\x03 \x01(\x08R\x0breturnImage\x125\n\x16return_classifications\x18\x04 \x01(\x08R\x15returnClassifications\x12+\n\x11return_detections\x18\x05 \x01(\x08R\x10returnDetections\x12;\n\x1areturn_object_point_clouds\x18\x06 \x01(\x08R\x17returnObjectPointClouds\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\xd5\x02\n\x1cCaptureAllFromCameraResponse\x125\n\x05image\x18\x01 \x01(\x0b2\x1f.viam.component.camera.v1.ImageR\x05image\x12A\n\ndetections\x18\x02 \x03(\x0b2!.viam.service.vision.v1.DetectionR\ndetections\x12P\n\x0fclassifications\x18\x03 \x03(\x0b2&.viam.service.vision.v1.ClassificationR\x0fclassifications\x12:\n\x07objects\x18\x04 \x03(\x0b2 .viam.common.v1.PointCloudObjectR\x07objects\x12-\n\x05extra\x18c \x01(\x0b2\x17.google.protobuf.StructR\x05extra"\xca\x01\n\x15GetPropertiesResponse\x12;\n\x19classifications_supported\x18\x01 \x01(\x08R\x18classificationsSupported\x121\n\x14detections_supported\x18\x02 \x01(\x08R\x13detectionsSupported\x12A\n\x1dobject_point_clouds_supported\x18\x03 \x01(\x08R\x1aobjectPointCloudsSupported2\xcd\x0b\n\rVisionService\x12\xc8\x01\n\x17GetDetectionsFromCamera\x126.viam.service.vision.v1.GetDetectionsFromCameraRequest\x1a7.viam.service.vision.v1.GetDetectionsFromCameraResponse"<\x82\xd3\xe4\x93\x026"4/viam/api/v1/service/vision/{name}/camera_detections\x12\xa3\x01\n\rGetDetections\x12,.viam.service.vision.v1.GetDetectionsRequest\x1a-.viam.service.vision.v1.GetDetectionsResponse"5\x82\xd3\xe4\x93\x02/"-/viam/api/v1/service/vision/{name}/detections\x12\xdc\x01\n\x1cGetClassificationsFromCamera\x12;.viam.service.vision.v1.GetClassificationsFromCameraRequest\x1a<.viam.service.vision.v1.GetClassificationsFromCameraResponse"A\x82\xd3\xe4\x93\x02;"9/viam/api/v1/service/vision/{name}/camera_classifications\x12\xb7\x01\n\x12GetClassifications\x121.viam.service.vision.v1.GetClassificationsRequest\x1a2.viam.service.vision.v1.GetClassificationsResponse":\x82\xd3\xe4\x93\x024"2/viam/api/v1/service/vision/{name}/classifications\x12\xc1\x01\n\x14GetObjectPointClouds\x123.viam.service.vision.v1.GetObjectPointCloudsRequest\x1a4.viam.service.vision.v1.GetObjectPointCloudsResponse">\x82\xd3\xe4\x93\x028"6/viam/api/v1/service/vision/{name}/object_point_clouds\x12\xa7\x01\n\rGetProperties\x12,.viam.service.vision.v1.GetPropertiesRequest\x1a-.viam.service.vision.v1.GetPropertiesResponse"9\x82\xd3\xe4\x93\x023"1/viam/api/v1/service/vision/{name}/get_properties\x12\xb9\x01\n\x14CaptureAllFromCamera\x123.viam.service.vision.v1.CaptureAllFromCameraRequest\x1a4.viam.service.vision.v1.CaptureAllFromCameraResponse"6\x82\xd3\xe4\x93\x020"./viam/api/v1/service/vision/{name}/capture_all\x12\x87\x01\n\tDoCommand\x12 .viam.common.v1.DoCommandRequest\x1a!.viam.common.v1.DoCommandResponse"5\x82\xd3\xe4\x93\x02/"-/viam/api/v1/service/vision/{name}/do_commandB?\n\x1acom.viam.service.vision.v1Z!go.viam.com/api/service/vision/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'service.vision.v1.vision_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\n\x1acom.viam.service.vision.v1Z!go.viam.com/api/service/vision/v1' + _globals['_VISIONSERVICE'].methods_by_name['GetDetectionsFromCamera']._loaded_options = None + _globals['_VISIONSERVICE'].methods_by_name['GetDetectionsFromCamera']._serialized_options = b'\x82\xd3\xe4\x93\x026"4/viam/api/v1/service/vision/{name}/camera_detections' + _globals['_VISIONSERVICE'].methods_by_name['GetDetections']._loaded_options = None + _globals['_VISIONSERVICE'].methods_by_name['GetDetections']._serialized_options = b'\x82\xd3\xe4\x93\x02/"-/viam/api/v1/service/vision/{name}/detections' + _globals['_VISIONSERVICE'].methods_by_name['GetClassificationsFromCamera']._loaded_options = None + _globals['_VISIONSERVICE'].methods_by_name['GetClassificationsFromCamera']._serialized_options = b'\x82\xd3\xe4\x93\x02;"9/viam/api/v1/service/vision/{name}/camera_classifications' + _globals['_VISIONSERVICE'].methods_by_name['GetClassifications']._loaded_options = None + _globals['_VISIONSERVICE'].methods_by_name['GetClassifications']._serialized_options = b'\x82\xd3\xe4\x93\x024"2/viam/api/v1/service/vision/{name}/classifications' + _globals['_VISIONSERVICE'].methods_by_name['GetObjectPointClouds']._loaded_options = None + _globals['_VISIONSERVICE'].methods_by_name['GetObjectPointClouds']._serialized_options = b'\x82\xd3\xe4\x93\x028"6/viam/api/v1/service/vision/{name}/object_point_clouds' + _globals['_VISIONSERVICE'].methods_by_name['GetProperties']._loaded_options = None + _globals['_VISIONSERVICE'].methods_by_name['GetProperties']._serialized_options = b'\x82\xd3\xe4\x93\x023"1/viam/api/v1/service/vision/{name}/get_properties' + _globals['_VISIONSERVICE'].methods_by_name['CaptureAllFromCamera']._loaded_options = None + _globals['_VISIONSERVICE'].methods_by_name['CaptureAllFromCamera']._serialized_options = b'\x82\xd3\xe4\x93\x020"./viam/api/v1/service/vision/{name}/capture_all' + _globals['_VISIONSERVICE'].methods_by_name['DoCommand']._loaded_options = None + _globals['_VISIONSERVICE'].methods_by_name['DoCommand']._serialized_options = b'\x82\xd3\xe4\x93\x02/"-/viam/api/v1/service/vision/{name}/do_command' + _globals['_GETDETECTIONSREQUEST']._serialized_start = 177 + _globals['_GETDETECTIONSREQUEST']._serialized_end = 363 + _globals['_GETDETECTIONSRESPONSE']._serialized_start = 365 + _globals['_GETDETECTIONSRESPONSE']._serialized_end = 455 + _globals['_GETDETECTIONSFROMCAMERAREQUEST']._serialized_start = 458 + _globals['_GETDETECTIONSFROMCAMERAREQUEST']._serialized_end = 590 + _globals['_GETDETECTIONSFROMCAMERARESPONSE']._serialized_start = 592 + _globals['_GETDETECTIONSFROMCAMERARESPONSE']._serialized_end = 692 + _globals['_DETECTION']._serialized_start = 695 + _globals['_DETECTION']._serialized_end = 1185 + _globals['_GETCLASSIFICATIONSREQUEST']._serialized_start = 1188 + _globals['_GETCLASSIFICATIONSREQUEST']._serialized_end = 1393 + _globals['_GETCLASSIFICATIONSRESPONSE']._serialized_start = 1395 + _globals['_GETCLASSIFICATIONSRESPONSE']._serialized_end = 1505 + _globals['_GETCLASSIFICATIONSFROMCAMERAREQUEST']._serialized_start = 1508 + _globals['_GETCLASSIFICATIONSFROMCAMERAREQUEST']._serialized_end = 1659 + _globals['_GETCLASSIFICATIONSFROMCAMERARESPONSE']._serialized_start = 1661 + _globals['_GETCLASSIFICATIONSFROMCAMERARESPONSE']._serialized_end = 1781 + _globals['_CLASSIFICATION']._serialized_start = 1783 + _globals['_CLASSIFICATION']._serialized_end = 1862 + _globals['_GETOBJECTPOINTCLOUDSREQUEST']._serialized_start = 1865 + _globals['_GETOBJECTPOINTCLOUDSREQUEST']._serialized_end = 2023 + _globals['_GETOBJECTPOINTCLOUDSRESPONSE']._serialized_start = 2025 + _globals['_GETOBJECTPOINTCLOUDSRESPONSE']._serialized_end = 2144 + _globals['_GETPROPERTIESREQUEST']._serialized_start = 2146 + _globals['_GETPROPERTIESREQUEST']._serialized_end = 2235 + _globals['_CAPTUREALLFROMCAMERAREQUEST']._serialized_start = 2238 + _globals['_CAPTUREALLFROMCAMERAREQUEST']._serialized_end = 2563 + _globals['_CAPTUREALLFROMCAMERARESPONSE']._serialized_start = 2566 + _globals['_CAPTUREALLFROMCAMERARESPONSE']._serialized_end = 2907 + _globals['_GETPROPERTIESRESPONSE']._serialized_start = 2910 + _globals['_GETPROPERTIESRESPONSE']._serialized_end = 3112 + _globals['_VISIONSERVICE']._serialized_start = 3115 + _globals['_VISIONSERVICE']._serialized_end = 4600 \ No newline at end of file diff --git a/src/viam/gen/service/vision/v1/vision_pb2.pyi b/src/viam/gen/service/vision/v1/vision_pb2.pyi index 936ba05a4..8989ebbaf 100644 --- a/src/viam/gen/service/vision/v1/vision_pb2.pyi +++ b/src/viam/gen/service/vision/v1/vision_pb2.pyi @@ -5,128 +5,15 @@ isort:skip_file import builtins import collections.abc from .... import common +from .... import component import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.message import google.protobuf.struct_pb2 -import sys import typing -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor -class GetModelParameterSchemaRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - MODEL_TYPE_FIELD_NUMBER: builtins.int - name: builtins.str - 'name of the vision service' - model_type: builtins.str - 'name of the type of vision model' - - def __init__(self, *, name: builtins.str=..., model_type: builtins.str=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['model_type', b'model_type', 'name', b'name']) -> None: - ... -global___GetModelParameterSchemaRequest = GetModelParameterSchemaRequest - -class GetModelParameterSchemaResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - MODEL_PARAMETER_SCHEMA_FIELD_NUMBER: builtins.int - model_parameter_schema: builtins.bytes - 'the parameters as JSON bytes of a jsonschema.Schema' - - def __init__(self, *, model_parameter_schema: builtins.bytes=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['model_parameter_schema', b'model_parameter_schema']) -> None: - ... -global___GetModelParameterSchemaResponse = GetModelParameterSchemaResponse - -class GetDetectorNamesRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - name: builtins.str - - def __init__(self, *, name: builtins.str=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: - ... -global___GetDetectorNamesRequest = GetDetectorNamesRequest - -class GetDetectorNamesResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - DETECTOR_NAMES_FIELD_NUMBER: builtins.int - - @property - def detector_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: - """detectors in the registry""" - - def __init__(self, *, detector_names: collections.abc.Iterable[builtins.str] | None=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['detector_names', b'detector_names']) -> None: - ... -global___GetDetectorNamesResponse = GetDetectorNamesResponse - -class AddDetectorRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - DETECTOR_NAME_FIELD_NUMBER: builtins.int - DETECTOR_MODEL_TYPE_FIELD_NUMBER: builtins.int - DETECTOR_PARAMETERS_FIELD_NUMBER: builtins.int - name: builtins.str - detector_name: builtins.str - detector_model_type: builtins.str - - @property - def detector_parameters(self) -> google.protobuf.struct_pb2.Struct: - ... - - def __init__(self, *, name: builtins.str=..., detector_name: builtins.str=..., detector_model_type: builtins.str=..., detector_parameters: google.protobuf.struct_pb2.Struct | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['detector_parameters', b'detector_parameters']) -> builtins.bool: - ... - - def ClearField(self, field_name: typing_extensions.Literal['detector_model_type', b'detector_model_type', 'detector_name', b'detector_name', 'detector_parameters', b'detector_parameters', 'name', b'name']) -> None: - ... -global___AddDetectorRequest = AddDetectorRequest - -class AddDetectorResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__(self) -> None: - ... -global___AddDetectorResponse = AddDetectorResponse - -class RemoveDetectorRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - DETECTOR_NAME_FIELD_NUMBER: builtins.int - name: builtins.str - 'name of the vision service' - detector_name: builtins.str - 'name of detector in registry' - - def __init__(self, *, name: builtins.str=..., detector_name: builtins.str=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['detector_name', b'detector_name', 'name', b'name']) -> None: - ... -global___RemoveDetectorRequest = RemoveDetectorRequest - -class RemoveDetectorResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__(self) -> None: - ... -global___RemoveDetectorResponse = RemoveDetectorResponse - +@typing.final class GetDetectionsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -134,7 +21,7 @@ class GetDetectionsRequest(google.protobuf.message.Message): WIDTH_FIELD_NUMBER: builtins.int HEIGHT_FIELD_NUMBER: builtins.int MIME_TYPE_FIELD_NUMBER: builtins.int - DETECTOR_NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'name of the vision service' image: builtins.bytes @@ -145,16 +32,22 @@ class GetDetectionsRequest(google.protobuf.message.Message): 'the height of the image' mime_type: builtins.str 'the actual MIME type of image' - detector_name: builtins.str - 'name of the registered detector to use' - def __init__(self, *, name: builtins.str=..., image: builtins.bytes=..., width: builtins.int=..., height: builtins.int=..., mime_type: builtins.str=..., detector_name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., image: builtins.bytes=..., width: builtins.int=..., height: builtins.int=..., mime_type: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['detector_name', b'detector_name', 'height', b'height', 'image', b'image', 'mime_type', b'mime_type', 'name', b'name', 'width', b'width']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'height', b'height', 'image', b'image', 'mime_type', b'mime_type', 'name', b'name', 'width', b'width']) -> None: ... global___GetDetectionsRequest = GetDetectionsRequest +@typing.final class GetDetectionsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor DETECTIONS_FIELD_NUMBER: builtins.int @@ -166,29 +59,36 @@ class GetDetectionsResponse(google.protobuf.message.Message): def __init__(self, *, detections: collections.abc.Iterable[global___Detection] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['detections', b'detections']) -> None: + def ClearField(self, field_name: typing.Literal['detections', b'detections']) -> None: ... global___GetDetectionsResponse = GetDetectionsResponse +@typing.final class GetDetectionsFromCameraRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int CAMERA_NAME_FIELD_NUMBER: builtins.int - DETECTOR_NAME_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'name of the vision service' camera_name: builtins.str 'name of camera source to use as input' - detector_name: builtins.str - 'name of the registered detector to use' - def __init__(self, *, name: builtins.str=..., camera_name: builtins.str=..., detector_name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, name: builtins.str=..., camera_name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['camera_name', b'camera_name', 'detector_name', b'detector_name', 'name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['camera_name', b'camera_name', 'extra', b'extra', 'name', b'name']) -> None: ... global___GetDetectionsFromCameraRequest = GetDetectionsFromCameraRequest +@typing.final class GetDetectionsFromCameraResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor DETECTIONS_FIELD_NUMBER: builtins.int @@ -200,10 +100,11 @@ class GetDetectionsFromCameraResponse(google.protobuf.message.Message): def __init__(self, *, detections: collections.abc.Iterable[global___Detection] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['detections', b'detections']) -> None: + def ClearField(self, field_name: typing.Literal['detections', b'detections']) -> None: ... global___GetDetectionsFromCameraResponse = GetDetectionsFromCameraResponse +@typing.final class Detection(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor X_MIN_FIELD_NUMBER: builtins.int @@ -212,6 +113,10 @@ class Detection(google.protobuf.message.Message): Y_MAX_FIELD_NUMBER: builtins.int CONFIDENCE_FIELD_NUMBER: builtins.int CLASS_NAME_FIELD_NUMBER: builtins.int + X_MIN_NORMALIZED_FIELD_NUMBER: builtins.int + Y_MIN_NORMALIZED_FIELD_NUMBER: builtins.int + X_MAX_NORMALIZED_FIELD_NUMBER: builtins.int + Y_MAX_NORMALIZED_FIELD_NUMBER: builtins.int x_min: builtins.int 'the four corners of the box' y_min: builtins.int @@ -221,118 +126,55 @@ class Detection(google.protobuf.message.Message): 'the confidence of the detection' class_name: builtins.str 'label associated with the detected object' + x_min_normalized: builtins.float + 'the four corners of the box, in proportion to the respective image dimension' + y_min_normalized: builtins.float + x_max_normalized: builtins.float + y_max_normalized: builtins.float - def __init__(self, *, x_min: builtins.int | None=..., y_min: builtins.int | None=..., x_max: builtins.int | None=..., y_max: builtins.int | None=..., confidence: builtins.float=..., class_name: builtins.str=...) -> None: + def __init__(self, *, x_min: builtins.int | None=..., y_min: builtins.int | None=..., x_max: builtins.int | None=..., y_max: builtins.int | None=..., confidence: builtins.float=..., class_name: builtins.str=..., x_min_normalized: builtins.float | None=..., y_min_normalized: builtins.float | None=..., x_max_normalized: builtins.float | None=..., y_max_normalized: builtins.float | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['_x_max', b'_x_max', '_x_min', b'_x_min', '_y_max', b'_y_max', '_y_min', b'_y_min', 'x_max', b'x_max', 'x_min', b'x_min', 'y_max', b'y_max', 'y_min', b'y_min']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['_x_max', b'_x_max', '_x_max_normalized', b'_x_max_normalized', '_x_min', b'_x_min', '_x_min_normalized', b'_x_min_normalized', '_y_max', b'_y_max', '_y_max_normalized', b'_y_max_normalized', '_y_min', b'_y_min', '_y_min_normalized', b'_y_min_normalized', 'x_max', b'x_max', 'x_max_normalized', b'x_max_normalized', 'x_min', b'x_min', 'x_min_normalized', b'x_min_normalized', 'y_max', b'y_max', 'y_max_normalized', b'y_max_normalized', 'y_min', b'y_min', 'y_min_normalized', b'y_min_normalized']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['_x_max', b'_x_max', '_x_min', b'_x_min', '_y_max', b'_y_max', '_y_min', b'_y_min', 'class_name', b'class_name', 'confidence', b'confidence', 'x_max', b'x_max', 'x_min', b'x_min', 'y_max', b'y_max', 'y_min', b'y_min']) -> None: + def ClearField(self, field_name: typing.Literal['_x_max', b'_x_max', '_x_max_normalized', b'_x_max_normalized', '_x_min', b'_x_min', '_x_min_normalized', b'_x_min_normalized', '_y_max', b'_y_max', '_y_max_normalized', b'_y_max_normalized', '_y_min', b'_y_min', '_y_min_normalized', b'_y_min_normalized', 'class_name', b'class_name', 'confidence', b'confidence', 'x_max', b'x_max', 'x_max_normalized', b'x_max_normalized', 'x_min', b'x_min', 'x_min_normalized', b'x_min_normalized', 'y_max', b'y_max', 'y_max_normalized', b'y_max_normalized', 'y_min', b'y_min', 'y_min_normalized', b'y_min_normalized']) -> None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal['_x_max', b'_x_max']) -> typing_extensions.Literal['x_max'] | None: + def WhichOneof(self, oneof_group: typing.Literal['_x_max', b'_x_max']) -> typing.Literal['x_max'] | None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal['_x_min', b'_x_min']) -> typing_extensions.Literal['x_min'] | None: + def WhichOneof(self, oneof_group: typing.Literal['_x_max_normalized', b'_x_max_normalized']) -> typing.Literal['x_max_normalized'] | None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal['_y_max', b'_y_max']) -> typing_extensions.Literal['y_max'] | None: + def WhichOneof(self, oneof_group: typing.Literal['_x_min', b'_x_min']) -> typing.Literal['x_min'] | None: ... @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal['_y_min', b'_y_min']) -> typing_extensions.Literal['y_min'] | None: - ... -global___Detection = Detection - -class GetClassifierNamesRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - name: builtins.str - 'name of the vision service' - - def __init__(self, *, name: builtins.str=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: - ... -global___GetClassifierNamesRequest = GetClassifierNamesRequest - -class GetClassifierNamesResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - CLASSIFIER_NAMES_FIELD_NUMBER: builtins.int - - @property - def classifier_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: - ... - - def __init__(self, *, classifier_names: collections.abc.Iterable[builtins.str] | None=...) -> None: - ... - - def ClearField(self, field_name: typing_extensions.Literal['classifier_names', b'classifier_names']) -> None: - ... -global___GetClassifierNamesResponse = GetClassifierNamesResponse - -class AddClassifierRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - CLASSIFIER_NAME_FIELD_NUMBER: builtins.int - CLASSIFIER_MODEL_TYPE_FIELD_NUMBER: builtins.int - CLASSIFIER_PARAMETERS_FIELD_NUMBER: builtins.int - name: builtins.str - 'name of the vision service' - classifier_name: builtins.str - 'name of classifier to add to registry' - classifier_model_type: builtins.str - 'the type of classifier' - - @property - def classifier_parameters(self) -> google.protobuf.struct_pb2.Struct: - """additional parameters""" - - def __init__(self, *, name: builtins.str=..., classifier_name: builtins.str=..., classifier_model_type: builtins.str=..., classifier_parameters: google.protobuf.struct_pb2.Struct | None=...) -> None: - ... - - def HasField(self, field_name: typing_extensions.Literal['classifier_parameters', b'classifier_parameters']) -> builtins.bool: + def WhichOneof(self, oneof_group: typing.Literal['_x_min_normalized', b'_x_min_normalized']) -> typing.Literal['x_min_normalized'] | None: ... - def ClearField(self, field_name: typing_extensions.Literal['classifier_model_type', b'classifier_model_type', 'classifier_name', b'classifier_name', 'classifier_parameters', b'classifier_parameters', 'name', b'name']) -> None: - ... -global___AddClassifierRequest = AddClassifierRequest - -class AddClassifierResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__(self) -> None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_y_max', b'_y_max']) -> typing.Literal['y_max'] | None: ... -global___AddClassifierResponse = AddClassifierResponse - -class RemoveClassifierRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - CLASSIFIER_NAME_FIELD_NUMBER: builtins.int - name: builtins.str - 'name of the vision service' - classifier_name: builtins.str - 'name of the classifier in registry' - def __init__(self, *, name: builtins.str=..., classifier_name: builtins.str=...) -> None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_y_max_normalized', b'_y_max_normalized']) -> typing.Literal['y_max_normalized'] | None: ... - def ClearField(self, field_name: typing_extensions.Literal['classifier_name', b'classifier_name', 'name', b'name']) -> None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_y_min', b'_y_min']) -> typing.Literal['y_min'] | None: ... -global___RemoveClassifierRequest = RemoveClassifierRequest - -class RemoveClassifierResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - def __init__(self) -> None: + @typing.overload + def WhichOneof(self, oneof_group: typing.Literal['_y_min_normalized', b'_y_min_normalized']) -> typing.Literal['y_min_normalized'] | None: ... -global___RemoveClassifierResponse = RemoveClassifierResponse +global___Detection = Detection +@typing.final class GetClassificationsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int @@ -340,8 +182,8 @@ class GetClassificationsRequest(google.protobuf.message.Message): WIDTH_FIELD_NUMBER: builtins.int HEIGHT_FIELD_NUMBER: builtins.int MIME_TYPE_FIELD_NUMBER: builtins.int - CLASSIFIER_NAME_FIELD_NUMBER: builtins.int N_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'name of the vision service' image: builtins.bytes @@ -352,18 +194,24 @@ class GetClassificationsRequest(google.protobuf.message.Message): 'the height of the image' mime_type: builtins.str 'the actual MIME type of image' - classifier_name: builtins.str - 'the name of the registered classifier' n: builtins.int 'the number of classifications desired' - def __init__(self, *, name: builtins.str=..., image: builtins.bytes=..., width: builtins.int=..., height: builtins.int=..., mime_type: builtins.str=..., classifier_name: builtins.str=..., n: builtins.int=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., image: builtins.bytes=..., width: builtins.int=..., height: builtins.int=..., mime_type: builtins.str=..., n: builtins.int=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['classifier_name', b'classifier_name', 'height', b'height', 'image', b'image', 'mime_type', b'mime_type', 'n', b'n', 'name', b'name', 'width', b'width']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'height', b'height', 'image', b'image', 'mime_type', b'mime_type', 'n', b'n', 'name', b'name', 'width', b'width']) -> None: ... global___GetClassificationsRequest = GetClassificationsRequest +@typing.final class GetClassificationsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor CLASSIFICATIONS_FIELD_NUMBER: builtins.int @@ -375,32 +223,39 @@ class GetClassificationsResponse(google.protobuf.message.Message): def __init__(self, *, classifications: collections.abc.Iterable[global___Classification] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['classifications', b'classifications']) -> None: + def ClearField(self, field_name: typing.Literal['classifications', b'classifications']) -> None: ... global___GetClassificationsResponse = GetClassificationsResponse +@typing.final class GetClassificationsFromCameraRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int CAMERA_NAME_FIELD_NUMBER: builtins.int - CLASSIFIER_NAME_FIELD_NUMBER: builtins.int N_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'name of the vision service' camera_name: builtins.str 'the image encoded as bytes' - classifier_name: builtins.str - 'the name of the registered classifier' n: builtins.int 'the number of classifications desired' - def __init__(self, *, name: builtins.str=..., camera_name: builtins.str=..., classifier_name: builtins.str=..., n: builtins.int=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., camera_name: builtins.str=..., n: builtins.int=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['camera_name', b'camera_name', 'classifier_name', b'classifier_name', 'n', b'n', 'name', b'name']) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['camera_name', b'camera_name', 'extra', b'extra', 'n', b'n', 'name', b'name']) -> None: ... global___GetClassificationsFromCameraRequest = GetClassificationsFromCameraRequest +@typing.final class GetClassificationsFromCameraResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor CLASSIFICATIONS_FIELD_NUMBER: builtins.int @@ -412,10 +267,11 @@ class GetClassificationsFromCameraResponse(google.protobuf.message.Message): def __init__(self, *, classifications: collections.abc.Iterable[global___Classification] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['classifications', b'classifications']) -> None: + def ClearField(self, field_name: typing.Literal['classifications', b'classifications']) -> None: ... global___GetClassificationsFromCameraResponse = GetClassificationsFromCameraResponse +@typing.final class Classification(google.protobuf.message.Message): """the general form of the output from a classifier""" DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -429,129 +285,170 @@ class Classification(google.protobuf.message.Message): def __init__(self, *, class_name: builtins.str=..., confidence: builtins.float=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['class_name', b'class_name', 'confidence', b'confidence']) -> None: + def ClearField(self, field_name: typing.Literal['class_name', b'class_name', 'confidence', b'confidence']) -> None: ... global___Classification = Classification -class GetSegmenterNamesRequest(google.protobuf.message.Message): +@typing.final +class GetObjectPointCloudsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int + CAMERA_NAME_FIELD_NUMBER: builtins.int + MIME_TYPE_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str + camera_name: builtins.str + 'Name of a camera' + mime_type: builtins.str + 'Requested MIME type of response' + + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" + + def __init__(self, *, name: builtins.str=..., camera_name: builtins.str=..., mime_type: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... - def __init__(self, *, name: builtins.str=...) -> None: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name']) -> None: + def ClearField(self, field_name: typing.Literal['camera_name', b'camera_name', 'extra', b'extra', 'mime_type', b'mime_type', 'name', b'name']) -> None: ... -global___GetSegmenterNamesRequest = GetSegmenterNamesRequest +global___GetObjectPointCloudsRequest = GetObjectPointCloudsRequest -class GetSegmenterNamesResponse(google.protobuf.message.Message): +@typing.final +class GetObjectPointCloudsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - SEGMENTER_NAMES_FIELD_NUMBER: builtins.int + MIME_TYPE_FIELD_NUMBER: builtins.int + OBJECTS_FIELD_NUMBER: builtins.int + mime_type: builtins.str + 'Actual MIME type of response' @property - def segmenter_names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: - """segmenters in the registry""" + def objects(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.PointCloudObject]: + """List of objects in the scene""" - def __init__(self, *, segmenter_names: collections.abc.Iterable[builtins.str] | None=...) -> None: + def __init__(self, *, mime_type: builtins.str=..., objects: collections.abc.Iterable[common.v1.common_pb2.PointCloudObject] | None=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['segmenter_names', b'segmenter_names']) -> None: + def ClearField(self, field_name: typing.Literal['mime_type', b'mime_type', 'objects', b'objects']) -> None: ... -global___GetSegmenterNamesResponse = GetSegmenterNamesResponse +global___GetObjectPointCloudsResponse = GetObjectPointCloudsResponse -class AddSegmenterRequest(google.protobuf.message.Message): +@typing.final +class GetPropertiesRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int - SEGMENTER_NAME_FIELD_NUMBER: builtins.int - SEGMENTER_MODEL_TYPE_FIELD_NUMBER: builtins.int - SEGMENTER_PARAMETERS_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'name of the vision service' - segmenter_name: builtins.str - 'name of the segmenter' - segmenter_model_type: builtins.str - 'name of the segmenter model' @property - def segmenter_parameters(self) -> google.protobuf.struct_pb2.Struct: - """parameters of the segmenter model""" + def extra(self) -> google.protobuf.struct_pb2.Struct: + """Additional arguments to the method""" - def __init__(self, *, name: builtins.str=..., segmenter_name: builtins.str=..., segmenter_model_type: builtins.str=..., segmenter_parameters: google.protobuf.struct_pb2.Struct | None=...) -> None: + def __init__(self, *, name: builtins.str=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... - def HasField(self, field_name: typing_extensions.Literal['segmenter_parameters', b'segmenter_parameters']) -> builtins.bool: + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name', 'segmenter_model_type', b'segmenter_model_type', 'segmenter_name', b'segmenter_name', 'segmenter_parameters', b'segmenter_parameters']) -> None: + def ClearField(self, field_name: typing.Literal['extra', b'extra', 'name', b'name']) -> None: ... -global___AddSegmenterRequest = AddSegmenterRequest +global___GetPropertiesRequest = GetPropertiesRequest -class AddSegmenterResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__(self) -> None: - ... -global___AddSegmenterResponse = AddSegmenterResponse - -class RemoveSegmenterRequest(google.protobuf.message.Message): +@typing.final +class CaptureAllFromCameraRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor NAME_FIELD_NUMBER: builtins.int - SEGMENTER_NAME_FIELD_NUMBER: builtins.int + CAMERA_NAME_FIELD_NUMBER: builtins.int + RETURN_IMAGE_FIELD_NUMBER: builtins.int + RETURN_CLASSIFICATIONS_FIELD_NUMBER: builtins.int + RETURN_DETECTIONS_FIELD_NUMBER: builtins.int + RETURN_OBJECT_POINT_CLOUDS_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int name: builtins.str 'name of the vision service' - segmenter_name: builtins.str - 'name of segmenter in registry' + camera_name: builtins.str + 'name of camera source to use as input' + return_image: builtins.bool + 'whether or not including the image in the response' + return_classifications: builtins.bool + 'whether or not including classifications in the response' + return_detections: builtins.bool + 'whether or not including detections in the response' + return_object_point_clouds: builtins.bool + 'whether or not including pcd in the response' - def __init__(self, *, name: builtins.str=..., segmenter_name: builtins.str=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: ... - def ClearField(self, field_name: typing_extensions.Literal['name', b'name', 'segmenter_name', b'segmenter_name']) -> None: + def __init__(self, *, name: builtins.str=..., camera_name: builtins.str=..., return_image: builtins.bool=..., return_classifications: builtins.bool=..., return_detections: builtins.bool=..., return_object_point_clouds: builtins.bool=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: ... -global___RemoveSegmenterRequest = RemoveSegmenterRequest -class RemoveSegmenterResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor + def HasField(self, field_name: typing.Literal['extra', b'extra']) -> builtins.bool: + ... - def __init__(self) -> None: + def ClearField(self, field_name: typing.Literal['camera_name', b'camera_name', 'extra', b'extra', 'name', b'name', 'return_classifications', b'return_classifications', 'return_detections', b'return_detections', 'return_image', b'return_image', 'return_object_point_clouds', b'return_object_point_clouds']) -> None: ... -global___RemoveSegmenterResponse = RemoveSegmenterResponse +global___CaptureAllFromCameraRequest = CaptureAllFromCameraRequest -class GetObjectPointCloudsRequest(google.protobuf.message.Message): +@typing.final +class CaptureAllFromCameraResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - NAME_FIELD_NUMBER: builtins.int - CAMERA_NAME_FIELD_NUMBER: builtins.int - SEGMENTER_NAME_FIELD_NUMBER: builtins.int - MIME_TYPE_FIELD_NUMBER: builtins.int - name: builtins.str - camera_name: builtins.str - 'Name of a camera' - segmenter_name: builtins.str - 'Name of the segmentation algorithm' - mime_type: builtins.str - 'Requested MIME type of response' + IMAGE_FIELD_NUMBER: builtins.int + DETECTIONS_FIELD_NUMBER: builtins.int + CLASSIFICATIONS_FIELD_NUMBER: builtins.int + OBJECTS_FIELD_NUMBER: builtins.int + EXTRA_FIELD_NUMBER: builtins.int - def __init__(self, *, name: builtins.str=..., camera_name: builtins.str=..., segmenter_name: builtins.str=..., mime_type: builtins.str=...) -> None: + @property + def image(self) -> component.camera.v1.camera_pb2.Image: ... - def ClearField(self, field_name: typing_extensions.Literal['camera_name', b'camera_name', 'mime_type', b'mime_type', 'name', b'name', 'segmenter_name', b'segmenter_name']) -> None: + @property + def detections(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Detection]: ... -global___GetObjectPointCloudsRequest = GetObjectPointCloudsRequest -class GetObjectPointCloudsResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - MIME_TYPE_FIELD_NUMBER: builtins.int - OBJECTS_FIELD_NUMBER: builtins.int - mime_type: builtins.str - 'Actual MIME type of response' + @property + def classifications(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Classification]: + ... @property def objects(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[common.v1.common_pb2.PointCloudObject]: - """List of objects in the scene""" + ... - def __init__(self, *, mime_type: builtins.str=..., objects: collections.abc.Iterable[common.v1.common_pb2.PointCloudObject] | None=...) -> None: + @property + def extra(self) -> google.protobuf.struct_pb2.Struct: + ... + + def __init__(self, *, image: component.camera.v1.camera_pb2.Image | None=..., detections: collections.abc.Iterable[global___Detection] | None=..., classifications: collections.abc.Iterable[global___Classification] | None=..., objects: collections.abc.Iterable[common.v1.common_pb2.PointCloudObject] | None=..., extra: google.protobuf.struct_pb2.Struct | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['extra', b'extra', 'image', b'image']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['classifications', b'classifications', 'detections', b'detections', 'extra', b'extra', 'image', b'image', 'objects', b'objects']) -> None: + ... +global___CaptureAllFromCameraResponse = CaptureAllFromCameraResponse + +@typing.final +class GetPropertiesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + CLASSIFICATIONS_SUPPORTED_FIELD_NUMBER: builtins.int + DETECTIONS_SUPPORTED_FIELD_NUMBER: builtins.int + OBJECT_POINT_CLOUDS_SUPPORTED_FIELD_NUMBER: builtins.int + classifications_supported: builtins.bool + 'whether or not classifactions are supported by the vision service' + detections_supported: builtins.bool + 'whether or not detections are supported by the vision service' + object_point_clouds_supported: builtins.bool + 'whether or not 3d segmentation is supported by the vision service' + + def __init__(self, *, classifications_supported: builtins.bool=..., detections_supported: builtins.bool=..., object_point_clouds_supported: builtins.bool=...) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal['mime_type', b'mime_type', 'objects', b'objects']) -> None: + def ClearField(self, field_name: typing.Literal['classifications_supported', b'classifications_supported', 'detections_supported', b'detections_supported', 'object_point_clouds_supported', b'object_point_clouds_supported']) -> None: ... -global___GetObjectPointCloudsResponse = GetObjectPointCloudsResponse \ No newline at end of file +global___GetPropertiesResponse = GetPropertiesResponse \ No newline at end of file diff --git a/src/viam/gen/stream/__init__.py b/src/viam/gen/stream/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/stream/v1/__init__.py b/src/viam/gen/stream/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/gen/stream/v1/stream_grpc.py b/src/viam/gen/stream/v1/stream_grpc.py new file mode 100644 index 000000000..4bf9c1ce4 --- /dev/null +++ b/src/viam/gen/stream/v1/stream_grpc.py @@ -0,0 +1,59 @@ +import abc +import typing +import grpclib.const +import grpclib.client +import grpclib.exceptions +if typing.TYPE_CHECKING: + import grpclib.server +from ... import stream + +class StreamServiceBase(abc.ABC): + + @abc.abstractmethod + async def ListStreams(self, stream: 'grpclib.server.Stream[stream.v1.stream_pb2.ListStreamsRequest, stream.v1.stream_pb2.ListStreamsResponse]') -> None: + pass + + @abc.abstractmethod + async def AddStream(self, stream: 'grpclib.server.Stream[stream.v1.stream_pb2.AddStreamRequest, stream.v1.stream_pb2.AddStreamResponse]') -> None: + pass + + @abc.abstractmethod + async def GetStreamOptions(self, stream: 'grpclib.server.Stream[stream.v1.stream_pb2.GetStreamOptionsRequest, stream.v1.stream_pb2.GetStreamOptionsResponse]') -> None: + pass + + @abc.abstractmethod + async def SetStreamOptions(self, stream: 'grpclib.server.Stream[stream.v1.stream_pb2.SetStreamOptionsRequest, stream.v1.stream_pb2.SetStreamOptionsResponse]') -> None: + pass + + @abc.abstractmethod + async def RemoveStream(self, stream: 'grpclib.server.Stream[stream.v1.stream_pb2.RemoveStreamRequest, stream.v1.stream_pb2.RemoveStreamResponse]') -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return {'/proto.stream.v1.StreamService/ListStreams': grpclib.const.Handler(self.ListStreams, grpclib.const.Cardinality.UNARY_UNARY, stream.v1.stream_pb2.ListStreamsRequest, stream.v1.stream_pb2.ListStreamsResponse), '/proto.stream.v1.StreamService/AddStream': grpclib.const.Handler(self.AddStream, grpclib.const.Cardinality.UNARY_UNARY, stream.v1.stream_pb2.AddStreamRequest, stream.v1.stream_pb2.AddStreamResponse), '/proto.stream.v1.StreamService/GetStreamOptions': grpclib.const.Handler(self.GetStreamOptions, grpclib.const.Cardinality.UNARY_UNARY, stream.v1.stream_pb2.GetStreamOptionsRequest, stream.v1.stream_pb2.GetStreamOptionsResponse), '/proto.stream.v1.StreamService/SetStreamOptions': grpclib.const.Handler(self.SetStreamOptions, grpclib.const.Cardinality.UNARY_UNARY, stream.v1.stream_pb2.SetStreamOptionsRequest, stream.v1.stream_pb2.SetStreamOptionsResponse), '/proto.stream.v1.StreamService/RemoveStream': grpclib.const.Handler(self.RemoveStream, grpclib.const.Cardinality.UNARY_UNARY, stream.v1.stream_pb2.RemoveStreamRequest, stream.v1.stream_pb2.RemoveStreamResponse)} + +class UnimplementedStreamServiceBase(StreamServiceBase): + + async def ListStreams(self, stream: 'grpclib.server.Stream[stream.v1.stream_pb2.ListStreamsRequest, stream.v1.stream_pb2.ListStreamsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def AddStream(self, stream: 'grpclib.server.Stream[stream.v1.stream_pb2.AddStreamRequest, stream.v1.stream_pb2.AddStreamResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def GetStreamOptions(self, stream: 'grpclib.server.Stream[stream.v1.stream_pb2.GetStreamOptionsRequest, stream.v1.stream_pb2.GetStreamOptionsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def SetStreamOptions(self, stream: 'grpclib.server.Stream[stream.v1.stream_pb2.SetStreamOptionsRequest, stream.v1.stream_pb2.SetStreamOptionsResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + + async def RemoveStream(self, stream: 'grpclib.server.Stream[stream.v1.stream_pb2.RemoveStreamRequest, stream.v1.stream_pb2.RemoveStreamResponse]') -> None: + raise grpclib.exceptions.GRPCError(grpclib.const.Status.UNIMPLEMENTED) + +class StreamServiceStub: + + def __init__(self, channel: grpclib.client.Channel) -> None: + self.ListStreams = grpclib.client.UnaryUnaryMethod(channel, '/proto.stream.v1.StreamService/ListStreams', stream.v1.stream_pb2.ListStreamsRequest, stream.v1.stream_pb2.ListStreamsResponse) + self.AddStream = grpclib.client.UnaryUnaryMethod(channel, '/proto.stream.v1.StreamService/AddStream', stream.v1.stream_pb2.AddStreamRequest, stream.v1.stream_pb2.AddStreamResponse) + self.GetStreamOptions = grpclib.client.UnaryUnaryMethod(channel, '/proto.stream.v1.StreamService/GetStreamOptions', stream.v1.stream_pb2.GetStreamOptionsRequest, stream.v1.stream_pb2.GetStreamOptionsResponse) + self.SetStreamOptions = grpclib.client.UnaryUnaryMethod(channel, '/proto.stream.v1.StreamService/SetStreamOptions', stream.v1.stream_pb2.SetStreamOptionsRequest, stream.v1.stream_pb2.SetStreamOptionsResponse) + self.RemoveStream = grpclib.client.UnaryUnaryMethod(channel, '/proto.stream.v1.StreamService/RemoveStream', stream.v1.stream_pb2.RemoveStreamRequest, stream.v1.stream_pb2.RemoveStreamResponse) \ No newline at end of file diff --git a/src/viam/gen/stream/v1/stream_pb2.py b/src/viam/gen/stream/v1/stream_pb2.py new file mode 100644 index 000000000..de74da9d4 --- /dev/null +++ b/src/viam/gen/stream/v1/stream_pb2.py @@ -0,0 +1,39 @@ +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'stream/v1/stream.proto') +_sym_db = _symbol_database.Default() +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16stream/v1/stream.proto\x12\x0fproto.stream.v1"\x14\n\x12ListStreamsRequest"+\n\x13ListStreamsResponse\x12\x14\n\x05names\x18\x01 \x03(\tR\x05names"&\n\x10AddStreamRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x13\n\x11AddStreamResponse")\n\x13RemoveStreamRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"\x16\n\x14RemoveStreamResponse":\n\nResolution\x12\x14\n\x05width\x18\x01 \x01(\x05R\x05width\x12\x16\n\x06height\x18\x02 \x01(\x05R\x06height"-\n\x17GetStreamOptionsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name"Y\n\x18GetStreamOptionsResponse\x12=\n\x0bresolutions\x18\x01 \x03(\x0b2\x1b.proto.stream.v1.ResolutionR\x0bresolutions"j\n\x17SetStreamOptionsRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12;\n\nresolution\x18\x02 \x01(\x0b2\x1b.proto.stream.v1.ResolutionR\nresolution"\x1a\n\x18SetStreamOptionsResponse2\xec\x03\n\rStreamService\x12X\n\x0bListStreams\x12#.proto.stream.v1.ListStreamsRequest\x1a$.proto.stream.v1.ListStreamsResponse\x12R\n\tAddStream\x12!.proto.stream.v1.AddStreamRequest\x1a".proto.stream.v1.AddStreamResponse\x12g\n\x10GetStreamOptions\x12(.proto.stream.v1.GetStreamOptionsRequest\x1a).proto.stream.v1.GetStreamOptionsResponse\x12g\n\x10SetStreamOptions\x12(.proto.stream.v1.SetStreamOptionsRequest\x1a).proto.stream.v1.SetStreamOptionsResponse\x12[\n\x0cRemoveStream\x12$.proto.stream.v1.RemoveStreamRequest\x1a%.proto.stream.v1.RemoveStreamResponseB.Z,github.com/edaniels/gostream/proto/stream/v1b\x06proto3') +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'stream.v1.stream_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z,github.com/edaniels/gostream/proto/stream/v1' + _globals['_LISTSTREAMSREQUEST']._serialized_start = 43 + _globals['_LISTSTREAMSREQUEST']._serialized_end = 63 + _globals['_LISTSTREAMSRESPONSE']._serialized_start = 65 + _globals['_LISTSTREAMSRESPONSE']._serialized_end = 108 + _globals['_ADDSTREAMREQUEST']._serialized_start = 110 + _globals['_ADDSTREAMREQUEST']._serialized_end = 148 + _globals['_ADDSTREAMRESPONSE']._serialized_start = 150 + _globals['_ADDSTREAMRESPONSE']._serialized_end = 169 + _globals['_REMOVESTREAMREQUEST']._serialized_start = 171 + _globals['_REMOVESTREAMREQUEST']._serialized_end = 212 + _globals['_REMOVESTREAMRESPONSE']._serialized_start = 214 + _globals['_REMOVESTREAMRESPONSE']._serialized_end = 236 + _globals['_RESOLUTION']._serialized_start = 238 + _globals['_RESOLUTION']._serialized_end = 296 + _globals['_GETSTREAMOPTIONSREQUEST']._serialized_start = 298 + _globals['_GETSTREAMOPTIONSREQUEST']._serialized_end = 343 + _globals['_GETSTREAMOPTIONSRESPONSE']._serialized_start = 345 + _globals['_GETSTREAMOPTIONSRESPONSE']._serialized_end = 434 + _globals['_SETSTREAMOPTIONSREQUEST']._serialized_start = 436 + _globals['_SETSTREAMOPTIONSREQUEST']._serialized_end = 542 + _globals['_SETSTREAMOPTIONSRESPONSE']._serialized_start = 544 + _globals['_SETSTREAMOPTIONSRESPONSE']._serialized_end = 570 + _globals['_STREAMSERVICE']._serialized_start = 573 + _globals['_STREAMSERVICE']._serialized_end = 1065 \ No newline at end of file diff --git a/src/viam/gen/stream/v1/stream_pb2.pyi b/src/viam/gen/stream/v1/stream_pb2.pyi new file mode 100644 index 000000000..bae532ce7 --- /dev/null +++ b/src/viam/gen/stream/v1/stream_pb2.pyi @@ -0,0 +1,161 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import typing +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class ListStreamsRequest(google.protobuf.message.Message): + """ListStreamsRequest requests all streams registered.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___ListStreamsRequest = ListStreamsRequest + +@typing.final +class ListStreamsResponse(google.protobuf.message.Message): + """A ListStreamsResponse details streams registered.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAMES_FIELD_NUMBER: builtins.int + + @property + def names(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + ... + + def __init__(self, *, names: collections.abc.Iterable[builtins.str] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['names', b'names']) -> None: + ... +global___ListStreamsResponse = ListStreamsResponse + +@typing.final +class AddStreamRequest(google.protobuf.message.Message): + """A AddStreamRequest requests the given stream be added to the connection.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___AddStreamRequest = AddStreamRequest + +@typing.final +class AddStreamResponse(google.protobuf.message.Message): + """AddStreamResponse is returned after a successful AddStreamRequest.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___AddStreamResponse = AddStreamResponse + +@typing.final +class RemoveStreamRequest(google.protobuf.message.Message): + """A RemoveStreamRequest requests the given stream be removed from the connection.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___RemoveStreamRequest = RemoveStreamRequest + +@typing.final +class RemoveStreamResponse(google.protobuf.message.Message): + """RemoveStreamResponse is returned after a successful RemoveStreamRequest.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___RemoveStreamResponse = RemoveStreamResponse + +@typing.final +class Resolution(google.protobuf.message.Message): + """Resolution details the width and height of a stream.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + WIDTH_FIELD_NUMBER: builtins.int + HEIGHT_FIELD_NUMBER: builtins.int + width: builtins.int + height: builtins.int + + def __init__(self, *, width: builtins.int=..., height: builtins.int=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['height', b'height', 'width', b'width']) -> None: + ... +global___Resolution = Resolution + +@typing.final +class GetStreamOptionsRequest(google.protobuf.message.Message): + """GetStreamOptionsRequest requests the options for a particular stream.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + + def __init__(self, *, name: builtins.str=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name']) -> None: + ... +global___GetStreamOptionsRequest = GetStreamOptionsRequest + +@typing.final +class GetStreamOptionsResponse(google.protobuf.message.Message): + """GetStreamOptionsResponse details the options for a particular stream.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + RESOLUTIONS_FIELD_NUMBER: builtins.int + + @property + def resolutions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Resolution]: + ... + + def __init__(self, *, resolutions: collections.abc.Iterable[global___Resolution] | None=...) -> None: + ... + + def ClearField(self, field_name: typing.Literal['resolutions', b'resolutions']) -> None: + ... +global___GetStreamOptionsResponse = GetStreamOptionsResponse + +@typing.final +class SetStreamOptionsRequest(google.protobuf.message.Message): + """SetStreamOptionsRequest sets the options for a particular stream.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + NAME_FIELD_NUMBER: builtins.int + RESOLUTION_FIELD_NUMBER: builtins.int + name: builtins.str + + @property + def resolution(self) -> global___Resolution: + ... + + def __init__(self, *, name: builtins.str=..., resolution: global___Resolution | None=...) -> None: + ... + + def HasField(self, field_name: typing.Literal['resolution', b'resolution']) -> builtins.bool: + ... + + def ClearField(self, field_name: typing.Literal['name', b'name', 'resolution', b'resolution']) -> None: + ... +global___SetStreamOptionsRequest = SetStreamOptionsRequest + +@typing.final +class SetStreamOptionsResponse(google.protobuf.message.Message): + """SetStreamOptionsResponse is returned after a successful SetStreamOptionsRequest.""" + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__(self) -> None: + ... +global___SetStreamOptionsResponse = SetStreamOptionsResponse \ No newline at end of file diff --git a/src/viam/gen/tagger/v1/tagger_pb2.py b/src/viam/gen/tagger/v1/tagger_pb2.py index 30afd97a3..c8be831df 100644 --- a/src/viam/gen/tagger/v1/tagger_pb2.py +++ b/src/viam/gen/tagger/v1/tagger_pb2.py @@ -1,15 +1,16 @@ """Generated protocol buffer code.""" -from google.protobuf.internal import builder as _builder from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion(_runtime_version.Domain.PUBLIC, 5, 29, 2, '', 'tagger/v1/tagger.proto') _sym_db = _symbol_database.Default() from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16tagger/v1/tagger.proto\x12\ttagger.v1\x1a google/protobuf/descriptor.proto:3\n\x04tags\x12\x1d.google.protobuf.FieldOptions\x18\xc3\xe03 \x01(\tR\x04tags:>\n\noneof_tags\x12\x1d.google.protobuf.OneofOptions\x18\xc3\xe03 \x01(\tR\toneofTagsB4Z2github.com/srikrsna/protoc-gen-gotag/tagger;taggerb\x06proto3') -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'tagger.v1.tagger_pb2', globals()) -if _descriptor._USE_C_DESCRIPTORS == False: - google_dot_protobuf_dot_descriptor__pb2.FieldOptions.RegisterExtension(tags) - google_dot_protobuf_dot_descriptor__pb2.OneofOptions.RegisterExtension(oneof_tags) - DESCRIPTOR._options = None - DESCRIPTOR._serialized_options = b'Z2github.com/srikrsna/protoc-gen-gotag/tagger;tagger' \ No newline at end of file +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'tagger.v1.tagger_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z2github.com/srikrsna/protoc-gen-gotag/tagger;tagger' \ No newline at end of file diff --git a/src/viam/logging.py b/src/viam/logging.py index cc7de5a6f..f67cf8bd3 100644 --- a/src/viam/logging.py +++ b/src/viam/logging.py @@ -1,14 +1,119 @@ -from copy import copy +import asyncio import logging -from logging import DEBUG, INFO, WARN, WARNING, ERROR, FATAL # noqa: F401 -from typing import Dict +import sys +from copy import copy +from datetime import datetime +from logging import DEBUG, ERROR, FATAL, INFO, WARN, WARNING # noqa: F401 +from threading import Lock, Thread +from typing import TYPE_CHECKING, Dict, Iterable, List, Optional, Union + +from grpclib.exceptions import StreamTerminatedError + +import viam + +if TYPE_CHECKING: + from .robot.client import RobotClient + LOG_LEVEL = INFO LOGGERS: Dict[str, logging.Logger] = {} +_MODULE_PARENT: Optional["RobotClient"] = None + + +class _SingletonEventLoopThread: + _instance = None + _lock = Lock() + _ready_event = asyncio.Event() + _loop: Union[asyncio.AbstractEventLoop, None] + _thread: Thread + + def __new__(cls): + # Ensure singleton precondition + if cls._instance is None: + with cls._lock: + if cls._instance is None: + cls._instance = super(_SingletonEventLoopThread, cls).__new__(cls) + cls._instance._loop = None + cls._instance._thread = Thread(target=cls._instance._run) + cls._instance._thread.start() + return cls._instance + + def _run(self): + self._loop = asyncio.new_event_loop() + asyncio.set_event_loop(self._loop) + self._ready_event.set() + self._loop.run_forever() + + def stop(self): + if self._loop is not None: + self._loop.call_soon_threadsafe(self._loop.stop) + self._thread.join() + + def get_loop(self): + if self._loop is None: + raise RuntimeError("Event loop is None. Did you call .start() and .wait_until_ready()?") + return self._loop + + async def wait_until_ready(self): + await self._ready_event.wait() + + +class _ModuleHandler(logging.Handler): + _parent: "RobotClient" + _logger: logging.Logger + _worker: _SingletonEventLoopThread + + def __init__(self, parent: "RobotClient"): + super().__init__() + self._parent = parent + self._logger = logging.getLogger("ModuleLogger") + addHandlers(self._logger, True) + self._logger.setLevel(self.level) + self._worker = _SingletonEventLoopThread() + + def setLevel(self, level: Union[int, str]) -> None: + self._logger.setLevel(level) + return super().setLevel(level) + + async def handle_task_result(self, task: asyncio.Task): + try: + _ = task.result() + except (asyncio.CancelledError, asyncio.InvalidStateError, StreamTerminatedError): + pass + + def emit(self, record: logging.LogRecord): + assert isinstance(record, logging.LogRecord) + # Fully qualified name of form "{API triplet}/{name}", e.g. "rdk:component:arm/myarm" + name = record.name.replace(".", "/") + message = f"{record.filename}:{record.lineno}\t{record.getMessage()}" + stack = f"exc_info: {record.exc_info}, exc_text: {record.exc_text}, stack_info: {record.stack_info}" + time = datetime.fromtimestamp(record.created) + + try: + loop = self._worker.get_loop() + asyncio.run_coroutine_threadsafe( + self._asynchronously_emit(record, name, message, stack, time), + loop, + ) + except Exception as err: + # If the module log fails, log using stdout/stderr handlers + self._logger.error(f"ModuleLogger failed for {record.name} - {err}") + self._logger.log(record.levelno, message) + + async def _asynchronously_emit(self, record: logging.LogRecord, name: str, message: str, stack: str, time: datetime): + await self._worker.wait_until_ready() + task = self._worker.get_loop().create_task( + self._parent.log(name, record.levelname, time, message, stack), + name=f"{viam._TASK_PREFIX}-LOG-{record.created}", + ) + task.add_done_callback(lambda t: asyncio.run_coroutine_threadsafe(self.handle_task_result(t), self._worker.get_loop())) + def close(self): + self._worker.stop() + super().close() -class ColorFormatter(logging.Formatter): +class _ColorFormatter(logging.Formatter): MAPPING = { "DEBUG": 37, # white "INFO": 36, # cyan @@ -17,8 +122,8 @@ class ColorFormatter(logging.Formatter): "CRITICAL": 41, # white on red bg } - def __init__(self, patern): - logging.Formatter.__init__(self, patern) + def __init__(self, pattern): + logging.Formatter.__init__(self, pattern) def format(self, record): colored_record = copy(record) @@ -33,17 +138,79 @@ def getLogger(name: str) -> logging.Logger: logger = LOGGERS.get(name) if logger: return logger + logger = logging.getLogger(name) logger.setLevel(LOG_LEVEL) - handler = logging.StreamHandler() - format = ColorFormatter("%(asctime)s\t\t" + "%(levelname)s\t" + "%(name)s (%(filename)s:%(lineno)d)\t" + "%(message)s\t") - handler.setFormatter(format) - logger.addHandler(handler) + + addHandlers(logger) + LOGGERS[name] = logger return logger +def addHandlers(logger: logging.Logger, use_default_handlers=False): + _addHandlers([logger], use_default_handlers) + + +def update_log_level(logger: logging.Logger, level: Union[int, str]): + if level == "": + level = LOG_LEVEL + logger.setLevel(level) + for handler in logger.handlers: + handler.setLevel(level) + + +def _addHandlers(loggers: Iterable[logging.Logger], use_default_handlers=False): + format = _ColorFormatter("%(asctime)s\t\t" + "%(levelname)s\t" + "%(name)s (%(filename)s:%(lineno)d)\t" + "%(message)s\t") + + handlers: List[logging.Handler] = [] + + std_handler = logging.StreamHandler(stream=sys.stdout) + std_handler.setFormatter(format) + # filter out logs at error level or above + std_handler.setLevel(LOG_LEVEL) + std_handler.addFilter(filter=lambda record: (record.levelno < ERROR)) + + err_handler = logging.StreamHandler(stream=sys.stderr) + err_handler.setFormatter(format) + # filter out logs below error level + err_handler.setLevel(max(ERROR, LOG_LEVEL)) + + if _MODULE_PARENT is not None and not use_default_handlers: + mod_handler = _ModuleHandler(_MODULE_PARENT) + mod_handler.setFormatter(format) + mod_handler.setLevel(LOG_LEVEL) + handlers = [mod_handler] + else: + handlers = [std_handler, err_handler] + + for logger in loggers: + logger.handlers.clear() + if "viam.sessions_client" in LOGGERS and LOGGERS["viam.sessions_client"] == logger: + logger.addHandler(std_handler) + logger.addHandler(err_handler) + else: + for h in handlers: + logger.addHandler(h) + + +def setParent(parent: "RobotClient"): + global _MODULE_PARENT + _MODULE_PARENT = parent + _addHandlers(LOGGERS.values()) + + def setLevel(level: int): + global LOG_LEVEL LOG_LEVEL = level for logger in LOGGERS.values(): logger.setLevel(LOG_LEVEL) + _addHandlers(LOGGERS.values()) + + +def silence(): + setLevel(FATAL + 1) + + +def shutdown(): + logging.shutdown() diff --git a/src/viam/media/__init__.py b/src/viam/media/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/media/audio.py b/src/viam/media/audio.py new file mode 100644 index 000000000..56e5a8800 --- /dev/null +++ b/src/viam/media/audio.py @@ -0,0 +1,16 @@ +from dataclasses import dataclass + +from viam.proto.component.audioinput import AudioChunk, AudioChunkInfo +from viam.streams import Stream, StreamReader + + +@dataclass +class Audio: + """A block of audio data containing information about the block and the audio data""" + + info: AudioChunkInfo + chunk: AudioChunk + + +AudioReader = StreamReader[Audio] +AudioStream = Stream[Audio] diff --git a/src/viam/media/utils/__init__.py b/src/viam/media/utils/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/media/utils/pil/__init__.py b/src/viam/media/utils/pil/__init__.py new file mode 100644 index 000000000..4335b2d09 --- /dev/null +++ b/src/viam/media/utils/pil/__init__.py @@ -0,0 +1,51 @@ +from io import BytesIO + +from PIL import Image + +from ....media.video import CameraMimeType, ViamImage +from .viam_rgba_plugin import RGBA_FORMAT_LABEL + +# Formats that are supported by PIL +LIBRARY_SUPPORTED_FORMATS = ["JPEG", "PNG", RGBA_FORMAT_LABEL] + + +def viam_to_pil_image(image: ViamImage) -> Image.Image: + """ + Convert a ViamImage to a PIL.Image. + + In order to use this function, Pillow must be installed. + https://pillow.readthedocs.io/ + + Args: + image (ViamImage): The image to convert. + + Returns: + Image.Image: The resulting PIL.Image + """ + return Image.open(BytesIO(image.data), formats=LIBRARY_SUPPORTED_FORMATS) + + +def pil_to_viam_image(image: Image.Image, mime_type: CameraMimeType) -> ViamImage: + """ + Convert a PIL.Image to a ViamImage. + + In order to use this function, Pillow must be installed. + https://pillow.readthedocs.io/ + + Args: + image (Image.Image): The image to convert. + mime_type (CameraMimeType): The mime type to convert the image to. + + Returns: + ViamImage: The resulting ViamImage + """ + if mime_type.name in LIBRARY_SUPPORTED_FORMATS: + buf = BytesIO() + if image.mode == "RGBA" and mime_type == CameraMimeType.JPEG: + image = image.convert("RGB") + image.save(buf, format=mime_type.name) + data = buf.getvalue() + else: + raise ValueError(f"Cannot encode image to {mime_type}") + + return ViamImage(data, mime_type) diff --git a/src/viam/media/utils/pil/viam_rgba_plugin.py b/src/viam/media/utils/pil/viam_rgba_plugin.py new file mode 100644 index 000000000..75d1711d0 --- /dev/null +++ b/src/viam/media/utils/pil/viam_rgba_plugin.py @@ -0,0 +1,73 @@ +from typing import ClassVar, Tuple + +from PIL import Image +from PIL.ImageFile import ImageFile, PyDecoder, PyEncoder, _safe_read # type: ignore -- (njooma) this exists, manually checked +from PIL.ImageFile import _save as image_save # type: ignore -- (njooma) this exists, manually checked + +from ...viam_rgba import RGBA_FORMAT_LABEL, RGBA_HEADER_LENGTH, RGBA_MAGIC_NUMBER + + +def _accept(prefix: str): + return prefix[:4] == RGBA_MAGIC_NUMBER + + +class RGBAEncoder(PyEncoder): + ENCODER_NAME = RGBA_FORMAT_LABEL + + _pushes_fd = True + + def encode(self, bufsize): # pyright: ignore [reportIncompatibleMethodOverride] + data_arr = bytearray() + width, height = self.im.size + for y in range(height): + for x in range(width): + data_arr.extend(self.im.getpixel((x, y))) + data = bytes(data_arr) + + return len(data), 0, data + + +def _save_rgba(img, fp, filename): + width, height = img.size + fp.write(RGBA_MAGIC_NUMBER) + fp.write(width.to_bytes(4, byteorder="big")) + fp.write(height.to_bytes(4, byteorder="big")) + + image_save(img, fp, [(RGBAEncoder.ENCODER_NAME, (0, 0, width, height), 0, ("RGBA", 0, 1))]) + + +class RGBAImage(ImageFile): + format: ClassVar[str] = RGBA_FORMAT_LABEL # pyright: ignore [reportIncompatibleVariableOverride] + format_description = "Viam's Raw RGBA Format" + + def _open(self): + header = self.fp.read(RGBA_HEADER_LENGTH) + + width = int.from_bytes(header[4:8], "big") + height = int.from_bytes(header[8:12], "big") + self._size = width, height + if hasattr(self, "_mode"): + self._mode = "RGBA" + else: + self.mode = "RGBA" # type: ignore -- (njooma) newer versions of PIL hide this behind _mode, which is why we check + + # data descriptor + self.tile = [(RGBAEncoder.ENCODER_NAME, (0, 0, width, height), RGBA_HEADER_LENGTH, (self.mode, 0, 1))] + + +class RGBADecoder(PyDecoder): + _pulls_fd = True + + def decode(self, buffer) -> Tuple[int, int]: + width, height = self.im.size + self.set_as_raw(_safe_read(self.fd, width * height * 4)) + return -1, 0 + + +# Register Viam's custom RGBA format so that it can be read using Image.open +Image.register_open(RGBAImage.format, RGBAImage, _accept) +Image.register_save(RGBAImage.format, _save_rgba) +Image.register_mime(RGBAImage.format, "image/vnd.viam.rgba") +Image.register_extension(RGBAImage.format, ".viam.rgba") +Image.register_encoder(RGBAEncoder.ENCODER_NAME, RGBAEncoder) +Image.register_decoder(RGBA_FORMAT_LABEL, RGBADecoder) diff --git a/src/viam/media/viam_rgba.py b/src/viam/media/viam_rgba.py new file mode 100644 index 000000000..c11ebe483 --- /dev/null +++ b/src/viam/media/viam_rgba.py @@ -0,0 +1,10 @@ +# Viam uses a special header prepended to raw RGBA data. The header is composed of a +# 4-byte magic number followed by a 4-byte line of the width as a uint32 number +# and another for the height. Credit to Ben Zotto for inventing this formulation +# https://bzotto.medium.com/introducing-the-rgba-bitmap-file-format-4a8a94329e2c + +RGBA_MAGIC_NUMBER = bytes("RGBA", "utf-8") + +RGBA_FORMAT_LABEL = "VIAM_RGBA" + +RGBA_HEADER_LENGTH = 12 diff --git a/src/viam/media/video.py b/src/viam/media/video.py new file mode 100644 index 000000000..3b3f7fcc2 --- /dev/null +++ b/src/viam/media/video.py @@ -0,0 +1,217 @@ +from array import array +from enum import Enum +from typing import List, Optional, Tuple + +from typing_extensions import Self + +from viam.errors import NotSupportedError +from viam.proto.component.camera import Format + +from .viam_rgba import RGBA_HEADER_LENGTH, RGBA_MAGIC_NUMBER + + +class CameraMimeType(str, Enum): + VIAM_RGBA = "image/vnd.viam.rgba" + VIAM_RAW_DEPTH = "image/vnd.viam.dep" + JPEG = "image/jpeg" + PNG = "image/png" + PCD = "pointcloud/pcd" + + @classmethod + def from_string(cls, value: str) -> Self: + """Return the mimetype from a string. + + Args: + value (str): The mimetype as a string + + Returns: + Self: The mimetype + """ + value_mime = value[:-5] if value.endswith("+lazy") else value # ViamImage lazy encodes by default + return cls(value_mime) + + @classmethod + def from_proto(cls, format: Format.ValueType) -> "CameraMimeType": + """Returns the mimetype from a proto enum. + + Args: + format (Format.ValueType): The mimetype in a proto enum. + + Returns: + Self: The mimetype. + """ + mimetypes = { + Format.FORMAT_RAW_RGBA: CameraMimeType.VIAM_RGBA, + Format.FORMAT_RAW_DEPTH: CameraMimeType.VIAM_RAW_DEPTH, + Format.FORMAT_JPEG: CameraMimeType.JPEG, + Format.FORMAT_PNG: CameraMimeType.PNG, + } + return mimetypes.get(format, CameraMimeType.JPEG) + + def to_proto(self) -> Format.ValueType: + """Returns the mimetype in a proto enum. + + Returns: + Format.ValueType: The mimetype in a proto enum. + """ + formats = { + self.VIAM_RGBA: Format.FORMAT_RAW_RGBA, + self.VIAM_RAW_DEPTH: Format.FORMAT_RAW_DEPTH, + self.JPEG: Format.FORMAT_JPEG, + self.PNG: Format.FORMAT_PNG, + } + return formats.get(self, Format.FORMAT_UNSPECIFIED) + + +class ViamImage: + """A native implementation of an image. + + Provides the raw data and the mime type. + """ + + _data: bytes + _mime_type: CameraMimeType + _height: Optional[int] = None + _width: Optional[int] = None + + def __init__(self, data: bytes, mime_type: CameraMimeType) -> None: + self._data = data + self._mime_type = mime_type + self._width, self._height = _getDimensions(data, mime_type) + + @property + def data(self) -> bytes: + """The raw bytes of the image""" + return self._data + + @property + def mime_type(self) -> CameraMimeType: + """The mime type of the image""" + return self._mime_type + + @property + def width(self) -> Optional[int]: + """The width of the image""" + return self._width + + @property + def height(self) -> Optional[int]: + """The height of the image""" + return self._height + + def bytes_to_depth_array(self) -> List[List[int]]: + """ + Decode the data of an image that has the custom depth MIME type ``image/vnd.viam.dep`` into a standard representation. + + Raises: + NotSupportedError: Raised if the image is not of MIME type `image/vnd.viam.dep`. + + Returns: + List[List[int]]: The standard representation of the image. + """ + if self.mime_type != CameraMimeType.VIAM_RAW_DEPTH: + raise NotSupportedError("Type must be `image/vnd.viam.dep` to use bytes_to_depth_array()") + + self._width = int.from_bytes(self.data[8:16], "big") + self._height = int.from_bytes(self.data[16:24], "big") + depth_arr = array("H", self.data[24:]) + depth_arr.byteswap() + + depth_arr_2d = [[depth_arr[row * self._width + col] for col in range(self._width)] for row in range(self._height)] + return depth_arr_2d + + +class NamedImage(ViamImage): + """An implementation of ViamImage that contains a name attribute.""" + + name: str + """The name of the image + """ + + def __init__(self, name: str, data: bytes, mime_type: CameraMimeType) -> None: + self.name = name + super().__init__(data, mime_type) + + +def _getDimensions(image: bytes, mime_type: CameraMimeType) -> Tuple[Optional[int], Optional[int]]: + try: + if mime_type == CameraMimeType.JPEG: + return _getDimensionsFromJPEG(image) + if mime_type == CameraMimeType.PNG: + return _getDimensionsFromPNG(image) + if mime_type == CameraMimeType.VIAM_RGBA: + return _getDimensionsFromRGBA(image) + except ValueError: + return (None, None) + return (None, None) + + +def _getDimensionsFromJPEG(image: bytes) -> Tuple[int, int]: + # JPEG Specification: https://www.w3.org/Graphics/JPEG/itu-t81.pdf + # Specification for markers: Table B.1 + + offset = 0 + while offset < len(image): + while image[offset] == 0xFF: + # Skip all 0xFF bytes + offset += 1 + + marker = image[offset] + offset += 1 + if marker == 0x01: + # Temporary/private use marker + offset += 1 + continue + if marker in range(0xD0, 0xD7): + # Restart (RST) marker + offset += 1 + continue + if marker == 0xD8: + # Start of image (SOI) marker + offset += 1 + continue + if marker == 0xD9: + # End of image (EOI) marker + break + + length = int.from_bytes(image[offset : offset + 1], byteorder="big") # length of section + if marker == 0xC0 or marker == 0xC2: + height = int.from_bytes(image[offset + 3 : offset + 5], byteorder="big") + width = int.from_bytes(image[offset + 5 : offset + 7], byteorder="big") + return (width, height) + + offset += length + + raise ValueError("Invalid JPEG: Could not extract dimensions") + + +def _getDimensionsFromPNG(image: bytes) -> Tuple[int, int]: + # PNG Specification: https://www.w3.org/TR/png/ + + # PNG will always start with this signature + signature = image[:8] + if signature != [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]: + ValueError("Invalid PNG: Invalid signature") + + header = image[12:24] + chunk_type = header[:4].decode() + if chunk_type != "IHDR": + ValueError("Invalid PNG: Invalid headers") + + width = int.from_bytes(header[4:8], byteorder="big") + height = int.from_bytes(header[8:], byteorder="big") + return (width, height) + + +def _getDimensionsFromRGBA(image: bytes) -> Tuple[int, int]: + # Viam RGBA header comes in 3 4-byte chunks: + # * Magic Number/Signature + # * Width + # * Height + header = image[:RGBA_HEADER_LENGTH] + if header[:4] != RGBA_MAGIC_NUMBER: + raise ValueError("Invalid Viam RGBA: Invalid headers") + + width = int.from_bytes(header[4:8], byteorder="big") + height = int.from_bytes(header[8:], byteorder="big") + return (width, height) diff --git a/src/viam/module/__init__.py b/src/viam/module/__init__.py new file mode 100644 index 000000000..b5da7c29a --- /dev/null +++ b/src/viam/module/__init__.py @@ -0,0 +1,5 @@ +from .module import Module + +__all__ = [ + "Module", +] diff --git a/src/viam/module/module.py b/src/viam/module/module.py new file mode 100644 index 000000000..983178376 --- /dev/null +++ b/src/viam/module/module.py @@ -0,0 +1,281 @@ +import argparse +import io +import logging as pylogging +import os +import sys +from inspect import iscoroutinefunction +from threading import Lock +from typing import List, Mapping, Optional, Sequence, Tuple + +from grpclib.metadata import Deadline +from grpclib.utils import _service_name +from typing_extensions import Self + +from viam import logging +from viam.errors import ResourceNotFoundError, ValidationError +from viam.logging import update_log_level +from viam.proto.app.robot import ComponentConfig +from viam.proto.module import ( + AddResourceRequest, + HandlerDefinition, + HandlerMap, + ReadyRequest, + ReadyResponse, + ReconfigureResourceRequest, + RemoveResourceRequest, + ValidateConfigRequest, + ValidateConfigResponse, +) +from viam.proto.robot import ResourceRPCSubtype +from viam.resource.base import ResourceBase +from viam.resource.registry import Registry +from viam.resource.types import API, RESOURCE_TYPE_COMPONENT, RESOURCE_TYPE_SERVICE, Model, ResourceName, resource_name_from_string +from viam.robot.client import RobotClient +from viam.rpc.dial import DialOptions +from viam.rpc.server import Server + +from .service import ModuleRPCService +from .types import Reconfigurable, Stoppable + + +def _parse_module_args() -> argparse.Namespace: + """ + Parse command-line args. Used by the various `Module` entrypoints. + """ + p = argparse.ArgumentParser(description="Start this viam python module") + p.add_argument("socket_path", help="path where this module will serve a unix socket") + p.add_argument("--log-level", type=lambda name: pylogging._nameToLevel[name.upper()], default=logging.INFO) + return p.parse_args() + + +class Module: + _address: str + _parent_address: Optional[str] = None + _ready: bool + _log_level: int + _lock: Lock + parent: Optional[RobotClient] = None + server: Server + logger: pylogging.Logger + + @classmethod + def from_args(cls) -> Self: + """Create a new Module with the args provided in the command line. The first argument after the command must be + the socket path. If the second argument after the command is "--log-level=debug", the Module's logger will be + DEBUG level. Otherwise, it will be INFO level. See LogLevel documentation in the RDK for more information on how + to start modules with a "log-level" commandline argument. + + Raises: + Exception: If there is no socket path provided in the command line argument + + Returns: + Module: a new Module instance + """ + args = _parse_module_args() + return cls(args.socket_path, log_level=args.log_level) + + @classmethod + async def run_with_models(cls, *models: ResourceBase): + """ + Module entrypoint that takes a list of ResourceBase implementations. + In most cases you'll want to use run_from_registry instead (see below). + """ + module = cls.from_args() + for model in models: + if not hasattr(model, "MODEL"): + raise TypeError(f"missing MODEL field on {model}. Resource implementations must define MODEL") + module.add_model_from_registry(model.API, model.MODEL) # pyright: ignore [reportAttributeAccessIssue] + await module.start() + + @classmethod + async def run_from_registry(cls): + """ + Module entrypoint that automatically includes all the resources you've created in your program. + + Example: + + if __name__ == '__main__': + asyncio.run(Module.run_from_registry()) + + Full example at examples/easy_resource/main.py. + """ + module = cls.from_args() + for key in Registry.REGISTERED_RESOURCE_CREATORS().keys(): + module.add_model_from_registry(*key.split("/")) # pyright: ignore [reportArgumentType] + await module.start() + + def __init__(self, address: str, *, log_level: int = logging.INFO) -> None: + # When a module is launched by viam-server, its stdout is not connected to a tty. In + # response, python disables line buffering, which prevents `print` statements from being + # immediately flushed to viam-server. This behavior can be confusing, interfere with + # debugging, and is non-standard when compared to other languages. Here, stdout and stderr + # are reconfigured to immediately flush. + if isinstance(sys.stdout, io.TextIOWrapper): + sys.stdout.reconfigure(line_buffering=True) + if isinstance(sys.stderr, io.TextIOWrapper): + sys.stderr.reconfigure(line_buffering=True) + self._address = address + self.server = Server(resources=[], module_service=ModuleRPCService(self)) + self._log_level = log_level + + module_name = os.environ.get("VIAM_MODULE_NAME") + # this can happen if the user is running an old version of viam-server that doesn't set `VIAM_MODULE_NAME` + if module_name is None: + module_name = __name__ + self.logger = logging.getLogger(module_name) + self._ready = True + self._lock = Lock() + + async def _connect_to_parent(self): + if self.parent is None: + if self._parent_address is None: + raise ValueError("Parent address not found") + self.parent = await RobotClient.at_address( + self._parent_address, + RobotClient.Options( + dial_options=DialOptions(disable_webrtc=True, insecure=True), + log_level=self._log_level, + ), + ) + self.logger.debug("Starting module logging") + logging.setParent(self.parent) + + async def _get_resource(self, name: ResourceName) -> ResourceBase: + await self._connect_to_parent() + assert self.parent is not None + await self.parent.refresh() + if name.type == RESOURCE_TYPE_COMPONENT: + return self.parent.get_component(name) + elif name.type == RESOURCE_TYPE_SERVICE: + return self.parent.get_service(name) + raise ValueError("Dependency does not describe a component nor a service") + + async def _get_dependencies(self, dependencies: Sequence[str]) -> Mapping[ResourceName, ResourceBase]: + deps: Mapping[ResourceName, ResourceBase] = {} + for dep in dependencies: + rn = resource_name_from_string(dep) + deps[rn] = await self._get_resource(rn) + return deps + + async def start(self): + """Start the module service and gRPC server""" + try: + await self.server.serve(log_level=self._log_level, path=self._address) + finally: + await self.stop() + + async def stop(self): + """Stop the module service and gRPC server""" + self.logger.debug("Shutting down module") + try: + logging.shutdown() + if self.parent is not None: + await self.parent.close() + except Exception as e: + self.logger.error("Encountered error while shutting down module", exc_info=e) + + def set_ready(self, ready: bool): + """Set the module's ready state. The module automatically sets to READY on load. Setting to False can be useful + in instances where the module is not instantly ready (for example waiting on hardware) + + Args: + ready (bool): Whether the module is ready + """ + with self._lock: + self._ready = ready + + async def add_resource(self, request: AddResourceRequest, *, deadline: Optional[Deadline] = None): + dependencies = await self._get_dependencies(request.dependencies) + config: ComponentConfig = request.config + api = API.from_string(config.api) + model = Model.from_string(config.model, ignore_errors=True) + creator = Registry.lookup_resource_creator(api, model) + resource = creator(config, dependencies) + if deadline is not None and deadline.time_remaining() <= 0: + raise TimeoutError("Deadline expired") + update_log_level(resource.logger, config.log_configuration.level.upper()) + self.server.register(resource) + + async def reconfigure_resource(self, request: ReconfigureResourceRequest): + dependencies = await self._get_dependencies(request.dependencies) + config: ComponentConfig = request.config + api = API.from_string(config.api) + name = config.name + rn = ResourceName(namespace=api.namespace, type=api.resource_type, subtype=api.resource_subtype, name=name) + resource = self.server.get_resource(ResourceBase, rn) + if isinstance(resource, Reconfigurable): + resource.reconfigure(config, dependencies) + else: + if isinstance(resource, Stoppable): + if iscoroutinefunction(resource.stop): + await resource.stop() + else: + resource.stop() + add_request = AddResourceRequest(config=request.config, dependencies=request.dependencies) + await self.server.remove_resource(rn) + await self.add_resource(add_request) + + async def remove_resource(self, request: RemoveResourceRequest): + rn = resource_name_from_string(request.name) + resource = self.server.get_resource(ResourceBase, rn) + if isinstance(resource, Stoppable): + if iscoroutinefunction(resource.stop): + await resource.stop() + else: + resource.stop() + await self.server.remove_resource(rn) + + async def ready(self, request: ReadyRequest) -> ReadyResponse: + self._parent_address = request.parent_address + await self._connect_to_parent() + + svcname_to_models: Mapping[Tuple[str, API], List[Model]] = {} + for api_model_str in Registry.REGISTERED_RESOURCE_CREATORS().keys(): + api_str, model_str = api_model_str.split("/") + api = API.from_string(api_str) + model = Model.from_string(model_str) + + registration = Registry.lookup_api(api) + service = registration.rpc_service(self.server) + service_name = _service_name(service) + + models = svcname_to_models.get((service_name, api), []) + models.append(model) + svcname_to_models[(service_name, api)] = models + + handlers: List[HandlerDefinition] = [] + for key, value in svcname_to_models.items(): + svc_name, api = key + rpc_subtype = ResourceRPCSubtype( + subtype=ResourceName( + namespace=api.namespace, + type=api.resource_type, + subtype=api.resource_subtype, + name="", + ), + proto_service=svc_name, + ) + handler_def = HandlerDefinition(subtype=rpc_subtype, models=[str(model) for model in value]) + handlers.append(handler_def) + + return ReadyResponse(ready=self._ready, handlermap=HandlerMap(handlers=handlers)) + + def add_model_from_registry(self, api: API, model: Model): + """Add a pre-registered model to this Module""" + + # All we need to do is double check that the model has already been registered + try: + Registry.lookup_resource_creator(api, model) + except ResourceNotFoundError: + raise ValueError(f"Cannot add model because it has not been registered. API: {api}. Model: {model}") + + async def validate_config(self, request: ValidateConfigRequest) -> ValidateConfigResponse: + config: ComponentConfig = request.config + api = API.from_string(config.api) + model = Model.from_string(config.model) + validator = Registry.lookup_validator(api, model) + try: + dependencies = validator(config) + return ValidateConfigResponse(dependencies=dependencies) + except Exception as e: + raise ValidationError(f"{type(Exception)}: {e}").grpc_error diff --git a/src/viam/module/service.py b/src/viam/module/service.py new file mode 100644 index 000000000..b07168f1d --- /dev/null +++ b/src/viam/module/service.py @@ -0,0 +1,66 @@ +from typing import TYPE_CHECKING + +from grpclib import Status +from grpclib.server import Stream + +from viam.errors import ViamGRPCError +from viam.proto.module import ( + AddResourceRequest, + AddResourceResponse, + ModuleServiceBase, + ReadyRequest, + ReadyResponse, + ReconfigureResourceRequest, + ReconfigureResourceResponse, + RemoveResourceRequest, + RemoveResourceResponse, + ValidateConfigRequest, + ValidateConfigResponse, +) + +if TYPE_CHECKING: + from .module import Module + + +class ModuleRPCService(ModuleServiceBase): + _module: "Module" + + def __init__(self, module: "Module") -> None: + self._module = module + + async def AddResource(self, stream: Stream[AddResourceRequest, AddResourceResponse]) -> None: + request = await stream.recv_message() + assert request is not None + try: + await self._module.add_resource(request, deadline=stream.deadline) + except TimeoutError: + raise ViamGRPCError( + message="Timeout while adding resource", + grpc_code=Status.DEADLINE_EXCEEDED, + ) + await stream.send_message(AddResourceResponse()) + + async def ReconfigureResource(self, stream: Stream[ReconfigureResourceRequest, ReconfigureResourceResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await self._module.reconfigure_resource(request) + await stream.send_message(ReconfigureResourceResponse()) + + async def RemoveResource(self, stream: Stream[RemoveResourceRequest, RemoveResourceResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await self._module.remove_resource(request) + await stream.send_message(RemoveResourceResponse()) + + async def Ready(self, stream: Stream[ReadyRequest, ReadyResponse]) -> None: + request = await stream.recv_message() + assert request is not None + response = await self._module.ready(request) + await stream.send_message(response) + + async def ValidateConfig(self, stream: Stream[ValidateConfigRequest, ValidateConfigResponse]) -> None: + request = await stream.recv_message() + assert request is not None + response = await self._module.validate_config(request) + if response is not None: + await stream.send_message(response) diff --git a/src/viam/module/types.py b/src/viam/module/types.py new file mode 100644 index 000000000..b1cd3ea73 --- /dev/null +++ b/src/viam/module/types.py @@ -0,0 +1,23 @@ +from typing import Any, Mapping, Optional, Protocol, runtime_checkable + +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ResourceName +from viam.resource.base import ResourceBase + + +@runtime_checkable +class Reconfigurable(Protocol): + """The Reconfigurable protocol defines the requirements for making a resource Reconfigurable""" + + def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]): ... + + +@runtime_checkable +class Stoppable(Protocol): + """ + The Stoppable protocol defines the requirements for making a resource Stoppable. + + All resources that physically move should be Stoppable. + """ + + def stop(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs): ... diff --git a/src/viam/operations.py b/src/viam/operations.py new file mode 100644 index 000000000..bb016cf13 --- /dev/null +++ b/src/viam/operations.py @@ -0,0 +1,124 @@ +import asyncio +import functools +import sys +import time +from typing import Any, Callable, Coroutine, Mapping, Optional, TypeVar, cast +from uuid import UUID, uuid4 + +from typing_extensions import Self + +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + + +class Operation: + """An Operation represents a running operation. + + Every request made to a robot's components will create a new Operation on the server. For custom components built with this python-sdk, + you should check whether the operation has been cancelled to prevent long-running tasks from leaking. + """ + + ARG_NAME = "viam_operation" + + id: UUID + method: str + time_started: float + _cancel_event: asyncio.Event + _cancelled: bool + + def __init__(self, method: str, cancel_event: asyncio.Event, opid: Optional[UUID] = None) -> None: + self.id = uuid4() if opid is None else opid + self.method = method + self.time_started = time.time() + self._cancel_event = cancel_event + self._cancelled = False + + async def is_cancelled(self) -> bool: + if self._cancelled: + return self._cancelled + if self._cancel_event.is_set(): + self._cancelled = True + return self._cancelled + try: + await asyncio.sleep(0) + except asyncio.CancelledError: + self._cancelled = True + return self._cancelled + return False + + def __str__(self) -> str: + return f"Operation {self.id} : {self.method}" + + @classmethod + def _noop(cls) -> Self: + """Obtain a noop Operation. + This operation will always return ``False`` for ``is_cancelled()`` + """ + return cls("noop-operation", asyncio.Event()) + + +P = ParamSpec("P") +T = TypeVar("T") + +METADATA_KEY = "opid" + + +def opid_from_metadata(metadata: Optional[Mapping[str, str]]) -> Optional[UUID]: + if metadata is None: + return None + + opid = metadata.get(METADATA_KEY) + if opid is None: + return None + + return UUID(opid) + + +def run_with_operation(func: Callable[P, Coroutine[Any, Any, T]]) -> Callable[P, Coroutine[Any, Any, T]]: + """Run a component function with an ``Operation``. + Running a function with an Operation will allow the function + to know if/when the calling task was cancelled and take appropriate action + (for example stop long running tasks and exit early). + + If a timeout is provided to the function, the operation will cancel when the timeout is reached. + + An example use case is if a gRPC client disconnects after making a request. + Rather than continue to run a task for a receiver that is no longer there, + the component can cancel and clean up, saving resources. + + Args: + func (Callable[..., Coroutine[Any, Any, T]]): The function to be called with an Operation. + This function MUST accept ``**kwargs`` + or a parameter whose name is equal the value of ``Operation.ARG_NAME`` + + Returns: + T: The return of the function + """ + + @functools.wraps(func) + async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: + event = asyncio.Event() + func_name = func.__qualname__ + arg_names = ", ".join([str(a) for a in args]) + kwarg_names = ", ".join([f"{key}={value}" for (key, value) in kwargs.items()]) + method = f"{func_name}({arg_names}{', ' if len(arg_names) else ''}{kwarg_names})" + opid = opid_from_metadata(kwargs.get("metadata")) # type: ignore + operation = Operation(method, event, opid=opid) + kwargs[Operation.ARG_NAME] = operation + timeout = kwargs.get("timeout", None) + timer: Optional[asyncio.TimerHandle] = None + if timeout: + timeout = cast(float, timeout) + timer = asyncio.get_running_loop().call_later(timeout, event.set) + try: + return await asyncio.shield(func(*args, **kwargs)) + except asyncio.CancelledError: + event.set() + raise + finally: + if timer: + timer.cancel() + + return wrapper diff --git a/src/viam/proto/app/__init__.py b/src/viam/proto/app/__init__.py index 8726fee6d..6b307a839 100644 --- a/src/viam/proto/app/__init__.py +++ b/src/viam/proto/app/__init__.py @@ -2,95 +2,553 @@ @generated by Viam. Do not edit manually! """ -from ...gen.app.v1.app_grpc import AppServiceBase, AppServiceStub + +from ...gen.app.v1.app_grpc import AppServiceBase, AppServiceStub, UnimplementedAppServiceBase from ...gen.app.v1.app_pb2 import ( + PKCE, + AdditionalFragment, + AddRoleRequest, + AddRoleResponse, + APIKey, + APIKeyWithAuthorizations, + App, + AuthenticationType, + AuthenticatorInfo, + Authorization, + AuthorizationDetails, + AuthorizedPermissions, + BillingAddress, + ChangeRoleRequest, + ChangeRoleResponse, + CheckPermissionsRequest, + CheckPermissionsResponse, + ClientAuthentication, + CreateFragmentRequest, + CreateFragmentResponse, + CreateKeyFromExistingKeyAuthorizationsRequest, + CreateKeyFromExistingKeyAuthorizationsResponse, + CreateKeyRequest, + CreateKeyResponse, + CreateLocationRequest, + CreateLocationResponse, + CreateLocationSecretRequest, + CreateLocationSecretResponse, + CreateModuleRequest, + CreateModuleResponse, + CreateOAuthAppRequest, + CreateOAuthAppResponse, + CreateOrganizationInviteRequest, + CreateOrganizationInviteResponse, + CreateOrganizationRequest, + CreateOrganizationResponse, + CreateRegistryItemRequest, + CreateRegistryItemResponse, + CreateRobotPartSecretRequest, + CreateRobotPartSecretResponse, + DeleteFragmentRequest, + DeleteFragmentResponse, + DeleteFragmentTagRequest, + DeleteFragmentTagResponse, + DeleteKeyRequest, + DeleteKeyResponse, + DeleteLocationRequest, + DeleteLocationResponse, + DeleteLocationSecretRequest, + DeleteLocationSecretResponse, + DeleteOAuthAppRequest, + DeleteOAuthAppResponse, + DeleteOrganizationInviteRequest, + DeleteOrganizationInviteResponse, + DeleteOrganizationMemberRequest, + DeleteOrganizationMemberResponse, + DeleteOrganizationRequest, + DeleteOrganizationResponse, + DeleteRegistryItemRequest, + DeleteRegistryItemResponse, DeleteRobotPartRequest, DeleteRobotPartResponse, + DeleteRobotPartSecretRequest, + DeleteRobotPartSecretResponse, DeleteRobotRequest, DeleteRobotResponse, - FindRobotsRequest, - FindRobotsResponse, + DisableAuthServiceRequest, + DisableAuthServiceResponse, + DisableBillingServiceRequest, + DisableBillingServiceResponse, + EnableAuthServiceRequest, + EnableAuthServiceResponse, + EnableBillingServiceRequest, + EnableBillingServiceResponse, + EnabledGrant, Fragment, + FragmentError, + FragmentErrorType, + FragmentHistoryEntry, + FragmentRevision, + FragmentSummary, + FragmentTag, + FragmentUsage, + FragmentVisibility, + GetAppContentRequest, + GetAppContentResponse, + GetBillingServiceConfigRequest, + GetBillingServiceConfigResponse, + GetFragmentHistoryRequest, + GetFragmentHistoryResponse, + GetFragmentRequest, + GetFragmentResponse, + GetFragmentUsageRequest, + GetFragmentUsageResponse, + GetLocationMetadataRequest, + GetLocationMetadataResponse, + GetLocationRequest, + GetLocationResponse, + GetModuleRequest, + GetModuleResponse, + GetOrganizationMetadataRequest, + GetOrganizationMetadataResponse, + GetOrganizationNamespaceAvailabilityRequest, + GetOrganizationNamespaceAvailabilityResponse, + GetOrganizationRequest, + GetOrganizationResponse, + GetOrganizationsWithAccessToLocationRequest, + GetOrganizationsWithAccessToLocationResponse, + GetRegistryItemRequest, + GetRegistryItemResponse, + GetRobotAPIKeysRequest, + GetRobotAPIKeysResponse, + GetRobotMetadataRequest, + GetRobotMetadataResponse, GetRobotPartHistoryRequest, GetRobotPartHistoryResponse, GetRobotPartLogsRequest, GetRobotPartLogsResponse, + GetRobotPartMetadataRequest, + GetRobotPartMetadataResponse, GetRobotPartRequest, GetRobotPartResponse, GetRobotPartsRequest, GetRobotPartsResponse, GetRobotRequest, GetRobotResponse, + GetRoverRentalRobotsRequest, + GetRoverRentalRobotsResponse, + GetUserIDByEmailRequest, + GetUserIDByEmailResponse, + ListAuthorizationsRequest, + ListAuthorizationsResponse, + ListFragmentsRequest, + ListFragmentsResponse, + ListKeysRequest, + ListKeysResponse, ListLocationsRequest, ListLocationsResponse, + ListMachineFragmentsRequest, + ListMachineFragmentsResponse, + ListMachineSummariesRequest, + ListMachineSummariesResponse, + ListModulesRequest, + ListModulesResponse, + ListNestedFragmentsRequest, + ListNestedFragmentsResponse, + ListOAuthAppsRequest, + ListOAuthAppsResponse, + ListOrganizationMembersRequest, + ListOrganizationMembersResponse, + ListOrganizationsByUserRequest, + ListOrganizationsByUserResponse, ListOrganizationsRequest, ListOrganizationsResponse, + ListRegistryItemsRequest, + ListRegistryItemsResponse, + ListRobotsRequest, + ListRobotsResponse, Location, LocationAuth, LocationAuthRequest, LocationAuthResponse, - LogEntry, + LocationOrganization, + LocationSummary, + MachineSummary, MarkPartAsMainRequest, MarkPartAsMainResponse, + MarkPartForRestartRequest, + MarkPartForRestartResponse, + MLModelMetadata, + MLTrainingMetadata, + MLTrainingVersion, + Model, + Module, + ModuleFileInfo, + ModuleMetadata, + ModuleVersion, NewRobotPartRequest, NewRobotPartResponse, NewRobotRequest, NewRobotResponse, + OAuthConfig, Organization, + OrganizationGetLogoRequest, + OrganizationGetLogoResponse, + OrganizationGetSupportEmailRequest, + OrganizationGetSupportEmailResponse, + OrganizationIdentity, + OrganizationInvite, + OrganizationMember, + OrganizationSetLogoRequest, + OrganizationSetLogoResponse, + OrganizationSetSupportEmailRequest, + OrganizationSetSupportEmailResponse, + OrgDetails, + PartSummary, + ReadOAuthAppRequest, + ReadOAuthAppResponse, + RegistryItem, + RegistryItemStatus, + RemoveRoleRequest, + RemoveRoleResponse, + RenameKeyRequest, + RenameKeyResponse, + RenameRegistryItemRequest, + RenameRegistryItemResponse, + ResendOrganizationInviteRequest, + ResendOrganizationInviteResponse, + ResolvedFragment, Robot, RobotPart, RobotPartHistoryEntry, + RotateKeyRequest, + RotateKeyResponse, + RoverRentalRobot, + SearchOrganizationsRequest, + SearchOrganizationsResponse, + SetFragmentTagRequest, + SetFragmentTagResponse, + SharedSecret, + ShareLocationRequest, + ShareLocationResponse, + StorageConfig, TailRobotPartLogsRequest, TailRobotPartLogsResponse, + TransferRegistryItemRequest, + TransferRegistryItemResponse, + UnshareLocationRequest, + UnshareLocationResponse, + UpdateBillingServiceRequest, + UpdateBillingServiceResponse, + UpdateFragmentRequest, + UpdateFragmentResponse, + UpdateLocationMetadataRequest, + UpdateLocationMetadataResponse, + UpdateLocationRequest, + UpdateLocationResponse, + UpdateMLModelMetadata, + UpdateMLTrainingMetadata, + UpdateModuleMetadata, + UpdateModuleRequest, + UpdateModuleResponse, + UpdateOAuthAppRequest, + UpdateOAuthAppResponse, + UpdateOrganizationInviteAuthorizationsRequest, + UpdateOrganizationInviteAuthorizationsResponse, + UpdateOrganizationMetadataRequest, + UpdateOrganizationMetadataResponse, + UpdateOrganizationNamespaceRequest, + UpdateOrganizationNamespaceResponse, + UpdateOrganizationRequest, + UpdateOrganizationResponse, + UpdateRegistryItemRequest, + UpdateRegistryItemResponse, + UpdateRobotMetadataRequest, + UpdateRobotMetadataResponse, + UpdateRobotPartMetadataRequest, + UpdateRobotPartMetadataResponse, UpdateRobotPartRequest, UpdateRobotPartResponse, UpdateRobotRequest, UpdateRobotResponse, + UploadModuleFileRequest, + UploadModuleFileResponse, + Uploads, + URLValidation, + VersionHistory, + ViamAgentVersion, + ViamServerVersion, + Visibility, ) __all__ = [ "AppServiceBase", "AppServiceStub", + "UnimplementedAppServiceBase", + "APIKey", + "APIKeyWithAuthorizations", + "AddRoleRequest", + "AddRoleResponse", + "AdditionalFragment", + "App", + "AuthenticationType", + "AuthenticatorInfo", + "Authorization", + "AuthorizationDetails", + "AuthorizedPermissions", + "BillingAddress", + "ChangeRoleRequest", + "ChangeRoleResponse", + "CheckPermissionsRequest", + "CheckPermissionsResponse", + "ClientAuthentication", + "CreateFragmentRequest", + "CreateFragmentResponse", + "CreateKeyFromExistingKeyAuthorizationsRequest", + "CreateKeyFromExistingKeyAuthorizationsResponse", + "CreateKeyRequest", + "CreateKeyResponse", + "CreateLocationRequest", + "CreateLocationResponse", + "CreateLocationSecretRequest", + "CreateLocationSecretResponse", + "CreateModuleRequest", + "CreateModuleResponse", + "CreateOAuthAppRequest", + "CreateOAuthAppResponse", + "CreateOrganizationInviteRequest", + "CreateOrganizationInviteResponse", + "CreateOrganizationRequest", + "CreateOrganizationResponse", + "CreateRegistryItemRequest", + "CreateRegistryItemResponse", + "CreateRobotPartSecretRequest", + "CreateRobotPartSecretResponse", + "DeleteFragmentRequest", + "DeleteFragmentResponse", + "DeleteFragmentTagRequest", + "DeleteFragmentTagResponse", + "DeleteKeyRequest", + "DeleteKeyResponse", + "DeleteLocationRequest", + "DeleteLocationResponse", + "DeleteLocationSecretRequest", + "DeleteLocationSecretResponse", + "DeleteOAuthAppRequest", + "DeleteOAuthAppResponse", + "DeleteOrganizationInviteRequest", + "DeleteOrganizationInviteResponse", + "DeleteOrganizationMemberRequest", + "DeleteOrganizationMemberResponse", + "DeleteOrganizationRequest", + "DeleteOrganizationResponse", + "DeleteRegistryItemRequest", + "DeleteRegistryItemResponse", "DeleteRobotPartRequest", "DeleteRobotPartResponse", + "DeleteRobotPartSecretRequest", + "DeleteRobotPartSecretResponse", "DeleteRobotRequest", "DeleteRobotResponse", - "FindRobotsRequest", - "FindRobotsResponse", + "DisableAuthServiceRequest", + "DisableAuthServiceResponse", + "DisableBillingServiceRequest", + "DisableBillingServiceResponse", + "EnableAuthServiceRequest", + "EnableAuthServiceResponse", + "EnableBillingServiceRequest", + "EnableBillingServiceResponse", + "EnabledGrant", "Fragment", + "FragmentError", + "FragmentErrorType", + "FragmentHistoryEntry", + "FragmentRevision", + "FragmentSummary", + "FragmentTag", + "FragmentUsage", + "FragmentVisibility", + "GetAppContentRequest", + "GetAppContentResponse", + "GetBillingServiceConfigRequest", + "GetBillingServiceConfigResponse", + "GetFragmentHistoryRequest", + "GetFragmentHistoryResponse", + "GetFragmentRequest", + "GetFragmentResponse", + "GetFragmentUsageRequest", + "GetFragmentUsageResponse", + "GetLocationMetadataRequest", + "GetLocationMetadataResponse", + "GetLocationRequest", + "GetLocationResponse", + "GetModuleRequest", + "GetModuleResponse", + "GetOrganizationMetadataRequest", + "GetOrganizationMetadataResponse", + "GetOrganizationNamespaceAvailabilityRequest", + "GetOrganizationNamespaceAvailabilityResponse", + "GetOrganizationRequest", + "GetOrganizationResponse", + "GetOrganizationsWithAccessToLocationRequest", + "GetOrganizationsWithAccessToLocationResponse", + "GetRegistryItemRequest", + "GetRegistryItemResponse", + "GetRobotAPIKeysRequest", + "GetRobotAPIKeysResponse", + "GetRobotMetadataRequest", + "GetRobotMetadataResponse", "GetRobotPartHistoryRequest", "GetRobotPartHistoryResponse", "GetRobotPartLogsRequest", "GetRobotPartLogsResponse", + "GetRobotPartMetadataRequest", + "GetRobotPartMetadataResponse", "GetRobotPartRequest", "GetRobotPartResponse", "GetRobotPartsRequest", "GetRobotPartsResponse", "GetRobotRequest", "GetRobotResponse", + "GetRoverRentalRobotsRequest", + "GetRoverRentalRobotsResponse", + "GetUserIDByEmailRequest", + "GetUserIDByEmailResponse", + "ListAuthorizationsRequest", + "ListAuthorizationsResponse", + "ListFragmentsRequest", + "ListFragmentsResponse", + "ListKeysRequest", + "ListKeysResponse", "ListLocationsRequest", "ListLocationsResponse", + "ListMachineFragmentsRequest", + "ListMachineFragmentsResponse", + "ListMachineSummariesRequest", + "ListMachineSummariesResponse", + "ListModulesRequest", + "ListModulesResponse", + "ListNestedFragmentsRequest", + "ListNestedFragmentsResponse", + "ListOAuthAppsRequest", + "ListOAuthAppsResponse", + "ListOrganizationMembersRequest", + "ListOrganizationMembersResponse", + "ListOrganizationsByUserRequest", + "ListOrganizationsByUserResponse", "ListOrganizationsRequest", "ListOrganizationsResponse", + "ListRegistryItemsRequest", + "ListRegistryItemsResponse", + "ListRobotsRequest", + "ListRobotsResponse", "Location", "LocationAuth", "LocationAuthRequest", "LocationAuthResponse", - "LogEntry", + "LocationOrganization", + "LocationSummary", + "MLModelMetadata", + "MLTrainingMetadata", + "MLTrainingVersion", + "MachineSummary", "MarkPartAsMainRequest", "MarkPartAsMainResponse", + "MarkPartForRestartRequest", + "MarkPartForRestartResponse", + "Model", + "Module", + "ModuleFileInfo", + "ModuleMetadata", + "ModuleVersion", "NewRobotPartRequest", "NewRobotPartResponse", "NewRobotRequest", "NewRobotResponse", + "OAuthConfig", + "OrgDetails", "Organization", + "OrganizationGetLogoRequest", + "OrganizationGetLogoResponse", + "OrganizationGetSupportEmailRequest", + "OrganizationGetSupportEmailResponse", + "OrganizationIdentity", + "OrganizationInvite", + "OrganizationMember", + "OrganizationSetLogoRequest", + "OrganizationSetLogoResponse", + "OrganizationSetSupportEmailRequest", + "OrganizationSetSupportEmailResponse", + "PKCE", + "PartSummary", + "ReadOAuthAppRequest", + "ReadOAuthAppResponse", + "RegistryItem", + "RegistryItemStatus", + "RemoveRoleRequest", + "RemoveRoleResponse", + "RenameKeyRequest", + "RenameKeyResponse", + "RenameRegistryItemRequest", + "RenameRegistryItemResponse", + "ResendOrganizationInviteRequest", + "ResendOrganizationInviteResponse", + "ResolvedFragment", "Robot", "RobotPart", "RobotPartHistoryEntry", + "RotateKeyRequest", + "RotateKeyResponse", + "RoverRentalRobot", + "SearchOrganizationsRequest", + "SearchOrganizationsResponse", + "SetFragmentTagRequest", + "SetFragmentTagResponse", + "ShareLocationRequest", + "ShareLocationResponse", + "SharedSecret", + "StorageConfig", "TailRobotPartLogsRequest", "TailRobotPartLogsResponse", + "TransferRegistryItemRequest", + "TransferRegistryItemResponse", + "URLValidation", + "UnshareLocationRequest", + "UnshareLocationResponse", + "UpdateBillingServiceRequest", + "UpdateBillingServiceResponse", + "UpdateFragmentRequest", + "UpdateFragmentResponse", + "UpdateLocationMetadataRequest", + "UpdateLocationMetadataResponse", + "UpdateLocationRequest", + "UpdateLocationResponse", + "UpdateMLModelMetadata", + "UpdateMLTrainingMetadata", + "UpdateModuleMetadata", + "UpdateModuleRequest", + "UpdateModuleResponse", + "UpdateOAuthAppRequest", + "UpdateOAuthAppResponse", + "UpdateOrganizationInviteAuthorizationsRequest", + "UpdateOrganizationInviteAuthorizationsResponse", + "UpdateOrganizationMetadataRequest", + "UpdateOrganizationMetadataResponse", + "UpdateOrganizationNamespaceRequest", + "UpdateOrganizationNamespaceResponse", + "UpdateOrganizationRequest", + "UpdateOrganizationResponse", + "UpdateRegistryItemRequest", + "UpdateRegistryItemResponse", + "UpdateRobotMetadataRequest", + "UpdateRobotMetadataResponse", + "UpdateRobotPartMetadataRequest", + "UpdateRobotPartMetadataResponse", "UpdateRobotPartRequest", "UpdateRobotPartResponse", "UpdateRobotRequest", "UpdateRobotResponse", + "UploadModuleFileRequest", + "UploadModuleFileResponse", + "Uploads", + "VersionHistory", + "ViamAgentVersion", + "ViamServerVersion", + "Visibility", ] diff --git a/src/viam/proto/app/agent/__init__.py b/src/viam/proto/app/agent/__init__.py new file mode 100644 index 000000000..d65e26305 --- /dev/null +++ b/src/viam/proto/app/agent/__init__.py @@ -0,0 +1,28 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.app.agent.v1.agent_grpc import AgentDeviceServiceBase, AgentDeviceServiceStub, UnimplementedAgentDeviceServiceBase +from ....gen.app.agent.v1.agent_pb2 import ( + DeviceAgentConfigRequest, + DeviceAgentConfigResponse, + DeviceSubsystemConfig, + HostInfo, + PackageFormat, + UpdateInfo, + VersionInfo, +) + +__all__ = [ + "AgentDeviceServiceBase", + "AgentDeviceServiceStub", + "UnimplementedAgentDeviceServiceBase", + "DeviceAgentConfigRequest", + "DeviceAgentConfigResponse", + "DeviceSubsystemConfig", + "HostInfo", + "PackageFormat", + "UpdateInfo", + "VersionInfo", +] diff --git a/src/viam/proto/app/billing.py b/src/viam/proto/app/billing.py new file mode 100644 index 000000000..dde0cf3c9 --- /dev/null +++ b/src/viam/proto/app/billing.py @@ -0,0 +1,58 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ...gen.app.v1.billing_grpc import BillingServiceBase, BillingServiceStub, UnimplementedBillingServiceBase +from ...gen.app.v1.billing_pb2 import ( + GetAvailableBillingTiersRequest, + GetAvailableBillingTiersResponse, + GetCurrentMonthUsageRequest, + GetCurrentMonthUsageResponse, + GetInvoicePdfRequest, + GetInvoicePdfResponse, + GetInvoicesSummaryRequest, + GetInvoicesSummaryResponse, + GetOrgBillingInformationRequest, + GetOrgBillingInformationResponse, + InvoiceSummary, + PaymentMethodCard, + PaymentMethodType, + ResourceUsageCosts, + ResourceUsageCostsBySource, + SendPaymentRequiredEmailRequest, + SendPaymentRequiredEmailResponse, + SourceType, + UpdateOrganizationBillingTierRequest, + UpdateOrganizationBillingTierResponse, + UsageCost, + UsageCostType, +) + +__all__ = [ + "BillingServiceBase", + "BillingServiceStub", + "UnimplementedBillingServiceBase", + "GetAvailableBillingTiersRequest", + "GetAvailableBillingTiersResponse", + "GetCurrentMonthUsageRequest", + "GetCurrentMonthUsageResponse", + "GetInvoicePdfRequest", + "GetInvoicePdfResponse", + "GetInvoicesSummaryRequest", + "GetInvoicesSummaryResponse", + "GetOrgBillingInformationRequest", + "GetOrgBillingInformationResponse", + "InvoiceSummary", + "PaymentMethodCard", + "PaymentMethodType", + "ResourceUsageCosts", + "ResourceUsageCostsBySource", + "SendPaymentRequiredEmailRequest", + "SendPaymentRequiredEmailResponse", + "SourceType", + "UpdateOrganizationBillingTierRequest", + "UpdateOrganizationBillingTierResponse", + "UsageCost", + "UsageCostType", +] diff --git a/src/viam/proto/app/cloudslam/__init__.py b/src/viam/proto/app/cloudslam/__init__.py new file mode 100644 index 000000000..1f01b79f5 --- /dev/null +++ b/src/viam/proto/app/cloudslam/__init__.py @@ -0,0 +1,48 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.app.cloudslam.v1.cloud_slam_grpc import CloudSLAMServiceBase, CloudSLAMServiceStub, UnimplementedCloudSLAMServiceBase +from ....gen.app.cloudslam.v1.cloud_slam_pb2 import ( + CaptureInterval, + EndStatus, + GetActiveMappingSessionsForRobotRequest, + GetActiveMappingSessionsForRobotResponse, + GetMappingSessionMetadataByIDRequest, + GetMappingSessionMetadataByIDResponse, + GetMappingSessionPointCloudRequest, + GetMappingSessionPointCloudResponse, + ListMappingSessionsRequest, + ListMappingSessionsResponse, + MappingMetadata, + Module, + SensorInfo, + StartMappingSessionRequest, + StartMappingSessionResponse, + StopMappingSessionRequest, + StopMappingSessionResponse, +) + +__all__ = [ + "CloudSLAMServiceBase", + "CloudSLAMServiceStub", + "UnimplementedCloudSLAMServiceBase", + "CaptureInterval", + "EndStatus", + "GetActiveMappingSessionsForRobotRequest", + "GetActiveMappingSessionsForRobotResponse", + "GetMappingSessionMetadataByIDRequest", + "GetMappingSessionMetadataByIDResponse", + "GetMappingSessionPointCloudRequest", + "GetMappingSessionPointCloudResponse", + "ListMappingSessionsRequest", + "ListMappingSessionsResponse", + "MappingMetadata", + "Module", + "SensorInfo", + "StartMappingSessionRequest", + "StartMappingSessionResponse", + "StopMappingSessionRequest", + "StopMappingSessionResponse", +] diff --git a/src/viam/proto/app/data/__init__.py b/src/viam/proto/app/data/__init__.py index 8def81808..3d2559f59 100644 --- a/src/viam/proto/app/data/__init__.py +++ b/src/viam/proto/app/data/__init__.py @@ -2,25 +2,137 @@ @generated by Viam. Do not edit manually! """ -from ....gen.app.data.v1.data_grpc import DataServiceBase, DataServiceStub + +from ....gen.app.data.v1.data_grpc import DataServiceBase, DataServiceStub, UnimplementedDataServiceBase from ....gen.app.data.v1.data_pb2 import ( + AddBinaryDataToDatasetByIDsRequest, + AddBinaryDataToDatasetByIDsResponse, + AddBoundingBoxToImageByIDRequest, + AddBoundingBoxToImageByIDResponse, + AddTagsToBinaryDataByFilterRequest, + AddTagsToBinaryDataByFilterResponse, + AddTagsToBinaryDataByIDsRequest, + AddTagsToBinaryDataByIDsResponse, + Annotations, + BinaryData, + BinaryDataByFilterRequest, + BinaryDataByFilterResponse, + BinaryDataByIDsRequest, + BinaryDataByIDsResponse, + BinaryID, BinaryMetadata, + BoundingBox, + BoundingBoxLabelsByFilterRequest, + BoundingBoxLabelsByFilterResponse, CaptureInterval, - FileMetadata, - Filters, - QueryRequest, - QueryResponse, - TabularMetadata, + CaptureMetadata, + Classification, + ConfigureDatabaseUserRequest, + ConfigureDatabaseUserResponse, + DataRequest, + DeleteBinaryDataByFilterRequest, + DeleteBinaryDataByFilterResponse, + DeleteBinaryDataByIDsRequest, + DeleteBinaryDataByIDsResponse, + DeleteTabularDataRequest, + DeleteTabularDataResponse, + ExportTabularDataRequest, + ExportTabularDataResponse, + Filter, + GetDatabaseConnectionRequest, + GetDatabaseConnectionResponse, + GetLatestTabularDataRequest, + GetLatestTabularDataResponse, + Order, + RemoveBinaryDataFromDatasetByIDsRequest, + RemoveBinaryDataFromDatasetByIDsResponse, + RemoveBoundingBoxFromImageByIDRequest, + RemoveBoundingBoxFromImageByIDResponse, + RemoveTagsFromBinaryDataByFilterRequest, + RemoveTagsFromBinaryDataByFilterResponse, + RemoveTagsFromBinaryDataByIDsRequest, + RemoveTagsFromBinaryDataByIDsResponse, + TabularData, + TabularDataByFilterRequest, + TabularDataByFilterResponse, + TabularDataByMQLRequest, + TabularDataByMQLResponse, + TabularDataBySQLRequest, + TabularDataBySQLResponse, + TabularDataSource, + TabularDataSourceType, + TagsByFilterRequest, + TagsByFilterResponse, + TagsFilter, + TagsFilterType, + UpdateBoundingBoxRequest, + UpdateBoundingBoxResponse, ) __all__ = [ "DataServiceBase", "DataServiceStub", + "UnimplementedDataServiceBase", + "AddBinaryDataToDatasetByIDsRequest", + "AddBinaryDataToDatasetByIDsResponse", + "AddBoundingBoxToImageByIDRequest", + "AddBoundingBoxToImageByIDResponse", + "AddTagsToBinaryDataByFilterRequest", + "AddTagsToBinaryDataByFilterResponse", + "AddTagsToBinaryDataByIDsRequest", + "AddTagsToBinaryDataByIDsResponse", + "Annotations", + "BinaryData", + "BinaryDataByFilterRequest", + "BinaryDataByFilterResponse", + "BinaryDataByIDsRequest", + "BinaryDataByIDsResponse", + "BinaryID", "BinaryMetadata", + "BoundingBox", + "BoundingBoxLabelsByFilterRequest", + "BoundingBoxLabelsByFilterResponse", "CaptureInterval", - "FileMetadata", - "Filters", - "QueryRequest", - "QueryResponse", - "TabularMetadata", + "CaptureMetadata", + "Classification", + "ConfigureDatabaseUserRequest", + "ConfigureDatabaseUserResponse", + "DataRequest", + "DeleteBinaryDataByFilterRequest", + "DeleteBinaryDataByFilterResponse", + "DeleteBinaryDataByIDsRequest", + "DeleteBinaryDataByIDsResponse", + "DeleteTabularDataRequest", + "DeleteTabularDataResponse", + "ExportTabularDataRequest", + "ExportTabularDataResponse", + "Filter", + "GetDatabaseConnectionRequest", + "GetDatabaseConnectionResponse", + "GetLatestTabularDataRequest", + "GetLatestTabularDataResponse", + "Order", + "RemoveBinaryDataFromDatasetByIDsRequest", + "RemoveBinaryDataFromDatasetByIDsResponse", + "RemoveBoundingBoxFromImageByIDRequest", + "RemoveBoundingBoxFromImageByIDResponse", + "RemoveTagsFromBinaryDataByFilterRequest", + "RemoveTagsFromBinaryDataByFilterResponse", + "RemoveTagsFromBinaryDataByIDsRequest", + "RemoveTagsFromBinaryDataByIDsResponse", + "TabularData", + "TabularDataByFilterRequest", + "TabularDataByFilterResponse", + "TabularDataByMQLRequest", + "TabularDataByMQLResponse", + "TabularDataBySQLRequest", + "TabularDataBySQLResponse", + "TabularDataSource", + "TabularDataSourceType", + "TagsByFilterRequest", + "TagsByFilterResponse", + "TagsFilter", + "TagsFilterType", + "UpdateBoundingBoxRequest", + "UpdateBoundingBoxResponse", ] diff --git a/src/viam/proto/app/datapipelines/__init__.py b/src/viam/proto/app/datapipelines/__init__.py new file mode 100644 index 000000000..b380f5f27 --- /dev/null +++ b/src/viam/proto/app/datapipelines/__init__.py @@ -0,0 +1,56 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.app.datapipelines.v1.data_pipelines_grpc import ( + DataPipelinesServiceBase, + DataPipelinesServiceStub, + UnimplementedDataPipelinesServiceBase, +) +from ....gen.app.datapipelines.v1.data_pipelines_pb2 import ( + CreateDataPipelineRequest, + CreateDataPipelineResponse, + DataPipeline, + DataPipelineRun, + DataPipelineRunStatus, + DeleteDataPipelineRequest, + DeleteDataPipelineResponse, + DisableDataPipelineRequest, + DisableDataPipelineResponse, + EnableDataPipelineRequest, + EnableDataPipelineResponse, + GetDataPipelineRequest, + GetDataPipelineResponse, + ListDataPipelineRunsRequest, + ListDataPipelineRunsResponse, + ListDataPipelinesRequest, + ListDataPipelinesResponse, + UpdateDataPipelineRequest, + UpdateDataPipelineResponse, +) + +__all__ = [ + "DataPipelinesServiceBase", + "DataPipelinesServiceStub", + "UnimplementedDataPipelinesServiceBase", + "CreateDataPipelineRequest", + "CreateDataPipelineResponse", + "DataPipeline", + "DataPipelineRun", + "DataPipelineRunStatus", + "DeleteDataPipelineRequest", + "DeleteDataPipelineResponse", + "DisableDataPipelineRequest", + "DisableDataPipelineResponse", + "EnableDataPipelineRequest", + "EnableDataPipelineResponse", + "GetDataPipelineRequest", + "GetDataPipelineResponse", + "ListDataPipelineRunsRequest", + "ListDataPipelineRunsResponse", + "ListDataPipelinesRequest", + "ListDataPipelinesResponse", + "UpdateDataPipelineRequest", + "UpdateDataPipelineResponse", +] diff --git a/src/viam/proto/app/dataset/__init__.py b/src/viam/proto/app/dataset/__init__.py new file mode 100644 index 000000000..f165c1aac --- /dev/null +++ b/src/viam/proto/app/dataset/__init__.py @@ -0,0 +1,36 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.app.dataset.v1.dataset_grpc import DatasetServiceBase, DatasetServiceStub, UnimplementedDatasetServiceBase +from ....gen.app.dataset.v1.dataset_pb2 import ( + CreateDatasetRequest, + CreateDatasetResponse, + Dataset, + DeleteDatasetRequest, + DeleteDatasetResponse, + ListDatasetsByIDsRequest, + ListDatasetsByIDsResponse, + ListDatasetsByOrganizationIDRequest, + ListDatasetsByOrganizationIDResponse, + RenameDatasetRequest, + RenameDatasetResponse, +) + +__all__ = [ + "DatasetServiceBase", + "DatasetServiceStub", + "UnimplementedDatasetServiceBase", + "CreateDatasetRequest", + "CreateDatasetResponse", + "Dataset", + "DeleteDatasetRequest", + "DeleteDatasetResponse", + "ListDatasetsByIDsRequest", + "ListDatasetsByIDsResponse", + "ListDatasetsByOrganizationIDRequest", + "ListDatasetsByOrganizationIDResponse", + "RenameDatasetRequest", + "RenameDatasetResponse", +] diff --git a/src/viam/proto/app/datasync/__init__.py b/src/viam/proto/app/datasync/__init__.py index cf04bb36e..a9f8451b5 100644 --- a/src/viam/proto/app/datasync/__init__.py +++ b/src/viam/proto/app/datasync/__init__.py @@ -2,33 +2,43 @@ @generated by Viam. Do not edit manually! """ -from ....gen.app.datasync.v1.data_sync_grpc import DataSyncServiceBase, DataSyncServiceStub + +from ....gen.app.datasync.v1.data_sync_grpc import DataSyncServiceBase, DataSyncServiceStub, UnimplementedDataSyncServiceBase from ....gen.app.datasync.v1.data_sync_pb2 import ( - BinaryCapture, CaptureInterval, DataCaptureMetadata, + DataCaptureUploadMetadata, + DataCaptureUploadRequest, + DataCaptureUploadResponse, + DataType, FileData, + FileUploadRequest, + FileUploadResponse, + MimeType, SensorData, SensorMetadata, - TabularCapture, + StreamingDataCaptureUploadRequest, + StreamingDataCaptureUploadResponse, UploadMetadata, - UploadRequest, - UploadResponse, - UserFile, ) __all__ = [ "DataSyncServiceBase", "DataSyncServiceStub", - "BinaryCapture", + "UnimplementedDataSyncServiceBase", "CaptureInterval", "DataCaptureMetadata", + "DataCaptureUploadMetadata", + "DataCaptureUploadRequest", + "DataCaptureUploadResponse", + "DataType", "FileData", + "FileUploadRequest", + "FileUploadResponse", + "MimeType", "SensorData", "SensorMetadata", - "TabularCapture", + "StreamingDataCaptureUploadRequest", + "StreamingDataCaptureUploadResponse", "UploadMetadata", - "UploadRequest", - "UploadResponse", - "UserFile", ] diff --git a/src/viam/proto/app/end_user.py b/src/viam/proto/app/end_user.py new file mode 100644 index 000000000..f0290e5f0 --- /dev/null +++ b/src/viam/proto/app/end_user.py @@ -0,0 +1,34 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ...gen.app.v1.end_user_grpc import EndUserServiceBase, EndUserServiceStub, UnimplementedEndUserServiceBase +from ...gen.app.v1.end_user_pb2 import ( + AcceptLegalRequest, + AcceptLegalResponse, + GetAuthApplicationRequest, + GetAuthApplicationResponse, + IsLegalAcceptedRequest, + IsLegalAcceptedResponse, + RegisterAuthApplicationRequest, + RegisterAuthApplicationResponse, + UpdateAuthApplicationRequest, + UpdateAuthApplicationResponse, +) + +__all__ = [ + "EndUserServiceBase", + "EndUserServiceStub", + "UnimplementedEndUserServiceBase", + "AcceptLegalRequest", + "AcceptLegalResponse", + "GetAuthApplicationRequest", + "GetAuthApplicationResponse", + "IsLegalAcceptedRequest", + "IsLegalAcceptedResponse", + "RegisterAuthApplicationRequest", + "RegisterAuthApplicationResponse", + "UpdateAuthApplicationRequest", + "UpdateAuthApplicationResponse", +] diff --git a/src/viam/proto/app/mlinference/__init__.py b/src/viam/proto/app/mlinference/__init__.py new file mode 100644 index 000000000..4bd1fe182 --- /dev/null +++ b/src/viam/proto/app/mlinference/__init__.py @@ -0,0 +1,15 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.app.mlinference.v1.ml_inference_grpc import MLInferenceServiceBase, MLInferenceServiceStub, UnimplementedMLInferenceServiceBase +from ....gen.app.mlinference.v1.ml_inference_pb2 import GetInferenceRequest, GetInferenceResponse + +__all__ = [ + "MLInferenceServiceBase", + "MLInferenceServiceStub", + "UnimplementedMLInferenceServiceBase", + "GetInferenceRequest", + "GetInferenceResponse", +] diff --git a/src/viam/proto/app/mltraining/__init__.py b/src/viam/proto/app/mltraining/__init__.py new file mode 100644 index 000000000..179c6a403 --- /dev/null +++ b/src/viam/proto/app/mltraining/__init__.py @@ -0,0 +1,52 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.app.mltraining.v1.ml_training_grpc import MLTrainingServiceBase, MLTrainingServiceStub, UnimplementedMLTrainingServiceBase +from ....gen.app.mltraining.v1.ml_training_pb2 import ( + CancelTrainingJobRequest, + CancelTrainingJobResponse, + DeleteCompletedTrainingJobRequest, + DeleteCompletedTrainingJobResponse, + GetTrainingJobLogsRequest, + GetTrainingJobLogsResponse, + GetTrainingJobRequest, + GetTrainingJobResponse, + ListTrainingJobsRequest, + ListTrainingJobsResponse, + ModelFramework, + ModelType, + SubmitCustomTrainingJobRequest, + SubmitCustomTrainingJobResponse, + SubmitTrainingJobRequest, + SubmitTrainingJobResponse, + TrainingJobLogEntry, + TrainingJobMetadata, + TrainingStatus, +) + +__all__ = [ + "MLTrainingServiceBase", + "MLTrainingServiceStub", + "UnimplementedMLTrainingServiceBase", + "CancelTrainingJobRequest", + "CancelTrainingJobResponse", + "DeleteCompletedTrainingJobRequest", + "DeleteCompletedTrainingJobResponse", + "GetTrainingJobLogsRequest", + "GetTrainingJobLogsResponse", + "GetTrainingJobRequest", + "GetTrainingJobResponse", + "ListTrainingJobsRequest", + "ListTrainingJobsResponse", + "ModelFramework", + "ModelType", + "SubmitCustomTrainingJobRequest", + "SubmitCustomTrainingJobResponse", + "SubmitTrainingJobRequest", + "SubmitTrainingJobResponse", + "TrainingJobLogEntry", + "TrainingJobMetadata", + "TrainingStatus", +] diff --git a/src/viam/proto/app/model/__init__.py b/src/viam/proto/app/model/__init__.py deleted file mode 100644 index 715a8d0bd..000000000 --- a/src/viam/proto/app/model/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -""" -@generated by Viam. -Do not edit manually! -""" -from ....gen.app.model.v1.model_grpc import ModelServiceBase, ModelServiceStub -from ....gen.app.model.v1.model_pb2 import ( - DeleteMetadata, - DeleteRequest, - DeleteResponse, - DeployMetadata, - DeployRequest, - DeployResponse, - FileData, - SyncedModel, - UploadMetadata, - UploadRequest, - UploadResponse, -) - -__all__ = [ - "ModelServiceBase", - "ModelServiceStub", - "DeleteMetadata", - "DeleteRequest", - "DeleteResponse", - "DeployMetadata", - "DeployRequest", - "DeployResponse", - "FileData", - "SyncedModel", - "UploadMetadata", - "UploadRequest", - "UploadResponse", -] diff --git a/src/viam/proto/app/packages/__init__.py b/src/viam/proto/app/packages/__init__.py new file mode 100644 index 000000000..b09b26537 --- /dev/null +++ b/src/viam/proto/app/packages/__init__.py @@ -0,0 +1,38 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.app.packages.v1.packages_grpc import PackageServiceBase, PackageServiceStub, UnimplementedPackageServiceBase +from ....gen.app.packages.v1.packages_pb2 import ( + CreatePackageRequest, + CreatePackageResponse, + DeletePackageRequest, + DeletePackageResponse, + FileInfo, + GetPackageRequest, + GetPackageResponse, + ListPackagesRequest, + ListPackagesResponse, + Package, + PackageInfo, + PackageType, +) + +__all__ = [ + "PackageServiceBase", + "PackageServiceStub", + "UnimplementedPackageServiceBase", + "CreatePackageRequest", + "CreatePackageResponse", + "DeletePackageRequest", + "DeletePackageResponse", + "FileInfo", + "GetPackageRequest", + "GetPackageResponse", + "ListPackagesRequest", + "ListPackagesResponse", + "Package", + "PackageInfo", + "PackageType", +] diff --git a/src/viam/proto/app/robot.py b/src/viam/proto/app/robot.py index 542db280c..c74220c13 100644 --- a/src/viam/proto/app/robot.py +++ b/src/viam/proto/app/robot.py @@ -2,9 +2,11 @@ @generated by Viam. Do not edit manually! """ -from ...gen.app.v1.robot_grpc import RobotServiceBase, RobotServiceStub + +from ...gen.app.v1.robot_grpc import RobotServiceBase, RobotServiceStub, UnimplementedRobotServiceBase from ...gen.app.v1.robot_pb2 import ( AgentInfo, + AppValidationStatus, AuthConfig, AuthHandlerConfig, CertificateRequest, @@ -13,26 +15,39 @@ ComponentConfig, ConfigRequest, ConfigResponse, + CredentialsType, + ExternalAuthConfig, Frame, + JWKSFile, + LocationSecret, + LogConfiguration, + LogPatternConfig, LogRequest, LogResponse, + MaintenanceConfig, + ModuleConfig, NeedsRestartRequest, NeedsRestartResponse, NetworkConfig, Orientation, + PackageConfig, ProcessConfig, RemoteAuth, RemoteConfig, ResourceLevelServiceConfig, RobotConfig, ServiceConfig, + SessionsConfig, + TrafficTunnelEndpoint, Translation, ) __all__ = [ "RobotServiceBase", "RobotServiceStub", + "UnimplementedRobotServiceBase", "AgentInfo", + "AppValidationStatus", "AuthConfig", "AuthHandlerConfig", "CertificateRequest", @@ -41,18 +56,29 @@ "ComponentConfig", "ConfigRequest", "ConfigResponse", + "CredentialsType", + "ExternalAuthConfig", "Frame", + "JWKSFile", + "LocationSecret", + "LogConfiguration", + "LogPatternConfig", "LogRequest", "LogResponse", + "MaintenanceConfig", + "ModuleConfig", "NeedsRestartRequest", "NeedsRestartResponse", "NetworkConfig", "Orientation", + "PackageConfig", "ProcessConfig", "RemoteAuth", "RemoteConfig", "ResourceLevelServiceConfig", "RobotConfig", "ServiceConfig", + "SessionsConfig", + "TrafficTunnelEndpoint", "Translation", ] diff --git a/src/viam/proto/common/__init__.py b/src/viam/proto/common/__init__.py index 155b9004f..f835f2ac6 100644 --- a/src/viam/proto/common/__init__.py +++ b/src/viam/proto/common/__init__.py @@ -2,20 +2,32 @@ @generated by Viam. Do not edit manually! """ + from ...gen.common.v1.common_pb2 import ( ActuatorStatus, - AnalogStatus, - BoardStatus, - DigitalInterruptStatus, - GeoPoint, + Capsule, + DoCommandRequest, + DoCommandResponse, + GeoGeometry, GeometriesInFrame, Geometry, + GeoPoint, + GetGeometriesRequest, + GetGeometriesResponse, + GetKinematicsRequest, + GetKinematicsResponse, + GetReadingsRequest, + GetReadingsResponse, + KinematicsFileFormat, + LogEntry, + Mesh, Orientation, PointCloudObject, Pose, PoseInFrame, RectangularPrism, ResourceName, + ResponseMetadata, Sphere, Transform, Vector3, @@ -24,18 +36,29 @@ __all__ = [ "ActuatorStatus", - "AnalogStatus", - "BoardStatus", - "DigitalInterruptStatus", + "Capsule", + "DoCommandRequest", + "DoCommandResponse", + "GeoGeometry", "GeoPoint", "GeometriesInFrame", "Geometry", + "GetGeometriesRequest", + "GetGeometriesResponse", + "GetKinematicsRequest", + "GetKinematicsResponse", + "GetReadingsRequest", + "GetReadingsResponse", + "KinematicsFileFormat", + "LogEntry", + "Mesh", "Orientation", "PointCloudObject", "Pose", "PoseInFrame", "RectangularPrism", "ResourceName", + "ResponseMetadata", "Sphere", "Transform", "Vector3", diff --git a/src/viam/proto/component/arm/__init__.py b/src/viam/proto/component/arm/__init__.py index a1befe376..65297b318 100644 --- a/src/viam/proto/component/arm/__init__.py +++ b/src/viam/proto/component/arm/__init__.py @@ -2,13 +2,19 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.arm.v1.arm_grpc import ArmServiceBase, ArmServiceStub + +from ....gen.component.arm.v1.arm_grpc import ArmServiceBase, ArmServiceStub, UnimplementedArmServiceBase from ....gen.component.arm.v1.arm_pb2 import ( GetEndPositionRequest, GetEndPositionResponse, GetJointPositionsRequest, GetJointPositionsResponse, + IsMovingRequest, + IsMovingResponse, JointPositions, + MoveOptions, + MoveThroughJointPositionsRequest, + MoveThroughJointPositionsResponse, MoveToJointPositionsRequest, MoveToJointPositionsResponse, MoveToPositionRequest, @@ -21,11 +27,17 @@ __all__ = [ "ArmServiceBase", "ArmServiceStub", + "UnimplementedArmServiceBase", "GetEndPositionRequest", "GetEndPositionResponse", "GetJointPositionsRequest", "GetJointPositionsResponse", + "IsMovingRequest", + "IsMovingResponse", "JointPositions", + "MoveOptions", + "MoveThroughJointPositionsRequest", + "MoveThroughJointPositionsResponse", "MoveToJointPositionsRequest", "MoveToJointPositionsResponse", "MoveToPositionRequest", diff --git a/src/viam/proto/component/audioinput/__init__.py b/src/viam/proto/component/audioinput/__init__.py index 040365ad4..c0dde507b 100644 --- a/src/viam/proto/component/audioinput/__init__.py +++ b/src/viam/proto/component/audioinput/__init__.py @@ -2,7 +2,8 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.audioinput.v1.audioinput_grpc import AudioInputServiceBase, AudioInputServiceStub + +from ....gen.component.audioinput.v1.audioinput_grpc import AudioInputServiceBase, AudioInputServiceStub, UnimplementedAudioInputServiceBase from ....gen.component.audioinput.v1.audioinput_pb2 import ( AudioChunk, AudioChunkInfo, @@ -11,11 +12,13 @@ PropertiesRequest, PropertiesResponse, RecordRequest, + SampleFormat, ) __all__ = [ "AudioInputServiceBase", "AudioInputServiceStub", + "UnimplementedAudioInputServiceBase", "AudioChunk", "AudioChunkInfo", "ChunksRequest", @@ -23,4 +26,5 @@ "PropertiesRequest", "PropertiesResponse", "RecordRequest", + "SampleFormat", ] diff --git a/src/viam/proto/component/base/__init__.py b/src/viam/proto/component/base/__init__.py index 989045f34..74b5f2704 100644 --- a/src/viam/proto/component/base/__init__.py +++ b/src/viam/proto/component/base/__init__.py @@ -2,8 +2,13 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.base.v1.base_grpc import BaseServiceBase, BaseServiceStub + +from ....gen.component.base.v1.base_grpc import BaseServiceBase, BaseServiceStub, UnimplementedBaseServiceBase from ....gen.component.base.v1.base_pb2 import ( + GetPropertiesRequest, + GetPropertiesResponse, + IsMovingRequest, + IsMovingResponse, MoveStraightRequest, MoveStraightResponse, SetPowerRequest, @@ -19,6 +24,11 @@ __all__ = [ "BaseServiceBase", "BaseServiceStub", + "UnimplementedBaseServiceBase", + "GetPropertiesRequest", + "GetPropertiesResponse", + "IsMovingRequest", + "IsMovingResponse", "MoveStraightRequest", "MoveStraightResponse", "SetPowerRequest", diff --git a/src/viam/proto/component/board/__init__.py b/src/viam/proto/component/board/__init__.py index 70a85fe3d..2f5bc9874 100644 --- a/src/viam/proto/component/board/__init__.py +++ b/src/viam/proto/component/board/__init__.py @@ -2,12 +2,14 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.board.v1.board_grpc import BoardServiceBase, BoardServiceStub + +from ....gen.component.board.v1.board_grpc import BoardServiceBase, BoardServiceStub, UnimplementedBoardServiceBase from ....gen.component.board.v1.board_pb2 import ( GetDigitalInterruptValueRequest, GetDigitalInterruptValueResponse, GetGPIORequest, GetGPIOResponse, + PowerMode, PWMFrequencyRequest, PWMFrequencyResponse, PWMRequest, @@ -16,17 +18,23 @@ ReadAnalogReaderResponse, SetGPIORequest, SetGPIOResponse, + SetPowerModeRequest, + SetPowerModeResponse, SetPWMFrequencyRequest, SetPWMFrequencyResponse, SetPWMRequest, SetPWMResponse, - StatusRequest, - StatusResponse, + Status, + StreamTicksRequest, + StreamTicksResponse, + WriteAnalogRequest, + WriteAnalogResponse, ) __all__ = [ "BoardServiceBase", "BoardServiceStub", + "UnimplementedBoardServiceBase", "GetDigitalInterruptValueRequest", "GetDigitalInterruptValueResponse", "GetGPIORequest", @@ -35,6 +43,7 @@ "PWMFrequencyResponse", "PWMRequest", "PWMResponse", + "PowerMode", "ReadAnalogReaderRequest", "ReadAnalogReaderResponse", "SetGPIORequest", @@ -43,6 +52,11 @@ "SetPWMFrequencyResponse", "SetPWMRequest", "SetPWMResponse", - "StatusRequest", - "StatusResponse", + "SetPowerModeRequest", + "SetPowerModeResponse", + "Status", + "StreamTicksRequest", + "StreamTicksResponse", + "WriteAnalogRequest", + "WriteAnalogResponse", ] diff --git a/src/viam/proto/component/button/__init__.py b/src/viam/proto/component/button/__init__.py new file mode 100644 index 000000000..45dd96e59 --- /dev/null +++ b/src/viam/proto/component/button/__init__.py @@ -0,0 +1,15 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.component.button.v1.button_grpc import ButtonServiceBase, ButtonServiceStub, UnimplementedButtonServiceBase +from ....gen.component.button.v1.button_pb2 import PushRequest, PushResponse + +__all__ = [ + "ButtonServiceBase", + "ButtonServiceStub", + "UnimplementedButtonServiceBase", + "PushRequest", + "PushResponse", +] diff --git a/src/viam/proto/component/camera/__init__.py b/src/viam/proto/component/camera/__init__.py index b143fbe5a..bd1b35207 100644 --- a/src/viam/proto/component/camera/__init__.py +++ b/src/viam/proto/component/camera/__init__.py @@ -2,19 +2,23 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.camera.v1.camera_grpc import CameraServiceBase, CameraServiceStub + +from ....gen.component.camera.v1.camera_grpc import CameraServiceBase, CameraServiceStub, UnimplementedCameraServiceBase from ....gen.component.camera.v1.camera_pb2 import ( DistortionParameters, + Format, GetImageRequest, GetImageResponse, + GetImagesRequest, + GetImagesResponse, GetPointCloudRequest, GetPointCloudResponse, GetPropertiesRequest, GetPropertiesResponse, + Image, IntrinsicParameters, Property, RenderFrameRequest, - Video, Webcam, Webcams, ) @@ -22,17 +26,21 @@ __all__ = [ "CameraServiceBase", "CameraServiceStub", + "UnimplementedCameraServiceBase", "DistortionParameters", + "Format", "GetImageRequest", "GetImageResponse", + "GetImagesRequest", + "GetImagesResponse", "GetPointCloudRequest", "GetPointCloudResponse", "GetPropertiesRequest", "GetPropertiesResponse", + "Image", "IntrinsicParameters", "Property", "RenderFrameRequest", - "Video", "Webcam", "Webcams", ] diff --git a/src/viam/proto/component/encoder/__init__.py b/src/viam/proto/component/encoder/__init__.py new file mode 100644 index 000000000..ff442c7a1 --- /dev/null +++ b/src/viam/proto/component/encoder/__init__.py @@ -0,0 +1,28 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.component.encoder.v1.encoder_grpc import EncoderServiceBase, EncoderServiceStub, UnimplementedEncoderServiceBase +from ....gen.component.encoder.v1.encoder_pb2 import ( + GetPositionRequest, + GetPositionResponse, + GetPropertiesRequest, + GetPropertiesResponse, + PositionType, + ResetPositionRequest, + ResetPositionResponse, +) + +__all__ = [ + "EncoderServiceBase", + "EncoderServiceStub", + "UnimplementedEncoderServiceBase", + "GetPositionRequest", + "GetPositionResponse", + "GetPropertiesRequest", + "GetPropertiesResponse", + "PositionType", + "ResetPositionRequest", + "ResetPositionResponse", +] diff --git a/src/viam/proto/component/gantry/__init__.py b/src/viam/proto/component/gantry/__init__.py index 8f0084d3b..6c2649e9a 100644 --- a/src/viam/proto/component/gantry/__init__.py +++ b/src/viam/proto/component/gantry/__init__.py @@ -2,12 +2,17 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.gantry.v1.gantry_grpc import GantryServiceBase, GantryServiceStub + +from ....gen.component.gantry.v1.gantry_grpc import GantryServiceBase, GantryServiceStub, UnimplementedGantryServiceBase from ....gen.component.gantry.v1.gantry_pb2 import ( GetLengthsRequest, GetLengthsResponse, GetPositionRequest, GetPositionResponse, + HomeRequest, + HomeResponse, + IsMovingRequest, + IsMovingResponse, MoveToPositionRequest, MoveToPositionResponse, Status, @@ -18,10 +23,15 @@ __all__ = [ "GantryServiceBase", "GantryServiceStub", + "UnimplementedGantryServiceBase", "GetLengthsRequest", "GetLengthsResponse", "GetPositionRequest", "GetPositionResponse", + "HomeRequest", + "HomeResponse", + "IsMovingRequest", + "IsMovingResponse", "MoveToPositionRequest", "MoveToPositionResponse", "Status", diff --git a/src/viam/proto/component/generic/__init__.py b/src/viam/proto/component/generic/__init__.py index 907dee6be..9b7a1533e 100644 --- a/src/viam/proto/component/generic/__init__.py +++ b/src/viam/proto/component/generic/__init__.py @@ -2,12 +2,11 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.generic.v1.generic_grpc import GenericServiceBase, GenericServiceStub -from ....gen.component.generic.v1.generic_pb2 import DoCommandRequest, DoCommandResponse + +from ....gen.component.generic.v1.generic_grpc import GenericServiceBase, GenericServiceStub, UnimplementedGenericServiceBase __all__ = [ "GenericServiceBase", "GenericServiceStub", - "DoCommandRequest", - "DoCommandResponse", + "UnimplementedGenericServiceBase", ] diff --git a/src/viam/proto/component/gripper/__init__.py b/src/viam/proto/component/gripper/__init__.py index 899598a15..16ffdceea 100644 --- a/src/viam/proto/component/gripper/__init__.py +++ b/src/viam/proto/component/gripper/__init__.py @@ -2,14 +2,27 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.gripper.v1.gripper_grpc import GripperServiceBase, GripperServiceStub -from ....gen.component.gripper.v1.gripper_pb2 import GrabRequest, GrabResponse, OpenRequest, OpenResponse, StopRequest, StopResponse + +from ....gen.component.gripper.v1.gripper_grpc import GripperServiceBase, GripperServiceStub, UnimplementedGripperServiceBase +from ....gen.component.gripper.v1.gripper_pb2 import ( + GrabRequest, + GrabResponse, + IsMovingRequest, + IsMovingResponse, + OpenRequest, + OpenResponse, + StopRequest, + StopResponse, +) __all__ = [ "GripperServiceBase", "GripperServiceStub", + "UnimplementedGripperServiceBase", "GrabRequest", "GrabResponse", + "IsMovingRequest", + "IsMovingResponse", "OpenRequest", "OpenResponse", "StopRequest", diff --git a/src/viam/proto/component/inputcontroller/__init__.py b/src/viam/proto/component/inputcontroller/__init__.py index e91791faf..ba3f34568 100644 --- a/src/viam/proto/component/inputcontroller/__init__.py +++ b/src/viam/proto/component/inputcontroller/__init__.py @@ -2,7 +2,12 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.inputcontroller.v1.input_controller_grpc import InputControllerServiceBase, InputControllerServiceStub + +from ....gen.component.inputcontroller.v1.input_controller_grpc import ( + InputControllerServiceBase, + InputControllerServiceStub, + UnimplementedInputControllerServiceBase, +) from ....gen.component.inputcontroller.v1.input_controller_pb2 import ( Event, GetControlsRequest, @@ -19,6 +24,7 @@ __all__ = [ "InputControllerServiceBase", "InputControllerServiceStub", + "UnimplementedInputControllerServiceBase", "Event", "GetControlsRequest", "GetControlsResponse", diff --git a/src/viam/proto/component/motor/__init__.py b/src/viam/proto/component/motor/__init__.py index 6bca4f91f..b27fd8981 100644 --- a/src/viam/proto/component/motor/__init__.py +++ b/src/viam/proto/component/motor/__init__.py @@ -2,7 +2,8 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.motor.v1.motor_grpc import MotorServiceBase, MotorServiceStub + +from ....gen.component.motor.v1.motor_grpc import MotorServiceBase, MotorServiceStub, UnimplementedMotorServiceBase from ....gen.component.motor.v1.motor_pb2 import ( GetPositionRequest, GetPositionResponse, @@ -12,12 +13,16 @@ GoForResponse, GoToRequest, GoToResponse, + IsMovingRequest, + IsMovingResponse, IsPoweredRequest, IsPoweredResponse, ResetZeroPositionRequest, ResetZeroPositionResponse, SetPowerRequest, SetPowerResponse, + SetRPMRequest, + SetRPMResponse, Status, StopRequest, StopResponse, @@ -26,6 +31,7 @@ __all__ = [ "MotorServiceBase", "MotorServiceStub", + "UnimplementedMotorServiceBase", "GetPositionRequest", "GetPositionResponse", "GetPropertiesRequest", @@ -34,12 +40,16 @@ "GoForResponse", "GoToRequest", "GoToResponse", + "IsMovingRequest", + "IsMovingResponse", "IsPoweredRequest", "IsPoweredResponse", "ResetZeroPositionRequest", "ResetZeroPositionResponse", "SetPowerRequest", "SetPowerResponse", + "SetRPMRequest", + "SetRPMResponse", "Status", "StopRequest", "StopResponse", diff --git a/src/viam/proto/component/movementsensor/__init__.py b/src/viam/proto/component/movementsensor/__init__.py index 2310e3dd2..770de4997 100644 --- a/src/viam/proto/component/movementsensor/__init__.py +++ b/src/viam/proto/component/movementsensor/__init__.py @@ -2,7 +2,12 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.movementsensor.v1.movementsensor_grpc import MovementSensorServiceBase, MovementSensorServiceStub + +from ....gen.component.movementsensor.v1.movementsensor_grpc import ( + MovementSensorServiceBase, + MovementSensorServiceStub, + UnimplementedMovementSensorServiceBase, +) from ....gen.component.movementsensor.v1.movementsensor_pb2 import ( GetAccuracyRequest, GetAccuracyResponse, @@ -10,6 +15,8 @@ GetAngularVelocityResponse, GetCompassHeadingRequest, GetCompassHeadingResponse, + GetLinearAccelerationRequest, + GetLinearAccelerationResponse, GetLinearVelocityRequest, GetLinearVelocityResponse, GetOrientationRequest, @@ -23,12 +30,15 @@ __all__ = [ "MovementSensorServiceBase", "MovementSensorServiceStub", + "UnimplementedMovementSensorServiceBase", "GetAccuracyRequest", "GetAccuracyResponse", "GetAngularVelocityRequest", "GetAngularVelocityResponse", "GetCompassHeadingRequest", "GetCompassHeadingResponse", + "GetLinearAccelerationRequest", + "GetLinearAccelerationResponse", "GetLinearVelocityRequest", "GetLinearVelocityResponse", "GetOrientationRequest", diff --git a/src/viam/proto/component/posetracker/__init__.py b/src/viam/proto/component/posetracker/__init__.py index 7b81b96de..7073f07f1 100644 --- a/src/viam/proto/component/posetracker/__init__.py +++ b/src/viam/proto/component/posetracker/__init__.py @@ -2,12 +2,18 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.posetracker.v1.pose_tracker_grpc import PoseTrackerServiceBase, PoseTrackerServiceStub + +from ....gen.component.posetracker.v1.pose_tracker_grpc import ( + PoseTrackerServiceBase, + PoseTrackerServiceStub, + UnimplementedPoseTrackerServiceBase, +) from ....gen.component.posetracker.v1.pose_tracker_pb2 import GetPosesRequest, GetPosesResponse __all__ = [ "PoseTrackerServiceBase", "PoseTrackerServiceStub", + "UnimplementedPoseTrackerServiceBase", "GetPosesRequest", "GetPosesResponse", ] diff --git a/src/viam/proto/component/powersensor/__init__.py b/src/viam/proto/component/powersensor/__init__.py new file mode 100644 index 000000000..dbd1b7076 --- /dev/null +++ b/src/viam/proto/component/powersensor/__init__.py @@ -0,0 +1,30 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.component.powersensor.v1.powersensor_grpc import ( + PowerSensorServiceBase, + PowerSensorServiceStub, + UnimplementedPowerSensorServiceBase, +) +from ....gen.component.powersensor.v1.powersensor_pb2 import ( + GetCurrentRequest, + GetCurrentResponse, + GetPowerRequest, + GetPowerResponse, + GetVoltageRequest, + GetVoltageResponse, +) + +__all__ = [ + "PowerSensorServiceBase", + "PowerSensorServiceStub", + "UnimplementedPowerSensorServiceBase", + "GetCurrentRequest", + "GetCurrentResponse", + "GetPowerRequest", + "GetPowerResponse", + "GetVoltageRequest", + "GetVoltageResponse", +] diff --git a/src/viam/proto/component/sensor/__init__.py b/src/viam/proto/component/sensor/__init__.py index e47799f81..86939e994 100644 --- a/src/viam/proto/component/sensor/__init__.py +++ b/src/viam/proto/component/sensor/__init__.py @@ -2,12 +2,11 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.sensor.v1.sensor_grpc import SensorServiceBase, SensorServiceStub -from ....gen.component.sensor.v1.sensor_pb2 import GetReadingsRequest, GetReadingsResponse + +from ....gen.component.sensor.v1.sensor_grpc import SensorServiceBase, SensorServiceStub, UnimplementedSensorServiceBase __all__ = [ "SensorServiceBase", "SensorServiceStub", - "GetReadingsRequest", - "GetReadingsResponse", + "UnimplementedSensorServiceBase", ] diff --git a/src/viam/proto/component/servo/__init__.py b/src/viam/proto/component/servo/__init__.py index be1a214c2..2e4626130 100644 --- a/src/viam/proto/component/servo/__init__.py +++ b/src/viam/proto/component/servo/__init__.py @@ -2,10 +2,13 @@ @generated by Viam. Do not edit manually! """ -from ....gen.component.servo.v1.servo_grpc import ServoServiceBase, ServoServiceStub + +from ....gen.component.servo.v1.servo_grpc import ServoServiceBase, ServoServiceStub, UnimplementedServoServiceBase from ....gen.component.servo.v1.servo_pb2 import ( GetPositionRequest, GetPositionResponse, + IsMovingRequest, + IsMovingResponse, MoveRequest, MoveResponse, Status, @@ -16,8 +19,11 @@ __all__ = [ "ServoServiceBase", "ServoServiceStub", + "UnimplementedServoServiceBase", "GetPositionRequest", "GetPositionResponse", + "IsMovingRequest", + "IsMovingResponse", "MoveRequest", "MoveResponse", "Status", diff --git a/src/viam/proto/component/switch/__init__.py b/src/viam/proto/component/switch/__init__.py new file mode 100644 index 000000000..1fb43ec49 --- /dev/null +++ b/src/viam/proto/component/switch/__init__.py @@ -0,0 +1,26 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.component.switch.v1.switch_grpc import SwitchServiceBase, SwitchServiceStub, UnimplementedSwitchServiceBase +from ....gen.component.switch.v1.switch_pb2 import ( + GetNumberOfPositionsRequest, + GetNumberOfPositionsResponse, + GetPositionRequest, + GetPositionResponse, + SetPositionRequest, + SetPositionResponse, +) + +__all__ = [ + "SwitchServiceBase", + "SwitchServiceStub", + "UnimplementedSwitchServiceBase", + "GetNumberOfPositionsRequest", + "GetNumberOfPositionsResponse", + "GetPositionRequest", + "GetPositionResponse", + "SetPositionRequest", + "SetPositionResponse", +] diff --git a/src/viam/proto/component/testecho/__init__.py b/src/viam/proto/component/testecho/__init__.py new file mode 100644 index 000000000..a60f271f9 --- /dev/null +++ b/src/viam/proto/component/testecho/__init__.py @@ -0,0 +1,30 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.component.testecho.v1.testecho_grpc import TestEchoServiceBase, TestEchoServiceStub, UnimplementedTestEchoServiceBase +from ....gen.component.testecho.v1.testecho_pb2 import ( + EchoBiDiRequest, + EchoBiDiResponse, + EchoMultipleRequest, + EchoMultipleResponse, + EchoRequest, + EchoResponse, + StopRequest, + StopResponse, +) + +__all__ = [ + "TestEchoServiceBase", + "TestEchoServiceStub", + "UnimplementedTestEchoServiceBase", + "EchoBiDiRequest", + "EchoBiDiResponse", + "EchoMultipleRequest", + "EchoMultipleResponse", + "EchoRequest", + "EchoResponse", + "StopRequest", + "StopResponse", +] diff --git a/src/viam/proto/module/__init__.py b/src/viam/proto/module/__init__.py new file mode 100644 index 000000000..fdc3ea594 --- /dev/null +++ b/src/viam/proto/module/__init__.py @@ -0,0 +1,38 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ...gen.module.v1.module_grpc import ModuleServiceBase, ModuleServiceStub, UnimplementedModuleServiceBase +from ...gen.module.v1.module_pb2 import ( + AddResourceRequest, + AddResourceResponse, + HandlerDefinition, + HandlerMap, + ReadyRequest, + ReadyResponse, + ReconfigureResourceRequest, + ReconfigureResourceResponse, + RemoveResourceRequest, + RemoveResourceResponse, + ValidateConfigRequest, + ValidateConfigResponse, +) + +__all__ = [ + "ModuleServiceBase", + "ModuleServiceStub", + "UnimplementedModuleServiceBase", + "AddResourceRequest", + "AddResourceResponse", + "HandlerDefinition", + "HandlerMap", + "ReadyRequest", + "ReadyResponse", + "ReconfigureResourceRequest", + "ReconfigureResourceResponse", + "RemoveResourceRequest", + "RemoveResourceResponse", + "ValidateConfigRequest", + "ValidateConfigResponse", +] diff --git a/src/viam/proto/provisioning/__init__.py b/src/viam/proto/provisioning/__init__.py new file mode 100644 index 000000000..c39ab0bca --- /dev/null +++ b/src/viam/proto/provisioning/__init__.py @@ -0,0 +1,36 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ...gen.provisioning.v1.provisioning_grpc import ProvisioningServiceBase, ProvisioningServiceStub, UnimplementedProvisioningServiceBase +from ...gen.provisioning.v1.provisioning_pb2 import ( + CloudConfig, + GetNetworkListRequest, + GetNetworkListResponse, + GetSmartMachineStatusRequest, + GetSmartMachineStatusResponse, + NetworkInfo, + ProvisioningInfo, + SetNetworkCredentialsRequest, + SetNetworkCredentialsResponse, + SetSmartMachineCredentialsRequest, + SetSmartMachineCredentialsResponse, +) + +__all__ = [ + "ProvisioningServiceBase", + "ProvisioningServiceStub", + "UnimplementedProvisioningServiceBase", + "CloudConfig", + "GetNetworkListRequest", + "GetNetworkListResponse", + "GetSmartMachineStatusRequest", + "GetSmartMachineStatusResponse", + "NetworkInfo", + "ProvisioningInfo", + "SetNetworkCredentialsRequest", + "SetNetworkCredentialsResponse", + "SetSmartMachineCredentialsRequest", + "SetSmartMachineCredentialsResponse", +] diff --git a/src/viam/proto/robot/__init__.py b/src/viam/proto/robot/__init__.py index 3ae447a17..30cd6cbbf 100644 --- a/src/viam/proto/robot/__init__.py +++ b/src/viam/proto/robot/__init__.py @@ -2,69 +2,129 @@ @generated by Viam. Do not edit manually! """ -from ...gen.robot.v1.robot_grpc import RobotServiceBase, RobotServiceStub + +from ...gen.robot.v1.robot_grpc import RobotServiceBase, RobotServiceStub, UnimplementedRobotServiceBase from ...gen.robot.v1.robot_pb2 import ( BlockForOperationRequest, BlockForOperationResponse, CancelOperationRequest, CancelOperationResponse, - DiscoverComponentsRequest, - DiscoverComponentsResponse, - Discovery, - DiscoveryQuery, + ConfigStatus, FrameSystemConfig, FrameSystemConfigRequest, FrameSystemConfigResponse, + GetCloudMetadataRequest, + GetCloudMetadataResponse, + GetMachineStatusRequest, + GetMachineStatusResponse, + GetModelsFromModulesRequest, + GetModelsFromModulesResponse, GetOperationsRequest, GetOperationsResponse, + GetSessionsRequest, + GetSessionsResponse, GetStatusRequest, GetStatusResponse, + GetVersionRequest, + GetVersionResponse, + ListTunnelsRequest, + ListTunnelsResponse, + LogRequest, + LogResponse, + ModuleModel, Operation, + PeerConnectionInfo, + PeerConnectionType, ResourceNamesRequest, ResourceNamesResponse, ResourceRPCSubtype, ResourceRPCSubtypesRequest, ResourceRPCSubtypesResponse, + ResourceStatus, + RestartModuleRequest, + RestartModuleResponse, + SendSessionHeartbeatRequest, + SendSessionHeartbeatResponse, + Session, + ShutdownRequest, + ShutdownResponse, + StartSessionRequest, + StartSessionResponse, Status, StopAllRequest, StopAllResponse, StopExtraParameters, StreamStatusRequest, StreamStatusResponse, + TransformPCDRequest, + TransformPCDResponse, TransformPoseRequest, TransformPoseResponse, + Tunnel, + TunnelRequest, + TunnelResponse, ) __all__ = [ "RobotServiceBase", "RobotServiceStub", + "UnimplementedRobotServiceBase", "BlockForOperationRequest", "BlockForOperationResponse", "CancelOperationRequest", "CancelOperationResponse", - "DiscoverComponentsRequest", - "DiscoverComponentsResponse", - "Discovery", - "DiscoveryQuery", + "ConfigStatus", "FrameSystemConfig", "FrameSystemConfigRequest", "FrameSystemConfigResponse", + "GetCloudMetadataRequest", + "GetCloudMetadataResponse", + "GetMachineStatusRequest", + "GetMachineStatusResponse", + "GetModelsFromModulesRequest", + "GetModelsFromModulesResponse", "GetOperationsRequest", "GetOperationsResponse", + "GetSessionsRequest", + "GetSessionsResponse", "GetStatusRequest", "GetStatusResponse", + "GetVersionRequest", + "GetVersionResponse", + "ListTunnelsRequest", + "ListTunnelsResponse", + "LogRequest", + "LogResponse", + "ModuleModel", "Operation", + "PeerConnectionInfo", + "PeerConnectionType", "ResourceNamesRequest", "ResourceNamesResponse", "ResourceRPCSubtype", "ResourceRPCSubtypesRequest", "ResourceRPCSubtypesResponse", + "ResourceStatus", + "RestartModuleRequest", + "RestartModuleResponse", + "SendSessionHeartbeatRequest", + "SendSessionHeartbeatResponse", + "Session", + "ShutdownRequest", + "ShutdownResponse", + "StartSessionRequest", + "StartSessionResponse", "Status", "StopAllRequest", "StopAllResponse", "StopExtraParameters", "StreamStatusRequest", "StreamStatusResponse", + "TransformPCDRequest", + "TransformPCDResponse", "TransformPoseRequest", "TransformPoseResponse", + "Tunnel", + "TunnelRequest", + "TunnelResponse", ] diff --git a/src/viam/proto/rpc/auth.py b/src/viam/proto/rpc/auth.py index 23f3d3562..1704c74cd 100644 --- a/src/viam/proto/rpc/auth.py +++ b/src/viam/proto/rpc/auth.py @@ -2,7 +2,15 @@ @generated by Viam. Do not edit manually! """ -from ...gen.proto.rpc.v1.auth_grpc import AuthServiceBase, AuthServiceStub, ExternalAuthServiceBase, ExternalAuthServiceStub + +from ...gen.proto.rpc.v1.auth_grpc import ( + AuthServiceBase, + AuthServiceStub, + ExternalAuthServiceBase, + ExternalAuthServiceStub, + UnimplementedAuthServiceBase, + UnimplementedExternalAuthServiceBase, +) from ...gen.proto.rpc.v1.auth_pb2 import ( AuthenticateRequest, AuthenticateResponse, @@ -16,6 +24,8 @@ "AuthServiceStub", "ExternalAuthServiceBase", "ExternalAuthServiceStub", + "UnimplementedAuthServiceBase", + "UnimplementedExternalAuthServiceBase", "AuthenticateRequest", "AuthenticateResponse", "AuthenticateToRequest", diff --git a/src/viam/proto/rpc/examples/echo/__init__.py b/src/viam/proto/rpc/examples/echo/__init__.py index a74bf6539..60cefe6d9 100644 --- a/src/viam/proto/rpc/examples/echo/__init__.py +++ b/src/viam/proto/rpc/examples/echo/__init__.py @@ -2,7 +2,8 @@ @generated by Viam. Do not edit manually! """ -from .....gen.proto.rpc.examples.echo.v1.echo_grpc import EchoServiceBase, EchoServiceStub + +from .....gen.proto.rpc.examples.echo.v1.echo_grpc import EchoServiceBase, EchoServiceStub, UnimplementedEchoServiceBase from .....gen.proto.rpc.examples.echo.v1.echo_pb2 import ( EchoBiDiRequest, EchoBiDiResponse, @@ -15,6 +16,7 @@ __all__ = [ "EchoServiceBase", "EchoServiceStub", + "UnimplementedEchoServiceBase", "EchoBiDiRequest", "EchoBiDiResponse", "EchoMultipleRequest", diff --git a/src/viam/proto/rpc/examples/echoresource/__init__.py b/src/viam/proto/rpc/examples/echoresource/__init__.py new file mode 100644 index 000000000..18e72d044 --- /dev/null +++ b/src/viam/proto/rpc/examples/echoresource/__init__.py @@ -0,0 +1,30 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from .....gen.proto.rpc.examples.echoresource.v1.echoresource_grpc import ( + EchoResourceServiceBase, + EchoResourceServiceStub, + UnimplementedEchoResourceServiceBase, +) +from .....gen.proto.rpc.examples.echoresource.v1.echoresource_pb2 import ( + EchoResourceBiDiRequest, + EchoResourceBiDiResponse, + EchoResourceMultipleRequest, + EchoResourceMultipleResponse, + EchoResourceRequest, + EchoResourceResponse, +) + +__all__ = [ + "EchoResourceServiceBase", + "EchoResourceServiceStub", + "UnimplementedEchoResourceServiceBase", + "EchoResourceBiDiRequest", + "EchoResourceBiDiResponse", + "EchoResourceMultipleRequest", + "EchoResourceMultipleResponse", + "EchoResourceRequest", + "EchoResourceResponse", +] diff --git a/src/viam/proto/rpc/examples/fileupload/__init__.py b/src/viam/proto/rpc/examples/fileupload/__init__.py deleted file mode 100644 index a1a2203e4..000000000 --- a/src/viam/proto/rpc/examples/fileupload/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -@generated by Viam. -Do not edit manually! -""" -from .....gen.proto.rpc.examples.fileupload.v1.fileupload_grpc import FileUploadServiceBase, FileUploadServiceStub -from .....gen.proto.rpc.examples.fileupload.v1.fileupload_pb2 import UploadFileRequest, UploadFileResponse - -__all__ = [ - "FileUploadServiceBase", - "FileUploadServiceStub", - "UploadFileRequest", - "UploadFileResponse", -] diff --git a/src/viam/proto/rpc/webrtc/grpc.py b/src/viam/proto/rpc/webrtc/grpc.py index 02d27d2e0..bdb49fa2f 100644 --- a/src/viam/proto/rpc/webrtc/grpc.py +++ b/src/viam/proto/rpc/webrtc/grpc.py @@ -2,6 +2,7 @@ @generated by Viam. Do not edit manually! """ + from ....gen.proto.rpc.webrtc.v1.grpc_pb2 import ( Metadata, PacketMessage, @@ -15,7 +16,7 @@ Stream, Strings, ) -from ....gen.proto.rpc.webrtc.v1.signaling_grpc import SignalingServiceBase, SignalingServiceStub +from ....gen.proto.rpc.webrtc.v1.signaling_grpc import SignalingServiceBase, SignalingServiceStub, UnimplementedSignalingServiceBase __all__ = [ "Metadata", @@ -31,4 +32,5 @@ "Strings", "SignalingServiceBase", "SignalingServiceStub", + "UnimplementedSignalingServiceBase", ] diff --git a/src/viam/proto/rpc/webrtc/signaling.py b/src/viam/proto/rpc/webrtc/signaling.py index 11ce2fa83..a47b89175 100644 --- a/src/viam/proto/rpc/webrtc/signaling.py +++ b/src/viam/proto/rpc/webrtc/signaling.py @@ -2,11 +2,13 @@ @generated by Viam. Do not edit manually! """ -from ....gen.proto.rpc.webrtc.v1.signaling_grpc import SignalingServiceBase, SignalingServiceStub + +from ....gen.proto.rpc.webrtc.v1.signaling_grpc import SignalingServiceBase, SignalingServiceStub, UnimplementedSignalingServiceBase from ....gen.proto.rpc.webrtc.v1.signaling_pb2 import ( AnswerRequest, AnswerRequestDoneStage, AnswerRequestErrorStage, + AnswerRequestHeartbeatStage, AnswerRequestInitStage, AnswerRequestUpdateStage, AnswerResponse, @@ -30,9 +32,11 @@ __all__ = [ "SignalingServiceBase", "SignalingServiceStub", + "UnimplementedSignalingServiceBase", "AnswerRequest", "AnswerRequestDoneStage", "AnswerRequestErrorStage", + "AnswerRequestHeartbeatStage", "AnswerRequestInitStage", "AnswerRequestUpdateStage", "AnswerResponse", diff --git a/src/viam/proto/service/datamanager/__init__.py b/src/viam/proto/service/datamanager/__init__.py index f26e297d1..49dc628ba 100644 --- a/src/viam/proto/service/datamanager/__init__.py +++ b/src/viam/proto/service/datamanager/__init__.py @@ -2,12 +2,18 @@ @generated by Viam. Do not edit manually! """ -from ....gen.service.datamanager.v1.data_manager_grpc import DataManagerServiceBase, DataManagerServiceStub + +from ....gen.service.datamanager.v1.data_manager_grpc import ( + DataManagerServiceBase, + DataManagerServiceStub, + UnimplementedDataManagerServiceBase, +) from ....gen.service.datamanager.v1.data_manager_pb2 import SyncRequest, SyncResponse __all__ = [ "DataManagerServiceBase", "DataManagerServiceStub", + "UnimplementedDataManagerServiceBase", "SyncRequest", "SyncResponse", ] diff --git a/src/viam/proto/service/discovery/__init__.py b/src/viam/proto/service/discovery/__init__.py new file mode 100644 index 000000000..3593fd1fe --- /dev/null +++ b/src/viam/proto/service/discovery/__init__.py @@ -0,0 +1,15 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.service.discovery.v1.discovery_grpc import DiscoveryServiceBase, DiscoveryServiceStub, UnimplementedDiscoveryServiceBase +from ....gen.service.discovery.v1.discovery_pb2 import DiscoverResourcesRequest, DiscoverResourcesResponse + +__all__ = [ + "DiscoveryServiceBase", + "DiscoveryServiceStub", + "UnimplementedDiscoveryServiceBase", + "DiscoverResourcesRequest", + "DiscoverResourcesResponse", +] diff --git a/src/viam/proto/service/generic/__init__.py b/src/viam/proto/service/generic/__init__.py new file mode 100644 index 000000000..bdfbb40f9 --- /dev/null +++ b/src/viam/proto/service/generic/__init__.py @@ -0,0 +1,12 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.service.generic.v1.generic_grpc import GenericServiceBase, GenericServiceStub, UnimplementedGenericServiceBase + +__all__ = [ + "GenericServiceBase", + "GenericServiceStub", + "UnimplementedGenericServiceBase", +] diff --git a/src/viam/proto/service/mlmodel/__init__.py b/src/viam/proto/service/mlmodel/__init__.py new file mode 100644 index 000000000..b6687bdef --- /dev/null +++ b/src/viam/proto/service/mlmodel/__init__.py @@ -0,0 +1,54 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ....gen.service.mlmodel.v1.mlmodel_grpc import MLModelServiceBase, MLModelServiceStub, UnimplementedMLModelServiceBase +from ....gen.service.mlmodel.v1.mlmodel_pb2 import ( + File, + FlatTensor, + FlatTensorDataDouble, + FlatTensorDataFloat, + FlatTensorDataInt8, + FlatTensorDataInt16, + FlatTensorDataInt32, + FlatTensorDataInt64, + FlatTensorDataUInt8, + FlatTensorDataUInt16, + FlatTensorDataUInt32, + FlatTensorDataUInt64, + FlatTensors, + InferRequest, + InferResponse, + LabelType, + Metadata, + MetadataRequest, + MetadataResponse, + TensorInfo, +) + +__all__ = [ + "MLModelServiceBase", + "MLModelServiceStub", + "UnimplementedMLModelServiceBase", + "File", + "FlatTensor", + "FlatTensorDataDouble", + "FlatTensorDataFloat", + "FlatTensorDataInt16", + "FlatTensorDataInt32", + "FlatTensorDataInt64", + "FlatTensorDataInt8", + "FlatTensorDataUInt16", + "FlatTensorDataUInt32", + "FlatTensorDataUInt64", + "FlatTensorDataUInt8", + "FlatTensors", + "InferRequest", + "InferResponse", + "LabelType", + "Metadata", + "MetadataRequest", + "MetadataResponse", + "TensorInfo", +] diff --git a/src/viam/proto/service/motion/__init__.py b/src/viam/proto/service/motion/__init__.py index 792654443..6f7ce6b37 100644 --- a/src/viam/proto/service/motion/__init__.py +++ b/src/viam/proto/service/motion/__init__.py @@ -2,23 +2,67 @@ @generated by Viam. Do not edit manually! """ -from ....gen.service.motion.v1.motion_grpc import MotionServiceBase, MotionServiceStub + +from ....gen.service.motion.v1.motion_grpc import MotionServiceBase, MotionServiceStub, UnimplementedMotionServiceBase from ....gen.service.motion.v1.motion_pb2 import ( + CollisionSpecification, + ComponentState, + Constraints, + GetPlanRequest, + GetPlanResponse, GetPoseRequest, GetPoseResponse, + LinearConstraint, + ListPlanStatusesRequest, + ListPlanStatusesResponse, + MotionConfiguration, + MoveOnGlobeRequest, + MoveOnGlobeResponse, + MoveOnMapRequest, + MoveOnMapResponse, MoveRequest, MoveResponse, - MoveSingleComponentRequest, - MoveSingleComponentResponse, + ObstacleDetector, + OrientationConstraint, + Plan, + PlanState, + PlanStatus, + PlanStatusWithID, + PlanStep, + PlanWithStatus, + StopPlanRequest, + StopPlanResponse, ) __all__ = [ "MotionServiceBase", "MotionServiceStub", + "UnimplementedMotionServiceBase", + "CollisionSpecification", + "ComponentState", + "Constraints", + "GetPlanRequest", + "GetPlanResponse", "GetPoseRequest", "GetPoseResponse", + "LinearConstraint", + "ListPlanStatusesRequest", + "ListPlanStatusesResponse", + "MotionConfiguration", + "MoveOnGlobeRequest", + "MoveOnGlobeResponse", + "MoveOnMapRequest", + "MoveOnMapResponse", "MoveRequest", "MoveResponse", - "MoveSingleComponentRequest", - "MoveSingleComponentResponse", + "ObstacleDetector", + "OrientationConstraint", + "Plan", + "PlanState", + "PlanStatus", + "PlanStatusWithID", + "PlanStep", + "PlanWithStatus", + "StopPlanRequest", + "StopPlanResponse", ] diff --git a/src/viam/proto/service/navigation/__init__.py b/src/viam/proto/service/navigation/__init__.py index 2765e35c3..c0c7f058c 100644 --- a/src/viam/proto/service/navigation/__init__.py +++ b/src/viam/proto/service/navigation/__init__.py @@ -2,7 +2,8 @@ @generated by Viam. Do not edit manually! """ -from ....gen.service.navigation.v1.navigation_grpc import NavigationServiceBase, NavigationServiceStub + +from ....gen.service.navigation.v1.navigation_grpc import NavigationServiceBase, NavigationServiceStub, UnimplementedNavigationServiceBase from ....gen.service.navigation.v1.navigation_pb2 import ( AddWaypointRequest, AddWaypointResponse, @@ -10,8 +11,17 @@ GetLocationResponse, GetModeRequest, GetModeResponse, + GetObstaclesRequest, + GetObstaclesResponse, + GetPathsRequest, + GetPathsResponse, + GetPropertiesRequest, + GetPropertiesResponse, GetWaypointsRequest, GetWaypointsResponse, + MapType, + Mode, + Path, RemoveWaypointRequest, RemoveWaypointResponse, SetModeRequest, @@ -22,14 +32,24 @@ __all__ = [ "NavigationServiceBase", "NavigationServiceStub", + "UnimplementedNavigationServiceBase", "AddWaypointRequest", "AddWaypointResponse", "GetLocationRequest", "GetLocationResponse", "GetModeRequest", "GetModeResponse", + "GetObstaclesRequest", + "GetObstaclesResponse", + "GetPathsRequest", + "GetPathsResponse", + "GetPropertiesRequest", + "GetPropertiesResponse", "GetWaypointsRequest", "GetWaypointsResponse", + "MapType", + "Mode", + "Path", "RemoveWaypointRequest", "RemoveWaypointResponse", "SetModeRequest", diff --git a/src/viam/proto/service/sensors/__init__.py b/src/viam/proto/service/sensors/__init__.py index 7a8d02d6d..0f7a04277 100644 --- a/src/viam/proto/service/sensors/__init__.py +++ b/src/viam/proto/service/sensors/__init__.py @@ -2,12 +2,14 @@ @generated by Viam. Do not edit manually! """ -from ....gen.service.sensors.v1.sensors_grpc import SensorsServiceBase, SensorsServiceStub + +from ....gen.service.sensors.v1.sensors_grpc import SensorsServiceBase, SensorsServiceStub, UnimplementedSensorsServiceBase from ....gen.service.sensors.v1.sensors_pb2 import GetReadingsRequest, GetReadingsResponse, GetSensorsRequest, GetSensorsResponse, Readings __all__ = [ "SensorsServiceBase", "SensorsServiceStub", + "UnimplementedSensorsServiceBase", "GetReadingsRequest", "GetReadingsResponse", "GetSensorsRequest", diff --git a/src/viam/proto/service/shell/__init__.py b/src/viam/proto/service/shell/__init__.py index 1383ac938..47a2a44f6 100644 --- a/src/viam/proto/service/shell/__init__.py +++ b/src/viam/proto/service/shell/__init__.py @@ -2,12 +2,35 @@ @generated by Viam. Do not edit manually! """ -from ....gen.service.shell.v1.shell_grpc import ShellServiceBase, ShellServiceStub -from ....gen.service.shell.v1.shell_pb2 import ShellRequest, ShellResponse + +from ....gen.service.shell.v1.shell_grpc import ShellServiceBase, ShellServiceStub, UnimplementedShellServiceBase +from ....gen.service.shell.v1.shell_pb2 import ( + CopyFilesFromMachineRequest, + CopyFilesFromMachineRequestMetadata, + CopyFilesFromMachineResponse, + CopyFilesFromMachineResponseMetadata, + CopyFilesSourceType, + CopyFilesToMachineRequest, + CopyFilesToMachineRequestMetadata, + CopyFilesToMachineResponse, + FileData, + ShellRequest, + ShellResponse, +) __all__ = [ "ShellServiceBase", "ShellServiceStub", + "UnimplementedShellServiceBase", + "CopyFilesFromMachineRequest", + "CopyFilesFromMachineRequestMetadata", + "CopyFilesFromMachineResponse", + "CopyFilesFromMachineResponseMetadata", + "CopyFilesSourceType", + "CopyFilesToMachineRequest", + "CopyFilesToMachineRequestMetadata", + "CopyFilesToMachineResponse", + "FileData", "ShellRequest", "ShellResponse", ] diff --git a/src/viam/proto/service/slam/__init__.py b/src/viam/proto/service/slam/__init__.py index 13f22ce20..0703e95d2 100644 --- a/src/viam/proto/service/slam/__init__.py +++ b/src/viam/proto/service/slam/__init__.py @@ -2,14 +2,35 @@ @generated by Viam. Do not edit manually! """ -from ....gen.service.slam.v1.slam_grpc import SLAMServiceBase, SLAMServiceStub -from ....gen.service.slam.v1.slam_pb2 import GetMapRequest, GetMapResponse, GetPositionRequest, GetPositionResponse + +from ....gen.service.slam.v1.slam_grpc import SLAMServiceBase, SLAMServiceStub, UnimplementedSLAMServiceBase +from ....gen.service.slam.v1.slam_pb2 import ( + GetInternalStateRequest, + GetInternalStateResponse, + GetPointCloudMapRequest, + GetPointCloudMapResponse, + GetPositionRequest, + GetPositionResponse, + GetPropertiesRequest, + GetPropertiesResponse, + MappingMode, + SensorInfo, + SensorType, +) __all__ = [ "SLAMServiceBase", "SLAMServiceStub", - "GetMapRequest", - "GetMapResponse", + "UnimplementedSLAMServiceBase", + "GetInternalStateRequest", + "GetInternalStateResponse", + "GetPointCloudMapRequest", + "GetPointCloudMapResponse", "GetPositionRequest", "GetPositionResponse", + "GetPropertiesRequest", + "GetPropertiesResponse", + "MappingMode", + "SensorInfo", + "SensorType", ] diff --git a/src/viam/proto/service/vision/__init__.py b/src/viam/proto/service/vision/__init__.py index 9c2525a5d..a31dcafb7 100644 --- a/src/viam/proto/service/vision/__init__.py +++ b/src/viam/proto/service/vision/__init__.py @@ -2,75 +2,45 @@ @generated by Viam. Do not edit manually! """ -from ....gen.service.vision.v1.vision_grpc import VisionServiceBase, VisionServiceStub + +from ....gen.service.vision.v1.vision_grpc import UnimplementedVisionServiceBase, VisionServiceBase, VisionServiceStub from ....gen.service.vision.v1.vision_pb2 import ( - AddClassifierRequest, - AddClassifierResponse, - AddDetectorRequest, - AddDetectorResponse, - AddSegmenterRequest, - AddSegmenterResponse, + CaptureAllFromCameraRequest, + CaptureAllFromCameraResponse, Classification, Detection, GetClassificationsFromCameraRequest, GetClassificationsFromCameraResponse, GetClassificationsRequest, GetClassificationsResponse, - GetClassifierNamesRequest, - GetClassifierNamesResponse, GetDetectionsFromCameraRequest, GetDetectionsFromCameraResponse, GetDetectionsRequest, GetDetectionsResponse, - GetDetectorNamesRequest, - GetDetectorNamesResponse, - GetModelParameterSchemaRequest, - GetModelParameterSchemaResponse, GetObjectPointCloudsRequest, GetObjectPointCloudsResponse, - GetSegmenterNamesRequest, - GetSegmenterNamesResponse, - RemoveClassifierRequest, - RemoveClassifierResponse, - RemoveDetectorRequest, - RemoveDetectorResponse, - RemoveSegmenterRequest, - RemoveSegmenterResponse, + GetPropertiesRequest, + GetPropertiesResponse, ) __all__ = [ + "UnimplementedVisionServiceBase", "VisionServiceBase", "VisionServiceStub", - "AddClassifierRequest", - "AddClassifierResponse", - "AddDetectorRequest", - "AddDetectorResponse", - "AddSegmenterRequest", - "AddSegmenterResponse", + "CaptureAllFromCameraRequest", + "CaptureAllFromCameraResponse", "Classification", "Detection", "GetClassificationsFromCameraRequest", "GetClassificationsFromCameraResponse", "GetClassificationsRequest", "GetClassificationsResponse", - "GetClassifierNamesRequest", - "GetClassifierNamesResponse", "GetDetectionsFromCameraRequest", "GetDetectionsFromCameraResponse", "GetDetectionsRequest", "GetDetectionsResponse", - "GetDetectorNamesRequest", - "GetDetectorNamesResponse", - "GetModelParameterSchemaRequest", - "GetModelParameterSchemaResponse", "GetObjectPointCloudsRequest", "GetObjectPointCloudsResponse", - "GetSegmenterNamesRequest", - "GetSegmenterNamesResponse", - "RemoveClassifierRequest", - "RemoveClassifierResponse", - "RemoveDetectorRequest", - "RemoveDetectorResponse", - "RemoveSegmenterRequest", - "RemoveSegmenterResponse", + "GetPropertiesRequest", + "GetPropertiesResponse", ] diff --git a/src/viam/proto/stream/__init__.py b/src/viam/proto/stream/__init__.py new file mode 100644 index 000000000..405378954 --- /dev/null +++ b/src/viam/proto/stream/__init__.py @@ -0,0 +1,36 @@ +""" +@generated by Viam. +Do not edit manually! +""" + +from ...gen.stream.v1.stream_grpc import StreamServiceBase, StreamServiceStub, UnimplementedStreamServiceBase +from ...gen.stream.v1.stream_pb2 import ( + AddStreamRequest, + AddStreamResponse, + GetStreamOptionsRequest, + GetStreamOptionsResponse, + ListStreamsRequest, + ListStreamsResponse, + RemoveStreamRequest, + RemoveStreamResponse, + Resolution, + SetStreamOptionsRequest, + SetStreamOptionsResponse, +) + +__all__ = [ + "StreamServiceBase", + "StreamServiceStub", + "UnimplementedStreamServiceBase", + "AddStreamRequest", + "AddStreamResponse", + "GetStreamOptionsRequest", + "GetStreamOptionsResponse", + "ListStreamsRequest", + "ListStreamsResponse", + "RemoveStreamRequest", + "RemoveStreamResponse", + "Resolution", + "SetStreamOptionsRequest", + "SetStreamOptionsResponse", +] diff --git a/src/viam/py.typed b/src/viam/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/registry.py b/src/viam/registry.py deleted file mode 100644 index 051f14e03..000000000 --- a/src/viam/registry.py +++ /dev/null @@ -1,109 +0,0 @@ -from dataclasses import dataclass -from typing import Any, Callable, Coroutine, Dict, Generic, Mapping, Type, TypeVar - -from google.protobuf.struct_pb2 import Struct -from grpclib.client import Channel - -from viam.components.component_base import ComponentBase -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError, DuplicateComponentError -from viam.proto.robot import Status - -Component = TypeVar("Component", bound=ComponentBase) - - -async def default_create_status(component: ComponentBase) -> Status: - return Status(name=component.get_resource_name(component.name), status=Struct()) - - -@dataclass -class ComponentRegistration(Generic[Component]): - """An object representing a component to be registered. - - This object is generic over the component, and it includes various functionality for the component, such as creating its RPC client - or status. - - If creating a custom Component type, you should register the component by creating a `ComponentRegistration` object and registering it - to the `Registry`. - """ - - component_type: Type[Component] - """The type of the Component to be registered - """ - - name: str - """The name of the Component type - """ - - rpc_service: Type[ComponentServiceBase] - """The RPC service of the component. This must extend from `ComponentServiceBase` - """ - - create_rpc_client: Callable[[str, Channel], Component] - """A function that will create the RPC client for this component - """ - - create_status: Callable[[Component], Coroutine[Any, Any, Status]] = default_create_status - """A function to create a Status object for this component. - - If the Component does not provide a custom status type, the default implementation can be used. - """ - - -class Registry: - """The global registry of robotic parts. - - **NB** The Registry should almost never be used directly - - The Registry keeps track of the types of Components that are available on robots using this SDK. All the base component types are - pre-registered (e.g. Arm, Motor). - - If you create a new component type that is not an extension of any of the existing base component types, then you must register said - component using `Registry.register(...)`. - """ - - _COMPONENTS: Dict[str, ComponentRegistration] = {} - - @classmethod - def register(cls, registration: ComponentRegistration): - """Register a Component with the Registry - - Args: - registration (ComponentRegistration): Object containing registration data for the component - - Raises: - DuplicateComponentError: Raised if the Component to register is already in the registry - """ - if registration.name in cls._COMPONENTS: - raise DuplicateComponentError(registration.name) - cls._COMPONENTS[registration.name] = registration - - @classmethod - def lookup(cls, component_name: str) -> ComponentRegistration: - """Lookup and retrieve a registered component by its name - - Args: - component_name (str): The name of the component - - Raises: - ComponentNotFoundError: Raised if the component type is not registered - - Returns: - ComponentRegistration: The registration object of the component - """ - try: - return cls._COMPONENTS[component_name] - except KeyError: - raise ComponentNotFoundError("component", component_name) - - @classmethod - @property - def REGISTERED_COMPONENTS(cls) -> Mapping[str, ComponentRegistration]: - """The dictionary of all registered components - - Key: Name of the component type - - Value: The registration object for the component type - - Returns: - Mapping[str, ComponentRegistration]: All registered components - """ - return cls._COMPONENTS.copy() diff --git a/src/viam/resource/__init__.py b/src/viam/resource/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/viam/resource/base.py b/src/viam/resource/base.py new file mode 100644 index 000000000..57d4cadf7 --- /dev/null +++ b/src/viam/resource/base.py @@ -0,0 +1,123 @@ +from abc import abstractmethod +from logging import Logger +from typing import TYPE_CHECKING, Any, ClassVar, Mapping, Optional, Protocol, runtime_checkable + +from typing_extensions import Self + +from viam.operations import Operation +from viam.proto.common import ResourceName + +from .types import API + +if TYPE_CHECKING: + from viam.robot.client import RobotClient + from viam.utils import ValueTypes + + +@runtime_checkable +class ResourceBase(Protocol): + """ + The base requirements for a Resource. + """ + + API: ClassVar["API"] + """The API of the Resource""" + + name: str + """The name of the Resource""" + + logger: Logger + """A logger allowing for setting log levels on a per-resource basis""" + + @classmethod + def get_resource_name(cls, name: str) -> ResourceName: + """ + Get the ResourceName for this Resource with the given name + + :: + + # Can be used with any resource, using an arm as an example + my_arm_name = Arm.get_resource_name("my_arm") + + Args: + name (str): The name of the Resource + + Returns: + ResourceName: The ResourceName of this Resource + """ + return ResourceName( + namespace=cls.API.namespace, + type=cls.API.resource_type, + subtype=cls.API.resource_subtype, + name=name, + ) + + @classmethod + @abstractmethod + def from_robot(cls, robot: "RobotClient", name: str) -> Self: + """Get the Resource named ``name`` from the provided robot. + + :: + + # Can be used with any resource, using an arm as an example + my_arm = Arm.from_robot(robot, "my_arm") + + Args: + robot (RobotClient): The robot + name (str): The name of the Resource + + Returns: + Self: The Resource, if it exists on the robot + """ + ... + + @abstractmethod + async def do_command( + self, command: Mapping[str, "ValueTypes"], *, timeout: Optional[float] = None, **kwargs + ) -> Mapping[str, "ValueTypes"]: + """Send/Receive arbitrary commands to the Resource + + :: + + command = {"cmd": "test", "data1": 500} + result = await component.do_command(command) + + Args: + command (Mapping[str, ValueTypes]): The command to execute + + Raises: + NotImplementedError: Raised if the Resource does not support arbitrary commands + + Returns: + Mapping[str, ValueTypes]: Result of the executed command + """ + ... + + def get_operation(self, kwargs: Mapping[str, Any]) -> Operation: + """Get the ``Operation`` associated with the currently running function. + + When writing custom resources, you should get the ``Operation`` by calling this function and check to see if it's cancelled. + If the ``Operation`` is cancelled, then you can perform any necessary (terminating long running tasks, cleaning up connections, etc. + ). + + Args: + kwargs (Mapping[str, Any]): The kwargs object containing the operation + + Returns: + viam.operations.Operation: The operation associated with this function + """ + return kwargs.get(Operation.ARG_NAME, Operation._noop()) + + async def close(self): + """Safely shut down the resource and prevent further use. + + Close must be idempotent. Later configuration may allow a resource to be "open" again. + If a resource does not want or need a close function, it is assumed that the resource does not need to return errors when future + non-Close methods are called. + + :: + + await component.close() + + """ + return diff --git a/src/viam/resource/easy_resource.py b/src/viam/resource/easy_resource.py new file mode 100644 index 000000000..a3529a7b2 --- /dev/null +++ b/src/viam/resource/easy_resource.py @@ -0,0 +1,153 @@ +import inspect +import re +from abc import ABCMeta +from typing import Callable, ClassVar, Mapping, Sequence, Union + +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ResourceName + +from .. import logging +from ..errors import MethodNotImplementedError +from .base import ResourceBase +from .registry import Registry, ResourceCreatorRegistration +from .types import API, Model, ModelFamily + +modelRegex = re.compile(r"^([^:]+):([^:]+):([^:]+)$") + +logger = logging.getLogger(__name__) + + +def _parse_model(orig: Union[str, Model]) -> Model: + "take a model or string and turn it into a Model" + if isinstance(orig, Model): + return orig + match = modelRegex.match(orig) + if not match: + raise ValueError(f"MODEL {orig} doesn't match expected format 'org:type:name'") + *family, name = match.groups() + return Model(ModelFamily(*family), name) + + +def _create_stub_fn(name: str, is_async: bool) -> Callable: + """ + This creates a sync or async stub function which returns a MethodNotImplementedError. + The stub_model decorator uses these to stub out an abstract base class. + """ + if is_async: + # note: this is a pyright bug https://github.com/microsoft/pyright/issues/2136 + async def stub_fn(*args, **kwargs): # pyright: ignore [reportRedeclaration] + logger.info(f"{name} not implemented") + raise MethodNotImplementedError(name) + + else: + + def stub_fn(*args, **kwargs): + logger.info(f"{name} not implemented") + raise MethodNotImplementedError(name) + + stub_fn.__name__ = f"{name}_stub" + return stub_fn + + +def stub_model(cls: ABCMeta) -> ABCMeta: + """ + Class decorator which adds error implementations of abstract functions. This means they will fail + when called, rather than the default where they fail when instantiated. This is intended for developers + who want to build and test incrementally, not for production use. + + Example: + + @stub_model + class MyMotor(Motor, EasyResource): + MODEL = 'viam:motor:easy-resource-example' + + Normally this class would fail to instantiate. With the decorator, it will succeed but the unimplemented + methods will throw errors at runtime. + """ + for attr in list(cls.__abstractmethods__): + val = getattr(cls, attr) + is_async = inspect.iscoroutinefunction(val) + stub_fn = _create_stub_fn(attr, is_async) + setattr(cls, attr, stub_fn) + logger.debug("patched %s.%s with %s", cls, attr, stub_fn) + cls.__abstractmethods__ -= {attr} + return cls + + +class EasyResource: + """ + EasyResource is a mixin that simplifies the process of creating Viam modules (extension programs) + and resources (the resource classes provided by those extension programs). + + Basic usage: + + :: + + class MyModel(Sensor, EasyResource): + MODEL = "my-org:sensor:my-sensor" + + async def get_readings(self, **kwargs): + return {"ok": True} + + See examples/easy_resource/main.py for extended usage. + """ + + API: ClassVar[API] + MODEL: ClassVar[Model] + + def __init_subclass__(cls, register=True, **kwargs): + """ + When you subclass this mixin, it parses cls.MODEL and registers cls in global registry. + """ + super().__init_subclass__(**kwargs) + if not hasattr(cls, "MODEL"): + raise ValueError("Please define a MODEL with the format 'org:type:name' on your class, for example 'viam:camera:IMX219'") + cls.MODEL = _parse_model(cls.MODEL) + if register: + cls.register() + + def __init__(self, name: str): + # note: this mirrors the constructor for ComponentBase and ServiceBase. + self.name = name + + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]): + """ + This is passed to register_resource_creator; the default implementation calls reconfigure() + when an instance of your model is instantiated. You can override this in your subclass. + """ + self = cls(config.name) + logger.debug("created %s %s %s", cls.API, cls.MODEL, config.name) + self.reconfigure(config, dependencies) + return self + + @classmethod + def validate_config(cls, config: ComponentConfig) -> Sequence[str]: + """This method allows you to validate the configuration object received from the machine, + as well as to return any implicit dependencies based on that `config`. + + Args: + config (ComponentConfig): The configuration for this resource + + Returns: + Sequence[str]: A list of implicit dependencies + """ + return [] + + @classmethod + def register(cls): + """ + This adds the model to the global registry. It is called by __init_subclass__ and you typically + won't call it directly. + """ + logger.debug("registering %s %s", cls.API, cls.MODEL) + # note: We could fix this pyright-ignore if EasyResource inherited ResourceBase, but that crashes in the mro() + # walk in ResourceManager.register. + Registry.register_resource_creator( + cls.API, + cls.MODEL, + ResourceCreatorRegistration(cls.new, cls.validate_config), # pyright: ignore [reportArgumentType] + ) + + def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]): + logger.debug("reconfigure %s %s", self.API, self.MODEL) diff --git a/src/viam/resource/manager.py b/src/viam/resource/manager.py new file mode 100644 index 000000000..2153fc107 --- /dev/null +++ b/src/viam/resource/manager.py @@ -0,0 +1,126 @@ +from threading import RLock +from typing import Dict, List, Type, TypeVar + +from viam.logging import getLogger +from viam.proto.common import ResourceName +from viam.resource.base import ResourceBase +from viam.resource.registry import Registry + +from ..components.component_base import ComponentBase +from ..errors import DuplicateResourceError, ResourceNotFoundError +from ..services.service_base import ServiceBase + +LOGGER = getLogger(__name__) +ResourceType = TypeVar("ResourceType", bound=ResourceBase) + + +class ResourceManager: + """ + Registry containing all components registered to this server. + """ + + resources: Dict[ResourceName, ResourceBase] + _short_to_long_name: Dict[str, List[ResourceName]] + _lock: RLock + + def __init__(self, components: List[ResourceBase] = []) -> None: + self._lock = RLock() + self.resources = {} + self._short_to_long_name = {} + for component in components: + self.register(component) + + def register(self, resource: ResourceBase): + """ + Register a new resource with the registry. + Resources may not have the same name. + If a resource is remote and the short name is unique, save a short name version. + + Raises: + DuplicateResourceError: Error if attempting to register resource + with the name of an existing resource + ResourceNotFoundError: Raised if the API of the resource is not registered + + Args: + resource (ResourceBase): The resource to register + """ + Registry.lookup_api(resource.API) # confirm the API is registered in Registry + + _BaseClasses = (ResourceBase, ComponentBase, ServiceBase) + rnames: Dict[ResourceName, ResourceBase] = {} + for subtype in resource.__class__.mro(): + if subtype in _BaseClasses: + continue + if hasattr(subtype, "get_resource_name"): + rn = subtype.get_resource_name(resource.name) # type: ignore + rnames[rn] = resource + for rn in rnames: + if ":" in rn.name: + short_name = rn.name.split(":")[-1] + if short_name in self._short_to_long_name and rn not in self._short_to_long_name[short_name]: + self._short_to_long_name[short_name].append(rn) + elif short_name not in self._short_to_long_name: + self._short_to_long_name[short_name] = [rn] + + if rnames.keys() & self.resources.keys(): + raise DuplicateResourceError(resource.name) + + with self._lock: + self.resources.update(rnames) + + def get_resource(self, of_type: Type[ResourceType], name: ResourceName) -> ResourceType: + """ + Return a resource from the registry. + If a unique short name version is given, return a remote resource with the name. + + Args: + of_type (Type[ResourceType]): The type of the resource + name (viam.proto.common.ResourceName): The name of the resource + + Raises: + ResourceNotFoundError: Error if resource with the given type + and name does not exist in the registry + + Returns: + ResourceType: The resource + """ + with self._lock: + resource = self.resources.get(name, None) + if resource and isinstance(resource, of_type): + return resource + + if name.name in self._short_to_long_name and len(self._short_to_long_name[name.name]) == 1: + return self.get_resource(of_type, self._short_to_long_name[name.name][0]) + raise ResourceNotFoundError(name.subtype, name.name) + + async def remove_resource(self, name: ResourceName): + """Remove the resource with the specified ```ResourceName```. + + Args: + name (viam.proto.common.ResourceName): The ResourceName of the resource + """ + with self._lock: + try: + resource = self.resources[name] + await resource.close() + except Exception as e: + raise e + finally: + del self.resources[name] + + async def close(self): + """Close the resourcce manager by removing all resources. + Please note that any errors will not raise an exception. Errors will still be logged.""" + rns = [key for key in self.resources.keys()] + with self._lock: + for rn in rns: + try: + await self.remove_resource(rn) + except Exception as e: + LOGGER.error(f"Error while closing {rn.name}:", e) + + def _resource_by_name_only(self, name: str) -> ResourceBase: + for rname, resource in self.resources.items(): + if rname.name == name: + return resource + raise ResourceNotFoundError("resource", name) diff --git a/src/viam/resource/registry.py b/src/viam/resource/registry.py new file mode 100644 index 000000000..b29516162 --- /dev/null +++ b/src/viam/resource/registry.py @@ -0,0 +1,199 @@ +from dataclasses import dataclass +from threading import Lock +from typing import TYPE_CHECKING, Callable, ClassVar, Dict, Generic, Mapping, Type, TypeVar + +from grpclib.client import Channel + +from viam.errors import DuplicateResourceError, ResourceNotFoundError, ValidationError + +from .base import ResourceBase + +if TYPE_CHECKING: + from .rpc_service_base import ResourceRPCServiceBase + from .types import API, Model, ResourceCreator, Validator + +Resource = TypeVar("Resource", bound=ResourceBase) + + +@dataclass +class ResourceCreatorRegistration: + """An object representing a resource creator to be registered. + + If creating a custom Resource creator, you should register the creator by creating a ``ResourceCreatorRegistration`` object and + registering it to the ``Registry``. + """ + + creator: "ResourceCreator" + """A function that can create a resource given a mapping of dependencies (``ResourceName`` to ``ResourceBase`` + """ + + validator: "Validator" = lambda x: [] + """A function that can validate a resource and return implicit dependencies. + + If called without a validator function, default to a function returning an empty Sequence + """ + + +@dataclass +class ResourceRegistration(Generic[Resource]): + """An object representing a resource to be registered. + + This object is generic over the ``ResourceBase``, and it includes various functionality for the resource, + such as creating its RPC client or status. + + If creating a custom Resource type, you should register the resource by creating a ``ResourceRegistration`` object and registering it + to the ``Registry``. + """ + + resource_type: Type[Resource] + """The type of the Resource to be registered + """ + + rpc_service: Type["ResourceRPCServiceBase"] + """The type of the RPC service of the resource. This must extend from ``RPCServiceBase`` + """ + + create_rpc_client: Callable[[str, Channel], Resource] + """A function that will create the RPC client for this resource + """ + + +class Registry: + """The global registry of robotic parts. + + **NB** The Registry should almost never be used directly + + The Registry keeps track of the types of Resources that are available on robots using this SDK. All the base resource types are + pre-registered (for example Arm, Motor). + + If you create a new resource type that is not an extension of any of the existing base resource types, then you must register said + resource using ``Registry.register(...)``. + """ + + _APIS: ClassVar[Dict["API", ResourceRegistration]] = {} + _RESOURCES: ClassVar[Dict[str, ResourceCreatorRegistration]] = {} + _lock: ClassVar[Lock] = Lock() + + @classmethod + def register_api(cls, registration: ResourceRegistration[Resource]): + """Register a API with the Registry + + Args: + registration (ResourceRegistration): Object containing registration data for the API + + Raises: + DuplicateResourceError: Raised if the API to register is already in the registry + ValidationError: Raised if registration is missing any necessary parameters + """ + with cls._lock: + if registration.resource_type.API in cls._APIS: + raise DuplicateResourceError(str(registration.resource_type.API)) + + if registration.resource_type and registration.rpc_service and registration.create_rpc_client: + cls._APIS[registration.resource_type.API] = registration + else: + raise ValidationError("Passed resource registration does not have correct parameters") + + @classmethod + def register_resource_creator(cls, api: "API", model: "Model", registration: ResourceCreatorRegistration): + """Register a specific ``Model`` and validator function for the specific resource ``API`` with the Registry + + Args: + api (API): The API of the resource + model (Model): The Model of the resource + registration (ResourceCreatorRegistration): The registration functions of the model + + Raises: + DuplicateResourceError: Raised if the API and Model pairing is already registered + ValidationError: Raised if registration does not have creator + """ + key = f"{api}/{model}" + with cls._lock: + if key in cls._RESOURCES: + raise DuplicateResourceError(key) + + if registration.creator: + cls._RESOURCES[key] = registration + else: + raise ValidationError("A creator function was not provided") + + @classmethod + def lookup_api(cls, api: "API") -> ResourceRegistration: + """Lookup and retrieve a registered API by its name + + Args: + api (str): The API of the resource + + Raises: + ResourceNotFoundError: Raised if the API is not registered + + Returns: + ResourceRegistration: The registration object of the resource + """ + with cls._lock: + try: + return cls._APIS[api] + except KeyError: + raise ResourceNotFoundError(api.resource_type, api.resource_subtype) + + @classmethod + def lookup_resource_creator(cls, api: "API", model: "Model") -> "ResourceCreator": + """Lookup and retrieve a registered resource creator by its API and model + + Args: + api (API): The API of the resource + model (Model): The Model of the resource + + Raises: + ResourceNotFoundError: Raised if the API Model pairing is not registered + + Returns: + ResourceCreator: The function to create the resource + """ + with cls._lock: + try: + return cls._RESOURCES[f"{api}/{model}"].creator + except KeyError: + raise ResourceNotFoundError(api.resource_type, api.resource_subtype) + + @classmethod + def lookup_validator(cls, api: "API", model: "Model") -> "Validator": + """Lookup and retrieve a registered validator function by its API and model. If there is none, return None + + Args: + api (API): The API of the resource + model (Model): The Model of the resource + + Returns: + Validator: The function to validate the resource + """ + try: + return cls._RESOURCES[f"{api}/{model}"].validator + except AttributeError: + return lambda x: [] + except KeyError: + raise ResourceNotFoundError(api.resource_type, api.resource_subtype) + + @classmethod + def REGISTERED_APIS(cls) -> Mapping["API", ResourceRegistration]: + """The dictionary of all registered resources + - Key: API of the resource + - Value: The registration object for the resource + + Returns: + Mapping[API, ResourceRegistration]: All registered resources + """ + with cls._lock: + return cls._APIS.copy() + + @classmethod + def REGISTERED_RESOURCE_CREATORS(cls) -> Mapping[str, "ResourceCreatorRegistration"]: + """The dictionary of all registered resources + - Key: API/model + - Value: The ResourceCreatorRegistration for the resource + + Returns: + Mapping[str, ResourceCreatorRegistration]: All registered resources + """ + with cls._lock: + return cls._RESOURCES.copy() diff --git a/src/viam/resource/rpc_client_base.py b/src/viam/resource/rpc_client_base.py new file mode 100644 index 000000000..7d1ee762c --- /dev/null +++ b/src/viam/resource/rpc_client_base.py @@ -0,0 +1,65 @@ +from random import choice +from string import ascii_lowercase +from typing import Any, Dict, Protocol, runtime_checkable + +from grpclib.client import Channel + +from viam.rpc.types import RPCServiceStubBase + + +@runtime_checkable +class ResourceRPCClientBase(Protocol): + """ + Base RPC client for a resource. + Resource RPC clients must inherit from this class + """ + + class Metadata: + metadata: Dict[str, str] = {} + + def enable_debug_logging(self, key: str = ""): + """Enables server-side debug logging for resource methods. + + Args: + key (str): The key to associate debug logs with. If not provided, will default to a randomly generated string value. + """ + if key == "": + key = "".join(choice(ascii_lowercase) for i in range(6)) + self.metadata["dtname"] = key + + def disable_debug_logging(self): + """Disables server-side debug logging for resource methods.""" + del self.metadata["dtname"] + + def add_metadata(self, key: str, value: str): + """Adds a key-value pair to the metadata""" + self.metadata[key] = value + + def delete_metadata(self, key: str): + """Removes a key-value pair from the metadata by key""" + del self.metadata[key] + + @property + def proto(self): + """Returns metadata in a gRPC-appropriate form""" + return [(k, v) for k, v in self.metadata.items()] + + channel: Channel + client: Any + + +class ReconfigurableResourceRPCClientBase(ResourceRPCClientBase): + """A base RPC client that can reset its channel. + + Useful if connection is lost and then regained. + """ + + def reset_channel(self, channel: Channel): + """Called when the RPC channel was reset. Passes in the new channel. + + Args: + channel (Channel): The new RPC Channel + """ + self.channel = channel + if isinstance(self.client, RPCServiceStubBase): + self.client = self.client.__class__(channel) diff --git a/src/viam/resource/rpc_service_base.py b/src/viam/resource/rpc_service_base.py new file mode 100644 index 000000000..9fbca2e03 --- /dev/null +++ b/src/viam/resource/rpc_service_base.py @@ -0,0 +1,48 @@ +import abc +from typing import TYPE_CHECKING, Generic, Type + +from viam.components.component_base import ComponentBase +from viam.errors import ResourceNotFoundError +from viam.resource.manager import ResourceType +from viam.rpc.types import RPCServiceBase +from viam.services.service_base import ServiceBase + +from .base import ResourceBase + +if TYPE_CHECKING: + from viam.resource.manager import ResourceManager + + +class ResourceRPCServiceBase(abc.ABC, RPCServiceBase, Generic[ResourceType]): + """ + Base RPC service for a resource. + All resource RPC services must inherit from this class. + """ + + RESOURCE_TYPE = Type + manager: "ResourceManager" + + def __init__(self, manager: "ResourceManager"): + self.manager = manager + + def get_resource(self, name: str) -> ResourceType: + """ + Return the resource with the given name if it exists in the registry. + If the resource does not exist in the registry, + this function will raise an error + + Args: + name (str): Name of the resource + + Raises: + GRPCError with the status code Status.NOT_FOUND + + Returns: + ResourceType: The resource + """ + try: + if self.RESOURCE_TYPE == ComponentBase or self.RESOURCE_TYPE == ResourceBase or self.RESOURCE_TYPE == ServiceBase: + return self.manager._resource_by_name_only(name) # type: ignore + return self.manager.get_resource(self.RESOURCE_TYPE, self.RESOURCE_TYPE.get_resource_name(name)) # type: ignore + except ResourceNotFoundError as e: + raise e.grpc_error diff --git a/src/viam/resource/types.py b/src/viam/resource/types.py new file mode 100644 index 000000000..f98803855 --- /dev/null +++ b/src/viam/resource/types.py @@ -0,0 +1,213 @@ +import re +import sys +from typing import TYPE_CHECKING, Callable, ClassVar, Mapping, Optional, Protocol, Sequence, runtime_checkable + +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + +from typing_extensions import Self + +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import GetGeometriesRequest, GetGeometriesResponse, ResourceName + +if TYPE_CHECKING: + from .base import ResourceBase + +RESOURCE_NAMESPACE_RDK = "rdk" +RESOURCE_TYPE_COMPONENT = "component" +RESOURCE_TYPE_SERVICE = "service" + + +class API: + """Represents a known component/service (resource) API""" + + namespace: str + """The namespace of the resource""" + + resource_type: str + """The type of the resource, for example `component` or `service`""" + + resource_subtype: str + """The subtype of the resource for example `servo`, `arm`, `vision`""" + + def __init__(self, namespace: str, resource_type: str, resource_subtype: str): + self.namespace = namespace + self.resource_type = resource_type + self.resource_subtype = resource_subtype + + def __str__(self) -> str: + return f"{self.namespace}:{self.resource_type}:{self.resource_subtype}" + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: object) -> bool: + if isinstance(other, API): + return str(self) == str(other) + return False + + @classmethod + def from_resource_name(cls, resource_name: ResourceName) -> Self: + """Convert a ```ResourceName``` into a ```API``` + + Args: + resource_name (viam.proto.common.ResourceName): The ResourceName to convert + + Returns: + Self: A new API + """ + return cls(resource_name.namespace, resource_name.type, resource_name.subtype) + + @classmethod + def from_string(cls, string: str) -> Self: + """Create a ```API``` from its string representation (namespace:resource_type:resource_subtype) + + Args: + string (str): The API as a string + + Raises: + ValueError: Raised if the string does not represent a valid API + + Returns: + Self: A new API + """ + regex = re.compile(r"^([\w-]+):([\w-]+):([\w-]+)$") + match = regex.match(string) + if not match: + raise ValueError(f"{string} is not a valid API") + return cls(match.group(1), match.group(2), match.group(3)) + + +class ModelFamily: + """Represents a family of related models""" + + namespace: str + """The namespace of the model family""" + + family: str + """The family name""" + + DEFAULT_FAMILY_NAME: ClassVar[str] = "builtin" + + DEFAULT: ClassVar["ModelFamily"] + + def __init__(self, namespace: str, family: str): + self.namespace = namespace + self.family = family + + def __str__(self) -> str: + return f"{self.namespace}:{self.family}" + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: object) -> bool: + if isinstance(other, ModelFamily): + return str(self) == str(other) + return False + + +ModelFamily.DEFAULT = ModelFamily(RESOURCE_NAMESPACE_RDK, ModelFamily.DEFAULT_FAMILY_NAME) + + +class Model: + """Represents a specific model within a family of models""" + + model_family: ModelFamily + """The family to which this model belongs""" + + name: str + """The name of the model""" + + def __init__(self, model_family: ModelFamily, name: str): + self.model_family = model_family + self.name = name + + def __str__(self) -> str: + return f"{self.model_family}:{self.name}" + + def __repr__(self) -> str: + return f"" + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: object) -> bool: + if isinstance(other, Model): + return str(self) == str(other) + return False + + @classmethod + def from_string(cls, model: str, *, ignore_errors=False) -> Self: + """Create a ```Model``` from its string representation (namespace:family:name). + + Args: + model (str): The Model as a string + ignore_errors (bool, optional): If namespace or family are not found in the string, default to empty string + rather than raise an exception. Defaults to False. + + Raises: + ValueError: Raised if the provided string is not a valid Model + + Returns: + Self: The Model + """ + regex = re.compile(r"^([\w-]+):([\w-]+):([\w-]+)$") + match = regex.match(model) + if match: + namespace = match.group(1) + family = match.group(2) + name = match.group(3) + model_family = ModelFamily(namespace, family) + elif ignore_errors: + model_family = ModelFamily("", "") + name = model + else: + raise ValueError(f"{model} is not a valid Model") + + return cls(model_family, name) + + +def resource_name_from_string(string: str) -> ResourceName: + """Create a ResourceName from its string representation (namespace:resource_type:resource_subtype/name) + + Args: + string (str): The ResourceName as a string + + Raises: + ValueError: Raised if the provided string is not a valid ResourceName + + Returns: + viam.proto.common.ResourceName: The new ResourceName + """ + regex = re.compile(r"^([\w-]+:[\w-]+:(?:[\w-]+))\/?([\w-]+:(?:[\w-]+:)*)?(.+)?$") + match = regex.match(string) + if not match: + raise ValueError(f"{string} is not a valid ResourceName") + parts = match[1].split(":") + if len(parts) != 3: + raise ValueError(f"{string} is not a valid ResourceName") + if match[2]: + name = f"{match[2]}{match[3]}" + else: + name = match[3] + return ResourceName(namespace=parts[0], type=parts[1], subtype=parts[2], name=name) + + +ResourceCreator: TypeAlias = Callable[[ComponentConfig, Mapping[ResourceName, "ResourceBase"]], "ResourceBase"] +Validator: TypeAlias = Callable[[ComponentConfig], Sequence[str]] + + +@runtime_checkable +class SupportsGetGeometries(Protocol): + """The SupportsGetGeometries protocol defines the requirements for a resource to call get_geometries.""" + + async def GetGeometries(self, request: GetGeometriesRequest, *, timeout: Optional[float] = None, **kwargs) -> GetGeometriesResponse: ... diff --git a/src/viam/robot/__init__.py b/src/viam/robot/__init__.py index 95ba8acd8..e69de29bb 100644 --- a/src/viam/robot/__init__.py +++ b/src/viam/robot/__init__.py @@ -1,70 +0,0 @@ -""" -@generated by Viam. -Do not edit manually! -""" -from ..gen.robot.v1.robot_grpc import RobotServiceBase, RobotServiceStub -from ..gen.robot.v1.robot_pb2 import ( - BlockForOperationRequest, - BlockForOperationResponse, - CancelOperationRequest, - CancelOperationResponse, - DiscoverComponentsRequest, - DiscoverComponentsResponse, - Discovery, - DiscoveryQuery, - FrameSystemConfig, - FrameSystemConfigRequest, - FrameSystemConfigResponse, - GetOperationsRequest, - GetOperationsResponse, - GetStatusRequest, - GetStatusResponse, - Operation, - ResourceNamesRequest, - ResourceNamesResponse, - ResourceRPCSubtype, - ResourceRPCSubtypesRequest, - ResourceRPCSubtypesResponse, - Status, - StopAllRequest, - StopAllResponse, - StopExtraParameters, - StreamStatusRequest, - StreamStatusResponse, - TransformPoseRequest, - TransformPoseResponse, -) - -__all__ = [ - "RobotServiceBase", - "RobotServiceStub", - "BlockForOperationRequest", - "BlockForOperationResponse", - "CancelOperationRequest", - "CancelOperationResponse", - "DiscoverComponentsRequest", - "DiscoverComponentsResponse", - "Discovery", - "DiscoveryQuery", - "FrameSystemConfig", - "FrameSystemConfigRequest", - "FrameSystemConfigResponse", - "GetOperationsRequest", - "GetOperationsResponse", - "GetStatusRequest", - "GetStatusResponse", - "Operation", - "ResourceNamesRequest", - "ResourceNamesResponse", - "ResourceRPCSubtype", - "ResourceRPCSubtypesRequest", - "ResourceRPCSubtypesResponse", - "Status", - "StopAllRequest", - "StopAllResponse", - "StopExtraParameters", - "StreamStatusRequest", - "StreamStatusResponse", - "TransformPoseRequest", - "TransformPoseResponse", -] diff --git a/src/viam/robot/client.py b/src/viam/robot/client.py index 28b8494f3..d733dfd02 100644 --- a/src/viam/robot/client.py +++ b/src/viam/robot/client.py @@ -1,66 +1,115 @@ import asyncio +import sys from dataclasses import dataclass -from threading import Lock -from typing import Any, Dict, List, Optional +from datetime import datetime +from threading import RLock +from typing import Any, Dict, List, Optional, Union -import viam +from grpclib import GRPCError, Status from grpclib.client import Channel from typing_extensions import Self + +import viam from viam import logging from viam.components.component_base import ComponentBase -from viam.components.resource_manager import ResourceManager -from viam.errors import ComponentNotFoundError, ViamError -from viam.proto.common import PoseInFrame, ResourceName, Transform +from viam.errors import ResourceNotFoundError +from viam.proto.common import LogEntry, PoseInFrame, ResourceName, Transform from viam.proto.robot import ( - DiscoverComponentsRequest, - DiscoverComponentsResponse, - Discovery, - DiscoveryQuery, + BlockForOperationRequest, + CancelOperationRequest, FrameSystemConfig, FrameSystemConfigRequest, FrameSystemConfigResponse, - GetStatusRequest, - GetStatusResponse, + GetCloudMetadataRequest, + GetCloudMetadataResponse, + GetMachineStatusRequest, + GetMachineStatusResponse, + GetModelsFromModulesRequest, + GetModelsFromModulesResponse, + GetOperationsRequest, + GetOperationsResponse, + GetVersionRequest, + GetVersionResponse, + LogRequest, + ModuleModel, + Operation, ResourceNamesRequest, ResourceNamesResponse, RobotServiceStub, + ShutdownRequest, StopAllRequest, StopExtraParameters, TransformPoseRequest, TransformPoseResponse, ) -from viam.registry import Registry -from viam.rpc.dial import DialOptions, dial_direct -from viam.utils import dict_to_struct +from viam.resource.base import ResourceBase +from viam.resource.manager import ResourceManager +from viam.resource.registry import Registry +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase, ResourceRPCClientBase +from viam.resource.types import API, RESOURCE_TYPE_COMPONENT, RESOURCE_TYPE_SERVICE +from viam.rpc.dial import DialOptions, ViamChannel, _dial_inner, dial +from viam.services.service_base import ServiceBase +from viam.sessions_client import SessionsClient +from viam.utils import datetime_to_timestamp, dict_to_struct LOGGER = logging.getLogger(__name__) class RobotClient: - """gRPC client for a Robot. This class should be used for all interactions with a robot. + """gRPC client for a machine. This class should be used for all interactions with a machine. - There are 2 ways to instantiate a robot client: + There are 2 ways to instantiate a robot client:: - RobotClient.at_address(...)\n + RobotClient.at_address(...) RobotClient.with_channel(...) - You can use the client standalone or within a context + You can use the client standalone or within a context:: - robot = await RobotClient.at_address(...)\n - async with await RobotClient.with_channel(...) as robot: ... + machine = await RobotClient.at_address(...) + async with await RobotClient.with_channel(...) as machine: ... - You must `close()` the robot to release resources. + You must ``close()`` the machine to release resources. - Note: Robots used within a context are automatically closed UNLESS created with a channel. Robots created using `with_channel` are + Note: Machines used within a context are automatically closed UNLESS created with a channel. Machines created using ``with_channel`` are not automatically closed. + + Establish a Connection:: + + import asyncio + + from viam.rpc.dial import DialOptions, Credentials + from viam.robot.client import RobotClient + + + async def connect(): + opts = RobotClient.Options.with_api_key( + # Replace "" (including brackets) with your machine's API key + api_key='', + # Replace "" (including brackets) with your machine's API key ID + api_key_id='' + ) + return await RobotClient.at_address('', opts) + + + async def main(): + # Make a RobotClient + machine = await connect() + print('Resources:') + print(machine.resource_names) + await machine.close() + + if __name__ == '__main__': + asyncio.run(main()) + + For more information, see `Machine Management API `_. """ @dataclass class Options: refresh_interval: int = 0 """ - How often to refresh the status/parts of the robot in seconds. - If not set, the robot will not be refreshed automatically + How often to refresh the status of the parts of the machine in seconds. + If not set, the machine will not be refreshed automatically """ dial_options: Optional[DialOptions] = None @@ -73,84 +122,234 @@ class Options: The log level to output """ + check_connection_interval: int = 10 + """ + The frequency (in seconds) at which to check if the machine is still connected. 0 (zero) signifies no connection checks + """ + + attempt_reconnect_interval: int = 1 + """ + The frequency (in seconds) at which to attempt to reconnect a disconnected machine. 0 (zero) signifies no reconnection attempts + """ + + disable_sessions: bool = False + """ + Whether sessions are disabled + """ + + @classmethod + def with_api_key(cls, api_key: str, api_key_id: str, **kwargs) -> Self: + """ + Create RobotClient.Options with an API key for credentials and default values for other arguments. + + :: + + # Replace "" (including brackets) with your machine's API key + api_key = '' + # Replace "" (including brackets) with your machine's API key ID + api_key_id = '' + + opts = RobotClient.Options.with_api_key(api_key, api_key_id) + + machine = await RobotClient.at_address('', opts) + + Args: + api_key (str): your API key + api_key_id (str): your API key ID. Must be a valid UUID + + Raises: + ValueError: Raised if the api_key_id is not a valid UUID + + Returns: + Self: the RobotClient.Options + + For more information, see `Establish a connection `_. + """ + self = cls(**kwargs) + dial_opts = DialOptions.with_api_key(api_key, api_key_id) + self.dial_options = dial_opts + return self + @classmethod async def at_address(cls, address: str, options: Options) -> Self: - """Create a robot client that is connected to the robot at the provided address. + """Create a robot client that is connected to the machine at the provided address. + + :: + + async def connect(): + + opts = RobotClient.Options.with_api_key( + # Replace "" (including brackets) with your machine's API key + api_key='', + # Replace "" (including brackets) with your machine's API key ID + api_key_id='' + ) + return await RobotClient.at_address('ADDRESS FROM THE VIAM APP', opts) + + + async def main(): + # Make a RobotClient + machine = await connect() Args: - address (str): Address of the robot (IP address, URL, etc.) + address (str): Address of the machine (IP address, URL, etc.) options (Options): Options for connecting and refreshing Returns: Self: the RobotClient + + For more information, see `Establish a connection `_. """ - channel = await dial_direct(address, options.dial_options) - robot = await RobotClient.with_channel(channel, options) - robot._should_close_channel = True - return robot + logging.setLevel(options.log_level) + channel = await dial(address, options.dial_options) + machine = await cls._with_channel(channel, options, True, robot_addr=address) + machine._address = address + return machine @classmethod - async def with_channel(cls, channel: Channel, options: Options) -> Self: - """Create a robot that is connected to a robot over the given channel. + async def with_channel(cls, channel: Union[Channel, ViamChannel], options: Options) -> Self: + """Create a machine that is connected to a machine over the given channel. + + Any machines created using this method will *NOT* automatically close the channel upon exit. + + :: + + from viam.robot.client import RobotClient + from viam.rpc.dial import DialOptions, dial + + + async def connect_with_channel() -> RobotClient: + async with await dial('ADDRESS', DialOptions()) as channel: + return await RobotClient.with_channel(channel, RobotClient.Options()) - Any robots created using this method will *NOT* automatically close the channel upon exit. + machine = await connect_with_channel() Args: - channel (Channel): The gRPC channel that is connected to a robot + channel (ViamChannel): The channel that is connected to a machine, obtained by ``viam.rpc.dial`` options (Options): Options for refreshing. Any connection options will be ignored. Returns: Self: the RobotClient + + For more information, see `Establish a connection `_. """ logging.setLevel(options.log_level) + return await cls._with_channel(channel, options, False) + + @classmethod + async def _with_channel( + cls, channel: Union[Channel, ViamChannel], options: Options, close_channel: bool, robot_addr: Optional[str] = None + ): + """INTERNAL USE ONLY""" self = cls() - self._channel = channel + + if isinstance(channel, Channel): + self._channel = channel + self._viam_channel = None + else: + self._channel = channel.channel + self._viam_channel = channel + + self._connected = True self._client = RobotServiceStub(self._channel) self._manager = ResourceManager() - self._lock = Lock() + self._lock = RLock() self._resource_names = [] - self._should_close_channel = False - await self.refresh() + self._should_close_channel = close_channel + self._options = options + self._address = self._channel._path if self._channel._path else f"{self._channel._host}:{self._channel._port}" + self._sessions_client = SessionsClient( + self._channel, self._address, self._options.dial_options, disabled=self._options.disable_sessions, robot_addr=robot_addr + ) + + try: + await self.refresh() + except Exception: + LOGGER.error("Unable to establish a connection to the machine. Ensure the machine is online and reachable and try again.") + await self.close() + raise ConnectionError("Unable to establish a connection to the machine.") if options.refresh_interval > 0: self._refresh_task = asyncio.create_task( self._refresh_every(options.refresh_interval), name=f"{viam._TASK_PREFIX}-robot_refresh_metadata" ) + if options.check_connection_interval > 0 or options.attempt_reconnect_interval > 0: + self._check_connection_task = asyncio.create_task( + self._check_connection(options.check_connection_interval, options.attempt_reconnect_interval), + name=f"{viam._TASK_PREFIX}-robot_check_connection", + ) + return self _channel: Channel - _lock: Lock + _viam_channel: Optional[ViamChannel] + _lock: RLock _manager: ResourceManager _client: RobotServiceStub + _connected: bool + _address: str + _options: Options _refresh_task: Optional[asyncio.Task] = None + _check_connection_task: Optional[asyncio.Task] = None _resource_names: List[ResourceName] _should_close_channel: bool + _closed: bool = False + _sessions_client: SessionsClient async def refresh(self): """ - Manually refresh the underlying parts of this robot + Manually refresh the underlying parts of this machine. + + :: + + await machine.refresh() + + For more information, see `Machine Management API `_. """ response: ResourceNamesResponse = await self._client.ResourceNames(ResourceNamesRequest()) resource_names: List[ResourceName] = list(response.resources) - if resource_names == self._resource_names: - return - manager = ResourceManager() - for rname in resource_names: - if rname.type != "component": - continue - if rname.subtype == "remote": - continue - subtype = rname.subtype - try: - manager.register(Registry.lookup(subtype).create_rpc_client(rname.name, self._channel)) - except ComponentNotFoundError: - LOGGER.warn(f"Component of type {subtype} is not implemented") with self._lock: + if resource_names == self._resource_names: + return + for rname in resource_names: + if rname.type not in [RESOURCE_TYPE_COMPONENT, RESOURCE_TYPE_SERVICE]: + continue + if rname.subtype == "remote": + continue + + await self._create_or_reset_client(rname) + + for rname in self.resource_names: + if rname not in resource_names: + await self._manager.remove_resource(rname) + self._resource_names = resource_names - if manager.components != self._manager.components: - self._manager = manager + + async def _create_or_reset_client(self, resourceName: ResourceName): + if resourceName in self._manager.resources: + res = self._manager.get_resource(ResourceBase, resourceName) + + # If the channel hasn't changed, we don't need to do anything for existing clients + if isinstance(res, ResourceRPCClientBase) or (hasattr(res, "channel") and isinstance(getattr(res, "channel"), Channel)): + if self._channel is res.channel: # type: ignore + return + + if isinstance(res, ReconfigurableResourceRPCClientBase): + res.reset_channel(self._channel) + else: + await self._manager.remove_resource(resourceName) + self._manager.register( + Registry.lookup_api(API.from_resource_name(resourceName)).create_rpc_client(resourceName.name, self._channel) + ) + else: + try: + self._manager.register( + Registry.lookup_api(API.from_resource_name(resourceName)).create_rpc_client(resourceName.name, self._channel) + ) + except ResourceNotFoundError: + pass async def _refresh_every(self, interval: int): while True: @@ -160,84 +359,244 @@ async def _refresh_every(self, interval: int): except Exception as e: LOGGER.error("Failed to refresh status", exc_info=e) + async def _check_connection(self, check_every: int, reconnect_every: int): + if check_every <= 0: + check_every = reconnect_every + if check_every <= 0 and reconnect_every <= 0: + return + + while True: + await asyncio.sleep(check_every) + + # Failure to grab resources could be for spurious, non-networking reasons. Try three times just to be safe. + connection_error = None + for _ in range(3): + try: + await self._client.ResourceNames(ResourceNamesRequest(), timeout=1) + connection_error = None + break + except Exception as e: + connection_error = e + await asyncio.sleep(0.1) + if connection_error: + msg = "Lost connection to machine." + if reconnect_every > 0: + msg += ( + f" Attempting to reconnect to {self._address} every {reconnect_every} second{'s' if reconnect_every != 1 else ''}" + ) + LOGGER.error(msg, exc_info=connection_error) + self._close_channel() + self._connected = False + + if reconnect_every <= 0: + continue + + if self._connected: + continue + + reconnect_attempts = self._options.dial_options.max_reconnect_attempts if self._options.dial_options else 3 + + for _ in range(reconnect_attempts): + try: + self._sessions_client.reset() + + channel = await _dial_inner(self._address, self._options.dial_options) + + client: RobotServiceStub + if isinstance(channel, Channel): + client = RobotServiceStub(channel) + else: + client = RobotServiceStub(channel.channel) + await client.ResourceNames(ResourceNamesRequest()) + + if isinstance(channel, Channel): + self._channel = channel + self._viam_channel = None + else: + self._channel = channel.channel + self._viam_channel = channel + self._client = RobotServiceStub(self._channel) + direct_dial_address = self._channel._path if self._channel._path else f"{self._channel._host}:{self._channel._port}" + self._sessions_client = SessionsClient( + channel=self._channel, + direct_dial_address=direct_dial_address, + dial_options=self._options.dial_options, + disabled=self._options.disable_sessions, + robot_addr=self._address, + ) + + await self.refresh() + self._connected = True + LOGGER.debug("Successfully reconnected machine") + break + except Exception as e: + LOGGER.error(f"Failed to reconnect, trying again in {reconnect_every}sec", exc_info=e) + self._sessions_client.reset() + self._close_channel() + await asyncio.sleep(reconnect_every) + if not self._connected: + # We failed to reconnect, sys.exit() so that this thread doesn't stick around forever. + sys.exit() + def get_component(self, name: ResourceName) -> ComponentBase: """Get a component using its ResourceName. - This function should not be used except in specific cases. The method `Component.from_robot(...)` is the preferred method - for obtaining components. + This function should not be called directly except in specific cases. The method ``Component.from_robot(...)`` is the preferred + method for obtaining components. + :: - `arm = Arm.from_robot(robot=robot, name='my_arm')` + arm = Arm.from_robot(robot=machine, name="my_arm") - Because this function returns a generic `ComponentBase` rather than the specific + Because this function returns a generic ``ComponentBase`` rather than the specific component type, it will be necessary to cast the returned component to the desired component. This can be done using a few different methods: - - Assertion + - Assertion:: - arm = robot.get_component(Arm.get_resource_name('my_arm'))\n - assert isinstance(arm, Arm)\n - end_pos = await arm.get_end_position()\n + arm = machine.get_component(Arm.get_resource_name("my_arm")) + assert isinstance(arm, Arm) + end_pos = await arm.get_end_position() - - Explicit cast + - Explicit cast:: - from typing import cast\n\n - arm = robot.get_component(Arm.get_resource_name('my_arm'))\n - arm = cast(Arm, arm)\n - end_pos = await arm.get_end_position()\n + from typing import cast + arm = machine.get_component(Arm.get_resource_name("my_arm")) + arm = cast(Arm, arm) + end_pos = await arm.get_end_position() - Declare type on variable assignment. - - Note: If using an IDE, a type error may be shown which can be ignored. - arm: Arm = robot.get_component(Arm.get_resource_name('my_arm')) # type: ignore\n - end_pos = await arm.get_end_position()\n + - Note: If using an IDE, a type error may be shown which can be ignored. + :: + + arm: Arm = machine.get_component(Arm.get_resource_name("my_arm")) # type: ignore + end_pos = await arm.get_end_position() Args: - name (ResourceName): The component's name + name (viam.proto.common.ResourceName): The component's ResourceName Raises: - ViamError: Raised if the requested resource is not a component + ValueError: Raised if the requested resource is not a component ComponentNotFoundError: Error if component with the given type and name does not exist in the registry Returns: ComponentBase: The component + + For more information, see `Machine Management API `_. """ - if name.type != "component": - raise ViamError(f"ResourceName does not describe a component: {name}") + if name.type != RESOURCE_TYPE_COMPONENT: + raise ValueError(f"ResourceName does not describe a component: {name}") with self._lock: - return self._manager.get_component(ComponentBase, name.name) + return self._manager.get_resource(ComponentBase, name) + + def get_service(self, name: ResourceName) -> ServiceBase: + """Get a service using its ResourceName + + This function should not be called directly except in specific cases. The method ``Service.from_robot(...)`` is the preferred + method for obtaining services. + :: + + service = MyService.from_robot(robot=machine, name="my_service") + + Because this function returns a generic ``ServiceBase`` rather than a specific service type, it will be necessary to cast the + returned service to the desired service. This can be done using a few methods: + + - Assertion:: + + service = machine.get_service(MyService.get_resource_name("my_service")) + assert isinstance(service, MyService) + + - Explicit cast:: + + from typing import cast + service = machine.get_service(MyService.get_resource_name("my_service")) + service = cast(MyService, my_service) + + - Declare type on variable assignment + + - Note: If using an IDE, a type error may be shown which can be ignored. + :: + + service: MyService = machine.get_service(MyService.get_resource_name("my_service")) # type: ignore + + Args: + name (viam.proto.common.ResourceName): The service's ResourceName + + Raises: + ValueError: Raised if the requested resource is not a component + ComponentNotFoundError: Error if component with the given type and name does not exist in the registry + + Returns: + ServiceBase: The service + + For more information, see `Machine Management API `_. + """ + if name.type != RESOURCE_TYPE_SERVICE: + raise ValueError(f"ResourceName does not describe a service: {name}") + with self._lock: + return self._manager.get_resource(ServiceBase, name) @property def resource_names(self) -> List[ResourceName]: """ Get a list of all resource names + :: + + resource_names = machine.resource_names + Returns: - List[ResourceName]: The list of resource names + List[viam.proto.common.ResourceName]: The list of resource names + + For more information, see `Machine Management API `_. """ with self._lock: return [r for r in self._resource_names] + def _close_channel(self, *, tab_count=0): + tabs = "".join(["\t" for _ in range(tab_count)]) + if self._viam_channel is not None: + LOGGER.debug(f"{tabs} Closing ViamChannel instance") + self._viam_channel.close() + else: + LOGGER.debug(f"{tabs} Closing grpc-lib Channel instance") + self._channel.close() + async def close(self): """ - Cleanly close the underlying connections and stop any periodic tasks + Cleanly close the underlying connections and stop any periodic tasks. + + :: + + await machine.close() + + For more information, see `Machine Management API `_. """ LOGGER.debug("Closing RobotClient") + if self._closed: + LOGGER.debug("RobotClient is already closed") + return + try: self._lock.release() except RuntimeError: pass + self._sessions_client.reset() + # Cancel all tasks created by VIAM LOGGER.debug("Closing tasks spawned by Viam") tasks = [task for task in asyncio.all_tasks() if task.get_name().startswith(viam._TASK_PREFIX)] for task in tasks: - LOGGER.debug(f"Closing task {task.get_name()}") + LOGGER.debug(f"\tClosing task {task.get_name()}") task.cancel() await asyncio.gather(*tasks, return_exceptions=True) if self._should_close_channel: LOGGER.debug("Closing gRPC channel to remote robot") - self._channel.close() + self._close_channel(tab_count=1) + + self._closed = True async def __aenter__(self): return self @@ -245,22 +604,59 @@ async def __aenter__(self): async def __aexit__(self, exc_type, exc_value, traceback): await self.close() - ########## - # STATUS # - ########## - async def get_status(self, components: Optional[List[ResourceName]] = None): + ############## + # OPERATIONS # + ############## + + async def get_operations(self) -> List[Operation]: """ - Get the status of the robot's components. You can optionally - provide a list of `ResourceName` for which you want statuses. + Get the list of operations currently running on the machine. + + :: + + operations = await machine.get_operations() + + Returns: + List[viam.proto.robot.Operation]: The list of operations currently running on a given machine. + + For more information, see `Machine Management API `_. + """ + request = GetOperationsRequest() + response: GetOperationsResponse = await self._client.GetOperations(request) + return list(response.operations) + + async def cancel_operation(self, id: str): + """ + Cancels the specified operation on the machine. + + :: + + await machine.cancel_operation("INSERT OPERATION ID") Args: - components (Optional[List[ResourceName]]): Optional list of - `ResourceName` for components you want statuses. + id (str): ID of operation to cancel. + + For more information, see `Machine Management API `_. """ - names = components if components is not None else [] - request = GetStatusRequest(resource_names=names) - response: GetStatusResponse = await self._client.GetStatus(request) - return list(response.status) + request = CancelOperationRequest(id=id) + await self._client.CancelOperation(request) + + async def block_for_operation(self, id: str): + """ + Blocks on the specified operation on the machine. This function will only return when the specific operation + has finished or has been cancelled. + + :: + + await machine.block_for_operation("INSERT OPERATION ID") + + Args: + id (str): ID of operation to block on. + + For more information, see `Machine Management API `_. + """ + request = BlockForOperationRequest(id=id) + await self._client.BlockForOperation(request) ################ # FRAME SYSTEM # @@ -268,9 +664,18 @@ async def get_status(self, components: Optional[List[ResourceName]] = None): async def get_frame_system_config(self, additional_transforms: Optional[List[Transform]] = None) -> List[FrameSystemConfig]: """ - Get the configuration of the frame system of a given robot. + Get the configuration of the frame system of a given machine. + + :: - Returns (Config): The configuration of a given robot's frame system. + # Get a list of each of the reference frames configured on the machine. + frame_system = await machine.get_frame_system_config() + print(f"frame system configuration: {frame_system}") + + Returns: + List[viam.proto.robot.FrameSystemConfig]: The configuration of a given machine's frame system. + + For more information, see `Machine Management API `_. """ request = FrameSystemConfigRequest(supplemental_transforms=additional_transforms) response: FrameSystemConfigResponse = await self._client.FrameSystemConfig(request) @@ -282,35 +687,68 @@ async def transform_pose( """ Transform a given source Pose from the reference frame to a new specified destination which is a reference frame. + :: + + from viam.proto.common import Pose, PoseInFrame + + pose = Pose( + x=1.0, # X coordinate in mm + y=2.0, # Y coordinate in mm + z=3.0, # Z coordinate in mm + o_x=0.0, # X component of orientation vector + o_y=0.0, # Y component of orientation vector + o_z=0.0, # Z component of orientation vector + theta=0.0 # Orientation angle in degrees + ) + + pose_in_frame = PoseInFrame( + reference_frame="world", + pose=pose + ) + + transformed_pose = await machine.transform_pose(pose_in_frame, "world") + Args: - query (Pose): The pose that should be transformed. + query (viam.proto.common.PoseInFrame): The pose that should be transformed. destination (str) : The name of the reference frame to transform the given pose to. + Returns: + PoseInFrame: The pose and the reference frame for the new destination. + + For more information, see `Machine Management API `_. """ request = TransformPoseRequest(source=query, destination=destination, supplemental_transforms=additional_transforms) response: TransformPoseResponse = await self._client.TransformPose(request) return response.pose - ####################### - # COMPONENT DISCOVERY # - ####################### + async def transform_point_cloud(self): + raise NotImplementedError() - async def discover_components( + ################# + # MODULE MODELS # + ################# + + async def get_models_from_modules( self, - queries: List[DiscoveryQuery], - ) -> List[Discovery]: + ) -> List[ModuleModel]: """ - Get the list of discovered component configurations. + Get a list of all models provided by local and registry modules on the machine. + This includes models that are not currently configured on the machine. - Args: + :: + + # Get module models + module_models = await machine.get_models_from_modules(qs) - queries (List[DiscoveryQuery]): The list of component models to lookup configurations for. + Args: + Returns: + List[ModuleModel]: A list of discovered models. """ - request = DiscoverComponentsRequest(queries=queries) - response: DiscoverComponentsResponse = await self._client.DiscoverComponents(request) - return list(response.discovery) + request = GetModelsFromModulesRequest() + response: GetModelsFromModulesResponse = await self._client.GetModelsFromModules(request) + return list(response.models) ############ # STOP ALL # @@ -318,15 +756,154 @@ async def discover_components( async def stop_all(self, extra: Dict[ResourceName, Dict[str, Any]] = {}): """ - Cancel all current and outstanding operations for the robot and stop all actuators and movement + Cancel all current and outstanding operations for the machine and stop all actuators and movement. + + :: + + # Cancel all current and outstanding operations for the machine and stop all actuators and movement. + await machine.stop_all() Args: - extra (Dict[ResourceName, Dict[str, Any]]): Any extra parameters to pass to the components' `stop` methods, keyed on the - component's `ResourceName` + extra (Dict[viam.proto.common.ResourceName, Dict[str, Any]]): Any extra parameters to pass to the resources' ``stop`` methods, + keyed on the resource's ``ResourceName``. + For more information, see `Machine Management API `_. """ + ep: List[StopExtraParameters] = [] for name, params in extra.items(): ep.append(StopExtraParameters(name=name, params=dict_to_struct(params))) request = StopAllRequest(extra=ep) await self._client.StopAll(request) + + ####### + # LOG # + ####### + + async def log(self, name: str, level: str, time: datetime, message: str, stack: str): + """Send log from Python module over gRPC. + + Create a LogEntry object from the log to send to RDK. + + Args: + name (str): The logger's name. + level (str): The level of the log. + time (datetime): The log creation time. + message (str): The log message. + stack (str): The stack information of the log. + + For more information, see `Machine Management API `_. + """ + entry = LogEntry(level=level, time=datetime_to_timestamp(time), logger_name=name, message=message, stack=stack) + request = LogRequest(logs=[entry]) + await self._client.Log(request) + + ###################### + # Get Cloud Metadata # + ###################### + + async def get_cloud_metadata(self) -> GetCloudMetadataResponse: + """ + Get app-related information about the machine. + + :: + + metadata = await machine.get_cloud_metadata() + print(metadata.machine_id) + print(metadata.machine_part_id) + print(metadata.primary_org_id) + print(metadata.location_id) + + Returns: + viam.proto.robot.GetCloudMetadataResponse: App-related metadata. + + For more information, see `Machine Management API `_. + """ + + request = GetCloudMetadataRequest() + return await self._client.GetCloudMetadata(request) + + ############ + # Shutdown # + ############ + + async def shutdown(self): + """ + Shutdown shuts down the machine. + + :: + + await machine.shutdown() + + Raises: + GRPCError: Raised with DeadlineExceeded status if shutdown request times out, or if + the machine server shuts down before having a chance to send a response. Raised with + status Unavailable if server is unavailable, or if machine server is in the process of + shutting down when response is ready. + + For more information, see `Machine Management API `_. + """ + request = ShutdownRequest() + try: + await self._client.Shutdown(request) + LOGGER.info("robot shutdown successful") + except GRPCError as e: + if e.status == Status.INTERNAL or e.status == Status.UNKNOWN: + LOGGER.info("robot shutdown successful") + elif e.status == Status.UNAVAILABLE: + LOGGER.warn("server unavailable, likely due to successful robot shutdown") + raise e + elif e.status == Status.DEADLINE_EXCEEDED: + LOGGER.warn("request timeout, robot shutdown may still be successful") + raise e + else: + raise e + + ###################### + # Get Version # + ###################### + + async def get_version(self) -> GetVersionResponse: + """ + Get version information about the machine. + + :: + + result = await machine.get_version() + print(result.platform) + print(result.version) + print(result.api_version) + + Returns: + viam.proto.robot.GetVersionResponse: Machine version related information. + + For more information, see `Machine Management API `_. + """ + + request = GetVersionRequest() + return await self._client.GetVersion(request) + + ###################### + # Get Machine Status # + ###################### + + async def get_machine_status(self) -> GetMachineStatusResponse: + """ + Get status information about the machine's resources and configuration. + + :: + + machine_status = await machine.get_machine_status() + machine_state = machine_status.state + resource_statuses = machine_status.resources + cloud_metadata = machine_status.resources[0].cloud_metadata + config_status = machine_status.config + + Returns: + viam.proto.robot.GetMachineStatusResponse: current status of the machine (initializing or running), resources (List[ResourceStatus]) and config of the machine. + + For more information, see `Machine Management API `_. + """ + + request = GetMachineStatusRequest() + return await self._client.GetMachineStatus(request) diff --git a/src/viam/robot/service.py b/src/viam/robot/service.py index fc4b08ae9..151fe7e9f 100644 --- a/src/viam/robot/service.py +++ b/src/viam/robot/service.py @@ -1,69 +1,37 @@ -import asyncio -from typing import Any, Dict, Iterable, List +from typing import Any, Dict, List, Set from grpclib.server import Stream + from viam import logging -from viam.components.service_base import ComponentServiceBase -from viam.errors import MethodNotImplementedError, ViamGRPCError +from viam.components.movement_sensor import MovementSensor +from viam.components.sensor import Sensor +from viam.errors import ViamGRPCError from viam.proto.common import ResourceName from viam.proto.robot import ( - BlockForOperationRequest, - BlockForOperationResponse, - CancelOperationRequest, - CancelOperationResponse, - DiscoverComponentsRequest, - DiscoverComponentsResponse, - FrameSystemConfigRequest, - FrameSystemConfigResponse, - GetOperationsRequest, - GetOperationsResponse, - GetStatusRequest, - GetStatusResponse, ResourceNamesRequest, ResourceNamesResponse, - ResourceRPCSubtypesRequest, - ResourceRPCSubtypesResponse, - RobotServiceBase, - Status, StopAllRequest, StopAllResponse, - StreamStatusRequest, - StreamStatusResponse, - TransformPoseRequest, - TransformPoseResponse, + UnimplementedRobotServiceBase, ) -from viam.registry import Registry -from viam.utils import resource_names_for_component, struct_to_dict +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import resource_names_for_resource, struct_to_dict LOGGER = logging.getLogger(__name__) -class RobotService(RobotServiceBase, ComponentServiceBase): +class RobotService(UnimplementedRobotServiceBase, ResourceRPCServiceBase): def _generate_metadata(self) -> List[ResourceName]: - md: List[ResourceName] = [] - - for component in self.manager.components.values(): - md.extend(resource_names_for_component(component)) - - return md + md: Set[ResourceName] = set() - async def _generate_status(self, resource_names: Iterable[ResourceName]) -> List[Status]: - statuses: List[Status] = [] + for resource in self.manager.resources.values(): + # If the resource is a MovementSensor, DO NOT include Sensor as well (it will get added via MovementSensor) + if resource.API == Sensor.API and MovementSensor.get_resource_name(resource.name) in self.manager.resources: + continue - for component in self.manager.components.values(): - for registration in Registry.REGISTERED_COMPONENTS.values(): - if isinstance(component, registration.component_type): - if resource_names and component.get_resource_name(component.name) not in resource_names: - continue - try: - status = await registration.create_status(component) - statuses.append(status) - except ViamGRPCError as e: - raise e.grpc_error + md.update(resource_names_for_resource(resource)) - if resource_names: - statuses = [s for s in statuses if s.name in resource_names] - return statuses + return list(md) async def ResourceNames(self, stream: Stream[ResourceNamesRequest, ResourceNamesResponse]) -> None: request = await stream.recv_message() @@ -72,47 +40,6 @@ async def ResourceNames(self, stream: Stream[ResourceNamesRequest, ResourceNames response = ResourceNamesResponse(resources=metadata) await stream.send_message(response) - async def GetStatus(self, stream: Stream[GetStatusRequest, GetStatusResponse]) -> None: - request = await stream.recv_message() - assert request is not None - status = await self._generate_status(request.resource_names) - response = GetStatusResponse(status=status) - await stream.send_message(response) - - async def StreamStatus(self, stream: Stream[StreamStatusRequest, StreamStatusResponse]) -> None: - request = await stream.recv_message() - assert request is not None - interval = 1 - every = request.every.ToSeconds() - if every > 0: - interval = every - while True: - status = await self._generate_status(request.resource_names) - response = StreamStatusResponse(status=status) - await stream.send_message(response) - await asyncio.sleep(interval) - - async def GetOperations(self, stream: Stream[GetOperationsRequest, GetOperationsResponse]) -> None: - raise MethodNotImplementedError("GetOperations").grpc_error - - async def ResourceRPCSubtypes(self, stream: Stream[ResourceRPCSubtypesRequest, ResourceRPCSubtypesResponse]) -> None: - raise MethodNotImplementedError("ResourceRPCSubtypes").grpc_error - - async def CancelOperation(self, stream: Stream[CancelOperationRequest, CancelOperationResponse]) -> None: - raise MethodNotImplementedError("CancelOperation").grpc_error - - async def BlockForOperation(self, stream: Stream[BlockForOperationRequest, BlockForOperationResponse]) -> None: - raise MethodNotImplementedError("BlockForOperation").grpc_error - - async def FrameSystemConfig(self, stream: Stream[FrameSystemConfigRequest, FrameSystemConfigResponse]) -> None: - raise MethodNotImplementedError("FrameSystemConfig").grpc_error - - async def TransformPose(self, stream: Stream[TransformPoseRequest, TransformPoseResponse]) -> None: - raise MethodNotImplementedError("TransformPose").grpc_error - - async def DiscoverComponents(self, stream: Stream[DiscoverComponentsRequest, DiscoverComponentsResponse]) -> None: - raise MethodNotImplementedError("DiscoverComponents").grpc_error - async def StopAll(self, stream: Stream[StopAllRequest, StopAllResponse]) -> None: request = await stream.recv_message() assert request is not None @@ -122,7 +49,7 @@ async def StopAll(self, stream: Stream[StopAllRequest, StopAllResponse]) -> None extra[ex.name] = struct_to_dict(ex.params) errors: List[str] = [] - for component in self.manager.components.values(): + for component in self.manager.resources.values(): if callable(getattr(component, "stop", None)): try: rn = component.get_resource_name(component.name) diff --git a/src/viam/rpc/dial.py b/src/viam/rpc/dial.py index 69dc0eb74..c43fb43d8 100644 --- a/src/viam/rpc/dial.py +++ b/src/viam/rpc/dial.py @@ -1,66 +1,133 @@ -from dataclasses import dataclass +import ctypes +import pathlib import re import socket import ssl -from typing import Optional, Tuple, Type +import sys +import uuid +import warnings +from dataclasses import dataclass +from typing import Callable, Literal, Optional, Tuple, Type, Union + from grpclib.client import Channel, Stream from grpclib.const import Cardinality -from grpclib.metadata import _MetadataLike, Deadline +from grpclib.events import SendRequest, listen +from grpclib.metadata import Deadline, _MetadataLike +from grpclib.protocol import H2Protocol from grpclib.stream import _RecvType, _SendType +from typing_extensions import Self -from viam.proto.rpc.auth import AuthenticateRequest, AuthServiceStub, Credentials as PBCredentials -from viam.errors import InsecureConnectionError +from viam import logging +from viam.errors import InsecureConnectionError, ViamError +from viam.proto.rpc.auth import AuthenticateRequest, AuthServiceStub +from viam.proto.rpc.auth import Credentials as PBCredentials +from viam.utils import to_thread +from viam.version_metadata import API_VERSION, SDK_VERSION - -class RTCConfiguration: - pass +LOGGER = logging.getLogger(__name__) @dataclass -class DialWebRTCOptions: - disable_tricle_ice: bool - rtc_config: RTCConfiguration +class Credentials: + """Credentials to connect to the robot and the Viam app.""" + type: Union[Literal["robot-location-secret"], Literal["robot-secret"], Literal["api-key"]] + """The type of credential + """ -@dataclass -class Credentials: - type: str payload: str + """The credential + """ class DialOptions: + disable_webrtc: bool + """Bypass Web RTC and connect directly to the robot. + """ + auth_entity: Optional[str] + """The URL to authenticate against. Should be used if the address passed in and FQDN of the server do not match. + """ + credentials: Optional[Credentials] - webrtc_options: Optional[DialWebRTCOptions] - external_auth_address: Optional[str] + """Credentials for connecting to the robot + """ + + insecure: bool = False + """Determine if the RPC connection is TLS based. Must be provided to + establish an insecure connection. Otherwise, a TLS based connection + will be assumed.""" - #: Determine if the RPC connection is TLS based. Must be provided to - #: establish an insecure connection. Otherwise, a TLS based connection - #: will be assumed. - insecure: bool + allow_insecure_downgrade: bool = False + """Allow the RPC connection to be downgraded to an insecure connection + if detected. This is only used when credentials are not present.""" - #: Allow the RPC connection to be downgraded to an insecure connection - #: if detected. This is only used when credentials are not present. - allow_insecure_downgrade: bool + allow_insecure_with_creds_downgrade: bool = False + """Allow the RPC connection to be downgraded to an insecure connection + if detected, even with credentials present. This is generally + unsafe to use, but can be requested.""" - #: Allow the RPC connection to be downgraded to an insecure connection - #: if detected, even with credentials present. This is generally - #: unsafe to use, but can be requested. - allow_insecure_with_creds_downgrade: bool + max_reconnect_attempts: int = 3 + """Max number of times the client attempts to reconnect when connection is lost""" + + initial_connection_attempts: int = 3 + """Max number of times the client will attempt to establish an initial connection + If set to a non-positive integer, then there will be no limit to initial connection attempts""" + + initial_connection_attempt_timeout: float + """Number of seconds before dial connection times out on initial connection attempts + Defaults to whatever value is set in the `timeout` field""" + + timeout: float = 20 + """Number of seconds before the dial connection times out + Set to 20sec to match _defaultOfferDeadline in goutils/rpc/wrtc_call_queue.go""" def __init__( self, + *, + disable_webrtc: bool = False, auth_entity: Optional[str] = None, credentials: Optional[Credentials] = None, insecure: bool = False, allow_insecure_downgrade: bool = False, - allow_insecure_with_creds_downgrade=False, + allow_insecure_with_creds_downgrade: bool = False, + max_reconnect_attempts: int = 3, + timeout: float = 20, + initial_connection_attempts: int = 3, + initial_connection_attempt_timeout: Optional[float] = None, ) -> None: + self.disable_webrtc = disable_webrtc self.auth_entity = auth_entity self.credentials = credentials self.insecure = insecure self.allow_insecure_downgrade = allow_insecure_downgrade - self.allow_insecure_with_creds_downgrade = allow_insecure_with_creds_downgrade # noqa: E501 + self.allow_insecure_with_creds_downgrade = allow_insecure_with_creds_downgrade + self.max_reconnect_attempts = max_reconnect_attempts + self.timeout = timeout + self.initial_connection_attempts = initial_connection_attempts + self.initial_connection_attempt_timeout = initial_connection_attempt_timeout if initial_connection_attempt_timeout else timeout + + @classmethod + def with_api_key(cls, api_key: str, api_key_id: str) -> Self: + """Create DialOptions with an API key for credentials and default values for other arguments. + + Args: + api_key (str): your API key + api_key_id (str): your API key ID. Must be a valid UUID + + Raises: + ValueError: Raised if the api_key_id is not a valid UUID + + Returns: + Self: the DialOptions + """ + try: + uuid.UUID(api_key_id) + except ValueError: + raise ValueError(f"{api_key_id} is not a valid UUID") + + credentials = Credentials(type="api-key", payload=api_key) + return cls(credentials=credentials, auth_entity=api_key_id) def _host_port_from_url(url) -> Tuple[Optional[str], Optional[int]]: @@ -93,6 +160,27 @@ async def _get_access_token(channel: Channel, address: str, opts: DialOptions) - class AuthenticatedChannel(Channel): _metadata: _MetadataLike + def __init__( + self, + host: Optional[str] = None, + port: Optional[int] = None, + *, + ssl: Union[None, bool, ssl.SSLContext] = None, + server_hostname: Optional[str] = None, + ): + super().__init__(host, port, ssl=ssl) + self._server_hostname = server_hostname + + async def _create_connection(self) -> H2Protocol: + _, protocol = await self._loop.create_connection( + self._protocol_factory, + self._host, + self._port, + ssl=self._ssl, + server_hostname=self._server_hostname, + ) + return protocol + def request( self, name: str, @@ -109,28 +197,195 @@ def request( return super().request(name, cardinality, request_type, reply_type, timeout=timeout, deadline=deadline, metadata=metadata) -async def dial_direct(address: str, options: Optional[DialOptions] = None) -> Channel: +@dataclass +class ViamChannel: + channel: Channel + release: Callable[[], None] + _closed: bool = False + + def close(self): + if not self._closed: + try: + self.channel.close() + except RuntimeError as e: + # ignore event loop is closed errors - robot is getting shutdown + if len(e.args) > 0 and e.args[0] == "Event loop is closed": + LOGGER.debug("ViamChannel might not have shut down cleanly - Event loop was closed") + return + raise + finally: + self.release() + self._closed = True + + def __del__(self): + self.close() + + async def __aenter__(self): + return self + + async def __aexit__(self, exc_type, exc_value, traceback): + self.close() + + +class _Runtime: + _lib: ctypes.CDLL + _ptr: ctypes.c_void_p + + def __init__(self) -> None: + suffix = "dylib" if sys.platform == "darwin" else "so" if "linux" in sys.platform else "dll" + LOGGER.debug("Creating new viam-rust-utils runtime") + libname = pathlib.Path(__file__).parent.absolute() / f"libviam_rust_utils.{suffix}" + self._lib = ctypes.CDLL(libname.__str__()) + self._lib.init_rust_runtime.argtypes = () + self._lib.init_rust_runtime.restype = ctypes.c_void_p + + self._lib.dial.argtypes = ( + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_char_p, + ctypes.c_bool, + ctypes.c_float, + ctypes.c_void_p, + ) + self._lib.dial.restype = ctypes.c_void_p + + self._lib.free_rust_runtime.argtypes = (ctypes.c_void_p,) + self._lib.free_rust_runtime.restype = None + + self._lib.free_string.argtypes = (ctypes.c_void_p,) + self._lib.free_string.restype = None + + self._ptr = self._lib.init_rust_runtime() + + async def dial(self, address: str, options: DialOptions) -> Tuple[Optional[str], ctypes.c_void_p]: + type = options.credentials.type if options.credentials else "" + payload = options.credentials.payload if options.credentials else "" + insecure = ( + options.insecure + or options.allow_insecure_with_creds_downgrade + or (not type and not payload and options.allow_insecure_downgrade) + ) + + LOGGER.debug(f"Dialing {address} using viam-rust-utils library") + path_ptr = await to_thread( + self._lib.dial, + address.encode("utf-8"), + options.auth_entity.encode("utf-8") if options.auth_entity else None, + type.encode("utf-8") if type else None, + payload.encode("utf-8") if payload else None, + insecure, + ctypes.c_float(options.timeout), + self._ptr, + ) + path = ctypes.cast(path_ptr, ctypes.c_char_p).value + path = path.decode("utf-8") if path else "" + return (path, path_ptr) + + def release(self): + LOGGER.debug("Freeing viam-rust-utils runtime") + self._lib.free_rust_runtime(self._ptr) + + def free_str(self, ptr: ctypes.c_void_p): + LOGGER.debug("Freeing socket string") + self._lib.free_string(ptr) + + +async def dial(address: str, options: Optional[DialOptions] = None) -> ViamChannel: + options = options if options else DialOptions() + timeout = options.timeout + options.timeout = options.initial_connection_attempt_timeout + if options.initial_connection_attempts == 0: + options.initial_connection_attempts = -1 + attempt_countdown = options.initial_connection_attempts + exception: Exception + while attempt_countdown != 0: + try: + chan = await _dial_inner(address, options) + options.timeout = timeout + return chan + except Exception as e: + exception = e + attempt_countdown -= 1 + # the only way we could get here is if we failed at least once which means we've set the + # exception, so typechecker concerns about a possibly unbounded variable are unfounded + raise exception # type: ignore + + +def _create_chan(path: str) -> Channel: + if sys.platform == "win32" or sys.platform == "cygwin": + # we have to use a TCP connection, so we want a host and port for our channel. + host, port = _host_port_from_url(path) + return Channel(host=host, port=port, ssl=None) + # we're not on windows and so can use a UDS + return Channel(path=path, ssl=None) + + +async def _dial_inner(address: str, options: Optional[DialOptions] = None) -> ViamChannel: + async def send_request(event: SendRequest): + event.metadata["viam-client"] = f"python;v{SDK_VERSION};v{API_VERSION}" + + opts = options if options else DialOptions() + if opts.disable_webrtc: + channel = await _dial_direct(address, options) + listen(channel, SendRequest, send_request) + return ViamChannel(channel, lambda: None) + runtime = _Runtime() + path, path_ptr = await runtime.dial(address, opts) + if path: + LOGGER.info(f"Connecting to socket: {path}") + chan = _create_chan(path) + listen(chan, SendRequest, send_request) + + def release(): + runtime.free_str(path_ptr) + runtime.release() + channel = ViamChannel(chan, release) + return channel + + runtime.release() + raise ViamError(f"Unable to establish a connection to {address}") + + +async def _dial_direct(address: str, options: Optional[DialOptions] = None) -> Channel: opts = options if options else DialOptions() insecure = opts.insecure + if pathlib.Path(address).is_socket(): + return Channel(path=address) + host, port = _host_port_from_url(address) if not port: port = 80 if insecure else 443 + server_hostname = host if insecure: ctx = None else: - ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH) + is_local_host = host is not None and (host.startswith("localhost") or host.startswith("0.0.0.0") or host.startswith("127.")) + if is_local_host: + ctx = ssl._create_unverified_context(purpose=ssl.Purpose.SERVER_AUTH) + else: + ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH) ctx.minimum_version = ssl.TLSVersion.TLSv1_2 ctx.set_ciphers("ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20") ctx.set_alpn_protocols(["h2"]) + if ( + options is not None + and options.auth_entity + and host != options.auth_entity + and options.credentials is not None + and options.credentials.type != "api-key" + ): + server_hostname = options.auth_entity + # Test if downgrade is required. downgrade = False - with socket.create_connection((host, port)) as sock: + with socket.create_connection((host, port), timeout=opts.timeout) as sock: try: - with ctx.wrap_socket(sock, server_hostname=host) as ssock: + with ctx.wrap_socket(sock, server_hostname=server_hostname) as ssock: _ = ssock.version() except ssl.SSLError as e: if e.reason != "WRONG_VERSION_NUMBER": @@ -146,7 +401,7 @@ async def dial_direct(address: str, options: Optional[DialOptions] = None) -> Ch ctx = None if opts.credentials: - channel = AuthenticatedChannel(host, port, ssl=ctx) + channel = AuthenticatedChannel(host, port, ssl=ctx, server_hostname=server_hostname) access_token = await _get_access_token(channel, address, opts) metadata = {"authorization": f"Bearer {access_token}"} channel._metadata = metadata @@ -154,3 +409,12 @@ async def dial_direct(address: str, options: Optional[DialOptions] = None) -> Ch channel = Channel(host, port, ssl=ctx) return channel + + +async def dial_direct(address: str, options: Optional[DialOptions] = None) -> Channel: + warnings.warn("dial_direct is deprecated. Use rpc.dial.dial instead.", DeprecationWarning, stacklevel=2) + return await _dial_direct(address, options) + + +async def _dial_app(app_url: str) -> Channel: + return await _dial_direct(app_url) diff --git a/src/viam/rpc/server.py b/src/viam/rpc/server.py index fc4029694..5ea55c73f 100644 --- a/src/viam/rpc/server.py +++ b/src/viam/rpc/server.py @@ -1,19 +1,27 @@ -import asyncio -import signal -from typing import List +from typing import TYPE_CHECKING, Callable, List, Optional +from grpclib import GRPCError, Status +from grpclib._typing import IServable +from grpclib.const import Handler from grpclib.events import RecvRequest, listen from grpclib.reflection.service import ServerReflection from grpclib.server import Server as GRPCServer +from grpclib.server import Stream from grpclib.utils import graceful_exit + from viam import logging -from viam.components.component_base import ComponentBase -from viam.components.resource_manager import ResourceManager -from viam.registry import Registry +from viam.errors import ViamGRPCError +from viam.resource.base import ResourceBase +from viam.resource.manager import ResourceManager +from viam.resource.registry import Registry +from viam.resource.rpc_service_base import ResourceRPCServiceBase from viam.robot.service import RobotService from .signaling import SignalingService +if TYPE_CHECKING: + from viam.module.service import ModuleRPCService + LOGGER = logging.getLogger(__name__) @@ -22,74 +30,101 @@ class Server(ResourceManager): gRPC Server """ - def __init__(self, components: List[ComponentBase]): + def __init__(self, resources: List[ResourceBase], *, module_service: Optional["ModuleRPCService"] = None): """ - Initialize the Server with a list of components + Initialize the Server with a list of resources to be managed. Args: - components (List[ComponentBase]): List of components to be managed + resources (List[ComponentBase]): List of resources to be managed """ - super().__init__(components) + super().__init__(resources) + + services = [SignalingService(), RobotService(manager=self)] + for registration in Registry.REGISTERED_APIS().values(): + if issubclass(registration.rpc_service, ResourceRPCServiceBase): + services.append(registration.rpc_service(manager=self)) + else: + services.append(registration.rpc_service()) - services = [ - SignalingService(), - RobotService(manager=self), - *[registration.rpc_service(manager=self) for registration in Registry.REGISTERED_COMPONENTS.values()], - ] + if module_service is not None: + services.append(module_service) services = ServerReflection.extend(services) + services = _patch_mappings(services) + self._server = GRPCServer(services) - async def _grpc_event_handler(self, event: RecvRequest): + async def _grpc_recvrequest_handler(self, event: RecvRequest): host = None port = None address = event.peer.addr() if address: host = address[0] port = address[1] - msg = "[gRPC Request] " + f'{host or "xxxx"}:{port or "xxxx"} - ' + f"{event.method_name}" - LOGGER.info(msg) + method_func = event.method_func + + async def log_resource_name(stream: Stream): + recv_msg = stream.recv_message + + async def rcv_and_log_msg(): + msg = await recv_msg() + log_msg = f"[gRPC] Received message from {host or 'xxxx'}:{port or 'xxxx'} - {event.method_name}" + if msg and hasattr(msg, "name"): + log_msg += f" for resource named: {msg.name}" + LOGGER.debug(log_msg) + return msg + + stream.recv_message = rcv_and_log_msg + try: + return await method_func(stream) + finally: + LOGGER.debug(f"[gRPC] Finished call from {host or 'xxxx'}:{port or 'xxxx'} - {event.method_name}") + + event.method_func = log_resource_name async def serve( self, - host: str = "localhost", - port: int = 9090, - log_level: int = logging.INFO, + host: Optional[str] = "localhost", + port: Optional[int] = 9090, + log_level: Optional[int] = logging.INFO, + *, + path: Optional[str] = None, ): """ Server the gRPC server on the provided host and port Args: - host (str, optional): Desired hostname of the server. - Defaults to 'localhost'. - port (int, optional): Desired port of the server. - Defaults to 9090. - log_level(int, optional): The minimum log level. - To not receive any logs, set to None - Defaults to logging.INFO + host (Optional[str], optional): Desired hostname of the server. Defaults to "localhost". + port (Optional[int], optional): Desired port of the server. Defaults to 9090. + log_level (Optional[int], optional): The minimum log level. To not receive any logs, set to None. Defaults to logging.INFO. + path (Optional[str], optional): UNIX socket path. Takes precedence over `host` and `port` if set. Defaults to None. """ - logging.setLevel(log_level) - listen(self._server, RecvRequest, self._grpc_event_handler) - - loop = asyncio.get_running_loop() - for signame in {"SIGINT", "SIGTERM"}: - loop.add_signal_handler(getattr(signal, signame), self.close) + if log_level is None: + logging.silence() + else: + logging.setLevel(log_level) + listen(self._server, RecvRequest, self._grpc_recvrequest_handler) with graceful_exit([self._server]): - await self._server.start(host, port) - LOGGER.info(f"Serving on {host}:{port}") + if path: + await self._server.start(path=path) + LOGGER.info(f"Serving on {path}") + else: + await self._server.start(host, port) + LOGGER.info(f"Serving on {host}:{port}") await self._server.wait_closed() - - def close(self): - self._server.close() + await self.close() + LOGGER.debug("gRPC server closed") @classmethod async def create_and_serve( cls, - components: List[ComponentBase], - host: str = "localhost", - port: int = 9090, + components: List[ResourceBase], + host: Optional[str] = "localhost", + port: Optional[int] = 9090, log_level: int = logging.INFO, + *, + path: Optional[str] = None, ): """ Convenience method to create and start the server. @@ -101,6 +136,66 @@ async def create_and_serve( log_level(int, optional): The minimum log level. To not receive any logs, set to None. Defaults to logging.INFO + path (Optional[str], optional): UNIX socket path. Takes precedence over `host` and `port` if set. Defaults to None. """ server = cls(components) - await server.serve(host, port, log_level) + await server.serve(host, port, log_level, path=path) + + +def _grpc_error_wrapper(func: Callable): + """ + Wrap a function so that any exceptions get raised as GRPCErrors to the client. + + Args: + func (Callable): The function that should be wrapped + + Returns: + The method that is now wrapped to raise GRPCErrors + """ + + async def interceptor(*args, **kwargs): + try: + new_func = await func(*args, **kwargs) + return new_func + except GRPCError: + raise + except ViamGRPCError as e: + raise e.grpc_error + except Exception as e: + tb = e.__traceback__ + file_name = None + func_name = None + line_num = None + # only print the last entry in the stacktrace - not perfect but gives users a starting point + while tb is not None: + file_name = tb.tb_frame.f_code.co_filename + func_name = tb.tb_frame.f_code.co_name + line_num = tb.tb_lineno + tb = tb.tb_next + raise GRPCError(Status.UNKNOWN, f"{e.__class__.__name__} - {e} - {file_name=} {func_name=} {line_num=}") + + return interceptor + + +def _patch_mappings(services: List[IServable]) -> List[IServable]: + """Replace the methods of all given services with a wrapped method that has error handling + + Args: + services (List[IServable]): The services that should be patched + + Returns: + services (List[IServable]): The patched services with new mapping functions + """ + for service in services: + + def patch_mapping(): + mapping = service.__mapping__() + new_mapping = {} + for method, handler in mapping.items(): + new_method = _grpc_error_wrapper(handler[0]) + new_mapping[method] = Handler(new_method, *handler[1:]) + + return lambda: new_mapping + + service.__mapping__ = patch_mapping() + return services diff --git a/src/viam/rpc/signaling.py b/src/viam/rpc/signaling.py index dcfd1b796..4818c3dbd 100644 --- a/src/viam/rpc/signaling.py +++ b/src/viam/rpc/signaling.py @@ -1,16 +1,17 @@ from grpclib.const import Status from grpclib.exceptions import GRPCError from grpclib.server import Stream + from viam.proto.rpc.webrtc.signaling import ( - SignalingServiceBase, + AnswerRequest, + AnswerResponse, CallRequest, CallResponse, CallUpdateRequest, CallUpdateResponse, - AnswerRequest, - AnswerResponse, OptionalWebRTCConfigRequest, OptionalWebRTCConfigResponse, + SignalingServiceBase, ) diff --git a/src/viam/rpc/types.py b/src/viam/rpc/types.py new file mode 100644 index 000000000..a1e91c9a6 --- /dev/null +++ b/src/viam/rpc/types.py @@ -0,0 +1,22 @@ +from abc import abstractmethod +from typing import Mapping, Protocol, runtime_checkable + +import grpclib +from grpclib._typing import IServable +from grpclib.client import Channel + + +class RPCServiceBase(IServable): + """The base requirements for an RPC Service. + + An RPC Service is a service that can handle incoming RPC requests. + Services that implement the service bases generated by grpclib will automatically conform to this protocol. + """ + + @abstractmethod + def __mapping__(self) -> Mapping[str, grpclib.const.Handler]: ... + + +@runtime_checkable +class RPCServiceStubBase(Protocol): + def __init__(self, channel: Channel) -> None: ... diff --git a/src/viam/services/discovery/__init__.py b/src/viam/services/discovery/__init__.py new file mode 100644 index 000000000..ed1d49e2e --- /dev/null +++ b/src/viam/services/discovery/__init__.py @@ -0,0 +1,12 @@ +from viam.resource.registry import Registry, ResourceRegistration +from viam.services.discovery.service import DiscoveryRPCService + +from .client import DiscoveryClient +from .discovery import Discovery + +__all__ = [ + "DiscoveryClient", + "Discovery", +] + +Registry.register_api(ResourceRegistration(Discovery, DiscoveryRPCService, lambda name, channel: DiscoveryClient(name, channel))) diff --git a/src/viam/services/discovery/client.py b/src/viam/services/discovery/client.py new file mode 100644 index 000000000..486b62add --- /dev/null +++ b/src/viam/services/discovery/client.py @@ -0,0 +1,55 @@ +from typing import Any, List, Mapping, Optional + +from grpclib.client import Channel + +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.service.discovery import ( + DiscoverResourcesRequest, + DiscoverResourcesResponse, + DiscoveryServiceStub, +) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, struct_to_dict + +from .discovery import Discovery + + +class DiscoveryClient(Discovery, ReconfigurableResourceRPCClientBase): + """ + Connect to the Discovery service, which allows you to discover resources on a machine. + """ + + client: DiscoveryServiceStub + + def __init__(self, name: str, channel: Channel): + super().__init__(name) + self.channel = channel + self.client = DiscoveryServiceStub(channel) + + async def discover_resources( + self, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> List[ComponentConfig]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DiscoverResourcesRequest( + name=self.name, + extra=dict_to_struct(extra), + ) + response: DiscoverResourcesResponse = await self.client.DiscoverResources(request, timeout=timeout, metadata=md) + return list(response.discoveries) + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) diff --git a/src/viam/services/discovery/discovery.py b/src/viam/services/discovery/discovery.py new file mode 100644 index 000000000..7a74a78cc --- /dev/null +++ b/src/viam/services/discovery/discovery.py @@ -0,0 +1,52 @@ +import abc +from typing import Final, List, Mapping, Optional + +from viam.proto.app.robot import ComponentConfig +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE +from viam.utils import ValueTypes + +from ..service_base import ServiceBase + + +class Discovery(ServiceBase): + """ + Discovery represents a Discovery service. + + This acts as an abstract base class for any drivers representing specific + discovery implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, "discovery" + ) + + @abc.abstractmethod + async def discover_resources( + self, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> List[ComponentConfig]: + """Get all component configs of discovered resources on a machine + + :: + + my_discovery = DiscoveryClient.from_robot(machine, "my_discovery") + + # Get the discovered resources + result = await my_discovery.discover_resources( + "my_discovery", + ) + discoveries = result.discoveries + + Args: + name (str): The name of the discover service + + Returns: + List[ComponentConfig]: A list of ComponentConfigs that describe + the components found by a discover service + + """ + ... diff --git a/src/viam/services/discovery/service.py b/src/viam/services/discovery/service.py new file mode 100644 index 000000000..35eed96c1 --- /dev/null +++ b/src/viam/services/discovery/service.py @@ -0,0 +1,43 @@ +from grpclib.server import Stream + +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.service.discovery import ( + DiscoverResourcesRequest, + DiscoverResourcesResponse, + UnimplementedDiscoveryServiceBase, +) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict + +from .discovery import Discovery + + +class DiscoveryRPCService(UnimplementedDiscoveryServiceBase, ResourceRPCServiceBase): + """ + gRPC service for a Discovery service + """ + + RESOURCE_TYPE = Discovery + + async def DiscoverResources(self, stream: Stream[DiscoverResourcesRequest, DiscoverResourcesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + discovery = self.get_resource(request.name) + extra = struct_to_dict(request.extra) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await discovery.discover_resources( + extra=extra, + timeout=timeout, + ) + response = DiscoverResourcesResponse( + discoveries=result, + ) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + discovery = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await discovery.do_command(struct_to_dict(request.command), timeout=timeout) + await stream.send_message(DoCommandResponse(result=dict_to_struct(result))) diff --git a/src/viam/services/generic/__init__.py b/src/viam/services/generic/__init__.py new file mode 100644 index 000000000..4bf17db42 --- /dev/null +++ b/src/viam/services/generic/__init__.py @@ -0,0 +1,18 @@ +import viam.gen.service.generic.v1.generic_pb2 # Need this import for Generic service descriptors to resolve +from viam.resource.registry import Registry, ResourceRegistration + +from .client import GenericClient +from .generic import Generic +from .service import GenericRPCService + +__all__ = [ + "Generic", +] + +Registry.register_api( + ResourceRegistration( + Generic, + GenericRPCService, + lambda name, channel: GenericClient(name, channel), + ) +) diff --git a/src/viam/services/generic/client.py b/src/viam/services/generic/client.py new file mode 100644 index 000000000..d9283496d --- /dev/null +++ b/src/viam/services/generic/client.py @@ -0,0 +1,58 @@ +from typing import Any, Mapping, Optional + +from grpclib import GRPCError, Status +from grpclib.client import Channel + +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.service.generic import GenericServiceStub +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase, ResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, struct_to_dict + +from .generic import Generic + + +class GenericClient(Generic, ReconfigurableResourceRPCClientBase): + """ + gRPC client for the Generic service. + """ + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = GenericServiceStub(channel) + super().__init__(name) + + async def do_command( + self, + command: Mapping[str, Any], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, Any]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + try: + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + except GRPCError as e: + if e.status == Status.UNIMPLEMENTED: + raise NotImplementedError() + raise e + + return struct_to_dict(response.result) + + +async def do_command( + channel: Channel, name: str, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs +) -> Mapping[str, ValueTypes]: + """Convenience method to allow service clients to execute ``do_command`` functions + + Args: + channel (Channel): A gRPC channel + name (str): The name of the component + command (Dict[str, Any]): The command to execute + + Returns: + Dict[str, Any]: The result of the executed command + """ + md = kwargs.get("metadata", ResourceRPCClientBase.Metadata()).proto + client = GenericClient(name, channel) + return await client.do_command(command, timeout=timeout, metadata=md) diff --git a/src/viam/services/generic/generic.py b/src/viam/services/generic/generic.py new file mode 100644 index 000000000..45a0fe0f3 --- /dev/null +++ b/src/viam/services/generic/generic.py @@ -0,0 +1,58 @@ +from typing import Final + +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE + +from ..service_base import ServiceBase + + +class Generic(ServiceBase): + """ + Generic service, which represents any type of service that can execute arbitrary commands + + This acts as an abstract base class for any drivers representing generic services. + This cannot be used on its own. If the ``__init__()`` function is overridden, it must call the ``super().__init__()`` function. + + To create a Generic service (an arbitrary service that can process commands), this ``Generic`` service should be subclassed + and the ``do_command`` function implemented. + + Example:: + + class ComplexService(Generic): + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs + ) -> Mapping[str, ValueTypes]: + result = {key: False for key in command.keys()} + for (name, args) in command.items(): + if name == 'set_val': + self.set_val(*args) + result[name] = True + if name == 'get_val': + result[name] = self.val + if name == 'complex_command': + self.complex_command(*args) + result[name] = True + return result + + def set_val(self, val: int): + self.val = val + + def complex_command(self, arg1, arg2, arg3): + ... + + To execute commands, simply call the ``do_command`` function with the appropriate parameters. + :: + + await service.do_command({'set_val': 10}) + service.val # 10 + await service.do_command({'set_val': 5}) + service.val # 5 + """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, "generic" + ) diff --git a/src/viam/services/generic/service.py b/src/viam/services/generic/service.py new file mode 100644 index 000000000..f7381fddb --- /dev/null +++ b/src/viam/services/generic/service.py @@ -0,0 +1,29 @@ +from grpclib import GRPCError, Status +from grpclib.server import Stream + +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.service.generic import GenericServiceBase +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.services.service_base import ServiceBase +from viam.utils import dict_to_struct, struct_to_dict + + +class GenericRPCService(GenericServiceBase, ResourceRPCServiceBase): + """ + gRPC Service for a Generic service + """ + + RESOURCE_TYPE = ServiceBase + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + service = self.get_resource(name) + try: + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await service.do_command(struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + except NotImplementedError: + raise GRPCError(Status.UNIMPLEMENTED, f"``DO`` command is unimplemented for service named: {name}") + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) diff --git a/src/viam/services/mlmodel/__init__.py b/src/viam/services/mlmodel/__init__.py new file mode 100644 index 000000000..da6ba840e --- /dev/null +++ b/src/viam/services/mlmodel/__init__.py @@ -0,0 +1,24 @@ +try: + import numpy +except ImportError: + import warnings + + warnings.warn( + ( + """MLModel support in the Viam Python SDK requires the installation of an +additional dependency: numpy. Update your package using the extra [mlmodel] +for example `pip install viam-sdk[mlmodel]` or the equivalent update in your dependency manager.""" + ), + ) + raise + +from viam.proto.service.mlmodel import File, LabelType, Metadata, TensorInfo +from viam.resource.registry import Registry, ResourceRegistration + +from .client import MLModelClient +from .mlmodel import MLModel +from .service import MLModelRPCService + +__all__ = ["File", "LabelType", "Metadata", "MLModel", "MLModelClient", "TensorInfo"] + +Registry.register_api(ResourceRegistration(MLModel, MLModelRPCService, lambda name, channel: MLModelClient(name, channel))) diff --git a/src/viam/services/mlmodel/client.py b/src/viam/services/mlmodel/client.py new file mode 100644 index 000000000..f36d003d2 --- /dev/null +++ b/src/viam/services/mlmodel/client.py @@ -0,0 +1,37 @@ +from typing import Dict, Mapping, Optional + +from grpclib.client import Channel +from numpy.typing import NDArray + +from viam.proto.service.mlmodel import InferRequest, InferResponse, MetadataRequest, MetadataResponse, MLModelServiceStub +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.services.mlmodel.utils import flat_tensors_to_ndarrays, ndarrays_to_flat_tensors +from viam.utils import ValueTypes, dict_to_struct + +from .mlmodel import Metadata, MLModel + + +class MLModelClient(MLModel, ReconfigurableResourceRPCClientBase): + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = MLModelServiceStub(channel) + super().__init__(name) + + async def infer( + self, + input_tensors: Dict[str, NDArray], + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Dict[str, NDArray]: + md = kwargs.get("metadata", self.Metadata()).proto + request = InferRequest(name=self.name, input_tensors=ndarrays_to_flat_tensors(input_tensors), extra=dict_to_struct(extra)) + response: InferResponse = await self.client.Infer(request, timeout=timeout, metadata=md) + return flat_tensors_to_ndarrays(response.output_tensors) + + async def metadata(self, *, extra: Optional[Mapping[str, ValueTypes]] = None, timeout: Optional[float] = None, **kwargs) -> Metadata: + md = kwargs.get("metadata", self.Metadata()).proto + request = MetadataRequest(name=self.name, extra=dict_to_struct(extra)) + response: MetadataResponse = await self.client.Metadata(request, timeout=timeout, metadata=md) + return response.metadata diff --git a/src/viam/services/mlmodel/mlmodel.py b/src/viam/services/mlmodel/mlmodel.py new file mode 100644 index 000000000..0416eb4a0 --- /dev/null +++ b/src/viam/services/mlmodel/mlmodel.py @@ -0,0 +1,78 @@ +import abc +from typing import Dict, Final, Mapping, Optional + +from numpy.typing import NDArray + +from viam.proto.service.mlmodel import Metadata +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE +from viam.utils import ValueTypes + +from ..service_base import ServiceBase + + +class MLModel(ServiceBase): + """ + MLModel represents a Machine Learning Model service. + + This acts as an abstract base class for any drivers representing specific + arm implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + For more information, see `ML model service `_. + """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, "mlmodel" + ) + + @abc.abstractmethod + async def infer( + self, + input_tensors: Dict[str, NDArray], + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> Dict[str, NDArray]: + """Take an already ordered input tensor as an array, make an inference on the model, and return an output tensor map. + + :: + + import numpy as np + + my_mlmodel = MLModelClient.from_robot(robot=machine, name="my_mlmodel_service") + + image_data = np.zeros((1, 384, 384, 3), dtype=np.uint8) + + # Create the input tensors dictionary + input_tensors = { + "image": image_data + } + + output_tensors = await my_mlmodel.infer(input_tensors) + + Args: + input_tensors (Dict[str, NDArray]): A dictionary of input flat tensors as specified in the metadata + + Returns: + Dict[str, NDArray]: A dictionary of output flat tensors as specified in the metadata + + For more information, see `ML model service `_. + """ + ... + + @abc.abstractmethod + async def metadata(self, *, extra: Optional[Mapping[str, ValueTypes]] = None, timeout: Optional[float] = None) -> Metadata: + """Get the metadata (such as name, type, expected tensor/array shape, inputs, and outputs) associated with the ML model. + + :: + + my_mlmodel = MLModelClient.from_robot(robot=machine, name="my_mlmodel_service") + + metadata = await my_mlmodel.metadata() + + Returns: + Metadata: The metadata + + For more information, see `ML model service `_. + """ + ... diff --git a/src/viam/services/mlmodel/service.py b/src/viam/services/mlmodel/service.py new file mode 100644 index 000000000..ffd3050eb --- /dev/null +++ b/src/viam/services/mlmodel/service.py @@ -0,0 +1,38 @@ +from grpclib.server import Stream + +from viam.proto.service.mlmodel import InferRequest, InferResponse, MetadataRequest, MetadataResponse, MLModelServiceBase +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.services.mlmodel.utils import flat_tensors_to_ndarrays, ndarrays_to_flat_tensors +from viam.utils import struct_to_dict + +from .mlmodel import MLModel + + +class MLModelRPCService(MLModelServiceBase, ResourceRPCServiceBase): + """ + gRPC service for a ML Model service + """ + + RESOURCE_TYPE = MLModel + + async def Infer(self, stream: Stream[InferRequest, InferResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + mlmodel = self.get_resource(name) + extra = struct_to_dict(request.extra) + timeout = stream.deadline.time_remaining() if stream.deadline else None + output_tensors = await mlmodel.infer(input_tensors=flat_tensors_to_ndarrays(request.input_tensors), extra=extra, timeout=timeout) + response = InferResponse(output_tensors=ndarrays_to_flat_tensors(output_tensors)) + await stream.send_message(response) + + async def Metadata(self, stream: Stream[MetadataRequest, MetadataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + mlmodel = self.get_resource(name) + extra = struct_to_dict(request.extra) + timeout = stream.deadline.time_remaining() if stream.deadline else None + metadata = await mlmodel.metadata(extra=extra, timeout=timeout) + response = MetadataResponse(metadata=metadata) + await stream.send_message(response) diff --git a/src/viam/services/mlmodel/utils.py b/src/viam/services/mlmodel/utils.py new file mode 100644 index 000000000..c2dbead82 --- /dev/null +++ b/src/viam/services/mlmodel/utils.py @@ -0,0 +1,101 @@ +from typing import Dict + +import numpy as np +from numpy.typing import NDArray +from packaging.version import Version + +from viam.proto.service.mlmodel import ( + FlatTensor, + FlatTensorDataDouble, + FlatTensorDataFloat, + FlatTensorDataInt8, + FlatTensorDataInt16, + FlatTensorDataInt32, + FlatTensorDataInt64, + FlatTensorDataUInt8, + FlatTensorDataUInt16, + FlatTensorDataUInt32, + FlatTensorDataUInt64, + FlatTensors, +) + + +def flat_tensors_to_ndarrays(flat_tensors: FlatTensors) -> Dict[str, NDArray]: + property_name_to_dtype = { + "float_tensor": np.float32, + "double_tensor": np.float64, + "int8_tensor": np.int8, + "int16_tensor": np.int16, + "int32_tensor": np.int32, + "int64_tensor": np.int64, + "uint8_tensor": np.uint8, + "uint16_tensor": np.uint16, + "uint32_tensor": np.uint32, + "uint64_tensor": np.uint64, + } + + def make_ndarray(flat_data, dtype, shape): + """Takes flat data (protobuf RepeatedScalarFieldContainer | bytes) to output an ndarray + of appropriate dtype and shape""" + make_array = np.frombuffer if dtype == np.int8 or dtype == np.uint8 else np.array + # As per proto, int16 and uint16 are stored as uint32. As of numpy v2, this creates + # some strange interactions with negative values for int16. Specifically, we end up + # trying to create an np.Int16 value with an out of bounds int due to rollover. + # Creating our array as a uint32 array initially and then casting to int16 solves this. + if Version(np.__version__) >= Version("2") and dtype == np.int16: + arr = np.astype(make_array(flat_data, np.uint32), np.int16) # pyright: ignore [reportAttributeAccessIssue] + + else: + arr = make_array(flat_data, dtype) + return arr.reshape(shape) + + ndarrays: Dict[str, NDArray] = dict() + for name, flat_tensor in flat_tensors.tensors.items(): + property_name = flat_tensor.WhichOneof("tensor") or flat_tensor.WhichOneof(b"tensor") + if property_name: + tensor_data = getattr(flat_tensor, property_name) + flat_data, dtype, shape = tensor_data.data, property_name_to_dtype[property_name], flat_tensor.shape + ndarrays[name] = make_ndarray(flat_data, dtype, shape) + return ndarrays + + +def ndarrays_to_flat_tensors(ndarrays: Dict[str, NDArray]) -> FlatTensors: + dtype_name_to_tensor_data_class = { + "float32": FlatTensorDataFloat, + "float64": FlatTensorDataDouble, + "int8": FlatTensorDataInt8, + "int16": FlatTensorDataInt16, + "int32": FlatTensorDataInt32, + "int64": FlatTensorDataInt64, + "uint8": FlatTensorDataUInt8, + "uint16": FlatTensorDataUInt16, + "uint32": FlatTensorDataUInt32, + "uint64": FlatTensorDataUInt64, + } + + def get_tensor_data(ndarray: NDArray): + """Takes an ndarray and returns the corresponding tensor data class instance + for example FlatTensorDataInt8, FlatTensorDataUInt8 etc.""" + tensor_data_class = dtype_name_to_tensor_data_class[ndarray.dtype.name] + data = ndarray.flatten() + if tensor_data_class == FlatTensorDataInt8 or tensor_data_class == FlatTensorDataUInt8: + data = data.tobytes() # as per the proto, int8 and uint8 are stored as bytes + elif tensor_data_class == FlatTensorDataInt16 or tensor_data_class == FlatTensorDataUInt16: + data = data.astype(np.uint32) # as per the proto, int16 and uint16 are stored as uint32 + tensor_data = tensor_data_class(data=data) + return tensor_data + + def get_tensor_data_type(ndarray: NDArray): + """Takes ndarray and returns a FlatTensor datatype property to be set + for example "float_tensor", "uint32_tensor" etc.""" + if ndarray.dtype == np.float32: + return "float_tensor" + elif ndarray.dtype == np.float64: + return "double_tensor" + return f"{ndarray.dtype.name}_tensor" + + tensors_mapping: Dict[str, FlatTensor] = dict() + for name, ndarray in ndarrays.items(): + prop_name, prop_value = get_tensor_data_type(ndarray), get_tensor_data(ndarray) + tensors_mapping[name] = FlatTensor(shape=ndarray.shape, **{prop_name: prop_value}) + return FlatTensors(tensors=tensors_mapping) diff --git a/src/viam/services/motion.py b/src/viam/services/motion.py deleted file mode 100644 index 0ecb93e26..000000000 --- a/src/viam/services/motion.py +++ /dev/null @@ -1,122 +0,0 @@ -from typing import Any, Mapping, Optional - -from grpclib.client import Channel - -from viam.proto.common import PoseInFrame, ResourceName, WorldState -from viam.proto.service.motion import ( - GetPoseRequest, - GetPoseResponse, - MotionServiceStub, - MoveRequest, - MoveResponse, - MoveSingleComponentRequest, - MoveSingleComponentResponse, -) -from viam.services.service_client_base import ServiceClientBase -from viam.utils import dict_to_struct - - -class MotionServiceClient(ServiceClientBase): - """Motion is a Viam service that coordinates motion planning across all of the components in a given robot. - - The motion planning service calculates a valid path that avoids self collision by default. If additional constraints are supplied in the - `world_state` message, the motion planning service will also account for those. - """ - - SERVICE_TYPE = "motion" - - def __init__(self, name: str, channel: Channel): - self.client = MotionServiceStub(channel) - self.name = name - - async def move( - self, - component_name: ResourceName, - destination: PoseInFrame, - world_state: Optional[WorldState] = None, - extra: Optional[Mapping[str, Any]] = None, - ) -> bool: - """Plan and execute a movement to move the component specified to its goal destination. - - - Args: - component_name (ResourceName): Name of a component on a given robot. - destination (PoseInFrame): The destination to move to, expressed as a `Pose` and the frame in which it was observed. - world_state (WorldState): When supplied, the motion service will create a plan that obeys any contraints expressed in the - WorldState message. - - Returns: - bool: Whether the move was successful - """ - if extra is None: - extra = {} - request = MoveRequest( - name=self.name, - destination=destination, - component_name=component_name, - world_state=world_state, - extra=dict_to_struct(extra), - ) - response: MoveResponse = await self.client.Move(request) - return response.success - - async def move_single_component( - self, - component_name: ResourceName, - destination: PoseInFrame, - world_state: Optional[WorldState] = None, - extra: Optional[Mapping[str, Any]] = None, - ) -> bool: - """ - This function will pass through a move command to a component with a `move_to_position` method that takes a `Pose`. `Arm`s are the - only component that support this. This method will transform the destination pose, given in an arbitrary frame, into the pose of the - arm. The arm will then move its most distal link to that pose. If you instead wish to move any other component than the arm end to - that pose, then you must manually adjust the given destination by the transform from the arm end to the intended component. - - Args: - component_name (ResourceName): Name of a component on a given robot. - destination (PoseInFrame): The destination to move to, expressed as a `Pose` and the frame in which it was observed. - world_state (WorldState): When supplied, the motion service will create a plan that obeys any contraints expressed in the - WorldState message. - - Returns: - bool: Whether the move was successful - """ - if extra is None: - extra = {} - request = MoveSingleComponentRequest( - name=self.name, - destination=destination, - component_name=component_name, - world_state=world_state, - extra=dict_to_struct(extra), - ) - response: MoveSingleComponentResponse = await self.client.MoveSingleComponent(request) - return response.success - - async def get_pose( - self, - component_name: ResourceName, - destination_frame: str, - extra: Optional[Mapping[str, Any]] = None, - ) -> PoseInFrame: - """ - Get the Pose and observer frame for any given component on a robot. - - Args: - component_name (ResourceName): Name of a component on a robot. - destination_frame (str): Name of the desired reference frame. - - Returns: - `Pose` (PoseInFrame): Pose of the given component and the frame in which it was observed. - """ - if extra is None: - extra = {} - request = GetPoseRequest( - name=self.name, - component_name=component_name, - destination_frame=destination_frame, - extra=dict_to_struct(extra), - ) - response: GetPoseResponse = await self.client.GetPose(request) - return response.pose diff --git a/src/viam/services/motion/__init__.py b/src/viam/services/motion/__init__.py new file mode 100644 index 000000000..38d662fec --- /dev/null +++ b/src/viam/services/motion/__init__.py @@ -0,0 +1,17 @@ +from viam.proto.service.motion import Constraints, MotionConfiguration +from viam.resource.registry import Registry, ResourceRegistration + +from .client import MotionClient +from .motion import Motion +from .service import MotionRPCService + +__all__ = ["Motion", "MotionClient", "MotionConfiguration", "Constraints"] + + +Registry.register_api( + ResourceRegistration( + Motion, + MotionRPCService, + lambda name, channel: MotionClient(name, channel), + ) +) diff --git a/src/viam/services/motion/client.py b/src/viam/services/motion/client.py new file mode 100644 index 000000000..8ed4ce6ad --- /dev/null +++ b/src/viam/services/motion/client.py @@ -0,0 +1,215 @@ +from typing import Any, Mapping, Optional, Sequence + +from grpclib.client import Channel + +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, + GeoGeometry, + Geometry, + GeoPoint, + Pose, + PoseInFrame, + ResourceName, + Transform, + WorldState, +) +from viam.proto.service.motion import ( + Constraints, + GetPlanRequest, + GetPlanResponse, + GetPoseRequest, + GetPoseResponse, + ListPlanStatusesRequest, + ListPlanStatusesResponse, + MotionConfiguration, + MotionServiceStub, + MoveOnGlobeRequest, + MoveOnGlobeResponse, + MoveOnMapRequest, + MoveOnMapResponse, + MoveRequest, + MoveResponse, + PlanStatusWithID, + StopPlanRequest, + StopPlanResponse, +) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, struct_to_dict + +from .motion import Motion + + +class MotionClient(Motion, ReconfigurableResourceRPCClientBase): + """ + gRPC client for the Motion service. + """ + + client: MotionServiceStub + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = MotionServiceStub(channel) + super().__init__(name) + + async def move( + self, + component_name: ResourceName, + destination: PoseInFrame, + world_state: Optional[WorldState] = None, + constraints: Optional[Constraints] = None, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> bool: + md = kwargs.get("metadata", self.Metadata()).proto + request = MoveRequest( + name=self.name, + destination=destination, + component_name=component_name, + world_state=world_state, + constraints=constraints, + extra=dict_to_struct(extra), + ) + response: MoveResponse = await self.client.Move(request, timeout=timeout, metadata=md) + return response.success + + async def move_on_globe( + self, + component_name: ResourceName, + destination: GeoPoint, + movement_sensor_name: ResourceName, + obstacles: Optional[Sequence[GeoGeometry]] = None, + heading: Optional[float] = None, + configuration: Optional[MotionConfiguration] = None, + *, + bounding_regions: Optional[Sequence[GeoGeometry]] = None, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> str: + md = kwargs.get("metadata", self.Metadata()).proto + request = MoveOnGlobeRequest( + name=self.name, + component_name=component_name, + destination=destination, + movement_sensor_name=movement_sensor_name, + obstacles=obstacles, + heading=heading, + motion_configuration=configuration, + bounding_regions=bounding_regions, + extra=dict_to_struct(extra), + ) + response: MoveOnGlobeResponse = await self.client.MoveOnGlobe(request, timeout=timeout, metadata=md) + return response.execution_id + + async def move_on_map( + self, + component_name: ResourceName, + destination: Pose, + slam_service_name: ResourceName, + configuration: Optional[MotionConfiguration] = None, + obstacles: Optional[Sequence[Geometry]] = None, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> str: + md = kwargs.get("metadata", self.Metadata()).proto + request = MoveOnMapRequest( + name=self.name, + destination=destination, + component_name=component_name, + slam_service_name=slam_service_name, + motion_configuration=configuration, + obstacles=obstacles, + extra=dict_to_struct(extra), + ) + response: MoveOnMapResponse = await self.client.MoveOnMap(request, timeout=timeout, metadata=md) + return response.execution_id + + async def stop_plan( + self, + component_name: ResourceName, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + md = kwargs.get("metadata", self.Metadata()).proto + + request = StopPlanRequest( + name=self.name, + component_name=component_name, + extra=dict_to_struct(extra), + ) + _: StopPlanResponse = await self.client.StopPlan(request, timeout=timeout, metadata=md) + return + + async def get_plan( + self, + component_name: ResourceName, + last_plan_only: bool = False, + execution_id: Optional[str] = None, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> GetPlanResponse: + md = kwargs.get("metadata", self.Metadata()).proto + + request = GetPlanRequest( + name=self.name, + component_name=component_name, + last_plan_only=last_plan_only, + execution_id=execution_id, + extra=dict_to_struct(extra), + ) + response: GetPlanResponse = await self.client.GetPlan(request, timeout=timeout, metadata=md) + return response + + async def list_plan_statuses( + self, + only_active_plans: bool = False, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Sequence[PlanStatusWithID]: + md = kwargs.get("metadata", self.Metadata()).proto + + request = ListPlanStatusesRequest( + name=self.name, + only_active_plans=only_active_plans, + extra=dict_to_struct(extra), + ) + response: ListPlanStatusesResponse = await self.client.ListPlanStatuses(request, timeout=timeout, metadata=md) + return response.plan_statuses_with_ids + + async def get_pose( + self, + component_name: ResourceName, + destination_frame: str, + supplemental_transforms: Optional[Sequence[Transform]] = None, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> PoseInFrame: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPoseRequest( + name=self.name, + component_name=component_name, + destination_frame=destination_frame, + supplemental_transforms=supplemental_transforms, + extra=dict_to_struct(extra), + ) + response: GetPoseResponse = await self.client.GetPose(request, timeout=timeout, metadata=md) + return response.pose + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) diff --git a/src/viam/services/motion/motion.py b/src/viam/services/motion/motion.py new file mode 100644 index 000000000..c6b6138c4 --- /dev/null +++ b/src/viam/services/motion/motion.py @@ -0,0 +1,378 @@ +import abc +import sys +from typing import Any, Final, Mapping, Optional, Sequence + +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + +from viam.proto.common import GeoGeometry, Geometry, GeoPoint, Pose, PoseInFrame, ResourceName, Transform, WorldState +from viam.proto.service.motion import Constraints, GetPlanResponse, MotionConfiguration, PlanStatusWithID +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE +from viam.utils import ValueTypes + +from ..service_base import ServiceBase + + +class Motion(ServiceBase): + """Motion is a Viam service that coordinates motion planning across all of the components in a given robot. + + The motion planning service calculates a valid path that avoids self collision by default. If additional constraints are supplied in the + ``world_state`` message, the motion planning service will also account for those. + + For more information, see `Motion service `_. + """ + + Plan: "TypeAlias" = GetPlanResponse + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, "motion" + ) + + @abc.abstractmethod + async def move( + self, + component_name: ResourceName, + destination: PoseInFrame, + world_state: Optional[WorldState] = None, + constraints: Optional[Constraints] = None, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + ) -> bool: + """Plan and execute a movement to move the component specified to its goal destination. + + Note: Frames designated with respect to components can also be used as the ``component_name`` when calling for a move. This + technique allows for planning and moving the frame itself to the ``destination``. To do so, simply create a resource name with + originating ReferenceFrame's name. Then pass in the resource name into ``component_name``. Ex:: + + resource_name = Gripper.get_resource_name("externalFrame") + success = await MotionServiceClient.move(resource_name, ...) + + :: + + motion = MotionClient.from_robot(robot=machine, name="builtin") + + # Assumes a gripper configured with name "my_gripper" on the machine + gripper_name = Gripper.get_resource_name("my_gripper") + my_frame = "my_gripper_offset" + + goal_pose = Pose(x=0, y=0, z=300, o_x=0, o_y=0, o_z=1, theta=0) + + # Move the gripper + moved = await motion.move(component_name=gripper_name, + destination=PoseInFrame(reference_frame="myFrame", + pose=goal_pose), + world_state=worldState, + constraints={}, + extra={}) + + Args: + component_name (viam.proto.common.ResourceName): Name of a component on a given robot. + destination (viam.proto.common.PoseInFrame): The destination to move to, expressed as a ``Pose`` and the frame in which it was + observed. + world_state (viam.proto.common.WorldState): When supplied, the motion service will create a plan that obeys any constraints + expressed in the WorldState message. + constraints (viam.proto.service.motion.Constraints): When supplied, the motion service will create a plan that obeys any + specified constraints. + + Returns: + bool: Whether the move was successful. + + For more information, see `Motion service `_. + """ + ... + + @abc.abstractmethod + async def move_on_globe( + self, + component_name: ResourceName, + destination: GeoPoint, + movement_sensor_name: ResourceName, + obstacles: Optional[Sequence[GeoGeometry]] = None, + heading: Optional[float] = None, + configuration: Optional[MotionConfiguration] = None, + *, + bounding_regions: Optional[Sequence[GeoGeometry]] = None, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> str: + """Move a component to a specific latitude and longitude, using a ``MovementSensor`` to check the location. + + ``move_on_globe()`` is non blocking, meaning the motion service will move the component to the destination + GPS point after ``move_on_globe()`` returns. + + Each successful ``move_on_globe()`` call returns a unique ExecutionID which you can use to identify all plans + generated during the ``move_on_globe()`` call. + + You can monitor the progress of the ``move_on_globe()`` call by querying ``get_plan()`` and ``list_plan_statuses()``. + + :: + + motion = MotionClient.from_robot(robot=machine, name="builtin") + + # Get the ResourceNames of the base and movement sensor + my_base_resource_name = Base.get_resource_name("my_base") + mvmnt_sensor_resource_name = MovementSensor.get_resource_name( + "my_movement_sensor") + # Define a destination GeoPoint at the GPS coordinates [0, 0] + my_destination = movement_sensor.GeoPoint(latitude=0, longitude=0) + + # Move the base component to the designated geographic location, as reported by the movement sensor + execution_id = await motion.move_on_globe( + component_name=my_base_resource_name, + destination=my_destination, + movement_sensor_name=mvmnt_sensor_resource_name) + + Args: + component_name (ResourceName): The ResourceName of the base to move. + destination (GeoPoint): The location of the component's destination, represented in geographic notation as a + GeoPoint (lat, lng). + movement_sensor_name (ResourceName): The ResourceName of the movement sensor that you want to use to check + the machine's location. + obstacles (Optional[Sequence[GeoGeometry]]): Obstacles to consider when planning the motion of the component, + with each represented as a GeoGeometry. Default: None + heading (Optional[float]): The compass heading, in degrees, that the machine's movement sensor should report + at the destination point. Range: [0-360) 0: North, 90: East, 180: South, 270: West. Default: None + configuration (Optional[MotionConfiguration]): The configuration you want to set across this machine for this + motion service. This parameter and each of its fields are optional. + + - obstacle_detectors (Sequence[ObstacleDetector]): The names of each vision service and camera resource pair + you want to use for transient obstacle avoidance. + + - position_polling_frequency_hz (float): The frequency in Hz to poll the position of the machine. + - obstacle_polling_frequency_hz (float): The frequency in Hz to poll the vision service for new obstacles. + - plan_deviation_m (float): The distance in meters that the machine can deviate from the motion plan. + - linear_m_per_sec (float): Linear velocity this machine should target when moving. + - angular_degs_per_sec (float): Angular velocity this machine should target when turning. + bounding_regions (Optional[Sequence[GeoGeometry]]): Set of obstacles which the robot must remain within while navigating + extra (Optional[Dict[str, Any]]): Extra options to pass to the underlying RPC call. + timeout (Optional[float]): An option to set how long to wait (in seconds) before calling a time-out and closing + the underlying RPC call. + + + Returns: + str: ExecutionID of the ``move_on_globe()`` call, which can be used to track execution progress. + + For more information, see `Motion service `_. + """ + ... + + @abc.abstractmethod + async def move_on_map( + self, + component_name: ResourceName, + destination: Pose, + slam_service_name: ResourceName, + configuration: Optional[MotionConfiguration] = None, + obstacles: Optional[Sequence[Geometry]] = None, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> str: + """ + Move a component to a specific pose, using a ``SlamService`` for the SLAM map, using a ``SLAM Service`` to check the location. + + ``move_on_map()`` is non blocking, meaning the motion service will move the component to the destination + Pose point after ``move_on_map()`` returns. + + Each successful ``move_on_map()`` call returns a unique ExecutionID which you can use to identify all plans + generated during the ``move_on_map()`` call. + + You can monitor the progress of the ``move_on_map()`` call by querying ``get_plan()`` and ``list_plan_statuses()``. + + :: + + motion = MotionClient.from_robot(robot=machine, name="builtin") + + # Get the ResourceNames of the base component and SLAM service + my_base_resource_name = Base.get_resource_name("my_base") + my_slam_service_name = SLAMClient.get_resource_name("my_slam_service") + + # Define a destination pose with respect to the origin of the map from the SLAM service "my_slam_service" + my_pose = Pose(y=10) + + # Move the base component to the destination pose of Y=10, a location of + # (0, 10, 0) in respect to the origin of the map + execution_id = await motion.move_on_map(component_name=my_base_resource_name, + destination=my_pose, + slam_service_name=my_slam_service_name) + + Args: + component_name (ResourceName): The ResourceName of the base to move. + destination (Pose): The destination, which can be any Pose with respect to the SLAM map's origin. + slam_service_name (ResourceName): The ResourceName of the SLAM service from which the SLAM map is requested. + configuration (Optional[MotionConfiguration]): The configuration you want to set across this machine for this motion service. + This parameter and each of its fields are optional. + + - obstacle_detectors (Sequence[ObstacleDetector]): The names of each vision service and camera resource pair you want to use + for transient obstacle avoidance. + + - position_polling_frequency_hz (float): The frequency in hz to poll the position of the machine. + - obstacle_polling_frequency_hz (float): The frequency in hz to poll the vision service for new obstacles. + - plan_deviation_m (float): The distance in meters that the machine can deviate from the motion plan. + - linear_m_per_sec (float): Linear velocity this machine should target when moving. + - angular_degs_per_sec (float): Angular velocity this machine should target when turning. + obstacles (Optional[Sequence[Geometry]]): Obstacles to be considered for motion planning. + extra (Optional[Dict[str, Any]]): Extra options to pass to the underlying RPC call. + timeout (Optional[float]): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying + RPC call. + + Returns: + str: ExecutionID of the ``move_on_map()`` call, which can be used to track execution progress. + + For more information, see `Motion service `_. + """ + ... + + @abc.abstractmethod + async def stop_plan( + self, + component_name: ResourceName, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ): + """Stop a component being moved by an in progress ``move_on_globe()`` or ``move_on_map()`` call. + + :: + + motion = MotionClient.from_robot(robot=machine, name="builtin") + + # Assuming a `move_on_globe()` started the execution + # Stop the base component which was instructed to move by `move_on_globe()` + # or `move_on_map()` + my_base_resource_name = Base.get_resource_name("my_base") + await motion.stop_plan(component_name=mvmnt_sensor) + + Args: + component_name (ResourceName): The component to stop + + For more information, see `Motion service `_. + """ + ... + + @abc.abstractmethod + async def get_plan( + self, + component_name: ResourceName, + last_plan_only: bool = False, + execution_id: Optional[str] = None, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> Plan: + """By default: returns the plan history of the most recent ``move_on_globe()`` or ``move_on_map()`` call to move a component. + + The plan history for executions before the most recent can be requested by providing an ExecutionID in the request. + + Returns a result if both of the following conditions are met: + + - the execution (call to ``move_on_globe()`` or ``move_on_map()``) is still executing **or** changed state within the last 24 hours + - the robot has not reinitialized + + Plans never change. + + Replans always create new plans. + + Replans share the ExecutionID of the previously executing plan. + + All repeated fields are in time ascending order. + + :: + + motion = MotionClient.from_robot(robot=machine, name="builtin") + my_base_resource_name = Base.get_resource_name("my_base") + # Get the plan(s) of the base component which was instructed to move by `MoveOnGlobe()` or `MoveOnMap()` + resp = await motion.get_plan(component_name=my_base_resource_name) + + Args: + component_name (ResourceName): The component to stop + last_plan_only (Optional[bool]): If supplied, the response will only return the last plan for the component / execution. + execution_id (Optional[str]): If supplied, the response will only return plans with the provided execution_id. + + Returns: + ``GetPlanResponse`` (GetPlanResponse): The current PlanWithStatus & replan history which matches the request + + For more information, see `Motion service `_. + """ + ... + + @abc.abstractmethod + async def list_plan_statuses( + self, + only_active_plans: bool = False, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> Sequence[PlanStatusWithID]: + """Returns the statuses of plans created by `move_on_globe()` or ``move_on_map()`` calls that meet at least one of the following + conditions since the motion service initialized: + + - the plan's status is in progress + - the plan's status changed state within the last 24 hours + + All repeated fields are in chronological order. + + :: + + motion = MotionClient.from_robot(robot=machine, name="builtin") + # List the plan statuses of the motion service within the TTL + resp = await motion.list_plan_statuses() + + Args: + only_active_plans (Optional[bool]): If supplied, the response will filter out any plans that are not executing. + + Returns: + ``ListPlanStatusesResponse`` (ListPlanStatusesResponse): List of last known statuses with the + associated IDs of all plans within the TTL ordered by timestamp in ascending order. + + For more information, see `Motion service `_. + """ + ... + + @abc.abstractmethod + async def get_pose( + self, + component_name: ResourceName, + destination_frame: str, + supplemental_transforms: Optional[Sequence[Transform]] = None, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + ) -> PoseInFrame: + """ + Get the Pose and observer frame for any given component on a robot. + + :: + + # Note that the example uses the ``Gripper`` class, but any component class that inherits from ``ComponentBase`` will work + # (``Arm``, ``Base``, etc). + + # Create a `component_name`: + component_name = Gripper.get_resource_name("my_gripper") + + from viam.components.gripper import Gripper + from viam.services.motion import MotionClient + + # Assume that the connect function is written and will return a valid machine. + robot = await connect() + + motion = MotionClient.from_robot(robot=machine, name="builtin") + gripperName = Gripper.get_resource_name("my_gripper") + gripperPoseInWorld = await motion.get_pose(component_name=gripperName, + destination_frame="world") + + Args: + component_name (viam.proto.common.ResourceName): Name of a component on a robot. + destination_frame (str): Name of the desired reference frame. + supplemental_transforms (Optional[List[viam.proto.common.Transform]]): Transforms used to augment the robot's frame while + calculating pose. + + Returns: + ``Pose`` (PoseInFrame): Pose of the given component and the frame in which it was observed. + + For more information, see `Motion service `_. + """ + ... diff --git a/src/viam/services/motion/service.py b/src/viam/services/motion/service.py new file mode 100644 index 000000000..bcc71d19c --- /dev/null +++ b/src/viam/services/motion/service.py @@ -0,0 +1,132 @@ +from grpclib.server import Stream + +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.service.motion import ( + GetPlanRequest, + GetPlanResponse, + GetPoseRequest, + GetPoseResponse, + ListPlanStatusesRequest, + ListPlanStatusesResponse, + MoveOnGlobeRequest, + MoveOnGlobeResponse, + MoveOnMapRequest, + MoveOnMapResponse, + MoveRequest, + MoveResponse, + StopPlanRequest, + StopPlanResponse, + UnimplementedMotionServiceBase, +) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict + +from .motion import Motion + + +class MotionRPCService(UnimplementedMotionServiceBase, ResourceRPCServiceBase[Motion]): + RESOURCE_TYPE = Motion + + async def Move(self, stream: Stream[MoveRequest, MoveResponse]) -> None: + request = await stream.recv_message() + assert request is not None + service = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await service.move( + request.component_name, + request.destination, + request.world_state, + request.constraints, + extra=struct_to_dict(request.extra), + timeout=timeout, + ) + response = MoveResponse(success=result) + await stream.send_message(response) + + async def MoveOnMap(self, stream: Stream[MoveOnMapRequest, MoveOnMapResponse]) -> None: + request = await stream.recv_message() + assert request is not None + service = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await service.move_on_map( + request.component_name, + request.destination, + request.slam_service_name, + request.motion_configuration, + request.obstacles, + extra=struct_to_dict(request.extra), + timeout=timeout, + ) + response = MoveOnMapResponse(execution_id=result) + await stream.send_message(response) + + async def MoveOnGlobe(self, stream: Stream[MoveOnGlobeRequest, MoveOnGlobeResponse]) -> None: + request = await stream.recv_message() + assert request is not None + service = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await service.move_on_globe( + request.component_name, + request.destination, + request.movement_sensor_name, + request.obstacles, + request.heading, + request.motion_configuration, + bounding_regions=request.bounding_regions, + extra=struct_to_dict(request.extra), + timeout=timeout, + ) + response = MoveOnGlobeResponse(execution_id=result) + await stream.send_message(response) + + async def GetPose(self, stream: Stream[GetPoseRequest, GetPoseResponse]) -> None: + request = await stream.recv_message() + assert request is not None + service = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await service.get_pose( + request.component_name, + request.destination_frame, + request.supplemental_transforms, + extra=struct_to_dict(request.extra), + timeout=timeout, + ) + response = GetPoseResponse(pose=result) + await stream.send_message(response) + + async def StopPlan(self, stream: Stream[StopPlanRequest, StopPlanResponse]) -> None: + request = await stream.recv_message() + assert request is not None + service = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await service.stop_plan(request.component_name, extra=struct_to_dict(request.extra), timeout=timeout) + response = StopPlanResponse() + await stream.send_message(response) + + async def ListPlanStatuses(self, stream: Stream[ListPlanStatusesRequest, ListPlanStatusesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + service = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await service.list_plan_statuses(request.only_active_plans, extra=struct_to_dict(request.extra), timeout=timeout) + response = ListPlanStatusesResponse(plan_statuses_with_ids=result) + await stream.send_message(response) + + async def GetPlan(self, stream: Stream[GetPlanRequest, GetPlanResponse]) -> None: + request = await stream.recv_message() + assert request is not None + service = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await service.get_plan( + request.component_name, request.last_plan_only, request.execution_id, extra=struct_to_dict(request.extra), timeout=timeout + ) + await stream.send_message(result) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + service = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await service.do_command(struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) diff --git a/src/viam/services/navigation/__init__.py b/src/viam/services/navigation/__init__.py new file mode 100644 index 000000000..e3587fc61 --- /dev/null +++ b/src/viam/services/navigation/__init__.py @@ -0,0 +1,11 @@ +from viam.proto.common import GeoGeometry, GeoPoint +from viam.proto.service.navigation import MapType, Mode, Path, Waypoint +from viam.resource.registry import Registry, ResourceRegistration + +from .client import NavigationClient +from .navigation import Navigation +from .service import NavigationRPCService + +__all__ = ["GeoPoint", "GeoGeometry", "NavigationClient", "Navigation", "Waypoint", "Mode", "Path", "MapType"] + +Registry.register_api(ResourceRegistration(Navigation, NavigationRPCService, lambda name, channel: NavigationClient(name, channel))) diff --git a/src/viam/services/navigation/client.py b/src/viam/services/navigation/client.py new file mode 100644 index 000000000..5782ebfec --- /dev/null +++ b/src/viam/services/navigation/client.py @@ -0,0 +1,99 @@ +from typing import List, Mapping, Optional + +from grpclib.client import Channel + +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.service.navigation import ( + AddWaypointRequest, + GetLocationRequest, + GetLocationResponse, + GetModeRequest, + GetModeResponse, + GetObstaclesRequest, + GetObstaclesResponse, + GetPathsRequest, + GetPathsResponse, + GetPropertiesRequest, + GetPropertiesResponse, + GetWaypointsRequest, + GetWaypointsResponse, + NavigationServiceStub, + Path, + RemoveWaypointRequest, + SetModeRequest, +) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, struct_to_dict + +from . import GeoGeometry, GeoPoint, MapType, Mode, Waypoint +from .navigation import Navigation + + +class NavigationClient(Navigation, ReconfigurableResourceRPCClientBase): + """ + Connect to the NavigationService, which allows the robot to navigate to specified locations. + """ + + client: NavigationServiceStub + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = NavigationServiceStub(channel) + super().__init__(name) + + async def get_paths(self, *, timeout: Optional[float] = None, **kwargs) -> List[Path]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPathsRequest(name=self.name) + response: GetPathsResponse = await self.client.GetPaths(request, timeout=timeout, metadata=md) + return list(response.paths) + + async def get_location(self, *, timeout: Optional[float] = None, **kwargs) -> GeoPoint: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetLocationRequest(name=self.name) + response: GetLocationResponse = await self.client.GetLocation(request, timeout=timeout, metadata=md) + return response.location + + async def get_obstacles(self, *, timeout: Optional[float] = None, **kwargs) -> List[GeoGeometry]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetObstaclesRequest(name=self.name) + response: GetObstaclesResponse = await self.client.GetObstacles(request, timeout=timeout, metadata=md) + return list(response.obstacles) + + async def get_waypoints(self, *, timeout: Optional[float] = None, **kwargs) -> List[Waypoint]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetWaypointsRequest(name=self.name) + response: GetWaypointsResponse = await self.client.GetWaypoints(request, timeout=timeout, metadata=md) + return list(response.waypoints) + + async def add_waypoint(self, point: GeoPoint, *, timeout: Optional[float] = None, **kwargs): + md = kwargs.get("metadata", self.Metadata()).proto + request = AddWaypointRequest(name=self.name, location=point) + await self.client.AddWaypoint(request, timeout=timeout, metadata=md) + + async def remove_waypoint(self, id: str, *, timeout: Optional[float] = None, **kwargs): + md = kwargs.get("metadata", self.Metadata()).proto + request = RemoveWaypointRequest(name=self.name, id=id) + await self.client.RemoveWaypoint(request, timeout=timeout, metadata=md) + + async def get_mode(self, *, timeout: Optional[float] = None, **kwargs) -> Mode.ValueType: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetModeRequest(name=self.name) + response: GetModeResponse = await self.client.GetMode(request, timeout=timeout, metadata=md) + return response.mode + + async def set_mode(self, mode: Mode.ValueType, *, timeout: Optional[float] = None, **kwargs): + md = kwargs.get("metadata", self.Metadata()).proto + request = SetModeRequest(name=self.name, mode=mode) + await self.client.SetMode(request, timeout=timeout, metadata=md) + + async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> MapType.ValueType: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPropertiesRequest(name=self.name) + response: GetPropertiesResponse = await self.client.GetProperties(request, timeout=timeout, metadata=md) + return response.map_type + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) diff --git a/src/viam/services/navigation/navigation.py b/src/viam/services/navigation/navigation.py new file mode 100644 index 000000000..a374e62e2 --- /dev/null +++ b/src/viam/services/navigation/navigation.py @@ -0,0 +1,250 @@ +import abc +from typing import Final, List, Optional + +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE + +from ..service_base import ServiceBase +from . import GeoGeometry, GeoPoint, MapType, Mode, Path, Waypoint + + +class Navigation(ServiceBase): + """ + Navigation represents a Navigation service. + + This acts as an abstract base class for any drivers representing specific + navigation service implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + For more information, see `Navigation service `_. + """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, "navigation" + ) + + @abc.abstractmethod + async def get_paths(self, *, timeout: Optional[float]) -> List[Path]: + """ + Get each path, the series of geo points the robot plans to travel through + to get to a destination waypoint, in the machine's motion planning. + + :: + + my_nav = NavigationClient.from_robot(robot=machine, name="my_nav_service") + + # Get a list containing each path stored by the navigation service + paths = await my_nav.get_paths() + + Args: + timeout (Optional[float]): An option to set how long to wait (in seconds) + before calling a time-out and closing the underlying RPC call. + + Returns: + List[navigation.Path]: An array comprised of Paths, where each path is either a user-provided destination or + a Waypoint, along with the corresponding set of geopoints. This outlines the route the machine is expected to take to + reach the specified destination or Waypoint. + + For more information, see `Navigation service `_. + """ + ... + + @abc.abstractmethod + async def get_location(self, *, timeout: Optional[float]) -> GeoPoint: + """ + Get the current location of the robot in the navigation service. + + :: + + my_nav = NavigationClient.from_robot(robot=machine, name="my_nav_service") + + # Get the current location of the robot in the navigation service + location = await my_nav.get_location() + + Args: + timeout (Optional[float]): An option to set how long to wait (in seconds) + before calling a time-out and closing the underlying RPC call. + + Returns: + navigation.GeoPoint: The current location of the robot in the navigation service, + represented in a GeoPoint with latitude and longitude values. + + For more information, see `Navigation service `_. + """ + ... + + @abc.abstractmethod + async def get_obstacles(self, *, timeout: Optional[float]) -> List[GeoGeometry]: + """ + Get an array or list of the obstacles currently in the service's data storage. + These are objects designated for the robot to avoid when navigating. + These include all transient obstacles which are discovered by the vision services configured for the navigation service, + in addition to the obstacles that are configured as a part of the service. + + :: + + my_nav = NavigationClient.from_robot(robot=machine, name="my_nav_service") + + # Get a list containing each obstacle stored by the navigation service + obstacles = await my_nav.get_obstacles() + + Args: + timeout (Optional[float]): An option to set how long to wait (in seconds) + before calling a time-out and closing the underlying RPC call. + + Returns: + List[navigation.GeoGeometry]: A list comprised of each GeoGeometry in the service's data storage. + These are objects designated for the robot to avoid when navigating. + + For more information, see `Navigation service `_. + """ + ... + + @abc.abstractmethod + async def get_waypoints(self, *, timeout: Optional[float]) -> List[Waypoint]: + """ + Get an array of waypoints currently in the service's data storage. + These are locations designated within a path for the robot to navigate to. + + :: + + my_nav = NavigationClient.from_robot(robot=machine, name="my_nav_service") + + # Get a list containing each waypoint stored by the navigation service + waypoints = await my_nav.get_waypoints() + + Args: + timeout (Optional[float]): An option to set how long to wait (in seconds) + before calling a time-out and closing the underlying RPC call. + + Returns: + List[navigation.Waypoint]: An array comprised of each Waypoint in the service's data storage. + These are locations designated within a path for the robot to navigate to. + + For more information, see `Navigation service `_. + """ + ... + + @abc.abstractmethod + async def add_waypoint(self, point: GeoPoint, *, timeout: Optional[float]): + """ + Add a waypoint to the service's data storage. + + :: + + my_nav = NavigationClient.from_robot(robot=machine, name="my_nav_service") + + # Create a new waypoint with latitude and longitude values of 0 degrees + location = GeoPoint(latitude=0, longitude=0) + + + # Add your waypoint to the service's data storage + await my_nav.add_waypoint(point=location) + + Args: + point (navigation.GeoPoint): The current location of the robot in the navigation service, + represented in a GeoPoint with latitude and longitude values. + timeout (Optional[float]): An option to set how long to wait (in seconds) + before calling a time-out and closing the underlying RPC call. + + For more information, see `Navigation service `_. + """ + ... + + @abc.abstractmethod + async def remove_waypoint(self, id: str, *, timeout: Optional[float]): + """ + Remove a waypoint from the service's data storage. If the robot is currently navigating to this waypoint, + the motion will be canceled, and the robot will proceed to the next waypoint. + + :: + + my_nav = NavigationClient.from_robot(robot=machine, name="my_nav_service") + + # Remove the waypoint matching that ObjectID from the service's data storage + await my_nav.remove_waypoint(waypoint_id) + + Args: + id (str): The MongoDB ObjectID of the Waypoint to remove from the service's data storage. + timeout (Optional[float]): An option to set how long to wait (in seconds) + before calling a time-out and closing the underlying RPC call. + + For more information, see `Navigation service `_. + """ + ... + + @abc.abstractmethod + async def get_mode(self, *, timeout: Optional[float]) -> Mode.ValueType: + """ + Get the Mode the service is operating in. + + There are two options for modes: MODE_MANUAL or MODE_WAYPOINT. + + MODE_WAYPOINT: Start to look for added waypoints and begin autonomous navigation. + MODE_MANUAL: Stop autonomous navigation between waypoints and allow the base to be controlled manually. + + :: + + my_nav = NavigationClient.from_robot(robot=machine, name="my_nav_service") + + # Get the Mode the service is operating in + await my_nav.get_mode() + + Args: + timeout (Optional[float]): An option to set how long to wait (in seconds) + before calling a time-out and closing the underlying RPC call. + + Returns: + navigation.Mode.ValueType: The Mode the service is operating in. + + For more information, see `Navigation service `_. + """ + ... + + @abc.abstractmethod + async def set_mode(self, mode: Mode.ValueType, *, timeout: Optional[float]): + """ + Set the Mode the service is operating in. + + There are two options for modes: MODE_MANUAL or MODE_WAYPOINT. + + MODE_WAYPOINT: Start to look for added waypoints and begin autonomous navigation. + MODE_MANUAL: Stop autonomous navigation between waypoints and allow the base to be controlled manually. + + :: + + my_nav = NavigationClient.from_robot(robot=machine, name="my_nav_service") + + # Set the Mode the service is operating in to MODE_WAYPOINT and begin navigation + await my_nav.set_mode(Mode.ValueType.MODE_WAYPOINT) + + Args: + timeout (Optional[float]): An option to set how long to wait (in seconds) + before calling a time-out and closing the underlying RPC call. + mode (navigation.Mode.ValueType): The Mode for the service to operate in. + + For more information, see `Navigation service `_. + """ + ... + + @abc.abstractmethod + async def get_properties(self, *, timeout: Optional[float]) -> MapType.ValueType: + """ + Get information about the navigation service. + + :: + + my_nav = NavigationClient.from_robot(robot=machine, name="my_nav_service") + + # Get the properties of the current navigation service. + nav_properties = await my_nav.get_properties() + + Args: + timeout (Optional[float]): An option to set how long to wait (in seconds) + before calling a time-out and closing the underlying RPC call. + + Returns: + MapType.ValueType: Information about the type of map the service is using. + + For more information, see `Navigation service `_. + """ + ... diff --git a/src/viam/services/navigation/service.py b/src/viam/services/navigation/service.py new file mode 100644 index 000000000..0f25a50e5 --- /dev/null +++ b/src/viam/services/navigation/service.py @@ -0,0 +1,137 @@ +from grpclib.server import Stream + +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.service.navigation import ( + AddWaypointRequest, + AddWaypointResponse, + GetLocationRequest, + GetLocationResponse, + GetModeRequest, + GetModeResponse, + GetObstaclesRequest, + GetObstaclesResponse, + GetPathsRequest, + GetPathsResponse, + GetPropertiesRequest, + GetPropertiesResponse, + GetWaypointsRequest, + GetWaypointsResponse, + NavigationServiceBase, + RemoveWaypointRequest, + RemoveWaypointResponse, + SetModeRequest, + SetModeResponse, +) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict + +from .navigation import Navigation + + +class NavigationRPCService(NavigationServiceBase, ResourceRPCServiceBase): + """ + gRPC Service for a Navigation service + """ + + RESOURCE_TYPE = Navigation + + async def GetPaths(self, stream: Stream[GetPathsRequest, GetPathsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + navigation = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + paths = await navigation.get_paths(timeout=timeout) + response = GetPathsResponse(paths=paths) + await stream.send_message(response) + + async def GetLocation(self, stream: Stream[GetLocationRequest, GetLocationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + navigation = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + location = await navigation.get_location(timeout=timeout) + response = GetLocationResponse(location=location) + await stream.send_message(response) + + async def GetObstacles(self, stream: Stream[GetObstaclesRequest, GetObstaclesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + navigation = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + obstacles = await navigation.get_obstacles(timeout=timeout) + response = GetObstaclesResponse(obstacles=obstacles) + await stream.send_message(response) + + async def GetWaypoints(self, stream: Stream[GetWaypointsRequest, GetWaypointsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + navigation = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + waypoints = await navigation.get_waypoints(timeout=timeout) + response = GetWaypointsResponse(waypoints=waypoints) + await stream.send_message(response) + + async def AddWaypoint(self, stream: Stream[AddWaypointRequest, AddWaypointResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + point = request.location + navigation = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await navigation.add_waypoint(point, timeout=timeout) + response = AddWaypointResponse() + await stream.send_message(response) + + async def RemoveWaypoint(self, stream: Stream[RemoveWaypointRequest, RemoveWaypointResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + id = request.id + navigation = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await navigation.remove_waypoint(id=id, timeout=timeout) + response = RemoveWaypointResponse() + await stream.send_message(response) + + async def GetMode(self, stream: Stream[GetModeRequest, GetModeResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + navigation = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + mode = await navigation.get_mode(timeout=timeout) + response = GetModeResponse(mode=mode) + await stream.send_message(response) + + async def SetMode(self, stream: Stream[SetModeRequest, SetModeResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + mode = request.mode + navigation = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + await navigation.set_mode(mode, timeout=timeout) + response = SetModeResponse() + await stream.send_message(response) + + async def GetProperties(self, stream: Stream[GetPropertiesRequest, GetPropertiesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + navigation = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + map_type = await navigation.get_properties(timeout=timeout) + response = GetPropertiesResponse(map_type=map_type) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + navigation = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await navigation.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) diff --git a/src/viam/services/sensors.py b/src/viam/services/sensors.py deleted file mode 100644 index c2bd8b45d..000000000 --- a/src/viam/services/sensors.py +++ /dev/null @@ -1,47 +0,0 @@ -from typing import Any, List, Mapping - -from grpclib.client import Channel - -from viam.proto.common import ResourceName -from viam.proto.service.sensors import ( - GetReadingsRequest, - GetReadingsResponse, - GetSensorsRequest, - GetSensorsResponse, - SensorsServiceStub, -) -from viam.utils import sensor_readings_value_to_native -from viam.services.service_client_base import ServiceClientBase - - -class SensorsServiceClient(ServiceClientBase): - """Connect to the SensorService, which centralizes all Sensors in a single place""" - - SERVICE_TYPE = "sensors" - - def __init__(self, name: str, channel: Channel): - self.client = SensorsServiceStub(channel) - self.name = name - - async def get_sensors(self) -> List[ResourceName]: - """Get the `ResourceName`s of all the `Sensor`s connected to this Robot - - Returns: - List[ResourceName]: The list of all Sensors - """ - request = GetSensorsRequest(name=self.name) - response: GetSensorsResponse = await self.client.GetSensors(request) - return list(response.sensor_names) - - async def get_readings(self, sensors: List[ResourceName]) -> Mapping[ResourceName, Mapping[str, Any]]: - """Get the readings from the specific sensors provided - - Args: - sensors (List[ResourceName]): The `ResourceName`s of the the `Sensor`s to get readings from - - Returns: - Mapping[ResourceName, Mapping[str, Any]]: The readings from the sensors, mapped by `ResourceName` - """ - request = GetReadingsRequest(name=self.name, sensor_names=sensors) - response: GetReadingsResponse = await self.client.GetReadings(request) - return {reading.name: sensor_readings_value_to_native(reading.readings) for reading in response.readings} diff --git a/src/viam/services/service_base.py b/src/viam/services/service_base.py new file mode 100644 index 000000000..87d3455b9 --- /dev/null +++ b/src/viam/services/service_base.py @@ -0,0 +1,78 @@ +import abc +from logging import Logger +from typing import TYPE_CHECKING, ClassVar, Mapping, Optional, cast + +from typing_extensions import Self + +from viam.logging import getLogger +from viam.resource.base import ResourceBase +from viam.utils import ValueTypes + +if TYPE_CHECKING: + from viam.resource.types import API + from viam.robot.client import RobotClient + + +class ServiceBase(abc.ABC, ResourceBase): + """This class describes the base functionality required for a Viam Service. + All services must inherit from this class. + """ + + API: ClassVar["API"] + + def __init__(self, name: str, *, logger: Optional[Logger] = None) -> None: + self.name = name + self.logger = logger if logger is not None else getLogger(f"{self.API}.{name}") + + @classmethod + def from_robot(cls, robot: "RobotClient", name: str) -> Self: + """Get the service named ``name`` from the provided robot. + + :: + + async def connect() -> RobotClient: + # Replace "" (including brackets) with your API key and "" with your API key ID + options = RobotClient.Options.with_api_key("", "") + # Replace "" (included brackets) with your machine's connection URL or FQDN + return await RobotClient.at_address("", options) + + async def main(): + robot = await connect() + + # Can be used with any resource, using the motion service as an example + motion = MotionClient.from_robot(robot=machine, name="builtin") + + robot.close() + + Args: + robot (RobotClient): The robot + name (str): The name of the service + + Returns: + Self: The service, if it exists on the robot + """ + service = robot.get_service(cls.get_resource_name(name)) + return cast(cls, service) # type: ignore + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + """Send/receive arbitrary commands. + + :: + + service = SERVICE.from_robot(robot=machine, "builtin") # replace SERVICE with the appropriate class + + my_command = { + "cmnd": "dosomething", + "someparameter": 52 + } + + # Can be used with any resource, using the motion service as an example + await service.do_command(command=my_command) + + Args: + command (Dict[str, ValueTypes]): The command to execute + + Returns: + Dict[str, ValueTypes]: Result of the executed command + """ + raise NotImplementedError() diff --git a/src/viam/services/service_client_base.py b/src/viam/services/service_client_base.py index 0e335489a..af95bb142 100644 --- a/src/viam/services/service_client_base.py +++ b/src/viam/services/service_client_base.py @@ -1,22 +1,26 @@ import abc -from typing import ClassVar, TYPE_CHECKING -from typing_extensions import Self +from typing import TYPE_CHECKING, ClassVar, Mapping, Optional + from grpclib.client import Channel -from viam.errors import ServiceNotImplementedError -from viam.proto.common import ResourceName +from typing_extensions import Self +from viam.errors import ResourceNotFoundError +from viam.proto.common import ResourceName +from viam.resource.base import API, ResourceBase +from viam.utils import ValueTypes if TYPE_CHECKING: from viam.robot.client import RobotClient -class ServiceClientBase(abc.ABC): +class ServiceClientBase(abc.ABC, ResourceBase): """ Base service client. All service clients must inherit from this class. """ - SERVICE_TYPE: ClassVar[str] + API: ClassVar[API] + channel: Channel def __init__(self, name: str, channel: Channel): self.name = name @@ -24,7 +28,7 @@ def __init__(self, name: str, channel: Channel): @classmethod def from_robot(cls, robot: "RobotClient", name: str = "builtin") -> Self: - """Get the service client named `name` from the provided robot. + """Get the service client named ``name`` from the provided robot. Args: robot (RobotClient): The robot @@ -33,7 +37,10 @@ def from_robot(cls, robot: "RobotClient", name: str = "builtin") -> Self: Returns: Self: The service client, if it exists on the robot """ - resource_name = ResourceName(namespace="rdk", type="service", subtype=cls.SERVICE_TYPE, name=name) + resource_name = ResourceName(namespace="rdk", type="service", subtype=cls.API.resource_subtype, name=name) if resource_name not in robot.resource_names: - raise ServiceNotImplementedError(resource_name.subtype, resource_name.name) + raise ResourceNotFoundError(resource_name.subtype, resource_name.name) return cls(name, robot._channel) + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + raise NotImplementedError() diff --git a/src/viam/services/slam/__init__.py b/src/viam/services/slam/__init__.py new file mode 100644 index 000000000..11c2e33de --- /dev/null +++ b/src/viam/services/slam/__init__.py @@ -0,0 +1,17 @@ +from viam.proto.common import Pose +from viam.proto.service.slam import MappingMode, SensorInfo +from viam.resource.registry import Registry, ResourceRegistration + +from .client import SLAMClient +from .service import SLAMRPCService +from .slam import SLAM + +__all__ = [ + "Pose", + "MappingMode", + "SensorInfo", + "SLAMClient", + "SLAM", +] + +Registry.register_api(ResourceRegistration(SLAM, SLAMRPCService, lambda name, channel: SLAMClient(name, channel))) diff --git a/src/viam/services/slam/client.py b/src/viam/services/slam/client.py new file mode 100644 index 000000000..1aecf9e8f --- /dev/null +++ b/src/viam/services/slam/client.py @@ -0,0 +1,62 @@ +from typing import List, Mapping, Optional + +from grpclib.client import Channel + +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.service.slam import ( + GetInternalStateRequest, + GetInternalStateResponse, + GetPointCloudMapRequest, + GetPointCloudMapResponse, + GetPositionRequest, + GetPositionResponse, + GetPropertiesRequest, + SLAMServiceStub, +) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, struct_to_dict + +from . import Pose +from .slam import SLAM + + +class SLAMClient(SLAM, ReconfigurableResourceRPCClientBase): + """ + Connect to the SLAMService, which allows the robot to create a map of its surroundings and find its location in that map. + """ + + client: SLAMServiceStub + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = SLAMServiceStub(channel) + super().__init__(name) + + async def get_position(self, *, timeout: Optional[float] = None, **kwargs) -> Pose: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPositionRequest(name=self.name) + response: GetPositionResponse = await self.client.GetPosition(request, timeout=timeout, metadata=md) + return response.pose + + async def get_point_cloud_map(self, return_edited_map: bool = False, *, timeout: Optional[float] = None, **kwargs) -> List[bytes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPointCloudMapRequest(name=self.name, return_edited_map=return_edited_map) + response: List[GetPointCloudMapResponse] = await self.client.GetPointCloudMap(request, timeout=timeout, metadata=md) + return [r.point_cloud_pcd_chunk for r in response] + + async def get_internal_state(self, *, timeout: Optional[float] = None, **kwargs) -> List[bytes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetInternalStateRequest(name=self.name) + response: List[GetInternalStateResponse] = await self.client.GetInternalState(request, timeout=timeout, metadata=md) + return [r.internal_state_chunk for r in response] + + async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> SLAM.Properties: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPropertiesRequest(name=self.name) + return await self.client.GetProperties(request, timeout=timeout, metadata=md) + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) diff --git a/src/viam/services/slam/service.py b/src/viam/services/slam/service.py new file mode 100644 index 000000000..0443b2914 --- /dev/null +++ b/src/viam/services/slam/service.py @@ -0,0 +1,75 @@ +from grpclib.server import Stream + +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.service.slam import ( + GetInternalStateRequest, + GetInternalStateResponse, + GetPointCloudMapRequest, + GetPointCloudMapResponse, + GetPositionRequest, + GetPositionResponse, + GetPropertiesRequest, + GetPropertiesResponse, + SLAMServiceBase, +) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict + +from .slam import SLAM + + +class SLAMRPCService(SLAMServiceBase, ResourceRPCServiceBase): + """ + gRPC Service for a SLAM service + """ + + RESOURCE_TYPE = SLAM + + async def GetInternalState(self, stream: Stream[GetInternalStateRequest, GetInternalStateResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + slam = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + chunks = await slam.get_internal_state(timeout=timeout) + for chunk in chunks: + response = GetInternalStateResponse(internal_state_chunk=chunk) + await stream.send_message(response) + + async def GetPointCloudMap(self, stream: Stream[GetPointCloudMapRequest, GetPointCloudMapResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + slam = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + chunks = await slam.get_point_cloud_map(return_edited_map=request.return_edited_map, timeout=timeout) + for chunk in chunks: + response = GetPointCloudMapResponse(point_cloud_pcd_chunk=chunk) + await stream.send_message(response) + + async def GetPosition(self, stream: Stream[GetPositionRequest, GetPositionResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + slam = self.get_resource(name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + position = await slam.get_position(timeout=timeout) + response = GetPositionResponse(pose=position) + await stream.send_message(response) + + async def GetProperties(self, stream: Stream[GetPropertiesRequest, GetPropertiesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + slam = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + properties = await slam.get_properties(timeout=timeout) + await stream.send_message(properties) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + slam = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await slam.do_command(command=struct_to_dict(request.command), timeout=timeout, metadata=stream.metadata) + response = DoCommandResponse(result=dict_to_struct(result)) + await stream.send_message(response) diff --git a/src/viam/services/slam/slam.py b/src/viam/services/slam/slam.py new file mode 100644 index 000000000..759c59f8c --- /dev/null +++ b/src/viam/services/slam/slam.py @@ -0,0 +1,111 @@ +import abc +import sys +from typing import Final, List, Optional + +from viam.proto.service.slam import GetPropertiesResponse +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE + +from ..service_base import ServiceBase +from . import Pose + +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + + +class SLAM(ServiceBase): + """ + SLAM represents a SLAM service. + + This acts as an abstract base class for any drivers representing specific + arm implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + For more information, see `SLAM service `_. + """ + + API: Final = API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, "slam") # pyright: ignore [reportIncompatibleVariableOverride] + + Properties: "TypeAlias" = GetPropertiesResponse + + @abc.abstractmethod + async def get_internal_state(self, *, timeout: Optional[float]) -> List[bytes]: + """ + Get the internal state of the SLAM algorithm required to continue mapping/localization. + + :: + + slam = SLAMClient.from_robot(robot=machine, name="my_slam_service") + + # Get the internal state of the SLAM algorithm required to continue mapping/localization. + internal_state = await slam.get_internal_state() + + Returns: + List[GetInternalStateResponse]: Chunks of the internal state of the SLAM algorithm + + For more information, see `SLAM service `_. + """ + ... + + @abc.abstractmethod + async def get_point_cloud_map(self, return_edited_map: bool = False, *, timeout: Optional[float]) -> List[bytes]: + """ + Get the point cloud map. + + :: + + slam_svc = SLAMClient.from_robot(robot=machine, name="my_slam_service") + + # Get the point cloud map in standard PCD format. + pcd_map = await slam_svc.get_point_cloud_map() + + Args: + return_edited_map (bool): signal to the SLAM service to return an edited map, if the map package contains one and if + the SLAM service supports the feature + + Returns: + List[GetPointCloudMapResponse]: Complete pointcloud in standard PCD format. Chunks of the PointCloud, concatenating all + GetPointCloudMapResponse.point_cloud_pcd_chunk values. + + For more information, see `SLAM service `_. + """ + ... + + @abc.abstractmethod + async def get_position(self, *, timeout: Optional[float]) -> Pose: + """ + Get current position of the specified component in the SLAM Map. + + :: + + slam_svc = SLAMClient.from_robot(robot=machine, name="my_slam_service") + + # Get the current position of the specified source component in the SLAM map as a Pose. + pose = await slam.get_position() + + Returns: + Pose: The current position of the specified component + + For more information, see `SLAM service `_. + """ + ... + + @abc.abstractmethod + async def get_properties(self, *, timeout: Optional[float]) -> Properties: + """ + Get information regarding the current SLAM session. + + :: + + slam_svc = SLAMClient.from_robot(robot=machine, name="my_slam_service") + + # Get the properties of your current SLAM session. + slam_properties = await slam_svc.get_properties() + + Returns: + Properties: The properties of SLAM + + For more information, see `SLAM service `_. + """ + ... diff --git a/src/viam/services/vision.py b/src/viam/services/vision.py deleted file mode 100644 index 9414b218e..000000000 --- a/src/viam/services/vision.py +++ /dev/null @@ -1,284 +0,0 @@ -import json -from dataclasses import dataclass -from enum import Enum -from typing import Any, List, Mapping, Sequence, Union - -from grpclib.client import Channel -from PIL.Image import Image - -from viam.components.types import CameraMimeType -from viam.proto.common import PointCloudObject -from viam.proto.service.vision import ( - AddClassifierRequest, - AddDetectorRequest, - AddSegmenterRequest, - Classification, - Detection, - GetClassificationsFromCameraRequest, - GetClassificationsFromCameraResponse, - GetClassificationsRequest, - GetClassificationsResponse, - GetClassifierNamesRequest, - GetClassifierNamesResponse, - GetDetectionsFromCameraRequest, - GetDetectionsFromCameraResponse, - GetDetectionsRequest, - GetDetectionsResponse, - GetDetectorNamesRequest, - GetDetectorNamesResponse, - GetModelParameterSchemaRequest, - GetModelParameterSchemaResponse, - GetObjectPointCloudsRequest, - GetObjectPointCloudsResponse, - GetSegmenterNamesRequest, - GetSegmenterNamesResponse, - RemoveClassifierRequest, - RemoveDetectorRequest, - RemoveSegmenterRequest, - VisionServiceStub, -) -from viam.services.service_client_base import ServiceClientBase -from viam.utils import dict_to_struct - - -class VisModelType(str, Enum): - DETECTOR_TF_LITE = "tflite_detector" - DETECTOR_TENSORFLOW = "tf_detector" - DETECTOR_COLOR = "color_detector" - CLASSIFIER_TFLITE = "tflite_classifier" - CLASSIFIER_TENSORFLOW = "tf_classifier" - - -@dataclass -class VisModelConfig: - name: str - type: VisModelType - parameters: Mapping[str, Any] - - -class VisionServiceClient(ServiceClientBase): - """ - Connect to the Vision service, which allows you to access various computer vision algorithms - (like detection, segmentation, tracking, etc) that usually only require a camera or image input. - """ - - SERVICE_TYPE = "vision" - - def __init__(self, name: str, channel: Channel): - self.client = VisionServiceStub(channel) - self.name = name - - async def get_detector_names(self) -> List[str]: - """Get the list of detectors currently registered in the service. - - Returns: - List[str]: The detector names - """ - request = GetDetectorNamesRequest(name=self.name) - response: GetDetectorNamesResponse = await self.client.GetDetectorNames(request) - return list(response.detector_names) - - async def add_detector(self, config: VisModelConfig): - """Add a new detector to the service. Returns nothing if successful, and an error if not. - Registers a new detector just as if you had put it in the original "register_models" field - in the robot config. Available types and their parameters can be found in the - vision service documentation. - - Args: - config (VisModelConfig): The configuration of the detector to add. - """ - request = AddDetectorRequest( - name=self.name, - detector_name=config.name, - detector_model_type=config.type, - detector_parameters=dict_to_struct(config.parameters), - ) - await self.client.AddDetector(request) - - async def remove_detector(self, detector_name: str): - """Remove the detector with the given name from the service. Returns nothing if successful. - - Args: - detector_name (str): The name of the detector to remove - """ - request = RemoveDetectorRequest(name=self.name, detector_name=detector_name) - await self.client.RemoveDetector(request) - - async def get_detections_from_camera(self, camera_name: str, detector_name: str) -> List[Detection]: - """Get a list of detections in the next image given a camera and a detector - - Args: - camera_name (str): The name of the camera to use for detection - detector_name (str): The name of the detector to use for detection - - Returns: - List[Detection]: A list of 2D bounding boxes, their labels, and the - confidence score of the labels, around the found objects in the next 2D image - from the given camera, with the given detector applied to it. - """ - request = GetDetectionsFromCameraRequest(name=self.name, camera_name=camera_name, detector_name=detector_name) - response: GetDetectionsFromCameraResponse = await self.client.GetDetectionsFromCamera(request) - return list(response.detections) - - async def get_detections(self, image: Image, detector_name: str) -> List[Detection]: - """Get a list of detections in the given image using the specified detector - - Args: - image (Image): The image to get detections from - detector_name (str): The name of the detector to use for detection - - Returns: - List[Detection]: A list of 2D bounding boxes, their labels, and the - confidence score of the labels, around the found objects in the next 2D image - from the given camera, with the given detector applied to it. - """ - mime_type = CameraMimeType.JPEG - request = GetDetectionsRequest( - name=self.name, - image=mime_type.encode_image(image), - width=image.width, - height=image.height, - mime_type=mime_type, - detector_name=detector_name, - ) - response: GetDetectionsResponse = await self.client.GetDetections(request) - return list(response.detections) - - async def get_classifier_names(self) -> List[str]: - """Get the list of classifiers currently registered to the service - - Returns: - List[str]: The list of classifier names - """ - request = GetClassifierNamesRequest(name=self.name) - response: GetClassifierNamesResponse = await self.client.GetClassifierNames(request) - return list(response.classifier_names) - - async def add_classifier(self, config: VisModelConfig): - """Add a classifier to the service. - - Args: - config (VisModelConfig): The configuration of the classifier - """ - request = AddClassifierRequest( - name=self.name, - classifier_name=config.name, - classifier_model_type=config.type, - classifier_parameters=dict_to_struct(config.parameters), - ) - await self.client.AddClassifier(request) - - async def remove_classifier(self, classifier_name: str): - """Remove the classifier with the given name from the service. Returns nothing if successful. - - Args: - classifier_name (str): The name of the classifier to remove - """ - request = RemoveClassifierRequest(name=self.name, classifier_name=classifier_name) - await self.client.RemoveClassifier(request) - - async def get_classifications_from_camera(self, camera_name: str, classifier_name: str, count: int) -> List[Classification]: - """Get a list of classifications in the next image given a camera and a classifier - - Args: - camera_name (str): The name of the camera to use for detection - classifier_name (str): The name of the classifier to use for classification - count (int): The number of classifications desired - - returns: - List[Classification]: The list of Classifications - """ - request = GetClassificationsFromCameraRequest(name=self.name, camera_name=camera_name, classifier_name=classifier_name, n=count) - response: GetClassificationsFromCameraResponse = await self.client.GetClassificationsFromCamera(request) - return list(response.classifications) - - async def get_classifications(self, image: Image, classifier_name: str) -> List[Classification]: - """Get a list of detections in the given image using the specified detector - - Args: - image (Image): The image to get detections from - classifier_name (str): The name of the detector to use for detection - - Returns: - List[Classification]: The list of Classifications - """ - mime_type = CameraMimeType.JPEG - request = GetClassificationsRequest( - name=self.name, - image=mime_type.encode_image(image), - width=image.width, - height=image.height, - mime_type=mime_type, - classifier_name=classifier_name, - ) - response: GetClassificationsResponse = await self.client.GetClassifications(request) - return list(response.classifications) - - async def get_segmenter_names(self) -> List[str]: - """ - Get the list of segmenters currently registered in the service. - - Returns: - List[str]: The segmenter names - """ - request = GetSegmenterNamesRequest(name=self.name) - response: GetSegmenterNamesResponse = await self.client.GetSegmenterNames(request) - return list(response.segmenter_names) - - async def add_segmenter(self, config: VisModelConfig): - """Add a segmenter to the service - - Args: - config (VisModelConfig): The configuration of the segmenter - """ - request = AddSegmenterRequest( - name=self.name, - segmenter_name=config.name, - segmenter_model_type=config.type, - segmenter_parameters=dict_to_struct(config.parameters), - ) - await self.client.AddSegmenter(request) - - async def remove_segmenter(self, segmenter_name: str): - """Remove the segmenter with the given name from the service. Returns nothing if successful. - - Args: - segmenter_name (str): The name of the segmenter to remove - """ - request = RemoveSegmenterRequest(name=self.name, segmenter_name=segmenter_name) - await self.client.RemoveSegmenter(request) - - async def get_model_parameters_schema(self, model_type: VisModelType) -> Mapping[str, Union[str, int, float, bool, Sequence, Mapping]]: - """ - Get the parameters needed to add a model to the vision registry. - - Args: - model_type (VisModelType): The name of model - - Returns: - Mapping[str, str | int | float | bool | Sequence | Mapping]: A dictionary representing the parameters as JSONSchema - """ - request = GetModelParameterSchemaRequest(name=self.name, model_type=model_type) - response: GetModelParameterSchemaResponse = await self.client.GetModelParameterSchema(request) - return json.loads(response.model_parameter_schema) - - async def get_object_point_clouds(self, camera_name: str, segmenter_name: str, parameters: Mapping[str, Any]) -> List[PointCloudObject]: - """ - Returns a list of the 3D point cloud objects and associated metadata in the latest - picture obtained from the specified 3D camera (using the specified segmenter). - - Args: - camera_name (str): The name of the camera - segmenter_name (str): The name of the segmenter - - Returns: - List[PointCloudObject]: The pointcloud objects with metadata - """ - request = GetObjectPointCloudsRequest( - name=self.name, - camera_name=camera_name, - segmenter_name=segmenter_name, - mime_type=CameraMimeType.PCD, - ) - response: GetObjectPointCloudsResponse = await self.client.GetObjectPointClouds(request) - return list(response.objects) diff --git a/src/viam/services/vision/__init__.py b/src/viam/services/vision/__init__.py new file mode 100644 index 000000000..3cf69e34f --- /dev/null +++ b/src/viam/services/vision/__init__.py @@ -0,0 +1,15 @@ +from viam.resource.registry import Registry, ResourceRegistration +from viam.services.vision.service import VisionRPCService + +from .client import Classification, Detection, VisionClient +from .vision import CaptureAllResult, Vision + +__all__ = [ + "CaptureAllResult", + "Classification", + "Detection", + "VisionClient", + "Vision", +] + +Registry.register_api(ResourceRegistration(Vision, VisionRPCService, lambda name, channel: VisionClient(name, channel))) diff --git a/src/viam/services/vision/client.py b/src/viam/services/vision/client.py new file mode 100644 index 000000000..930f02adf --- /dev/null +++ b/src/viam/services/vision/client.py @@ -0,0 +1,206 @@ +from typing import Any, List, Mapping, Optional + +from grpclib.client import Channel + +from viam.errors import ViamError +from viam.media.video import CameraMimeType, ViamImage +from viam.proto.common import DoCommandRequest, DoCommandResponse, PointCloudObject +from viam.proto.service.vision import ( + CaptureAllFromCameraRequest, + CaptureAllFromCameraResponse, + Classification, + Detection, + GetClassificationsFromCameraRequest, + GetClassificationsFromCameraResponse, + GetClassificationsRequest, + GetClassificationsResponse, + GetDetectionsFromCameraRequest, + GetDetectionsFromCameraResponse, + GetDetectionsRequest, + GetDetectionsResponse, + GetObjectPointCloudsRequest, + GetObjectPointCloudsResponse, + GetPropertiesRequest, + GetPropertiesResponse, + VisionServiceStub, +) +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.utils import ValueTypes, dict_to_struct, struct_to_dict + +from .vision import CaptureAllResult, Vision + + +class VisionClient(Vision, ReconfigurableResourceRPCClientBase): + """ + Connect to the Vision service, which allows you to access various computer vision algorithms + (like detection, segmentation, tracking, etc) that usually only require a camera or image input. + """ + + client: VisionServiceStub + + def __init__(self, name: str, channel: Channel): + super().__init__(name) + self.channel = channel + self.client = VisionServiceStub(channel) + + async def capture_all_from_camera( + self, + camera_name: str, + return_image: bool = False, + return_classifications: bool = False, + return_detections: bool = False, + return_object_point_clouds: bool = False, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> CaptureAllResult: + md = kwargs.get("metadata", self.Metadata()).proto + request = CaptureAllFromCameraRequest( + name=self.name, + camera_name=camera_name, + return_image=return_image, + return_classifications=return_classifications, + return_detections=return_detections, + return_object_point_clouds=return_object_point_clouds, + extra=dict_to_struct(extra), + ) + response: CaptureAllFromCameraResponse = await self.client.CaptureAllFromCamera(request, timeout=timeout, metadata=md) + result = CaptureAllResult() + result.extra = struct_to_dict(response.extra) + if return_image: + mime_type = CameraMimeType.from_proto(response.image.format) + img = ViamImage(response.image.image, mime_type) + result.image = img + if return_classifications: + result.classifications = list(response.classifications) + if return_detections: + result.detections = list(response.detections) + if return_object_point_clouds: + result.objects = list(response.objects) + return result + + async def get_detections_from_camera( + self, + camera_name: str, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> List[Detection]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetDetectionsFromCameraRequest(name=self.name, camera_name=camera_name, extra=dict_to_struct(extra)) + response: GetDetectionsFromCameraResponse = await self.client.GetDetectionsFromCamera(request, timeout=timeout, metadata=md) + return list(response.detections) + + async def get_detections( + self, + image: ViamImage, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> List[Detection]: + md = kwargs.get("metadata", self.Metadata()).proto + mime_type = CameraMimeType.JPEG + + if image.width is None or image.height is None: + raise ViamError(f"image {image} needs to have a specified width and height") + else: + request = GetDetectionsRequest( + name=self.name, + image=image.data, + width=image.width, + height=image.height, + mime_type=mime_type, + extra=dict_to_struct(extra), + ) + response: GetDetectionsResponse = await self.client.GetDetections(request, timeout=timeout, metadata=md) + return list(response.detections) + + async def get_classifications_from_camera( + self, + camera_name: str, + count: int, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> List[Classification]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetClassificationsFromCameraRequest(name=self.name, camera_name=camera_name, n=count, extra=dict_to_struct(extra)) + response: GetClassificationsFromCameraResponse = await self.client.GetClassificationsFromCamera( + request, timeout=timeout, metadata=md + ) + return list(response.classifications) + + async def get_classifications( + self, + image: ViamImage, + count: int, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> List[Classification]: + md = kwargs.get("metadata", self.Metadata()).proto + + mime_type = CameraMimeType.JPEG + if image.width is None or image.height is None: + raise ViamError(f"image {image} needs to have a specified width and height") + request = GetClassificationsRequest( + name=self.name, + image=image.data, + width=image.width, + height=image.height, + mime_type=mime_type, + n=count, + extra=dict_to_struct(extra), + ) + response: GetClassificationsResponse = await self.client.GetClassifications(request, timeout=timeout, metadata=md) + return list(response.classifications) + + async def get_object_point_clouds( + self, + camera_name: str, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> List[PointCloudObject]: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetObjectPointCloudsRequest( + name=self.name, + camera_name=camera_name, + mime_type=CameraMimeType.PCD, + extra=dict_to_struct(extra), + ) + response: GetObjectPointCloudsResponse = await self.client.GetObjectPointClouds(request, timeout=timeout, metadata=md) + return list(response.objects) + + async def get_properties( + self, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Vision.Properties: + md = kwargs.get("metadata", self.Metadata()).proto + request = GetPropertiesRequest( + name=self.name, + extra=dict_to_struct(extra), + ) + response: GetPropertiesResponse = await self.client.GetProperties(request, timeout=timeout, metadata=md) + return response + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + **kwargs, + ) -> Mapping[str, ValueTypes]: + md = kwargs.get("metadata", self.Metadata()).proto + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await self.client.DoCommand(request, timeout=timeout, metadata=md) + return struct_to_dict(response.result) diff --git a/src/viam/services/vision/service.py b/src/viam/services/vision/service.py new file mode 100644 index 000000000..3dd61dc6f --- /dev/null +++ b/src/viam/services/vision/service.py @@ -0,0 +1,146 @@ +from grpclib.server import Stream + +from viam.media.video import CameraMimeType, ViamImage +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.component.camera import Image +from viam.proto.service.vision import ( + CaptureAllFromCameraRequest, + CaptureAllFromCameraResponse, + GetClassificationsFromCameraRequest, + GetClassificationsFromCameraResponse, + GetClassificationsRequest, + GetClassificationsResponse, + GetDetectionsFromCameraRequest, + GetDetectionsFromCameraResponse, + GetDetectionsRequest, + GetDetectionsResponse, + GetObjectPointCloudsRequest, + GetObjectPointCloudsResponse, + GetPropertiesRequest, + GetPropertiesResponse, + UnimplementedVisionServiceBase, +) +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.utils import dict_to_struct, struct_to_dict + +from .vision import Vision + + +class VisionRPCService(UnimplementedVisionServiceBase, ResourceRPCServiceBase): + """ + gRPC service for a Vision service + """ + + RESOURCE_TYPE = Vision + + async def CaptureAllFromCamera(self, stream: Stream[CaptureAllFromCameraRequest, CaptureAllFromCameraResponse]) -> None: + request = await stream.recv_message() + assert request is not None + vision = self.get_resource(request.name) + extra = struct_to_dict(request.extra) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await vision.capture_all_from_camera( + request.camera_name, + return_image=request.return_image, + return_classifications=request.return_classifications, + return_detections=request.return_detections, + return_object_point_clouds=request.return_object_point_clouds, + extra=extra, + timeout=timeout, + ) + img = None + if result.image is not None: + fmt = result.image.mime_type.to_proto() + img_bytes = result.image.data + img = Image(source_name=request.camera_name, format=fmt, image=img_bytes) + response = CaptureAllFromCameraResponse( + image=img, + detections=result.detections, + classifications=result.classifications, + objects=result.objects, + extra=dict_to_struct(result.extra if result.extra else {}), + ) + await stream.send_message(response) + + async def GetDetectionsFromCamera(self, stream: Stream[GetDetectionsFromCameraRequest, GetDetectionsFromCameraResponse]) -> None: + request = await stream.recv_message() + assert request is not None + vision = self.get_resource(request.name) + extra = struct_to_dict(request.extra) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await vision.get_detections_from_camera(request.camera_name, extra=extra, timeout=timeout) + response = GetDetectionsFromCameraResponse(detections=result) + await stream.send_message(response) + + async def GetDetections(self, stream: Stream[GetDetectionsRequest, GetDetectionsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + vision = self.get_resource(request.name) + extra = struct_to_dict(request.extra) + timeout = stream.deadline.time_remaining() if stream.deadline else None + + mime_type = CameraMimeType.from_string(request.mime_type) + image = ViamImage(request.image, mime_type) + + result = await vision.get_detections(image, extra=extra, timeout=timeout) + response = GetDetectionsResponse(detections=result) + await stream.send_message(response) + + async def GetClassificationsFromCamera( + self, stream: Stream[GetClassificationsFromCameraRequest, GetClassificationsFromCameraResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + vision = self.get_resource(request.name) + extra = struct_to_dict(request.extra) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await vision.get_classifications_from_camera(request.camera_name, request.n, extra=extra, timeout=timeout) + response = GetClassificationsFromCameraResponse(classifications=result) + await stream.send_message(response) + + async def GetClassifications(self, stream: Stream[GetClassificationsRequest, GetClassificationsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + vision = self.get_resource(request.name) + extra = struct_to_dict(request.extra) + timeout = stream.deadline.time_remaining() if stream.deadline else None + + mime_type = CameraMimeType.from_string(request.mime_type) + image = ViamImage(request.image, mime_type) + + result = await vision.get_classifications(image, request.n, extra=extra, timeout=timeout) + response = GetClassificationsResponse(classifications=result) + await stream.send_message(response) + + async def GetObjectPointClouds(self, stream: Stream[GetObjectPointCloudsRequest, GetObjectPointCloudsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + vision = self.get_resource(request.name) + extra = struct_to_dict(request.extra) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await vision.get_object_point_clouds(request.camera_name, extra=extra, timeout=timeout) + response = GetObjectPointCloudsResponse(mime_type=CameraMimeType.PCD.value, objects=result) + await stream.send_message(response) + + async def GetProperties(self, stream: Stream[GetPropertiesRequest, GetPropertiesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + vision = self.get_resource(name) + extra = struct_to_dict(request.extra) + timeout = stream.deadline.time_remaining() if stream.deadline else None + properties = await vision.get_properties(extra=extra, timeout=timeout) + response = GetPropertiesResponse( + classifications_supported=properties.classifications_supported, + detections_supported=properties.detections_supported, + object_point_clouds_supported=properties.object_point_clouds_supported, + ) + await stream.send_message(response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + vision = self.get_resource(request.name) + timeout = stream.deadline.time_remaining() if stream.deadline else None + result = await vision.do_command(struct_to_dict(request.command), timeout=timeout) + await stream.send_message(DoCommandResponse(result=dict_to_struct(result))) diff --git a/src/viam/services/vision/vision.py b/src/viam/services/vision/vision.py new file mode 100644 index 000000000..6a3c65233 --- /dev/null +++ b/src/viam/services/vision/vision.py @@ -0,0 +1,315 @@ +import abc +import sys +from typing import Final, List, Mapping, Optional + +from viam.media.video import ViamImage +from viam.proto.common import PointCloudObject +from viam.proto.service.vision import Classification, Detection, GetPropertiesResponse +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE +from viam.utils import ValueTypes + +from ..service_base import ServiceBase + +if sys.version_info >= (3, 10): + from typing import TypeAlias +else: + from typing_extensions import TypeAlias + + +class CaptureAllResult: + """ + CaptureAllResult represents the collection of things that you have requested from the + CaptureAllFromCamera method. This is used most often for visualization purposes, since normally, + returning the image on every call to a classifier/detector/etc would be costly and unnecessary. + The default result for each field is None rather than the empty list to distinguish between + "there was no request for the classifier/detector to return a result" vs. + "the classifier/detector was requested, but there were no results". + """ + + def __init__( + self, + image: Optional[ViamImage] = None, + classifications: Optional[List[Classification]] = None, + detections: Optional[List[Detection]] = None, + objects: Optional[List[PointCloudObject]] = None, + extra: Optional[Mapping[str, ValueTypes]] = None, + ): + """ + Args: + image (ViamImage|None): The image from the GetImage request of the camera, if it was requested. + classifications (List[Classification]|None): The classifications from GetClassifications, if it was requested. + detections (List[Detection]|None): The detections from GetDetections, if it was requested. + objects (List[PointCloudObject]|None): the object point clouds from GetObjectPointClouds, if it was requested. + extra (dict): A catch all structure, usually for metadata, that a module writer might want to return. Default empty. + + Returns: + None + """ + self.image = image + self.classifications = classifications + self.detections = detections + self.objects = objects + self.extra = extra + + +class Vision(ServiceBase): + """ + Vision represents a Vision service. + + This acts as an abstract base class for any drivers representing specific + vision implementations. This cannot be used on its own. If the ``__init__()`` function is + overridden, it must call the ``super().__init__()`` function. + + For more information, see `Computer Vision service `_. + """ + + API: Final = API( # pyright: ignore [reportIncompatibleVariableOverride] + RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_SERVICE, "vision" + ) + + Properties: "TypeAlias" = GetPropertiesResponse + """ + Properties is a class that states what features are supported on the associated vision service. + Currently, these are the following properties: + - classifications_supported (bool): GetClassifications and GetClassificationsFromCamera are implemented. + - detections_supported (bool): GetDetections and GetDetectionsFromCamera are implemented. + - object_point_clouds_supported (bool): GetObjectPointClouds is implemented. + """ + + @abc.abstractmethod + async def capture_all_from_camera( + self, + camera_name: str, + return_image: bool = False, + return_classifications: bool = False, + return_detections: bool = False, + return_object_point_clouds: bool = False, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> CaptureAllResult: + """Get the next image, detections, classifications, and objects all together, + given a camera name. Used for visualization. + + :: + + my_detector = VisionClient.from_robot(machine, "my_detector") + + # Get the captured data for a camera + result = await my_detector.capture_all_from_camera( + "my_camera", + return_image=True, + return_detections=True, + ) + image = result.image + detections = result.detections + + Args: + camera_name (str): The name of the camera to use for detection + return_image (bool): Ask the vision service to return the camera's latest image + return_classifications (bool): Ask the vision service to return its latest classifications + return_detections (bool): Ask the vision service to return its latest detections + return_object_point_clouds (bool): Ask the vision service to return its latest 3D segmentations + + Returns: + vision.CaptureAllResult: A class that stores all potential returns from the vision service. + It can return the image from the camera along with its associated detections, classifications, + and objects, as well as any extra info the model may provide. + + For more information, see `Computer Vision service `_. + """ + ... + + @abc.abstractmethod + async def get_detections_from_camera( + self, + camera_name: str, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> List[Detection]: + """Get a list of detections in the next image given a camera and a detector + + :: + + my_detector = VisionClient.from_robot(robot=machine, "my_detector") + + # Get detections for the next image from the specified camera + detections = await my_detector.get_detections_from_camera("my_camera") + + Args: + camera_name (str): The name of the camera to use for detection + + Raises: + ViamError: Raised if given an image without a specified width and height + + Returns: + List[viam.proto.service.vision.Detection]: A list of 2D bounding boxes, their labels, and the + confidence score of the labels, around the found objects in the next 2D image + from the given camera, with the given detector applied to it. + + For more information, see `Computer Vision service `_. + """ + ... + + @abc.abstractmethod + async def get_detections( + self, + image: ViamImage, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> List[Detection]: + """Get a list of detections in the given image using the specified detector + + :: + + my_camera = Camera.from_robot(robot=machine, "my_camera") + my_detector = VisionClient.from_robot(robot=machine, "my_detector") + + # Get an image from the camera + img = await my_camera.get_image() + + # Get detections for that image + detections = await my_detector.get_detections(img) + + Args: + image (ViamImage): The image to get detections for + + Raises: + ViamError: Raised if given an image without a specified width and height + + Returns: + List[viam.proto.service.vision.Detection]: A list of 2D bounding boxes, their labels, and the + confidence score of the labels, around the found objects in the next 2D image + from the given camera, with the given detector applied to it. + + For more information, see `Computer Vision service `_. + """ + ... + + @abc.abstractmethod + async def get_classifications_from_camera( + self, + camera_name: str, + count: int, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> List[Classification]: + """Get a list of classifications in the next image given a camera and a classifier + + :: + + my_classifier = VisionClient.from_robot(robot=machine, "my_classifier") + + # Get the 2 classifications with the highest confidence scores for the next image from the camera + classifications = await my_classifier.get_classifications_from_camera( + "my_camera", 2) + + Args: + camera_name (str): The name of the camera to use for detection + count (int): The number of classifications desired + + returns: + List[viam.proto.service.vision.Classification]: The list of Classifications + + For more information, see `Computer Vision service `_. + """ + ... + + @abc.abstractmethod + async def get_classifications( + self, + image: ViamImage, + count: int, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> List[Classification]: + """Get a list of classifications in the given image using the specified classifier + + :: + + my_camera = Camera.from_robot(robot=machine, "my_camera") + my_classifier = VisionClient.from_robot(robot=machine, "my_classifier") + + # Get an image from the camera + img = await my_camera.get_image() + + # Get the 2 classifications with the highest confidence scores for the image + classifications = await my_classifier.get_classifications(img, 2) + + Args: + image (ViamImage): The image to get detections for + count (int): The number of classifications desired + + Returns: + List[viam.proto.service.vision.Classification]: The list of Classifications + + For more information, see `Computer Vision service `_. + """ + ... + + @abc.abstractmethod + async def get_object_point_clouds( + self, + camera_name: str, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> List[PointCloudObject]: + """ + Returns a list of the 3D point cloud objects and associated metadata in the latest + picture obtained from the specified 3D camera (using the specified segmenter). + + To deserialize the returned information into a numpy array, use the Open3D library. + + :: + + import numpy as np + import open3d as o3d + + my_segmenter = VisionClient.from_robot(robot=machine, "my_segmenter") + # Get the objects from the camera output + objects = await my_segmenter.get_object_point_clouds("my_camera") + # write the first object point cloud into a temporary file + with open("/tmp/pointcloud_data.pcd", "wb") as f: + f.write(objects[0].point_cloud) + pcd = o3d.io.read_point_cloud("/tmp/pointcloud_data.pcd") + points = np.asarray(pcd.points) + + Args: + camera_name (str): The name of the camera + + Returns: + List[viam.proto.common.PointCloudObject]: The pointcloud objects with metadata + + For more information, see `Computer Vision service `_. + """ + ... + + @abc.abstractmethod + async def get_properties( + self, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> Properties: + """ + Get info about what vision methods the vision service provides. Currently returns boolean values that + state whether the service implements the classification, detection, and/or 3D object segmentation methods. + + :: + + my_detector = VisionClient.from_robot(robot=machine, "my_detector") + properties = await my_detector.get_properties() + detections_supported = properties.detections_supported + classifications_supported = properties.classifications_supported + + Returns: + Properties: The properties of the vision service + + For more information, see `Computer Vision service `_. + """ + ... diff --git a/src/viam/sessions_client.py b/src/viam/sessions_client.py new file mode 100644 index 000000000..7228c2e41 --- /dev/null +++ b/src/viam/sessions_client.py @@ -0,0 +1,245 @@ +import asyncio +import importlib +import pkgutil +import sys +from copy import deepcopy +from datetime import timedelta +from enum import IntEnum +from threading import Lock, Thread +from typing import MutableMapping, Optional + +from grpclib import Status +from grpclib.client import Channel +from grpclib.events import RecvTrailingMetadata, SendRequest, listen +from grpclib.exceptions import GRPCError, StreamTerminatedError +from grpclib.metadata import _MetadataLike + +from viam import logging +from viam.gen.common.v1.common_pb2 import safety_heartbeat_monitored +from viam.proto.robot import RobotServiceStub, SendSessionHeartbeatRequest, StartSessionRequest, StartSessionResponse +from viam.rpc.dial import DialOptions, dial + +LOGGER = logging.getLogger(__name__) +SESSION_METADATA_KEY = "viam-sid" + + +class _SupportedState(IntEnum): + UNKNOWN = 0 + TRUE = 1 + FALSE = 2 + + +class SessionsClient: + """ + A Session allows a client to express that it is actively connected and + supports stopping actuating components when it's not. + """ + + channel: Channel + client: RobotServiceStub + _address: str # direct dial address, when using webRTC this is the local socket rather than a robot address + _robot_address: Optional[str] # the actual machine address on app.viam.com. important for creating a sessions client on Windows + _dial_options: DialOptions + _disabled: bool + _lock: Lock + _current_id: str + _heartbeat_interval: Optional[timedelta] + _supported: _SupportedState + _thread: Optional[Thread] + + _HEARTBEAT_MONITORED_METHODS: MutableMapping[str, bool] = {} + + def __init__( + self, + channel: Channel, + direct_dial_address: str, + dial_options: Optional[DialOptions], + *, + disabled: bool = False, + robot_addr: Optional[str] = None, + ): + self.channel = channel + self.client = RobotServiceStub(channel) + self._address = direct_dial_address + self._robot_address = robot_addr + self._disabled = disabled + self._dial_options = deepcopy(dial_options) if dial_options is not None else DialOptions() + if sys.platform != "win32" and sys.platform != "cygwin": + self._dial_options.disable_webrtc = True + self._lock = Lock() + self._current_id = "" + self._heartbeat_interval = None + self._supported = _SupportedState.UNKNOWN + self._thread = None + + listen(self.channel, SendRequest, self._send_request) + listen(self.channel, RecvTrailingMetadata, self._recv_trailers) + + def reset(self): + with self._lock: + self._reset() + + def _reset(self): + LOGGER.debug("resetting session") + self._supported = _SupportedState.UNKNOWN + self._current_id = "" + self._heartbeat_interval = None + if self._thread is not None: + try: + self._thread.join(timeout=1) + except RuntimeError: + LOGGER.debug("failed to join session heartbeat thread") + self._thread = None + + async def _send_request(self, event: SendRequest): + if self._disabled: + return + + if not self._is_safety_heartbeat_monitored(event.method_name): + return + + event.metadata.update(await self.metadata) + + async def _recv_trailers(self, event: RecvTrailingMetadata): + if event.status == Status.INVALID_ARGUMENT and event.status_message == "SESSION_EXPIRED": + LOGGER.debug("Session expired") + self.reset() + + @property + async def metadata(self) -> _MetadataLike: + with self._lock: + if self._disabled or self._supported != _SupportedState.UNKNOWN: + return self._metadata + + request = StartSessionRequest(resume=self._current_id) + try: + response: StartSessionResponse = await self.client.StartSession(request) + except GRPCError as error: + if error.status == Status.UNIMPLEMENTED: + with self._lock: + self._reset() + self._supported = _SupportedState.FALSE + return self._metadata + else: + raise + + if response is None: + raise GRPCError(status=Status.INTERNAL, message="Expected response to start session") + + if response.heartbeat_window is None: + raise GRPCError(status=Status.INTERNAL, message="Expected heartbeat window in response to start session") + + with self._lock: + self._supported = _SupportedState.TRUE + self._heartbeat_interval = response.heartbeat_window.ToTimedelta() + self._current_id = response.id + + # tick once to ensure heartbeats are supported + await self._heartbeat_tick(self.client) + + with self._lock: + if self._thread is not None: + self._reset() + if self._supported == _SupportedState.TRUE: + # We send heartbeats faster than the interval window to + # ensure that we don't fall outside of it and expire the session. + wait = self._heartbeat_interval.total_seconds() / 5 + + self._thread = Thread( + name="heartbeat-thread", + target=asyncio.run, + args=(self._heartbeat_process(wait),), + daemon=True, + ) + self._thread.start() + + return self._metadata + + async def _heartbeat_tick(self, client: RobotServiceStub): + with self._lock: + if not self._current_id: + LOGGER.debug("Failed to send heartbeat, session client reset") + return + request = SendSessionHeartbeatRequest(id=self._current_id) + + try: + await client.SendSessionHeartbeat(request) + except (GRPCError, StreamTerminatedError): + LOGGER.debug("Heartbeat terminated", exc_info=True) + self.reset() + else: + LOGGER.debug("Sent heartbeat successfully") + + def _get_local_addr(self) -> str: + if sys.platform != "win32" and sys.platform != "cygwin": + # if we're not on windows, we want the direct dial address + return self._address + + # return `robot_address` if it exists, otherwise fallback + # when using TCP (i.e., on Windows), we need to create a connection to the actual + # robot address for a sessions client to maintain connectivity successfully + return self._robot_address if self._robot_address is not None else self._address + + async def _heartbeat_process(self, wait: float): + addr = self._get_local_addr() + channel = await dial(address=addr, options=self._dial_options) + client = RobotServiceStub(channel.channel) + while True: + with self._lock: + if self._supported != _SupportedState.TRUE: + return + await self._heartbeat_tick(client) + await asyncio.sleep(wait) + + @property + def _metadata(self) -> _MetadataLike: + if self._supported == _SupportedState.TRUE and self._current_id != "": + return {SESSION_METADATA_KEY: self._current_id} + + return {} + + def _is_safety_heartbeat_monitored(self, method: str) -> bool: + if method in self._HEARTBEAT_MONITORED_METHODS: + return self._HEARTBEAT_MONITORED_METHODS[method] + + parts = method.split("/") + if len(parts) != 3: + self._HEARTBEAT_MONITORED_METHODS[method] = False + return False + service_path = parts[1] + method_name = parts[2] + + parts = service_path.split(".") + if len(parts) < 5: + self._HEARTBEAT_MONITORED_METHODS[method] = False + return False + if parts[0] != "viam": + self._HEARTBEAT_MONITORED_METHODS[method] = False + return False + resource_type = parts[1] + resource_subtype = parts[2] + version = parts[3] + service_name = parts[4] + try: + module = importlib.import_module(f"viam.gen.{resource_type}.{resource_subtype}.{version}") + submods = pkgutil.iter_modules(module.__path__) + for mod in submods: + if "_pb2" in mod.name: + submod = getattr(module, mod.name) + DESCRIPTOR = getattr(submod, "DESCRIPTOR") + for service in DESCRIPTOR.services_by_name.values(): + if service.name == service_name: + for method_actual in service.methods: + if method_actual.name == method_name: + options = method_actual.GetOptions() + if options.HasExtension(safety_heartbeat_monitored): + is_monitored = options.Extensions[safety_heartbeat_monitored] + self._HEARTBEAT_MONITORED_METHODS[method] = is_monitored + return is_monitored + self._HEARTBEAT_MONITORED_METHODS[method] = False + return False + self._HEARTBEAT_MONITORED_METHODS[method] = False + return False + except Exception: + self._HEARTBEAT_MONITORED_METHODS[method] = False + return False diff --git a/src/viam/streams.py b/src/viam/streams.py new file mode 100644 index 000000000..2daae42e1 --- /dev/null +++ b/src/viam/streams.py @@ -0,0 +1,44 @@ +import sys + +if sys.version_info >= (3, 9): + from collections.abc import AsyncIterator +else: + from typing import AsyncIterator + +from typing import Protocol, TypeVar + +StreamType = TypeVar("StreamType", covariant=True) + + +class Stream(Protocol[StreamType]): + async def next(self) -> StreamType: ... + + def __aiter__(self) -> AsyncIterator: + return self + + async def __anext__(self) -> StreamType: + return await self.next() + + +class StreamReader(Protocol[StreamType]): + async def read(self) -> StreamType: ... + + +class StreamSource(Protocol[StreamType]): + async def stream(self) -> Stream[StreamType]: ... + + +class StreamWithIterator(Stream[StreamType]): + _stream: AsyncIterator[StreamType] + + def __init__(self, stream: AsyncIterator[StreamType]): + self._stream = stream + + async def next(self) -> StreamType: + return await self._stream.__anext__() + + def __aiter__(self): + return self._stream + + async def __anext__(self) -> StreamType: + return await self._stream.__anext__() diff --git a/src/viam/utils.py b/src/viam/utils.py index 381b30a80..e8fa4e1b8 100644 --- a/src/viam/utils.py +++ b/src/viam/utils.py @@ -1,15 +1,42 @@ -from typing import Any, Dict, List, Mapping, SupportsFloat, Type, TypeVar +import asyncio +import contextvars +import functools +import sys +import threading +from datetime import datetime +from typing import Any, Callable, Dict, List, Mapping, Optional, SupportsBytes, SupportsFloat, Type, TypeVar, Union from google.protobuf.json_format import MessageToDict, ParseDict from google.protobuf.message import Message from google.protobuf.struct_pb2 import ListValue, Struct, Value +from google.protobuf.timestamp_pb2 import Timestamp -from viam.components.component_base import ComponentBase -from viam.proto.common import GeoPoint, Orientation, ResourceName, Vector3 -from viam.registry import Registry +from viam.proto.app.data import CaptureInterval, Filter, TagsFilter +from viam.proto.common import Geometry, GeoPoint, GetGeometriesRequest, GetGeometriesResponse, Orientation, ResourceName, Vector3 +from viam.resource.base import ResourceBase +from viam.resource.registry import Registry +from viam.resource.rpc_client_base import ResourceRPCClientBase +from viam.resource.types import API, SupportsGetGeometries +if sys.version_info >= (3, 9): + from collections.abc import Callable +else: + from typing import Callable -def primitive_to_value(v: Any) -> Value: +if sys.version_info >= (3, 10): + from typing import ParamSpec +else: + from typing_extensions import ParamSpec + + +ValueTypes = Union[bool, SupportsBytes, SupportsFloat, List, Mapping, str, None] +"""Types that can be encoded into a protobuf `Value`""" + +SensorReading = Union[ValueTypes, Vector3, GeoPoint, Orientation] +"""Types that can be returned from a sensor""" + + +def primitive_to_value(v: ValueTypes) -> Value: """ Create a new google.protobuf.struct_pb2.Value Supports primitive types of @@ -22,7 +49,7 @@ def primitive_to_value(v: Any) -> Value: - Bytes Args: - v (Any): object to convert to a Value + v (ValueTypes): object to convert to a Value Raises: TypeError: If the object cannot be converted @@ -40,7 +67,7 @@ def primitive_to_value(v: Any) -> Value: return Value(string_value=v) if isinstance(v, Dict): sv: Dict[str, Value] = {} - for (key, value) in v.items(): + for key, value in v.items(): if not isinstance(key, str): raise TypeError(f"Invalid UTF-8 in string: {key}") sv[key] = primitive_to_value(value) @@ -57,7 +84,7 @@ def primitive_to_value(v: Any) -> Value: raise TypeError(f"Invalid type {type(v)}") -def value_to_primitive(value: Value) -> Any: +def value_to_primitive(value: Value) -> ValueTypes: if value.HasField("list_value"): return [value_to_primitive(v) for v in value.list_value.values] if value.HasField("struct_value"): @@ -73,25 +100,14 @@ def value_to_primitive(value: Value) -> Any: return None -def resource_names_for_component(component: ComponentBase) -> List[ResourceName]: +def resource_names_for_resource(resource: ResourceBase) -> List[ResourceName]: rns: List[ResourceName] = [] - for klass in component.__class__.mro(): - component_type = "" - for registration in Registry.REGISTERED_COMPONENTS.values(): - if klass is registration.component_type: - component_type = registration.name - - if not component_type: - class_name = str(klass) - if "viam.components" not in class_name: - continue - if "ComponentBase" in class_name: - continue - - component_type = class_name.split("viam.components.")[1].split(".")[0] - - rns.append(ResourceName(namespace="rdk", type="component", subtype=component_type, name=component.name)) - break + + for klass in resource.__class__.mro(): + for registration in Registry.REGISTERED_APIS().values(): + if klass is registration.resource_type: + api: API = registration.resource_type.API + rns.append(ResourceName(namespace=api.namespace, type=api.resource_type, subtype=api.resource_subtype, name=resource.name)) return rns @@ -100,34 +116,70 @@ def message_to_struct(message: Message) -> Struct: struct.update( MessageToDict( message, - including_default_value_fields=True, - preserving_proto_field_name=True, + True, + True, ), ) return struct -T = TypeVar("T", bound=Message) +_T = TypeVar("_T", bound=Message) -def struct_to_message(struct: Struct, message_type: Type[T]) -> T: +def struct_to_message(struct: Struct, message_type: Type[_T]) -> _T: dct = struct_to_dict(struct) return ParseDict(dct, message_type()) -def dict_to_struct(obj: Mapping[str, Any]) -> Struct: +def dict_to_struct(obj: Optional[Mapping[str, ValueTypes]]) -> Struct: + def _convert(v: ValueTypes) -> Any: + if isinstance(v, bool): + return v + if isinstance(v, SupportsFloat): + return float(v) + if isinstance(v, SupportsBytes): + return bytes(v) + if isinstance(v, List): + return [_convert(vv) for vv in v] + if isinstance(v, Mapping): + return {k: _convert(vv) for (k, vv) in v.items()} + return v + + if obj is None: + obj = {} struct = Struct() - struct.update(obj) + struct.update({k: _convert(v) for (k, v) in obj.items()}) return struct -def struct_to_dict(struct: Struct) -> Dict[str, Any]: +def struct_to_dict(struct: Struct) -> Dict[str, ValueTypes]: return {key: value_to_primitive(value) for (key, value) in struct.fields.items()} -def sensor_readings_native_to_value(readings: Mapping[str, Any]) -> Mapping[str, Any]: +def datetime_to_timestamp(dt: Optional[datetime]) -> Optional[Timestamp]: + if dt is None: + return None + timestamp = Timestamp() + timestamp.FromDatetime(dt) + return timestamp + + +async def get_geometries( + client: SupportsGetGeometries, + name: str, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + metadata: ResourceRPCClientBase.Metadata = ResourceRPCClientBase.Metadata(), +) -> List[Geometry]: + md = metadata.proto + request = GetGeometriesRequest(name=name, extra=dict_to_struct(extra)) + response: GetGeometriesResponse = await client.GetGeometries(request, timeout=timeout, metadata=md) + return [geometry for geometry in response.geometries] + + +def sensor_readings_native_to_value(readings: Mapping[str, Any]) -> Mapping[str, Value]: prim_readings = dict(readings) - for (key, reading) in readings.items(): + for key, reading in readings.items(): if isinstance(reading, Vector3): prim_readings[key] = {"x": reading.x, "y": reading.y, "z": reading.z, "_type": "vector3"} elif isinstance(reading, GeoPoint): @@ -143,9 +195,9 @@ def sensor_readings_native_to_value(readings: Mapping[str, Any]) -> Mapping[str, return {key: primitive_to_value(value) for (key, value) in prim_readings.items()} -def sensor_readings_value_to_native(readings: Mapping[str, Value]) -> Mapping[str, Any]: - prim_readings = {key: value_to_primitive(value) for (key, value) in readings.items()} - for (key, reading) in prim_readings.items(): +def sensor_readings_value_to_native(readings: Mapping[str, Value]) -> Mapping[str, SensorReading]: + prim_readings: Dict[str, Any] = {key: value_to_primitive(value) for (key, value) in readings.items()} + for key, reading in prim_readings.items(): if isinstance(reading, Mapping): kind = reading.get("_type", "") if kind == "angular_velocity": @@ -157,3 +209,157 @@ def sensor_readings_value_to_native(readings: Mapping[str, Value]) -> Mapping[st elif kind == "orientation_vector_degrees": prim_readings[key] = Orientation(o_x=reading["ox"], o_y=reading["oy"], o_z=reading["oz"], theta=reading["theta"]) return prim_readings + + +class PointerCounter: + def __init__(self) -> None: + self._event = asyncio.Event() + self._lock = threading.Lock() + self._count = 0 + self._event.set() + + def increment(self) -> int: + self._lock.acquire() + self._count += 1 + self._event.clear() + self._lock.release() + return self._count + + def decrement(self) -> int: + self._lock.acquire() + assert self._count > 0, "Pointer count cannot go below zero" + self._count -= 1 + if self._count == 0: + self._event.set() + self._lock.release() + return self._count + + async def wait(self) -> None: + await self._event.wait() + + @property + def count(self) -> int: + with self._lock: + return self._count + + +_P = ParamSpec("_P") +_R = TypeVar("_R") + + +async def to_thread(func: Callable[_P, _R], *args: _P.args, **kwargs: _P.kwargs) -> _R: + """Asynchronously run a function in a separate thread. + + This is a copy of the function defined in the python source, + which is only available in python >= 3.9. + + See: https://github.com/python/cpython/blob/main/Lib/asyncio/threads.py + """ + if sys.version_info >= (3, 9): + return await asyncio.to_thread(func, *args, **kwargs) + loop = asyncio.events.get_running_loop() + ctx = contextvars.copy_context() + func_call = functools.partial(ctx.run, func, *args, **kwargs) + return await loop.run_in_executor(None, func_call) # type: ignore + + +def from_dm_from_extra(extra: Optional[Dict[str, Any]]) -> bool: + """Used in modular filter components to get the 'fromDataManagement' value from an extra map.""" + if extra is None: + return False + + return bool(extra.get("fromDataManagement", False)) + + +def create_filter( + component_name: Optional[str] = None, + component_type: Optional[str] = None, + method: Optional[str] = None, + robot_name: Optional[str] = None, + robot_id: Optional[str] = None, + part_name: Optional[str] = None, + part_id: Optional[str] = None, + location_ids: Optional[List[str]] = None, + organization_ids: Optional[List[str]] = None, + mime_type: Optional[List[str]] = None, + start_time: Optional[datetime] = None, + end_time: Optional[datetime] = None, + tags: Optional[List[str]] = None, + bbox_labels: Optional[List[str]] = None, + dataset_id: Optional[str] = None, +) -> Filter: + """Create a `Filter`. + + Args: + component_name (Optional[str]): Optional name of the component that captured the data being filtered (for example, "left_motor"). + component_type (Optional[str]): Optional type of the componenet that captured the data being filtered (for example, "motor"). + method (Optional[str]): Optional name of the method used to capture the data being filtered (for example, "IsPowered"). + robot_name (Optional[str]): Optional name of the robot associated with the data being filtered (for example, "viam_rover_1"). + robot_id (Optional[str]): Optional ID of the robot associated with the data being filtered. + part_name (Optional[str]): Optional name of the system part associated with the data being filtered (for example, + "viam_rover_1-main"). + part_id (Optional[str]): Optional ID of the system part associated with the data being filtered. + location_ids (Optional[List[str]]): Optional list of location IDs associated with the data being filtered. + organization_ids (Optional[List[str]]): Optional list of organization IDs associated with the data being filtered. + mime_type (Optional[List[str]]): Optional mime type of data being filtered (for example, "image/png"). + start_time (Optional[datetime.datetime]): Optional start time of an interval to filter data by. + end_time (Optional[datetime.datetime]): Optional end time of an interval to filter data by. + tags (Optional[List[str]]): Optional list of tags attached to the data being filtered (for example, ["test"]). + bbox_labels (Optional[List[str]]): Optional list of bounding box labels attached to the data being filtered (for example, ["square", + "circle"]). + dataset_id (Optional[str]): Optional ID of dataset associated with data being filtered + + Returns: + viam.proto.app.data.Filter: The `Filter` object. + """ + return Filter( + component_name=component_name if component_name else "", + component_type=component_type if component_type else "", + method=method if method else "", + robot_name=robot_name if robot_name else "", + robot_id=robot_id if robot_id else "", + part_name=part_name if part_name else "", + part_id=part_id if part_id else "", + location_ids=location_ids, + organization_ids=organization_ids, + mime_type=mime_type, + interval=( + CaptureInterval( + start=datetime_to_timestamp(start_time), + end=datetime_to_timestamp(end_time), + ) + ) + if start_time or end_time + else None, + tags_filter=TagsFilter(tags=tags), + bbox_labels=bbox_labels, + dataset_id=dataset_id if dataset_id else "", + ) + + +def _alias_param(param_name: str, param_alias: str) -> Callable: + """ + Decorator for aliasing a param in a function. Intended for providing backwards compatibility on params with name changes. + + Args: + param_name: name of param in function to alias + param_alias: alias that can be used for this param + Returns: + The input function, plus param alias. + """ + + def decorator(func: Callable): + @functools.wraps(func) + def wrapper(*args, **kwargs): + alias_param_value = kwargs.get(param_alias) + if alias_param_value: + # Only use alias value if param is not given. + if not kwargs.get(param_name): + kwargs[param_name] = alias_param_value + del kwargs[param_alias] + result = func(*args, **kwargs) + return result + + return wrapper + + return decorator diff --git a/src/viam/version_metadata.py b/src/viam/version_metadata.py new file mode 100644 index 000000000..38cedb9c9 --- /dev/null +++ b/src/viam/version_metadata.py @@ -0,0 +1,4 @@ +__version__ = "0.45.2" + +API_VERSION = "v0.1.433" +SDK_VERSION = __version__ diff --git a/tests/__init__.py b/tests/__init__.py index e69de29bb..b2bb53b5a 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,5 @@ +import pytest + + +def loose_approx(val): + return pytest.approx(val, rel=val * 1e-2) diff --git a/tests/data/fakeDM.vnd.viam.dep b/tests/data/fakeDM.vnd.viam.dep new file mode 100644 index 000000000..9a6793779 Binary files /dev/null and b/tests/data/fakeDM.vnd.viam.dep differ diff --git a/tests/mocks/components.py b/tests/mocks/components.py index a803f9131..16492aef5 100644 --- a/tests/mocks/components.py +++ b/tests/mocks/components.py @@ -1,15 +1,24 @@ +import sys + +if sys.version_info >= (3, 9): + from collections.abc import AsyncIterator +else: + from typing import AsyncIterator + from dataclasses import dataclass -from multiprocessing import Queue +from datetime import datetime, timedelta from secrets import choice -from typing import Any, Dict, List, Mapping, Optional, Tuple, Union +from typing import Any, Dict, List, Mapping, Optional, Tuple -from PIL import Image +from google.protobuf.timestamp_pb2 import Timestamp -from viam.components.arm import Arm, JointPositions +from viam.components.arm import Arm, JointPositions, KinematicsFileFormat +from viam.components.audio_input import AudioInput from viam.components.base import Base -from viam.components.board import Board -from viam.components.board.board import PostProcessor -from viam.components.camera import Camera, IntrinsicParameters, DistortionParameters +from viam.components.board import Board, Tick +from viam.components.button import Button +from viam.components.camera import Camera, DistortionParameters, IntrinsicParameters +from viam.components.encoder import Encoder from viam.components.gantry import Gantry from viam.components.generic import Generic as GenericComponent from viam.components.gripper import Gripper @@ -17,64 +26,128 @@ from viam.components.motor import Motor from viam.components.movement_sensor import MovementSensor from viam.components.pose_tracker import PoseTracker +from viam.components.power_sensor import PowerSensor from viam.components.sensor import Sensor from viam.components.servo import Servo -from viam.components.types import CameraMimeType, RawImage -from viam.errors import ComponentNotFoundError -from viam.proto.common import ( - AnalogStatus, - BoardStatus, - DigitalInterruptStatus, - GeoPoint, - Orientation, - Pose, - PoseInFrame, - Vector3, - WorldState, -) +from viam.components.switch import Switch +from viam.errors import ResourceNotFoundError +from viam.media.audio import Audio, AudioStream +from viam.media.video import CameraMimeType, NamedImage, ViamImage +from viam.proto.common import Capsule, Geometry, GeoPoint, Orientation, Pose, PoseInFrame, ResponseMetadata, Sphere, Vector3 +from viam.proto.component.audioinput import AudioChunk, AudioChunkInfo, SampleFormat +from viam.proto.component.board import PowerMode +from viam.proto.component.encoder import PositionType +from viam.streams import StreamWithIterator +from viam.utils import SensorReading, ValueTypes + +GEOMETRIES = [ + Geometry(center=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20), sphere=Sphere(radius_mm=2)), + Geometry(center=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20), capsule=Capsule(radius_mm=3, length_mm=8)), +] class MockArm(Arm): def __init__(self, name: str): - self.position = Pose( - x=1, - y=2, - z=3, - o_x=2, - o_y=3, - o_z=4, - theta=20, - ) + self.position = Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20) self.joint_positions = JointPositions(values=[0, 0, 0, 0, 0, 0]) self.is_stopped = True + self.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02") + self.geometries = GEOMETRIES self.extra = None + self.timeout: Optional[float] = None super().__init__(name) - async def get_end_position(self, extra: Optional[Dict[str, Any]] = None) -> Pose: + async def get_end_position(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Pose: self.extra = extra + self.timeout = timeout return self.position - async def move_to_position(self, pose: Pose, world_state: Optional[WorldState] = None, extra: Optional[Dict[str, Any]] = None): + async def move_to_position( + self, + pose: Pose, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): self.position = pose self.is_stopped = False self.extra = extra + self.timeout = timeout - async def get_joint_positions(self, extra: Optional[Dict[str, Any]] = None) -> JointPositions: + async def get_joint_positions( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> JointPositions: self.extra = extra + self.timeout = timeout return self.joint_positions - async def move_to_joint_positions(self, positions: JointPositions, extra: Optional[Dict[str, Any]] = None): + async def move_to_joint_positions( + self, positions: JointPositions, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ): self.joint_positions = positions self.is_stopped = False self.extra = extra + self.timeout = timeout - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): self.is_stopped = True self.extra = extra + self.timeout = timeout async def is_moving(self) -> bool: return not self.is_stopped + async def get_kinematics( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None + ) -> Tuple[KinematicsFileFormat.ValueType, bytes]: + self.extra = extra + self.timeout = timeout + return self.kinematics + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + + +class MockAudioInput(AudioInput): + def __init__(self, name: str, properties: AudioInput.Properties): + super().__init__(name) + self.geometries = GEOMETRIES + self.properties = properties + self.timeout: Optional[float] = None + + async def stream(self, *, timeout: Optional[float] = None, **kwargs) -> AudioStream: + async def read() -> AsyncIterator[Audio]: + for i in range(10): + yield Audio( + AudioChunkInfo( + sample_format=SampleFormat.SAMPLE_FORMAT_FLOAT32_INTERLEAVED, + channels=self.properties.channel_count, + sampling_rate=self.properties.sample_rate, + ), + AudioChunk(data=f"{i}".encode("utf-8"), length=182), + ) + + self.timeout = timeout + return StreamWithIterator(read()) + + async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> AudioInput.Properties: + self.timeout = timeout + return self.properties + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + class MockBase(Base): def __init__(self, name: str): @@ -85,10 +158,15 @@ def __init__(self, name: str): self.angular_pwr = Vector3(x=0, y=0, z=0) self.linear_vel = Vector3(x=0, y=0, z=0) self.angular_vel = Vector3(x=0, y=0, z=0) + self.geometries = GEOMETRIES self.extra: Optional[Dict[str, Any]] = None + self.timeout: Optional[float] = None + self.props = Base.Properties(1.0, 2.0, 3.0) super().__init__(name) - async def move_straight(self, distance: int, velocity: float, extra: Optional[Dict[str, Any]] = None): + async def move_straight( + self, distance: int, velocity: float, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ): if distance == 0 or velocity == 0: return await self.stop() @@ -99,31 +177,11 @@ async def move_straight(self, distance: int, velocity: float, extra: Optional[Di self.stopped = False self.extra = extra + self.timeout = timeout - async def move_arc( - self, - distance: int, - velocity: float, - angle: float, - extra: Optional[Dict[str, Any]] = None, + async def spin( + self, angle: float, velocity: float, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs ): - if distance == 0: - return await self.spin(angle, velocity) - - if velocity == 0: - return await self.stop() - - if velocity > 0: - self.position += distance - self.angle += angle - else: - self.position -= distance - self.angle -= angle - - self.stopped = False - self.extra = extra - - async def spin(self, angle: float, velocity: float, extra: Optional[Dict[str, Any]] = None): if angle == 0 or velocity == 0: return await self.stop() @@ -134,58 +192,69 @@ async def spin(self, angle: float, velocity: float, extra: Optional[Dict[str, An self.stopped = False self.extra = extra + self.timeout = timeout - async def set_velocity(self, linear: Vector3, angular: Vector3, extra: Optional[Dict[str, Any]] = None): + async def set_velocity( + self, linear: Vector3, angular: Vector3, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ): self.linear_vel = linear self.angular_vel = angular self.extra = extra + self.timeout = timeout - async def set_power(self, linear: Vector3, angular: Vector3, extra: Optional[Dict[str, Any]] = None): + async def set_power( + self, linear: Vector3, angular: Vector3, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ): self.linear_pwr = linear self.angular_pwr = angular self.extra = extra - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): self.stopped = True self.extra = extra + self.timeout = timeout async def is_moving(self) -> bool: return not self.stopped + async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> Base.Properties: + self.timeout = timeout + return self.props + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} -class MockAnalogReader(Board.AnalogReader): - def __init__(self, name: str, value: int): - self.value = value + +class MockAnalog(Board.Analog): + def __init__(self, name: str, value: int, min_range: float, max_range: float, step_size: float): + self.value = self.Value(value=value, min_range=min_range, max_range=max_range, step_size=step_size) + self.timeout: Optional[float] = None super().__init__(name) - async def read(self, extra: Optional[Dict[str, Any]] = None) -> int: + async def read(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Board.Analog.Value: self.extra = extra + self.timeout = timeout return self.value + async def write(self, value: int, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): + self.extra = extra + self.timeout = timeout + self.value.value = value + class MockDigitalInterrupt(Board.DigitalInterrupt): def __init__(self, name: str): self.high = False - self.last_tick = 0 - self.num_ticks = 0 - self.callbacks: List[Queue] = [] - self.post_processors: List[PostProcessor] = [] + self.val = 182 super().__init__(name) - async def value(self, extra: Optional[Dict[str, Any]] = None) -> int: - self.extra = extra - return self.num_ticks - - async def tick(self, high: bool, nanos: int): - self.high = high - self.last_tick = nanos - self.num_ticks += 1 - - async def add_callback(self, queue: Queue): - self.callbacks.append(queue) - - async def add_post_processor(self, processor: PostProcessor): - self.post_processors.append(processor) + async def value(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> int: + return self.val class MockGPIOPin(Board.GPIOPin): @@ -193,108 +262,196 @@ def __init__(self, name: str): self.high = False self.pwm = 0.0 self.pwm_freq = 0 + self.timeout: Optional[float] = None super().__init__(name) - async def get(self, extra: Optional[Dict[str, Any]] = None) -> bool: + async def get(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> bool: self.extra = extra + self.timeout = timeout return self.high - async def set(self, high: bool, extra: Optional[Dict[str, Any]] = None): + async def set(self, high: bool, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): self.high = high self.extra = extra + self.timeout = timeout - async def get_pwm(self, extra: Optional[Dict[str, Any]] = None) -> float: + async def get_pwm(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> float: self.extra = extra + self.timeout = timeout return self.pwm - async def set_pwm(self, duty_cycle: float, extra: Optional[Dict[str, Any]] = None): + async def set_pwm(self, duty_cycle: float, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): self.pwm = duty_cycle self.extra = extra + self.timeout = timeout - async def get_pwm_frequency(self, extra: Optional[Dict[str, Any]] = None) -> int: + async def get_pwm_frequency(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> int: self.extra = extra + self.timeout = timeout return self.pwm_freq - async def set_pwm_frequency(self, frequency: int, extra: Optional[Dict[str, Any]] = None): + async def set_pwm_frequency(self, frequency: int, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): self.pwm_freq = frequency self.extra = extra + self.timeout = timeout class MockBoard(Board): def __init__( self, name: str, - analog_readers: Dict[str, Board.AnalogReader], + analogs: Dict[str, Board.Analog], digital_interrupts: Dict[str, Board.DigitalInterrupt], gpio_pins: Dict[str, Board.GPIOPin], ): - self.analog_readers = analog_readers + self.analogs = analogs self.digital_interrupts = digital_interrupts + self.geometries = GEOMETRIES self.gpios = gpio_pins + self.timeout: Optional[float] = None super().__init__(name) - async def analog_reader_by_name(self, name: str) -> Board.AnalogReader: + async def analog_by_name(self, name: str) -> Board.Analog: try: - return self.analog_readers[name] + return self.analogs[name] except KeyError: - raise ComponentNotFoundError("Board.AnalogReader", name) + raise ResourceNotFoundError("Board.Analog", name) async def digital_interrupt_by_name(self, name: str) -> Board.DigitalInterrupt: try: return self.digital_interrupts[name] except KeyError: - raise ComponentNotFoundError("Board.DigitalInterrupt", name) + raise ResourceNotFoundError("Board.DigitalInterrupt", name) async def gpio_pin_by_name(self, name: str) -> Board.GPIOPin: try: return self.gpios[name] except KeyError: - raise ComponentNotFoundError("Board.GPIOPin", name) + raise ResourceNotFoundError("Board.GPIOPin", name) + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries - async def analog_reader_names(self) -> List[str]: - return [key for key in self.analog_readers.keys()] + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} - async def digital_interrupt_names(self) -> List[str]: - return [key for key in self.digital_interrupts.keys()] + async def set_power_mode( + self, mode: PowerMode.ValueType, duration: Optional[timedelta] = None, *, timeout: Optional[float] = None, **kwargs + ): + self.timeout = timeout + self.power_mode = mode + self.power_mode_duration = duration - async def status(self, extra: Optional[Dict[str, Any]] = None) -> BoardStatus: - self.extra = extra - return BoardStatus( - analogs={name: AnalogStatus(value=await analog.read()) for (name, analog) in self.analog_readers.items()}, - digital_interrupts={name: DigitalInterruptStatus(value=await di.value()) for (name, di) in self.digital_interrupts.items()}, - ) + async def stream_ticks(self, interrupts: List[Board.DigitalInterrupt], *, timeout: Optional[float] = None, **kwargs): + async def read() -> AsyncIterator[Tick]: + yield Tick(pin_name=interrupts[0].name, high=True, time=1000) - async def model_attributes(self) -> Board.Attributes: - return Board.Attributes(remote=True) + return StreamWithIterator(read()) class MockCamera(Camera): def __init__(self, name: str): - self.image = Image.new("RGBA", (100, 100), "#AABBCCDD") + self.image = ViamImage(b"data", CameraMimeType.PNG) + self.geometries = GEOMETRIES self.point_cloud = b"THIS IS A POINT CLOUD" + self.extra = None self.props = Camera.Properties( - True, - IntrinsicParameters(width_px=1, height_px=2, focal_x_px=3, focal_y_px=4, center_x_px=5, center_y_px=6), - DistortionParameters(model="no_distortion"), + supports_pcd=False, + intrinsic_parameters=IntrinsicParameters(width_px=1, height_px=2, focal_x_px=3, focal_y_px=4, center_x_px=5, center_y_px=6), + distortion_parameters=DistortionParameters(model="no_distortion"), + mime_types=[CameraMimeType.PNG, CameraMimeType.JPEG], + frame_rate=10.0, ) + self.timeout: Optional[float] = None + ts = Timestamp() + ts.FromDatetime(datetime(1970, 1, 1)) + self.metadata = ResponseMetadata(captured_at=ts) super().__init__(name) - async def get_image(self, mime_type: str = CameraMimeType.PNG) -> Union[Image.Image, RawImage]: - if not CameraMimeType.is_supported(mime_type) or mime_type == CameraMimeType.RAW: - return RawImage( - data=self.image.convert("RGBA").tobytes("raw", "RGBA"), - mime_type=mime_type, - width=self.image.width, - height=self.image.height, - ) - return self.image.copy() + async def get_image( + self, mime_type: str = "", extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> ViamImage: + self.extra = extra + self.timeout = timeout + return self.image - async def get_point_cloud(self) -> Tuple[bytes, str]: - return self.point_cloud, CameraMimeType.PCD.value + async def get_images(self, timeout: Optional[float] = None, **kwargs) -> Tuple[List[NamedImage], ResponseMetadata]: + self.timeout = timeout + return [NamedImage(self.name, self.image.data, self.image.mime_type)], self.metadata - async def get_properties(self) -> Camera.Properties: + async def get_point_cloud( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Tuple[bytes, str]: + self.extra = extra + self.timeout = timeout + return self.point_cloud, CameraMimeType.PCD + + async def get_properties(self, *, timeout: Optional[float] = None, **kwargs) -> Camera.Properties: + self.timeout = timeout return self.props + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + + +class MockEncoder(Encoder): + def __init__(self, name: str): + self.position: float = 0 + self.position_type = PositionType.POSITION_TYPE_TICKS_COUNT + self.geometries = GEOMETRIES + self.extra = None + self.timeout: Optional[float] = None + super().__init__(name) + + async def reset_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + self.position = 0 + self.extra = extra + self.timeout = timeout + + async def get_position( + self, + position_type: Optional[PositionType.ValueType] = None, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[float, PositionType.ValueType]: + self.extra = extra + self.timeout = timeout + return self.position, self.position_type + + async def get_properties( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Encoder.Properties: + self.extra = extra + self.timeout = timeout + return Encoder.Properties(ticks_count_supported=True, angle_degrees_supported=False) + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + class MockGantry(Gantry): def __init__(self, name: str, position: List[float], lengths: List[float]): @@ -302,68 +459,126 @@ def __init__(self, name: str, position: List[float], lengths: List[float]): self.lengths = lengths self.is_stopped = True self.extra = None + self.homed = True + self.speeds = Optional[List[float]] + self.geometries = GEOMETRIES + self.timeout: Optional[float] = None super().__init__(name) - async def get_position(self, extra: Optional[Dict[str, Any]] = None) -> List[float]: + async def get_position(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[float]: self.extra = extra + self.timeout = timeout return self.position async def move_to_position( self, positions: List[float], - world_state: Optional[WorldState] = None, + speeds: List[float], + *, extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, ): self.position = positions + self.speeds = speeds self.is_stopped = False self.extra = extra + self.timeout = timeout + + async def home(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> bool: + self.homed = True + self.extra = extra + self.timeout = timeout + return self.homed - async def get_lengths(self, extra: Optional[Dict[str, Any]] = None) -> List[float]: + async def get_lengths(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[float]: self.extra = extra + self.timeout = timeout return self.lengths - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): self.extra = extra self.is_stopped = True + self.timeout = timeout async def is_moving(self) -> bool: return not self.is_stopped + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + -class MockGeneric(GenericComponent): - async def do_command(self, command: Dict[str, Any]) -> Dict[str, Any]: +class MockGenericComponent(GenericComponent): + timeout: Optional[float] = None + geometries = GEOMETRIES + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + self.timeout = timeout return {key: True for key in command.keys()} class MockGripper(Gripper): def __init__(self, name: str): self.opened = False + self.geometries = GEOMETRIES + self.extra = None self.is_stopped = True + self.timeout: Optional[float] = None super().__init__(name) - async def open(self): + async def open(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): self.opened = True self.is_stopped = False + self.extra = extra + self.timeout = timeout - async def grab(self) -> bool: + async def grab(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> bool: self.opened = False self.is_stopped = False + self.timeout = timeout + self.extra = extra return choice([True, False]) - async def stop(self): + async def stop(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): self.is_stopped = True + self.extra = extra + self.timeout = timeout async def is_moving(self) -> bool: return not self.is_stopped + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + class MockInputController(Controller): def __init__(self, name: str): super().__init__(name) self.events: Dict[Control, Event] = {} self.callbacks: Dict[Control, Dict[EventType, Optional[ControlFunction]]] = {} + self.geometries = GEOMETRIES + self.timeout: Optional[float] = None + self.extra = None + self.reg_extra = None - async def get_controls(self) -> List[Control]: + async def get_controls(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> List[Control]: + self.extra = extra + self.timeout = timeout return [ Control.ABSOLUTE_X, Control.ABSOLUTE_Y, @@ -388,70 +603,176 @@ async def get_controls(self) -> List[Control]: Control.BUTTON_E_STOP, ] - async def get_events(self) -> Dict[Control, Event]: + async def get_events( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Dict[Control, Event]: + self.extra = extra + self.timeout = timeout return self.events - def register_control_callback(self, control: Control, triggers: List[EventType], function: Optional[ControlFunction]): + def register_control_callback( + self, + control: Control, + triggers: List[EventType], + function: Optional[ControlFunction], + extra: Optional[Dict[str, Any]] = None, + **kwargs, + ): self.callbacks[control] = {trigger: function for trigger in triggers} + self.reg_extra = extra - async def trigger_event(self, event: Event): + async def trigger_event(self, event: Event, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs): self.events[event.control] = event + self.extra = extra + self.timeout = timeout callback = self.callbacks.get(event.control, {}).get(event.event) if callback: callback(event) + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + class MockMotor(Motor): def __init__(self, name: str): self.position: float = 0 self.power = 0 self.powered = False + self.geometries = GEOMETRIES self.extra = None + self.timeout: Optional[float] = None super().__init__(name) - async def set_power(self, power: float, extra: Optional[Dict[str, Any]] = None): + async def set_power( + self, + power: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): self.power = power self.powered = power != 0 self.extra = extra + self.timeout = timeout - async def go_for(self, rpm: float, revolutions: float, extra: Optional[Dict[str, Any]] = None): + async def go_for( + self, + rpm: float, + revolutions: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): if rpm > 0: self.position += revolutions if rpm < 0: self.position -= revolutions self.powered = False self.extra = extra + self.timeout = timeout - async def go_to(self, rpm: float, position_revolutions: float, extra: Optional[Dict[str, Any]] = None): + async def go_to( + self, + rpm: float, + position_revolutions: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): if rpm != 0: self.position = position_revolutions self.powered = False self.extra = extra + self.timeout = timeout - async def reset_zero_position(self, offset: float, extra: Optional[Dict[str, Any]] = None): + async def set_rpm( + self, + rpm: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): + self.powered = True + self.extra = extra + self.timeout = timeout + + async def reset_zero_position( + self, + offset: float, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): self.offset = offset self.powered = False self.extra = extra + self.timeout = timeout - async def get_position(self, extra: Optional[Dict[str, Any]] = None) -> float: + async def get_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> float: self.extra = extra + self.timeout = timeout return self.position - async def get_properties(self, extra: Optional[Dict[str, Any]] = None) -> Motor.Properties: + async def get_properties( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Motor.Properties: self.extra = extra + self.timeout = timeout return Motor.Properties(position_reporting=True) - async def stop(self, extra: Optional[Dict[str, Any]] = None): + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ): await self.set_power(0) + self.timeout = timeout self.extra = extra - async def is_powered(self, extra: Optional[Dict[str, Any]] = None) -> bool: + async def is_powered( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Tuple[bool, float]: self.extra = extra - return self.powered + self.timeout = timeout + return self.powered, self.power async def is_moving(self) -> bool: return self.powered + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + class MockMovementSensor(MovementSensor): def __init__( @@ -461,42 +782,91 @@ def __init__( altitude: float, lin_vel: Vector3, ang_vel: Vector3, + lin_acc: Vector3, heading: float, orientation: Orientation, properties: MovementSensor.Properties, - accuracy: Mapping[str, float], + accuracy: MovementSensor.Accuracy, + readings: Mapping[str, float], ): super().__init__(name) self.coordinates = coordinates self.altitude = altitude self.lin_vel = lin_vel self.ang_vel = ang_vel + self.lin_acc = lin_acc self.heading = heading self.orientation = orientation self.properties = properties self.accuracy = accuracy + self.readings = readings + self.geometries = GEOMETRIES + self.extra: Optional[Dict[str, Any]] = None + self.timeout: Optional[float] = None - async def get_position(self) -> Tuple[GeoPoint, float]: + async def get_position( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Tuple[GeoPoint, float]: + self.extra = extra + self.timeout = timeout return (self.coordinates, self.altitude) - async def get_linear_velocity(self) -> Vector3: + async def get_linear_velocity(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Vector3: + self.extra = extra + self.timeout = timeout return self.lin_vel - async def get_angular_velocity(self) -> Vector3: + async def get_angular_velocity(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Vector3: + self.extra = extra + self.timeout = timeout return self.ang_vel - async def get_compass_heading(self) -> float: + async def get_linear_acceleration( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Vector3: + self.extra = extra + self.timeout = timeout + return self.lin_acc + + async def get_compass_heading(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> float: + self.extra = extra + self.timeout = timeout return self.heading - async def get_orientation(self) -> Orientation: + async def get_orientation(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Orientation: + self.extra = extra + self.timeout = timeout return self.orientation - async def get_properties(self) -> MovementSensor.Properties: + async def get_properties( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> MovementSensor.Properties: + self.extra = extra + self.timeout = timeout return self.properties - async def get_accuracy(self) -> Mapping[str, float]: + async def get_accuracy( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> MovementSensor.Accuracy: + self.extra = extra + self.timeout = timeout return self.accuracy + async def get_readings( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Mapping[str, float]: + self.extra = extra + self.timeout = timeout + return self.readings + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + @dataclass class MockPose: @@ -520,38 +890,173 @@ def __init__(self, name: str, poses: List[MockPose]): pose_map[str(idx)] = pose self.poses_result = pose_map self.name = name + self.geometries = GEOMETRIES + self.timeout: Optional[float] = None + self.extra: Optional[Mapping[str, Any]] = None - async def get_poses(self, body_names: List[str]) -> Dict[str, PoseInFrame]: + async def get_poses( + self, + body_names: List[str], + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + **kwargs, + ) -> Dict[str, PoseInFrame]: result: Dict[str, PoseInFrame] = {} for name, pose in self.poses_result.items(): result[name] = pose.to_pose_in_frame(name) + self.timeout = timeout + self.extra = extra return result + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + + +class MockPowerSensor(PowerSensor): + def __init__(self, name: str, voltage: float, current: float, is_ac: bool, power: float, readings: Mapping[str, float]): + super().__init__(name) + self.voltage = voltage + self.current = current + self.is_ac = is_ac + self.power = power + self.readings = readings + self.geometries = GEOMETRIES + self.extra: Optional[Dict[str, Any]] = None + self.timeout: Optional[float] = None + + async def get_voltage(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Tuple[float, bool]: + self.extra = extra + self.timeout = timeout + return (self.voltage, self.is_ac) + + async def get_current(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> Tuple[float, bool]: + self.extra = extra + self.timeout = timeout + return (self.current, self.is_ac) + + async def get_power(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> float: + self.extra = extra + self.timeout = timeout + return self.power + + async def get_readings( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Mapping[str, float]: + self.extra = extra + self.timeout = timeout + return self.readings + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + class MockSensor(Sensor): def __init__(self, name: str, result: Mapping[str, Any] = {"a": 0, "b": {"foo": "bar"}, "c": [1, 8, 2], "d": "Hello world!"}): self.readings = result + self.geometries = GEOMETRIES + self.extra: Optional[Mapping[str, Any]] = None + self.timeout: Optional[float] = None super().__init__(name) - async def get_readings(self) -> Mapping[str, Any]: + async def get_readings( + self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> Mapping[str, SensorReading]: + self.extra = extra + self.timeout = timeout return self.readings + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + class MockServo(Servo): def __init__(self, name: str): self.angle = 0 self.is_stopped = True + self.geometries = GEOMETRIES + self.timeout: Optional[float] = None + self.extra: Optional[Mapping[str, Any]] = None super().__init__(name) - async def move(self, angle: int): + async def move(self, angle: int, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs): + self.extra = extra self.angle = angle self.is_stopped = False + self.timeout = timeout - async def get_position(self) -> int: + async def get_position(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> int: + self.extra = extra + self.timeout = timeout return self.angle - async def stop(self): + async def stop(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs): + self.extra = extra self.is_stopped = True + self.timeout = timeout async def is_moving(self) -> bool: return not self.is_stopped + + async def get_geometries(self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None) -> List[Geometry]: + self.extra = extra + self.timeout = timeout + return self.geometries + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + + +class MockSwitch(Switch): + def __init__(self, name: str, number_of_positions: int = 3, position: int = 0): + self.number_of_positions = number_of_positions + self.position = position + self.timeout: Optional[float] = None + self.extra: Optional[Mapping[str, Any]] = None + super().__init__(name) + + async def get_position(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> int: + self.extra = extra + self.timeout = timeout + return self.position + + async def get_number_of_positions(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> int: + self.extra = extra + self.timeout = timeout + return self.number_of_positions + + async def set_position( + self, position: int, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs + ) -> None: + self.extra = extra + self.timeout = timeout + self.position = position + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + + +class MockButton(Button): + def __init__(self, name: str): + self.pushed = False + self.timeout: Optional[float] = None + self.extra: Optional[Mapping[str, Any]] = None + super().__init__(name) + + async def push(self, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None, **kwargs) -> None: + self.extra = extra + self.timeout = timeout + self.pushed = True + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} diff --git a/tests/mocks/module/__init__.py b/tests/mocks/module/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/mocks/module/gizmo/__init__.py b/tests/mocks/module/gizmo/__init__.py new file mode 100644 index 000000000..369c7e72d --- /dev/null +++ b/tests/mocks/module/gizmo/__init__.py @@ -0,0 +1,9 @@ +from viam.components.motor import * # noqa: F403 Need to import motor so the component registers itself +from viam.resource.registry import Registry, ResourceCreatorRegistration, ResourceRegistration + +from .api import Gizmo, GizmoClient, GizmoService +from .my_gizmo import MyGizmo + +Registry.register_api(ResourceRegistration(Gizmo, GizmoService, lambda name, channel: GizmoClient(name, channel))) + +Registry.register_resource_creator(Gizmo.API, MyGizmo.MODEL, ResourceCreatorRegistration(MyGizmo.new, MyGizmo.validate_config)) diff --git a/tests/mocks/module/gizmo/api.py b/tests/mocks/module/gizmo/api.py new file mode 100644 index 000000000..75269a2a8 --- /dev/null +++ b/tests/mocks/module/gizmo/api.py @@ -0,0 +1,157 @@ +import abc +from typing import Final, List, Mapping, Optional, Sequence + +from grpclib.client import Channel +from grpclib.server import Stream + +from viam.components.component_base import ComponentBase +from viam.components.generic.client import do_command +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.resource.types import API, RESOURCE_TYPE_COMPONENT +from viam.utils import ValueTypes + +from ..proto.gizmo_grpc import GizmoServiceBase, GizmoServiceStub +from ..proto.gizmo_pb2 import ( + DoOneBiDiStreamRequest, + DoOneBiDiStreamResponse, + DoOneClientStreamRequest, + DoOneClientStreamResponse, + DoOneRequest, + DoOneResponse, + DoOneServerStreamRequest, + DoOneServerStreamResponse, + DoTwoRequest, + DoTwoResponse, +) + + +class Gizmo(ComponentBase): + """Example component to use with the example module.""" + + API: Final = API("acme", RESOURCE_TYPE_COMPONENT, "gizmo") + + @abc.abstractmethod + async def do_one(self, arg1: str, **kwargs) -> bool: ... + + @abc.abstractmethod + async def do_one_client_stream(self, arg1: Sequence[str], **kwargs) -> bool: ... + + @abc.abstractmethod + async def do_one_server_stream(self, arg1: str, **kwargs) -> Sequence[bool]: ... + + @abc.abstractmethod + async def do_one_bidi_stream(self, arg1: Sequence[str], **kwargs) -> Sequence[bool]: ... + + @abc.abstractmethod + async def do_two(self, arg1: bool, **kwargs) -> str: ... + + +class GizmoService(GizmoServiceBase, ResourceRPCServiceBase): + """Example gRPC service for the Gizmo component""" + + RESOURCE_TYPE = Gizmo + + async def DoOne(self, stream: Stream[DoOneRequest, DoOneResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + gizmo = self.get_resource(name) + resp = await gizmo.do_one(request.arg1) + response = DoOneResponse(ret1=resp) + await stream.send_message(response) + + async def DoOneClientStream(self, stream: Stream[DoOneClientStreamRequest, DoOneClientStreamResponse]) -> None: + requests = [request async for request in stream] + args = [request.arg1 for request in requests] + names = [request.name for request in requests] + if len(set(names)) != 1: + raise Exception("Unexpectedly received requests for multiple Gizmos") + name = names[0] + gizmo = self.get_resource(name) + resp = await gizmo.do_one_client_stream(args) + response = DoOneClientStreamResponse(ret1=resp) + await stream.send_message(response) + + async def DoOneServerStream(self, stream: Stream[DoOneServerStreamRequest, DoOneServerStreamResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + gizmo = self.get_resource(name) + resps = await gizmo.do_one_server_stream(request.arg1) + for resp in resps: + await stream.send_message(DoOneServerStreamResponse(ret1=resp)) + + async def DoOneBiDiStream(self, stream: Stream[DoOneBiDiStreamRequest, DoOneBiDiStreamResponse]) -> None: + args: List[str] = [] + name: str = "" + async for request in stream: + args.append(request.arg1) + if name == "": + name = request.name + continue + if name != request.name: + raise Exception("Unexpectedly received requests for multiple Gizmos") + gizmo = self.get_resource(name) + + resps = await gizmo.do_one_bidi_stream(args) + for resp in resps: + await stream.send_message(DoOneBiDiStreamResponse(ret1=resp)) + + async def DoTwo(self, stream: Stream[DoTwoRequest, DoTwoResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + gizmo = self.get_resource(name) + resp = await gizmo.do_two(request.arg1) + response = DoTwoResponse(ret1=resp) + await stream.send_message(response) + + +class GizmoClient(Gizmo): + """Example gRPC client for the Gizmo component""" + + def __init__(self, name: str, channel: Channel): + self.channel = channel + self.client = GizmoServiceStub(channel) + super().__init__(name) + + async def do_one(self, arg1: str) -> bool: + resp: DoOneResponse = await self.client.DoOne(DoOneRequest(name=self.name, arg1=arg1)) + return resp.ret1 + + async def do_one_client_stream(self, arg1: Sequence[str]) -> bool: + async with self.client.DoOneClientStream.open() as stream: + await stream.send_request() + for arg in arg1: + await stream.send_message(DoOneClientStreamRequest(name=self.name, arg1=arg)) + await stream.end() + response = await stream.recv_message() + assert response is not None + return response.ret1 + + async def do_one_server_stream(self, arg1: str) -> Sequence[bool]: + async with self.client.DoOneServerStream.open() as stream: + await stream.send_message(DoOneServerStreamRequest(name=self.name, arg1=arg1)) + resps = [resp.ret1 async for resp in stream] + return resps + + async def do_one_bidi_stream(self, arg1: Sequence[str]) -> Sequence[bool]: + async with self.client.DoOneClientStream.open() as stream: + await stream.send_request() + for arg in arg1: + await stream.send_message(DoOneClientStreamRequest(name=self.name, arg1=arg)) + await stream.end() + resps = [resp.ret1 async for resp in stream] + return resps + + async def do_two(self, arg1: bool) -> str: + resp = await self.client.DoTwo(DoTwoRequest(name=self.name, arg1=arg1)) + return resp.ret1 + + async def do_command( + self, + command: Mapping[str, ValueTypes], + *, + timeout: Optional[float] = None, + ) -> Mapping[str, ValueTypes]: + return await do_command(self.channel, self.name, command, timeout=timeout) diff --git a/tests/mocks/module/gizmo/my_gizmo.py b/tests/mocks/module/gizmo/my_gizmo.py new file mode 100644 index 000000000..b745cadd1 --- /dev/null +++ b/tests/mocks/module/gizmo/my_gizmo.py @@ -0,0 +1,66 @@ +from typing import ClassVar, List, Mapping, Sequence + +from typing_extensions import Self + +from viam.components.component_base import ComponentBase +from viam.module.types import Reconfigurable +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ResourceName +from viam.resource.base import ResourceBase +from viam.resource.types import Model, ModelFamily + +from ..gizmo.api import Gizmo + + +class MyGizmo(Gizmo, Reconfigurable): + MODEL: ClassVar[Model] = Model(ModelFamily("acme", "demo"), "mygizmo") + my_arg: str + closed: bool = False + + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: + gizmo = cls(config.name) + gizmo.my_arg = config.attributes.fields["arg1"].string_value + return gizmo + + @classmethod + def validate_config(cls, config: ComponentConfig) -> List[str]: + if "invalid" in config.attributes.fields: + raise Exception(f"'invalid' attribute not allowed for model {cls.API}:{cls.MODEL}") + arg1 = config.attributes.fields["arg1"].string_value + if arg1 == "": + raise Exception("A arg1 attribute is required for Gizmo component.") + motor = [config.attributes.fields["motor"].string_value] + if motor == [""]: + raise Exception("A motor is required for Gizmo component.") + return motor + + async def do_one(self, arg1: str, **kwargs) -> bool: + return arg1 == self.my_arg + + async def do_one_client_stream(self, arg1: Sequence[str], **kwargs) -> bool: + if len(arg1) == 0: + return False + resp = True + for arg in arg1: + resp = resp and arg == self.my_arg + return resp + + async def do_one_server_stream(self, arg1: str, **kwargs) -> Sequence[bool]: + return [arg1 == self.my_arg, False, True, False] + + async def do_one_bidi_stream(self, arg1: Sequence[str], **kwargs) -> Sequence[bool]: + resps = [] + for arg in arg1: + resps.append(arg == self.my_arg) + return resps + + async def do_two(self, arg1: bool, **kwargs) -> str: + return f"arg1={arg1}" + + def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ComponentBase]): + self.my_arg = config.attributes.fields["arg1"].string_value + + async def close(self): + self.closed = True + return diff --git a/tests/mocks/module/proto/__init__.py b/tests/mocks/module/proto/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/mocks/module/proto/gizmo.proto b/tests/mocks/module/proto/gizmo.proto new file mode 100644 index 000000000..23b262f9a --- /dev/null +++ b/tests/mocks/module/proto/gizmo.proto @@ -0,0 +1,70 @@ +syntax = "proto3"; + +package acme.component.gizmo.v1; + +import "google/api/annotations.proto"; + +service GizmoService { + rpc DoOne(DoOneRequest) returns (DoOneResponse) { + option (google.api.http) = { + post: "/acme/api/v1/component/gizmo/{name}/do_one" + }; + } + + rpc DoOneClientStream(stream DoOneClientStreamRequest) returns (DoOneClientStreamResponse); + + rpc DoOneServerStream(DoOneServerStreamRequest) returns (stream DoOneServerStreamResponse); + + rpc DoOneBiDiStream(stream DoOneBiDiStreamRequest) returns (stream DoOneBiDiStreamResponse); + + rpc DoTwo(DoTwoRequest) returns (DoTwoResponse) { + option (google.api.http) = { + post: "/acme/api/v1/component/gizmo/{name}/do_two" + }; + } +} + +message DoOneRequest { + string name = 1; + string arg1 = 2; +} + +message DoOneResponse { + bool ret1 = 1; +} + +message DoOneServerStreamRequest { + string name = 1; + string arg1 = 2; +} + +message DoOneServerStreamResponse { + bool ret1 = 1; +} + +message DoOneClientStreamRequest { + string name = 1; + string arg1 = 2; +} + +message DoOneClientStreamResponse { + bool ret1 = 1; +} + +message DoOneBiDiStreamRequest { + string name = 1; + string arg1 = 2; +} + +message DoOneBiDiStreamResponse { + bool ret1 = 1; +} + +message DoTwoRequest { + string name = 1; + bool arg1 = 2; +} + +message DoTwoResponse { + string ret1 = 1; +} diff --git a/tests/mocks/module/proto/gizmo_grpc.py b/tests/mocks/module/proto/gizmo_grpc.py new file mode 100644 index 000000000..cbd09a435 --- /dev/null +++ b/tests/mocks/module/proto/gizmo_grpc.py @@ -0,0 +1,111 @@ +# Generated by the Protocol Buffers compiler. DO NOT EDIT! +# source: proto/gizmo.proto +# plugin: grpclib.plugin.main +import abc +import typing + +import grpclib.client +import grpclib.const + +if typing.TYPE_CHECKING: + import grpclib.server + +import google.api.annotations_pb2 + +from .. import proto + + +class GizmoServiceBase(abc.ABC): + @abc.abstractmethod + async def DoOne(self, stream: "grpclib.server.Stream[proto.gizmo_pb2.DoOneRequest, proto.gizmo_pb2.DoOneResponse]") -> None: + pass + + @abc.abstractmethod + async def DoOneClientStream( + self, stream: "grpclib.server.Stream[proto.gizmo_pb2.DoOneClientStreamRequest, proto.gizmo_pb2.DoOneClientStreamResponse]" + ) -> None: + pass + + @abc.abstractmethod + async def DoOneServerStream( + self, stream: "grpclib.server.Stream[proto.gizmo_pb2.DoOneServerStreamRequest, proto.gizmo_pb2.DoOneServerStreamResponse]" + ) -> None: + pass + + @abc.abstractmethod + async def DoOneBiDiStream( + self, stream: "grpclib.server.Stream[proto.gizmo_pb2.DoOneBiDiStreamRequest, proto.gizmo_pb2.DoOneBiDiStreamResponse]" + ) -> None: + pass + + @abc.abstractmethod + async def DoTwo(self, stream: "grpclib.server.Stream[proto.gizmo_pb2.DoTwoRequest, proto.gizmo_pb2.DoTwoResponse]") -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return { + "/acme.component.gizmo.v1.GizmoService/DoOne": grpclib.const.Handler( + self.DoOne, + grpclib.const.Cardinality.UNARY_UNARY, + proto.gizmo_pb2.DoOneRequest, + proto.gizmo_pb2.DoOneResponse, + ), + "/acme.component.gizmo.v1.GizmoService/DoOneClientStream": grpclib.const.Handler( + self.DoOneClientStream, + grpclib.const.Cardinality.STREAM_UNARY, + proto.gizmo_pb2.DoOneClientStreamRequest, + proto.gizmo_pb2.DoOneClientStreamResponse, + ), + "/acme.component.gizmo.v1.GizmoService/DoOneServerStream": grpclib.const.Handler( + self.DoOneServerStream, + grpclib.const.Cardinality.UNARY_STREAM, + proto.gizmo_pb2.DoOneServerStreamRequest, + proto.gizmo_pb2.DoOneServerStreamResponse, + ), + "/acme.component.gizmo.v1.GizmoService/DoOneBiDiStream": grpclib.const.Handler( + self.DoOneBiDiStream, + grpclib.const.Cardinality.STREAM_STREAM, + proto.gizmo_pb2.DoOneBiDiStreamRequest, + proto.gizmo_pb2.DoOneBiDiStreamResponse, + ), + "/acme.component.gizmo.v1.GizmoService/DoTwo": grpclib.const.Handler( + self.DoTwo, + grpclib.const.Cardinality.UNARY_UNARY, + proto.gizmo_pb2.DoTwoRequest, + proto.gizmo_pb2.DoTwoResponse, + ), + } + + +class GizmoServiceStub: + def __init__(self, channel: grpclib.client.Channel) -> None: + self.DoOne = grpclib.client.UnaryUnaryMethod( + channel, + "/acme.component.gizmo.v1.GizmoService/DoOne", + proto.gizmo_pb2.DoOneRequest, + proto.gizmo_pb2.DoOneResponse, + ) + self.DoOneClientStream = grpclib.client.StreamUnaryMethod( + channel, + "/acme.component.gizmo.v1.GizmoService/DoOneClientStream", + proto.gizmo_pb2.DoOneClientStreamRequest, + proto.gizmo_pb2.DoOneClientStreamResponse, + ) + self.DoOneServerStream = grpclib.client.UnaryStreamMethod( + channel, + "/acme.component.gizmo.v1.GizmoService/DoOneServerStream", + proto.gizmo_pb2.DoOneServerStreamRequest, + proto.gizmo_pb2.DoOneServerStreamResponse, + ) + self.DoOneBiDiStream = grpclib.client.StreamStreamMethod( + channel, + "/acme.component.gizmo.v1.GizmoService/DoOneBiDiStream", + proto.gizmo_pb2.DoOneBiDiStreamRequest, + proto.gizmo_pb2.DoOneBiDiStreamResponse, + ) + self.DoTwo = grpclib.client.UnaryUnaryMethod( + channel, + "/acme.component.gizmo.v1.GizmoService/DoTwo", + proto.gizmo_pb2.DoTwoRequest, + proto.gizmo_pb2.DoTwoResponse, + ) diff --git a/tests/mocks/module/proto/gizmo_pb2.py b/tests/mocks/module/proto/gizmo_pb2.py new file mode 100644 index 000000000..3dbaeaf8f --- /dev/null +++ b/tests/mocks/module/proto/gizmo_pb2.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: proto/gizmo.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x11proto/gizmo.proto\x12\x17\x61\x63me.component.gizmo.v1\x1a\x1cgoogle/api/annotations.proto"6\n\x0c\x44oOneRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04\x61rg1\x18\x02 \x01(\tR\x04\x61rg1"#\n\rDoOneResponse\x12\x12\n\x04ret1\x18\x01 \x01(\x08R\x04ret1"B\n\x18\x44oOneServerStreamRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04\x61rg1\x18\x02 \x01(\tR\x04\x61rg1"/\n\x19\x44oOneServerStreamResponse\x12\x12\n\x04ret1\x18\x01 \x01(\x08R\x04ret1"B\n\x18\x44oOneClientStreamRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04\x61rg1\x18\x02 \x01(\tR\x04\x61rg1"/\n\x19\x44oOneClientStreamResponse\x12\x12\n\x04ret1\x18\x01 \x01(\x08R\x04ret1"@\n\x16\x44oOneBiDiStreamRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04\x61rg1\x18\x02 \x01(\tR\x04\x61rg1"-\n\x17\x44oOneBiDiStreamResponse\x12\x12\n\x04ret1\x18\x01 \x01(\x08R\x04ret1"6\n\x0c\x44oTwoRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x12\n\x04\x61rg1\x18\x02 \x01(\x08R\x04\x61rg1"#\n\rDoTwoResponse\x12\x12\n\x04ret1\x18\x01 \x01(\tR\x04ret12\x9e\x05\n\x0cGizmoService\x12\x8a\x01\n\x05\x44oOne\x12%.acme.component.gizmo.v1.DoOneRequest\x1a&.acme.component.gizmo.v1.DoOneResponse"2\x82\xd3\xe4\x93\x02,"*/acme/api/v1/component/gizmo/{name}/do_one\x12|\n\x11\x44oOneClientStream\x12\x31.acme.component.gizmo.v1.DoOneClientStreamRequest\x1a\x32.acme.component.gizmo.v1.DoOneClientStreamResponse(\x01\x12|\n\x11\x44oOneServerStream\x12\x31.acme.component.gizmo.v1.DoOneServerStreamRequest\x1a\x32.acme.component.gizmo.v1.DoOneServerStreamResponse0\x01\x12x\n\x0f\x44oOneBiDiStream\x12/.acme.component.gizmo.v1.DoOneBiDiStreamRequest\x1a\x30.acme.component.gizmo.v1.DoOneBiDiStreamResponse(\x01\x30\x01\x12\x8a\x01\n\x05\x44oTwo\x12%.acme.component.gizmo.v1.DoTwoRequest\x1a&.acme.component.gizmo.v1.DoTwoResponse"2\x82\xd3\xe4\x93\x02,"*/acme/api/v1/component/gizmo/{name}/do_twoB*Z(go.acme.com/proto/api/component/gizmo/v1b\x06proto3' +) + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "proto.gizmo_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b"Z(go.acme.com/proto/api/component/gizmo/v1" + _GIZMOSERVICE.methods_by_name["DoOne"]._options = None + _GIZMOSERVICE.methods_by_name["DoOne"]._serialized_options = b'\202\323\344\223\002,"*/acme/api/v1/component/gizmo/{name}/do_one' + _GIZMOSERVICE.methods_by_name["DoTwo"]._options = None + _GIZMOSERVICE.methods_by_name["DoTwo"]._serialized_options = b'\202\323\344\223\002,"*/acme/api/v1/component/gizmo/{name}/do_two' + _DOONEREQUEST._serialized_start = 76 + _DOONEREQUEST._serialized_end = 130 + _DOONERESPONSE._serialized_start = 132 + _DOONERESPONSE._serialized_end = 167 + _DOONESERVERSTREAMREQUEST._serialized_start = 169 + _DOONESERVERSTREAMREQUEST._serialized_end = 235 + _DOONESERVERSTREAMRESPONSE._serialized_start = 237 + _DOONESERVERSTREAMRESPONSE._serialized_end = 284 + _DOONECLIENTSTREAMREQUEST._serialized_start = 286 + _DOONECLIENTSTREAMREQUEST._serialized_end = 352 + _DOONECLIENTSTREAMRESPONSE._serialized_start = 354 + _DOONECLIENTSTREAMRESPONSE._serialized_end = 401 + _DOONEBIDISTREAMREQUEST._serialized_start = 403 + _DOONEBIDISTREAMREQUEST._serialized_end = 467 + _DOONEBIDISTREAMRESPONSE._serialized_start = 469 + _DOONEBIDISTREAMRESPONSE._serialized_end = 514 + _DOTWOREQUEST._serialized_start = 516 + _DOTWOREQUEST._serialized_end = 570 + _DOTWORESPONSE._serialized_start = 572 + _DOTWORESPONSE._serialized_end = 607 + _GIZMOSERVICE._serialized_start = 610 + _GIZMOSERVICE._serialized_end = 1280 +# @@protoc_insertion_point(module_scope) diff --git a/tests/mocks/module/proto/gizmo_pb2.pyi b/tests/mocks/module/proto/gizmo_pb2.pyi new file mode 100644 index 000000000..d7e56e4cd --- /dev/null +++ b/tests/mocks/module/proto/gizmo_pb2.pyi @@ -0,0 +1,180 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class DoOneRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ARG1_FIELD_NUMBER: builtins.int + name: builtins.str + arg1: builtins.str + def __init__( + self, + *, + name: builtins.str = ..., + arg1: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["arg1", b"arg1", "name", b"name"]) -> None: ... + +global___DoOneRequest = DoOneRequest + +@typing_extensions.final +class DoOneResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RET1_FIELD_NUMBER: builtins.int + ret1: builtins.bool + def __init__( + self, + *, + ret1: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ret1", b"ret1"]) -> None: ... + +global___DoOneResponse = DoOneResponse + +@typing_extensions.final +class DoOneServerStreamRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ARG1_FIELD_NUMBER: builtins.int + name: builtins.str + arg1: builtins.str + def __init__( + self, + *, + name: builtins.str = ..., + arg1: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["arg1", b"arg1", "name", b"name"]) -> None: ... + +global___DoOneServerStreamRequest = DoOneServerStreamRequest + +@typing_extensions.final +class DoOneServerStreamResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RET1_FIELD_NUMBER: builtins.int + ret1: builtins.bool + def __init__( + self, + *, + ret1: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ret1", b"ret1"]) -> None: ... + +global___DoOneServerStreamResponse = DoOneServerStreamResponse + +@typing_extensions.final +class DoOneClientStreamRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ARG1_FIELD_NUMBER: builtins.int + name: builtins.str + arg1: builtins.str + def __init__( + self, + *, + name: builtins.str = ..., + arg1: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["arg1", b"arg1", "name", b"name"]) -> None: ... + +global___DoOneClientStreamRequest = DoOneClientStreamRequest + +@typing_extensions.final +class DoOneClientStreamResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RET1_FIELD_NUMBER: builtins.int + ret1: builtins.bool + def __init__( + self, + *, + ret1: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ret1", b"ret1"]) -> None: ... + +global___DoOneClientStreamResponse = DoOneClientStreamResponse + +@typing_extensions.final +class DoOneBiDiStreamRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ARG1_FIELD_NUMBER: builtins.int + name: builtins.str + arg1: builtins.str + def __init__( + self, + *, + name: builtins.str = ..., + arg1: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["arg1", b"arg1", "name", b"name"]) -> None: ... + +global___DoOneBiDiStreamRequest = DoOneBiDiStreamRequest + +@typing_extensions.final +class DoOneBiDiStreamResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RET1_FIELD_NUMBER: builtins.int + ret1: builtins.bool + def __init__( + self, + *, + ret1: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ret1", b"ret1"]) -> None: ... + +global___DoOneBiDiStreamResponse = DoOneBiDiStreamResponse + +@typing_extensions.final +class DoTwoRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ARG1_FIELD_NUMBER: builtins.int + name: builtins.str + arg1: builtins.bool + def __init__( + self, + *, + name: builtins.str = ..., + arg1: builtins.bool = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["arg1", b"arg1", "name", b"name"]) -> None: ... + +global___DoTwoRequest = DoTwoRequest + +@typing_extensions.final +class DoTwoResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RET1_FIELD_NUMBER: builtins.int + ret1: builtins.str + def __init__( + self, + *, + ret1: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["ret1", b"ret1"]) -> None: ... + +global___DoTwoResponse = DoTwoResponse diff --git a/tests/mocks/module/proto/summation.proto b/tests/mocks/module/proto/summation.proto new file mode 100644 index 000000000..caa041acf --- /dev/null +++ b/tests/mocks/module/proto/summation.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; + +package acme.service.summation.v1; + +import "google/api/annotations.proto"; + +service SummationService { + rpc Sum(SumRequest) returns (SumResponse) { + option (google.api.http) = { + post: "/acme/api/v1/service/summation/{name}/sum" + }; + } +} + +message SumRequest { + string name = 1; + repeated double numbers = 2; +} + +message SumResponse { + double sum = 1; +} diff --git a/tests/mocks/module/proto/summation_grpc.py b/tests/mocks/module/proto/summation_grpc.py new file mode 100644 index 000000000..b036c928d --- /dev/null +++ b/tests/mocks/module/proto/summation_grpc.py @@ -0,0 +1,41 @@ +# Generated by the Protocol Buffers compiler. DO NOT EDIT! +# source: src/proto/summation.proto +# plugin: grpclib.plugin.main +import abc +import typing + +import grpclib.client +import grpclib.const + +if typing.TYPE_CHECKING: + import grpclib.server + +import google.api.annotations_pb2 + +from .. import proto + + +class SummationServiceBase(abc.ABC): + @abc.abstractmethod + async def Sum(self, stream: "grpclib.server.Stream[proto.summation_pb2.SumRequest, proto.summation_pb2.SumResponse]") -> None: + pass + + def __mapping__(self) -> typing.Dict[str, grpclib.const.Handler]: + return { + "/acme.service.summation.v1.SummationService/Sum": grpclib.const.Handler( + self.Sum, + grpclib.const.Cardinality.UNARY_UNARY, + proto.summation_pb2.SumRequest, + proto.summation_pb2.SumResponse, + ), + } + + +class SummationServiceStub: + def __init__(self, channel: grpclib.client.Channel) -> None: + self.Sum = grpclib.client.UnaryUnaryMethod( + channel, + "/acme.service.summation.v1.SummationService/Sum", + proto.summation_pb2.SumRequest, + proto.summation_pb2.SumResponse, + ) diff --git a/tests/mocks/module/proto/summation_pb2.py b/tests/mocks/module/proto/summation_pb2.py new file mode 100644 index 000000000..0201217e4 --- /dev/null +++ b/tests/mocks/module/proto/summation_pb2.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: src/proto/summation.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.api import annotations_pb2 as google_dot_api_dot_annotations__pb2 + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile( + b'\n\x19src/proto/summation.proto\x12\x19\x61\x63me.service.summation.v1\x1a\x1cgoogle/api/annotations.proto":\n\nSumRequest\x12\x12\n\x04name\x18\x01 \x01(\tR\x04name\x12\x18\n\x07numbers\x18\x02 \x03(\x01R\x07numbers"\x1f\n\x0bSumResponse\x12\x10\n\x03sum\x18\x01 \x01(\x01R\x03sum2\x9c\x01\n\x10SummationService\x12\x87\x01\n\x03Sum\x12%.acme.service.summation.v1.SumRequest\x1a&.acme.service.summation.v1.SumResponse"1\x82\xd3\xe4\x93\x02+")/acme/api/v1/service/summation/{name}/sumb\x06proto3' +) + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "src.proto.summation_pb2", globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _SUMMATIONSERVICE.methods_by_name["Sum"]._options = None + _SUMMATIONSERVICE.methods_by_name["Sum"]._serialized_options = b'\202\323\344\223\002+")/acme/api/v1/service/summation/{name}/sum' + _SUMREQUEST._serialized_start = 86 + _SUMREQUEST._serialized_end = 144 + _SUMRESPONSE._serialized_start = 146 + _SUMRESPONSE._serialized_end = 177 + _SUMMATIONSERVICE._serialized_start = 180 + _SUMMATIONSERVICE._serialized_end = 336 +# @@protoc_insertion_point(module_scope) diff --git a/tests/mocks/module/proto/summation_pb2.pyi b/tests/mocks/module/proto/summation_pb2.pyi new file mode 100644 index 000000000..21d204dcb --- /dev/null +++ b/tests/mocks/module/proto/summation_pb2.pyi @@ -0,0 +1,51 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" +import builtins +import collections.abc +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.message +import sys + +if sys.version_info >= (3, 8): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing_extensions.final +class SumRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + NUMBERS_FIELD_NUMBER: builtins.int + name: builtins.str + @property + def numbers(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.float]: ... + def __init__( + self, + *, + name: builtins.str = ..., + numbers: collections.abc.Iterable[builtins.float] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["name", b"name", "numbers", b"numbers"]) -> None: ... + +global___SumRequest = SumRequest + +@typing_extensions.final +class SumResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SUM_FIELD_NUMBER: builtins.int + sum: builtins.float + def __init__( + self, + *, + sum: builtins.float = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["sum", b"sum"]) -> None: ... + +global___SumResponse = SumResponse diff --git a/tests/mocks/module/summation/__init__.py b/tests/mocks/module/summation/__init__.py new file mode 100644 index 000000000..3917d8ab7 --- /dev/null +++ b/tests/mocks/module/summation/__init__.py @@ -0,0 +1,8 @@ +from viam.resource.registry import Registry, ResourceCreatorRegistration, ResourceRegistration + +from .api import SummationClient, SummationRPCService, SummationService +from .my_summation import MySummationService + +Registry.register_api(ResourceRegistration(SummationService, SummationRPCService, lambda name, channel: SummationClient(name, channel))) + +Registry.register_resource_creator(SummationService.API, MySummationService.MODEL, ResourceCreatorRegistration(MySummationService.new)) diff --git a/tests/mocks/module/summation/api.py b/tests/mocks/module/summation/api.py new file mode 100644 index 000000000..d7f5acde3 --- /dev/null +++ b/tests/mocks/module/summation/api.py @@ -0,0 +1,49 @@ +import abc +from typing import Final, Sequence + +from grpclib.client import Channel +from grpclib.server import Stream + +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.resource.types import API, RESOURCE_TYPE_SERVICE +from viam.services.service_base import ServiceBase + +from ..proto.summation_grpc import SummationServiceBase, SummationServiceStub +from ..proto.summation_pb2 import SumRequest, SumResponse + + +class SummationService(ServiceBase): + """Example service to use with the example module""" + + API: Final = API("acme", RESOURCE_TYPE_SERVICE, "summation") + + @abc.abstractmethod + async def sum(self, nums: Sequence[float]) -> float: ... + + +class SummationRPCService(SummationServiceBase, ResourceRPCServiceBase): + """Example gRPC service for the Summation service""" + + RESOURCE_TYPE = SummationService + + async def Sum(self, stream: Stream[SumRequest, SumResponse]) -> None: + request = await stream.recv_message() + assert request is not None + name = request.name + service = self.get_resource(name) + resp = await service.sum(request.numbers) + await stream.send_message(SumResponse(sum=resp)) + + +class SummationClient(SummationService): + """Example gRPC client for the Summation Service""" + + def __init__(self, name: str, channel: Channel) -> None: + self.channel = channel + self.client = SummationServiceStub(channel) + super().__init__(name) + + async def sum(self, nums: Sequence[float]) -> float: + request = SumRequest(name=self.name, numbers=nums) + response: SumResponse = await self.client.Sum(request) + return response.sum diff --git a/tests/mocks/module/summation/my_summation.py b/tests/mocks/module/summation/my_summation.py new file mode 100644 index 000000000..f613d3885 --- /dev/null +++ b/tests/mocks/module/summation/my_summation.py @@ -0,0 +1,37 @@ +from typing import ClassVar, Mapping, Sequence + +from typing_extensions import Self + +from viam.module.types import Reconfigurable +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ResourceName +from viam.resource.base import ResourceBase +from viam.resource.types import Model + +from ..summation.api import SummationService + + +class MySummationService(SummationService, Reconfigurable): + MODEL: ClassVar[Model] = Model.from_string("acme:demo:mysum") + subtract: bool + + @classmethod + def new(cls, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]) -> Self: + summer = cls(config.name) + summer.subtract = config.attributes.fields["subtract"].bool_value or False + return summer + + async def sum(self, nums: Sequence[float]) -> float: + if len(nums) <= 0: + raise ValueError("Must provided at least one number to sum") + + result = 0 + for num in nums: + if self.subtract: + result -= num + else: + result += num + return result + + def reconfigure(self, config: ComponentConfig, dependencies: Mapping[ResourceName, ResourceBase]): + self.subtract = config.attributes.fields["subtract"].bool_value or False diff --git a/tests/mocks/robot.py b/tests/mocks/robot.py new file mode 100644 index 000000000..3532f2b53 --- /dev/null +++ b/tests/mocks/robot.py @@ -0,0 +1,117 @@ +from datetime import timedelta + +from google.protobuf.duration_pb2 import Duration +from grpclib.server import Stream + +from viam.errors import MethodNotImplementedError +from viam.proto.robot import ( + BlockForOperationRequest, + BlockForOperationResponse, + CancelOperationRequest, + CancelOperationResponse, + FrameSystemConfigRequest, + FrameSystemConfigResponse, + GetCloudMetadataRequest, + GetCloudMetadataResponse, + GetOperationsRequest, + GetOperationsResponse, + GetSessionsRequest, + GetSessionsResponse, + GetStatusRequest, + GetStatusResponse, + LogRequest, + LogResponse, + ResourceNamesRequest, + ResourceNamesResponse, + ResourceRPCSubtypesRequest, + ResourceRPCSubtypesResponse, + RestartModuleRequest, + RestartModuleResponse, + SendSessionHeartbeatRequest, + SendSessionHeartbeatResponse, + ShutdownRequest, + ShutdownResponse, + StartSessionRequest, + StartSessionResponse, + StopAllRequest, + StopAllResponse, + StreamStatusRequest, + StreamStatusResponse, + TransformPCDRequest, + TransformPCDResponse, + TransformPoseRequest, + TransformPoseResponse, + UnimplementedRobotServiceBase, +) + + +class MockRobot(UnimplementedRobotServiceBase): + SESSION_ID = "sid" + HEARTBEAT_INTERVAL = 2 + + def __init__(self): + self.heartbeat_count = 0 + super().__init__() + + async def RestartModule(self, stream: Stream[RestartModuleRequest, RestartModuleResponse]) -> None: + return None + + async def StartSession(self, stream: Stream[StartSessionRequest, StartSessionResponse]) -> None: + request = await stream.recv_message() + assert request is not None + heartbeat_window = Duration() + heartbeat_window.FromTimedelta(timedelta(seconds=self.HEARTBEAT_INTERVAL)) + response = StartSessionResponse(id=self.SESSION_ID, heartbeat_window=heartbeat_window) + await stream.send_message(response) + + async def SendSessionHeartbeat(self, stream: Stream[SendSessionHeartbeatRequest, SendSessionHeartbeatResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.heartbeat_count += 1 + response = SendSessionHeartbeatResponse() + await stream.send_message(response) + + async def ResourceNames(self, stream: Stream[ResourceNamesRequest, ResourceNamesResponse]) -> None: + raise MethodNotImplementedError("ResourceNames").grpc_error + + async def GetStatus(self, stream: Stream[GetStatusRequest, GetStatusResponse]) -> None: + raise MethodNotImplementedError("GetStatus").grpc_error + + async def StreamStatus(self, stream: Stream[StreamStatusRequest, StreamStatusResponse]) -> None: + raise MethodNotImplementedError("StreamStatus").grpc_error + + async def GetOperations(self, stream: Stream[GetOperationsRequest, GetOperationsResponse]) -> None: + raise MethodNotImplementedError("GetOperations").grpc_error + + async def ResourceRPCSubtypes(self, stream: Stream[ResourceRPCSubtypesRequest, ResourceRPCSubtypesResponse]) -> None: + raise MethodNotImplementedError("ResourceRPCSubtypes").grpc_error + + async def CancelOperation(self, stream: Stream[CancelOperationRequest, CancelOperationResponse]) -> None: + raise MethodNotImplementedError("CancelOperation").grpc_error + + async def BlockForOperation(self, stream: Stream[BlockForOperationRequest, BlockForOperationResponse]) -> None: + raise MethodNotImplementedError("BlockForOperation").grpc_error + + async def FrameSystemConfig(self, stream: Stream[FrameSystemConfigRequest, FrameSystemConfigResponse]) -> None: + raise MethodNotImplementedError("FrameSystemConfig").grpc_error + + async def TransformPose(self, stream: Stream[TransformPoseRequest, TransformPoseResponse]) -> None: + raise MethodNotImplementedError("TransformPose").grpc_error + + async def StopAll(self, stream: Stream[StopAllRequest, StopAllResponse]) -> None: + raise MethodNotImplementedError("StopAll").grpc_error + + async def GetSessions(self, stream: Stream[GetSessionsRequest, GetSessionsResponse]) -> None: + raise MethodNotImplementedError("GetSessions").grpc_error + + async def TransformPCD(self, stream: Stream[TransformPCDRequest, TransformPCDResponse]) -> None: + raise MethodNotImplementedError("TransformPCD").grpc_error + + async def Log(self, stream: Stream[LogRequest, LogResponse]) -> None: + raise MethodNotImplementedError("Log").grpc_error + + async def GetCloudMetadata(self, stream: Stream[GetCloudMetadataRequest, GetCloudMetadataResponse]) -> None: + raise MethodNotImplementedError("GetCloudMetadata").grpc_error + + async def Shutdown(self, stream: Stream[ShutdownRequest, ShutdownResponse]) -> None: + raise MethodNotImplementedError("Shutdown").grpc_error diff --git a/tests/mocks/services.py b/tests/mocks/services.py index b0ff44b6b..99669d0f9 100644 --- a/tests/mocks/services.py +++ b/tests/mocks/services.py @@ -1,232 +1,1775 @@ -import json +from datetime import datetime from typing import Any, Dict, List, Mapping, Optional, Sequence, Union +import bson +import numpy as np from grpclib.server import Stream +from numpy.typing import NDArray -from viam.components.types import CameraMimeType -from viam.proto.common import PointCloudObject, PoseInFrame, ResourceName +from viam.app.data_client import DataClient +from viam.gen.app.v1.app_pb2 import FragmentHistoryEntry, GetFragmentHistoryRequest, GetFragmentHistoryResponse +from viam.media.video import ViamImage +from viam.proto.app import ( + AddRoleRequest, + AddRoleResponse, + APIKeyWithAuthorizations, + Authorization, + ChangeRoleRequest, + ChangeRoleResponse, + CheckPermissionsRequest, + CheckPermissionsResponse, + CreateFragmentRequest, + CreateFragmentResponse, + CreateKeyFromExistingKeyAuthorizationsRequest, + CreateKeyFromExistingKeyAuthorizationsResponse, + CreateKeyRequest, + CreateKeyResponse, + CreateLocationRequest, + CreateLocationResponse, + CreateLocationSecretRequest, + CreateLocationSecretResponse, + CreateModuleRequest, + CreateModuleResponse, + CreateOrganizationInviteRequest, + CreateOrganizationInviteResponse, + CreateOrganizationRequest, + CreateOrganizationResponse, + CreateRegistryItemRequest, + CreateRegistryItemResponse, + CreateRobotPartSecretRequest, + CreateRobotPartSecretResponse, + DeleteFragmentRequest, + DeleteFragmentResponse, + DeleteKeyRequest, + DeleteKeyResponse, + DeleteLocationRequest, + DeleteLocationResponse, + DeleteLocationSecretRequest, + DeleteLocationSecretResponse, + DeleteOrganizationInviteRequest, + DeleteOrganizationInviteResponse, + DeleteOrganizationMemberRequest, + DeleteOrganizationMemberResponse, + DeleteOrganizationRequest, + DeleteOrganizationResponse, + DeleteRegistryItemRequest, + DeleteRegistryItemResponse, + DeleteRobotPartRequest, + DeleteRobotPartResponse, + DeleteRobotPartSecretRequest, + DeleteRobotPartSecretResponse, + DeleteRobotRequest, + DeleteRobotResponse, + Fragment, + GetFragmentRequest, + GetFragmentResponse, + GetLocationMetadataRequest, + GetLocationMetadataResponse, + GetLocationRequest, + GetLocationResponse, + GetModuleRequest, + GetModuleResponse, + GetOrganizationNamespaceAvailabilityRequest, + GetOrganizationNamespaceAvailabilityResponse, + GetOrganizationRequest, + GetOrganizationResponse, + GetOrganizationMetadataRequest, + GetOrganizationMetadataResponse, + GetOrganizationsWithAccessToLocationRequest, + GetOrganizationsWithAccessToLocationResponse, + GetRegistryItemRequest, + GetRegistryItemResponse, + GetRobotAPIKeysRequest, + GetRobotAPIKeysResponse, + GetRobotMetadataRequest, + GetRobotMetadataResponse, + GetRobotPartHistoryRequest, + GetRobotPartHistoryResponse, + GetRobotPartLogsRequest, + GetRobotPartLogsResponse, + GetRobotPartMetadataRequest, + GetRobotPartMetadataResponse, + GetRobotPartRequest, + GetRobotPartResponse, + GetRobotPartsRequest, + GetRobotPartsResponse, + GetRobotRequest, + GetRobotResponse, + GetRoverRentalRobotsRequest, + GetRoverRentalRobotsResponse, + GetUserIDByEmailRequest, + GetUserIDByEmailResponse, + ListAuthorizationsRequest, + ListAuthorizationsResponse, + ListFragmentsRequest, + ListFragmentsResponse, + ListKeysRequest, + ListKeysResponse, + ListLocationsRequest, + ListLocationsResponse, + ListModulesRequest, + ListModulesResponse, + ListOrganizationMembersRequest, + ListOrganizationMembersResponse, + ListOrganizationsByUserRequest, + ListOrganizationsByUserResponse, + ListOrganizationsRequest, + ListOrganizationsResponse, + ListRegistryItemsRequest, + ListRegistryItemsResponse, + ListRobotsRequest, + ListRobotsResponse, + Location, + LocationAuth, + LocationAuthRequest, + LocationAuthResponse, + MarkPartAsMainRequest, + MarkPartAsMainResponse, + MarkPartForRestartRequest, + MarkPartForRestartResponse, + Module, + NewRobotPartRequest, + NewRobotPartResponse, + NewRobotRequest, + NewRobotResponse, + Organization, + OrganizationIdentity, + OrganizationInvite, + OrganizationMember, + OrgDetails, + RegistryItem, + RemoveRoleRequest, + RemoveRoleResponse, + ResendOrganizationInviteRequest, + ResendOrganizationInviteResponse, + Robot, + RobotPart, + RobotPartHistoryEntry, + RotateKeyRequest, + RotateKeyResponse, + RoverRentalRobot, + ShareLocationRequest, + ShareLocationResponse, + TailRobotPartLogsRequest, + TailRobotPartLogsResponse, + UnimplementedAppServiceBase, + UnshareLocationRequest, + UnshareLocationResponse, + UpdateFragmentRequest, + UpdateFragmentResponse, + UpdateLocationMetadataRequest, + UpdateLocationMetadataResponse, + UpdateLocationRequest, + UpdateLocationResponse, + UpdateModuleRequest, + UpdateModuleResponse, + UpdateOrganizationInviteAuthorizationsRequest, + UpdateOrganizationInviteAuthorizationsResponse, + UpdateOrganizationMetadataRequest, + UpdateOrganizationMetadataResponse, + UpdateOrganizationRequest, + UpdateOrganizationResponse, + UpdateRegistryItemRequest, + UpdateRegistryItemResponse, + UpdateRobotMetadataRequest, + UpdateRobotMetadataResponse, + UpdateRobotPartMetadataRequest, + UpdateRobotPartMetadataResponse, + UpdateRobotPartRequest, + UpdateRobotPartResponse, + UpdateRobotRequest, + UpdateRobotResponse, + UploadModuleFileRequest, + UploadModuleFileResponse, +) +from viam.proto.app.billing import ( + GetCurrentMonthUsageRequest, + GetCurrentMonthUsageResponse, + GetInvoicePdfRequest, + GetInvoicePdfResponse, + GetInvoicesSummaryRequest, + GetInvoicesSummaryResponse, + GetOrgBillingInformationRequest, + GetOrgBillingInformationResponse, + UnimplementedBillingServiceBase, +) +from viam.proto.app.data import ( + AddBinaryDataToDatasetByIDsRequest, + AddBinaryDataToDatasetByIDsResponse, + AddBoundingBoxToImageByIDRequest, + AddBoundingBoxToImageByIDResponse, + AddTagsToBinaryDataByFilterRequest, + AddTagsToBinaryDataByFilterResponse, + AddTagsToBinaryDataByIDsRequest, + AddTagsToBinaryDataByIDsResponse, + BinaryData, + BinaryDataByFilterRequest, + BinaryDataByFilterResponse, + BinaryDataByIDsRequest, + BinaryDataByIDsResponse, + BoundingBoxLabelsByFilterRequest, + BoundingBoxLabelsByFilterResponse, + ConfigureDatabaseUserRequest, + ConfigureDatabaseUserResponse, + DeleteBinaryDataByFilterRequest, + DeleteBinaryDataByFilterResponse, + DeleteBinaryDataByIDsRequest, + DeleteBinaryDataByIDsResponse, + DeleteTabularDataRequest, + DeleteTabularDataResponse, + ExportTabularDataRequest, + ExportTabularDataResponse, + GetDatabaseConnectionRequest, + GetDatabaseConnectionResponse, + GetLatestTabularDataRequest, + GetLatestTabularDataResponse, + RemoveBinaryDataFromDatasetByIDsRequest, + RemoveBinaryDataFromDatasetByIDsResponse, + RemoveBoundingBoxFromImageByIDRequest, + RemoveBoundingBoxFromImageByIDResponse, + RemoveTagsFromBinaryDataByFilterRequest, + RemoveTagsFromBinaryDataByFilterResponse, + RemoveTagsFromBinaryDataByIDsRequest, + RemoveTagsFromBinaryDataByIDsResponse, + TabularData, + TabularDataByFilterRequest, + TabularDataByFilterResponse, + TabularDataByMQLRequest, + TabularDataByMQLResponse, + TabularDataBySQLRequest, + TabularDataBySQLResponse, + TagsByFilterRequest, + TagsByFilterResponse, + UnimplementedDataServiceBase, +) +from viam.proto.app.dataset import ( + CreateDatasetRequest, + CreateDatasetResponse, + Dataset, + DatasetServiceBase, + DeleteDatasetRequest, + DeleteDatasetResponse, + ListDatasetsByIDsRequest, + ListDatasetsByIDsResponse, + ListDatasetsByOrganizationIDRequest, + ListDatasetsByOrganizationIDResponse, + RenameDatasetRequest, + RenameDatasetResponse, +) +from viam.proto.app.datasync import ( + DataCaptureUploadRequest, + DataCaptureUploadResponse, + DataSyncServiceBase, + FileUploadRequest, + FileUploadResponse, + StreamingDataCaptureUploadRequest, + StreamingDataCaptureUploadResponse, +) +from viam.proto.app.mltraining import ( + CancelTrainingJobRequest, + CancelTrainingJobResponse, + DeleteCompletedTrainingJobRequest, + DeleteCompletedTrainingJobResponse, + GetTrainingJobRequest, + GetTrainingJobResponse, + ListTrainingJobsRequest, + ListTrainingJobsResponse, + SubmitCustomTrainingJobRequest, + SubmitCustomTrainingJobResponse, + SubmitTrainingJobRequest, + SubmitTrainingJobResponse, + TrainingJobMetadata, + UnimplementedMLTrainingServiceBase, +) +from viam.proto.app.packages import PackageType +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, + GeoGeometry, + GeoPoint, + LogEntry, + PointCloudObject, + Pose, + PoseInFrame, + ResourceName, +) +from viam.proto.provisioning import ( + GetNetworkListRequest, + GetNetworkListResponse, + GetSmartMachineStatusRequest, + GetSmartMachineStatusResponse, + NetworkInfo, + ProvisioningServiceBase, + SetNetworkCredentialsRequest, + SetNetworkCredentialsResponse, + SetSmartMachineCredentialsRequest, + SetSmartMachineCredentialsResponse, +) +from viam.proto.service.mlmodel import ( + FlatTensor, + FlatTensorDataDouble, + FlatTensorDataFloat, + FlatTensorDataInt8, + FlatTensorDataInt16, + FlatTensorDataInt32, + FlatTensorDataInt64, + FlatTensorDataUInt8, + FlatTensorDataUInt16, + FlatTensorDataUInt32, + FlatTensorDataUInt64, + FlatTensors, +) from viam.proto.service.motion import ( + Constraints, + GetPlanRequest, + GetPlanResponse, GetPoseRequest, GetPoseResponse, + ListPlanStatusesRequest, + ListPlanStatusesResponse, MotionServiceBase, + MoveOnGlobeRequest, + MoveOnGlobeResponse, + MoveOnMapRequest, + MoveOnMapResponse, MoveRequest, MoveResponse, - MoveSingleComponentRequest, - MoveSingleComponentResponse, -) -from viam.proto.service.sensors import ( - GetReadingsRequest, - GetReadingsResponse, - GetSensorsRequest, - GetSensorsResponse, - Readings, - SensorsServiceBase, + StopPlanRequest, + StopPlanResponse, ) -from viam.proto.service.vision import ( - AddClassifierRequest, - AddClassifierResponse, - AddDetectorRequest, - AddDetectorResponse, - AddSegmenterRequest, - AddSegmenterResponse, - Classification, - Detection, - GetClassificationsFromCameraRequest, - GetClassificationsFromCameraResponse, - GetClassificationsRequest, - GetClassificationsResponse, - GetClassifierNamesRequest, - GetClassifierNamesResponse, - GetDetectionsFromCameraRequest, - GetDetectionsFromCameraResponse, - GetDetectionsRequest, - GetDetectionsResponse, - GetDetectorNamesRequest, - GetDetectorNamesResponse, - GetModelParameterSchemaRequest, - GetModelParameterSchemaResponse, - GetObjectPointCloudsRequest, - GetObjectPointCloudsResponse, - GetSegmenterNamesRequest, - GetSegmenterNamesResponse, - RemoveClassifierRequest, - RemoveClassifierResponse, - RemoveDetectorRequest, - RemoveDetectorResponse, - RemoveSegmenterRequest, - RemoveSegmenterResponse, - VisionServiceBase, -) -from viam.utils import struct_to_dict +from viam.proto.service.navigation import MapType, Mode, Path, Waypoint +from viam.proto.service.slam import MappingMode, SensorInfo, SensorType +from viam.proto.service.vision import Classification, Detection +from viam.services.discovery import Discovery +from viam.services.generic import Generic as GenericService +from viam.services.mlmodel import File, LabelType, Metadata, MLModel, TensorInfo +from viam.services.mlmodel.utils import flat_tensors_to_ndarrays, ndarrays_to_flat_tensors +from viam.services.navigation import Navigation +from viam.services.slam import SLAM +from viam.services.vision import CaptureAllResult, Vision +from viam.utils import ValueTypes, datetime_to_timestamp, dict_to_struct, struct_to_dict + + +class MockVision(Vision): + def __init__( + self, + name: str, + detectors: List[str], + detections: List[Detection], + classifiers: List[str], + classifications: List[Classification], + segmenters: List[str], + point_clouds: List[PointCloudObject], + image: ViamImage, + properties: Vision.Properties, + ): + self.detectors = detectors + self.detections = detections + self.classifiers = classifiers + self.classifications = classifications + self.segmenters = segmenters + self.point_clouds = point_clouds + self.image = image + self.properties = properties + self.extra: Optional[Mapping[str, Any]] = None + self.timeout: Optional[float] = None + super().__init__(name) + + async def get_properties( + self, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + ) -> Vision.Properties: + self.extra = extra + self.timeout = timeout + return self.properties + + async def capture_all_from_camera( + self, + camera_name: str, + return_image: bool = False, + return_classifications: bool = False, + return_detections: bool = False, + return_object_point_clouds: bool = False, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + ) -> CaptureAllResult: + self.extra = extra + self.timeout = timeout + result = CaptureAllResult() + if return_image: + result.image = self.image + if return_classifications: + result.classifications = self.classifications + if return_detections: + result.detections = self.detections + if return_object_point_clouds: + result.objects = self.point_clouds + return result + + async def get_detections_from_camera( + self, camera_name: str, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None + ) -> List[Detection]: + self.extra = extra + self.timeout = timeout + return self.detections + + async def get_detections( + self, image: ViamImage, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None + ) -> List[Detection]: + self.extra = extra + self.timeout = timeout + return self.detections + + async def get_classifications_from_camera( + self, camera_name: str, count: int, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None + ) -> List[Classification]: + self.extra = extra + self.timeout = timeout + return self.classifications + + async def get_classifications( + self, image: ViamImage, count: int, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None + ) -> List[Classification]: + self.extra = extra + self.timeout = timeout + return self.classifications + + async def get_object_point_clouds( + self, camera_name: str, *, extra: Optional[Mapping[str, Any]] = None, timeout: Optional[float] = None + ) -> List[PointCloudObject]: + self.extra = extra + self.timeout = timeout + return self.point_clouds + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None) -> Mapping[str, ValueTypes]: + self.timeout = timeout + return {"cmd": command} + + +class MockDiscovery(Discovery): + def __init__( + self, + name: str, + ): + self.extra: Optional[Mapping[str, Any]] = None + self.timeout: Optional[float] = None + super().__init__(name) + + async def discover_resources( + self, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> List[ComponentConfig]: + self.extra = extra + self.timeout = timeout + result = ComponentConfig() + return [result] + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None) -> Mapping[str, ValueTypes]: + self.timeout = timeout + return {"cmd": command} + + +class MockMLModel(MLModel): + INT8_NDARRAY = np.array([[0, -1], [8, 8]], dtype=np.int8) + INT16_NDARRAY = np.array([1, 0, 0, 69, -1, 16], dtype=np.int16) + INT32_NDARRAY = np.array([0, -1, 32], dtype=np.int32) + INT64_NDARRAY = np.array([(-2) ** 63, -1, 64], dtype=np.int64) + UINT8_NDARRAY = np.array([0, 1, 8], dtype=np.uint8) + UINT16_NDARRAY = np.array([[0, 1], [16, 16]], dtype=np.uint16) + UINT32_NDARRAY = np.array([0, 1, 32], dtype=np.uint32) + UINT64_NDARRAY = np.array([0, 1, 2**63], dtype=np.uint64) + FLOAT_NDARRAY = np.array([[0, -88.8], [32, 32]], dtype=np.float32) + DOUBLE_NDARRAY = np.array([0, -88.8, 88888888], dtype=np.float64) + SQUARE_INT16_NDARRAY = np.array([[-257, 0, 256], [-257, 0, 256], [-257, 0, 256]], dtype=np.int16) + + EMPTY_NDARRAYS = {} + EMPTY_TENSORS = FlatTensors(tensors=None) + + DOUBLE_NDARRAYS = {"0": DOUBLE_NDARRAY} + DOUBLE_TENSORS = FlatTensors(tensors={"0": FlatTensor(shape=(3,), double_tensor=FlatTensorDataDouble(data=DOUBLE_NDARRAY))}) + + DOUBLE_FLOAT_NDARRAYS = {"0": DOUBLE_NDARRAY, "1": FLOAT_NDARRAY} + DOUBLE_FLOAT_TENSORS = FlatTensors( + tensors={ + "0": FlatTensor(shape=(3,), double_tensor=FlatTensorDataDouble(data=DOUBLE_NDARRAY)), + "1": FlatTensor(shape=(2, 2), float_tensor=FlatTensorDataFloat(data=FLOAT_NDARRAY.flatten())), + } + ) + + INTS_NDARRAYS = {"0": INT8_NDARRAY, "1": INT16_NDARRAY, "2": INT32_NDARRAY, "3": INT64_NDARRAY} + INTS_FLAT_TENSORS = FlatTensors( + tensors={ + "0": FlatTensor(shape=(2, 2), int8_tensor=FlatTensorDataInt8(data=INT8_NDARRAY.tobytes())), + "1": FlatTensor(shape=(6,), int16_tensor=FlatTensorDataInt16(data=INT16_NDARRAY.flatten().astype(np.uint32))), + "2": FlatTensor(shape=(3,), int32_tensor=FlatTensorDataInt32(data=INT32_NDARRAY)), + "3": FlatTensor(shape=(3,), int64_tensor=FlatTensorDataInt64(data=INT64_NDARRAY)), + } + ) + + UINTS_NDARRAYS = {"0": UINT8_NDARRAY, "1": UINT16_NDARRAY, "2": UINT32_NDARRAY, "3": UINT64_NDARRAY} + UINTS_FLAT_TENSORS = FlatTensors( + tensors={ + "0": FlatTensor(shape=(3,), uint8_tensor=FlatTensorDataUInt8(data=UINT8_NDARRAY.tobytes())), + "1": FlatTensor(shape=(2, 2), uint16_tensor=FlatTensorDataUInt16(data=UINT16_NDARRAY.flatten().astype(np.uint32))), + "2": FlatTensor(shape=(3,), uint32_tensor=FlatTensorDataUInt32(data=UINT32_NDARRAY)), + "3": FlatTensor(shape=(3,), uint64_tensor=FlatTensorDataUInt64(data=UINT64_NDARRAY)), + } + ) + + SQUARE_INT_UINT_NDARRAYS = {"0": SQUARE_INT16_NDARRAY, "1": UINT64_NDARRAY} + SQUARE_INT_UINT_TENSORS = { + "0": FlatTensor(shape=(3, 3), int16_tensor=FlatTensorDataInt16(data=SQUARE_INT16_NDARRAY)), + "1": FlatTensor(shape=(3,), int64_tensor=FlatTensorDataInt64(data=INT64_NDARRAY)), + } + + META_INPUTS = [TensorInfo(name="image", description="i0", data_type="uint8", shape=[300, 200])] + META_OUTPUTS = [ + TensorInfo(name="n_detections", description="o0", data_type="int32", shape=[1]), + TensorInfo(name="confidence_scores", description="o1", data_type="float32", shape=[3, 1]), + TensorInfo( + name="labels", + description="o2", + data_type="int32", + shape=[3, 1], + associated_files=[ + File( + name="category_labels.txt", + description="these labels represent types of plants", + label_type=LabelType.LABEL_TYPE_TENSOR_VALUE, + ) + ], + ), + TensorInfo(name="locations", description="o3", data_type="float32", shape=[4, 3, 1]), + ] + META = Metadata(name="fake_detector", type="object_detector", description="desc", input_info=META_INPUTS, output_info=META_OUTPUTS) + + def __init__(self, name: str): + self.timeout: Optional[float] = None + + super().__init__(name) + async def infer( + self, + input_tensors: Dict[str, NDArray], + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> Dict[str, NDArray]: + self.timeout = timeout + request_data = ndarrays_to_flat_tensors(input_tensors) + response_data = flat_tensors_to_ndarrays(request_data) + return response_data + + async def metadata(self, *, extra: Optional[Mapping[str, ValueTypes]] = None, timeout: Optional[float] = None) -> Metadata: + self.timeout = timeout + return self.META -class MockMotionService(MotionServiceBase): + +class MockMotion(MotionServiceBase): def __init__( self, move_responses: Dict[str, bool], - move_single_component_responses: Dict[str, bool], get_pose_responses: Dict[str, PoseInFrame], + get_plan_response: GetPlanResponse, + list_plan_statuses_response: ListPlanStatusesResponse, ): self.move_responses = move_responses - self.move_single_component_responses = move_single_component_responses self.get_pose_responses = get_pose_responses + self.get_plan_response = get_plan_response + self.list_plan_statuses_response = list_plan_statuses_response + self.constraints: Optional[Constraints] = None self.extra: Optional[Mapping[str, Any]] = None + self.timeout: Optional[float] = None async def Move(self, stream: Stream[MoveRequest, MoveResponse]) -> None: request = await stream.recv_message() assert request is not None name: ResourceName = request.component_name + self.constraints = request.constraints self.extra = struct_to_dict(request.extra) + self.timeout = stream.deadline.time_remaining() if stream.deadline else None success = self.move_responses[name.name] response = MoveResponse(success=success) await stream.send_message(response) - async def MoveSingleComponent(self, stream: Stream[MoveSingleComponentRequest, MoveSingleComponentResponse]) -> None: + async def MoveOnMap(self, stream: Stream[MoveOnMapRequest, MoveOnMapResponse]) -> None: request = await stream.recv_message() assert request is not None - name: ResourceName = request.component_name + self.component_name = request.component_name + self.destination = request.destination + self.slam_service = request.slam_service_name + self.configuration = request.motion_configuration + self.obstacles = request.obstacles self.extra = struct_to_dict(request.extra) - success = self.move_single_component_responses[name.name] - response = MoveSingleComponentResponse(success=success) - await stream.send_message(response) + self.timeout = stream.deadline.time_remaining() if stream.deadline else None + self.execution_id = "some execution id" + await stream.send_message(MoveOnMapResponse(execution_id=self.execution_id)) + + async def MoveOnGlobe(self, stream: Stream[MoveOnGlobeRequest, MoveOnGlobeResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.component_name = request.component_name + self.destination = request.destination + self.movement_sensor = request.movement_sensor_name + self.obstacles = request.obstacles + self.heading = request.heading + self.configuration = request.motion_configuration + self.bounding_regions = request.bounding_regions + self.extra = struct_to_dict(request.extra) + self.timeout = stream.deadline.time_remaining() if stream.deadline else None + self.execution_id = "some execution id" + await stream.send_message(MoveOnGlobeResponse(execution_id=self.execution_id)) async def GetPose(self, stream: Stream[GetPoseRequest, GetPoseResponse]) -> None: request = await stream.recv_message() assert request is not None name: ResourceName = request.component_name self.extra = struct_to_dict(request.extra) + self.timeout = stream.deadline.time_remaining() if stream.deadline else None pose = self.get_pose_responses[name.name] response = GetPoseResponse(pose=pose) await stream.send_message(response) + async def StopPlan(self, stream: Stream[StopPlanRequest, StopPlanResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.component_name = request.component_name + self.extra = struct_to_dict(request.extra) + self.timeout = stream.deadline.time_remaining() if stream.deadline else None + await stream.send_message(StopPlanResponse()) -class MockSensorsService(SensorsServiceBase): - def __init__(self, sensors: List[ResourceName], readings: List[Readings]): - self.sensors = sensors - self.readings = readings - - async def GetSensors(self, stream: Stream[GetSensorsRequest, GetSensorsResponse]) -> None: + async def ListPlanStatuses(self, stream: Stream[ListPlanStatusesRequest, ListPlanStatusesResponse]) -> None: request = await stream.recv_message() assert request is not None - response = GetSensorsResponse(sensor_names=self.sensors) + self.only_active_plans = request.only_active_plans + self.extra = struct_to_dict(request.extra) + self.timeout = stream.deadline.time_remaining() if stream.deadline else None + response = self.list_plan_statuses_response await stream.send_message(response) - async def GetReadings(self, stream: Stream[GetReadingsRequest, GetReadingsResponse]) -> None: + async def GetPlan(self, stream: Stream[GetPlanRequest, GetPlanResponse]) -> None: request = await stream.recv_message() assert request is not None - self.sensors_for_readings: List[ResourceName] = list(request.sensor_names) - response = GetReadingsResponse(readings=self.readings) - await stream.send_message(response) + self.component_name = request.component_name + self.last_plan_only = request.last_plan_only + self.execution_id = request.execution_id + self.extra = struct_to_dict(request.extra) + self.timeout = stream.deadline.time_remaining() if stream.deadline else None + await stream.send_message(self.get_plan_response) + + async def DoCommand(self, stream: Stream[DoCommandRequest, DoCommandResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.timeout = stream.deadline.time_remaining() if stream.deadline else None + await stream.send_message(DoCommandResponse(result=request.command)) + + +class MockSLAM(SLAM): + INTERNAL_STATE_CHUNKS = [bytes(5), bytes(2)] + POINT_CLOUD_PCD_CHUNKS = [bytes(3), bytes(2)] + POINT_CLOUD_PCD_CHUNKS_EDITED = [bytes(7), bytes(4)] + POSITION = Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20) + CLOUD_SLAM = False + MAPPING_MODE = MappingMode.MAPPING_MODE_UNSPECIFIED + INTERNAL_STATE_FILE_TYPE = ".pbstream" + SENSOR_INFO = [ + SensorInfo(name="my-camera", type=SensorType.SENSOR_TYPE_CAMERA), + SensorInfo(name="my-movement-sensor", type=SensorType.SENSOR_TYPE_MOVEMENT_SENSOR), + ] + + def __init__(self, name: str): + self.name = name + self.timeout: Optional[float] = None + self.properties = SLAM.Properties( + cloud_slam=self.CLOUD_SLAM, + mapping_mode=self.MAPPING_MODE, + internal_state_file_type=self.INTERNAL_STATE_FILE_TYPE, + sensor_info=self.SENSOR_INFO, + ) + super().__init__(name) + + async def get_internal_state(self, *, timeout: Optional[float] = None) -> List[bytes]: + self.timeout = timeout + return self.INTERNAL_STATE_CHUNKS + + async def get_point_cloud_map(self, return_edited_map: bool = False, *, timeout: Optional[float] = None) -> List[bytes]: + self.timeout = timeout + if return_edited_map: + return self.POINT_CLOUD_PCD_CHUNKS_EDITED + return self.POINT_CLOUD_PCD_CHUNKS + + async def get_position(self, *, timeout: Optional[float] = None) -> Pose: + self.timeout = timeout + return self.POSITION + + async def get_properties(self, *, timeout: Optional[float] = None) -> SLAM.Properties: + self.timeout = timeout + return self.properties + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + + +class MockNavigation(Navigation): + LOCATION = GeoPoint(latitude=100.0, longitude=150.0) + OBSTACLES = [GeoGeometry(location=GeoPoint(latitude=200.0, longitude=250.0))] + WAYPOINTS = [Waypoint(location=GeoPoint(latitude=300.0, longitude=350.0))] + PATHS = [Path(destination_waypoint_id="foo", geopoints=[LOCATION])] + + def __init__(self, name: str): + self.name = name + self.add_waypoints: list[GeoPoint] = [] + self.remove_waypoints: list[str] = [] + self.mode = Mode.MODE_UNSPECIFIED + self.map_type = MapType.MAP_TYPE_UNSPECIFIED + self.timeout: Optional[float] = None + super().__init__(name) + async def get_paths(self, *, timeout: Optional[float] = None) -> List[Path]: + self.timeout = timeout + return self.PATHS -class MockVisionService(VisionServiceBase): + async def get_location(self, *, timeout: Optional[float] = None) -> GeoPoint: + self.timeout = timeout + return self.LOCATION + + async def get_obstacles(self, *, timeout: Optional[float] = None) -> List[GeoGeometry]: + self.timeout = timeout + return self.OBSTACLES + + async def get_waypoints(self, *, timeout: Optional[float] = None) -> List[Waypoint]: + self.timeout = timeout + return self.WAYPOINTS + + async def add_waypoint(self, point: GeoPoint, *, timeout: Optional[float] = None): + self.timeout = timeout + self.add_waypoints.append(point) + + async def remove_waypoint(self, id: str, *, timeout: Optional[float] = None): + self.timeout = timeout + self.remove_waypoints.append(id) + + async def get_mode(self, *, timeout: Optional[float] = None) -> Mode.ValueType: + self.timeout = timeout + return self.mode + + async def set_mode(self, mode: Mode.ValueType, *, timeout: Optional[float] = None): + self.timeout = timeout + self.mode = mode + + async def get_properties(self, *, timeout: Optional[float] = None) -> MapType.ValueType: + self.timeout = timeout + return self.map_type + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + return {"command": command} + + +class MockProvisioning(ProvisioningServiceBase): def __init__( self, - detectors: List[str], - detections: List[Detection], - classifiers: List[str], - classifications: List[Classification], - segmenters: List[str], - point_clouds: List[PointCloudObject], - model_schema: Mapping[str, Mapping[str, Union[str, int, float, bool, Sequence, Mapping]]], + smart_machine_status: GetSmartMachineStatusResponse, + network_info: List[NetworkInfo], ): - self.detectors = detectors - self.detections = detections - self.classifiers = classifiers - self.classifications = classifications - self.segmenters = segmenters - self.point_clouds = point_clouds - self.model_schema = model_schema + self.smart_machine_status = smart_machine_status + self.network_info = network_info - async def GetDetectorNames(self, stream: Stream[GetDetectorNamesRequest, GetDetectorNamesResponse]) -> None: + async def GetNetworkList(self, stream: Stream[GetNetworkListRequest, GetNetworkListResponse]) -> None: request = await stream.recv_message() assert request is not None - response = GetDetectorNamesResponse(detector_names=self.detectors) - await stream.send_message(response) + await stream.send_message(GetNetworkListResponse(networks=self.network_info)) - async def AddDetector(self, stream: Stream[AddDetectorRequest, AddDetectorResponse]) -> None: + async def GetSmartMachineStatus(self, stream: Stream[GetSmartMachineStatusRequest, GetSmartMachineStatusResponse]) -> None: request = await stream.recv_message() assert request is not None - self.detectors.append(request.detector_name) - await stream.send_message(AddDetectorResponse()) + await stream.send_message(self.smart_machine_status) - async def RemoveDetector(self, stream: Stream[RemoveDetectorRequest, RemoveDetectorResponse]) -> None: + async def SetNetworkCredentials(self, stream: Stream[SetNetworkCredentialsRequest, SetNetworkCredentialsResponse]) -> None: request = await stream.recv_message() assert request is not None - self.detectors.remove(request.detector_name) - await stream.send_message(RemoveDetectorResponse()) + self.network_type = request.type + self.ssid = request.ssid + self.psk = request.psk + await stream.send_message(SetNetworkCredentialsResponse()) - async def GetDetectionsFromCamera(self, stream: Stream[GetDetectionsFromCameraRequest, GetDetectionsFromCameraResponse]) -> None: + async def SetSmartMachineCredentials( + self, + stream: Stream[SetSmartMachineCredentialsRequest, SetSmartMachineCredentialsResponse], + ) -> None: request = await stream.recv_message() assert request is not None - response = GetDetectionsFromCameraResponse(detections=self.detections) - await stream.send_message(response) + self.cloud_config = request.cloud + await stream.send_message(SetSmartMachineCredentialsResponse()) + + +class MockData(UnimplementedDataServiceBase): + def __init__( + self, + tabular_response: List[DataClient.TabularData], + tabular_export_response: List[ExportTabularDataResponse], + tabular_query_response: List[Dict[str, Union[ValueTypes, datetime]]], + binary_response: List[BinaryData], + delete_remove_response: int, + tags_response: List[str], + bbox_labels_response: List[str], + hostname_response: str, + ): + self.tabular_response = tabular_response + self.tabular_export_response = tabular_export_response + self.tabular_query_response = tabular_query_response + self.binary_response = binary_response + self.delete_remove_response = delete_remove_response + self.tags_response = tags_response + self.bbox_labels_response = bbox_labels_response + self.hostname_response = hostname_response + self.was_tabular_data_requested = False + self.was_binary_data_requested = False - async def GetDetections(self, stream: Stream[GetDetectionsRequest, GetDetectionsResponse]) -> None: + async def TabularDataByFilter(self, stream: Stream[TabularDataByFilterRequest, TabularDataByFilterResponse]) -> None: request = await stream.recv_message() assert request is not None - response = GetDetectionsResponse(detections=self.detections) - await stream.send_message(response) + if self.was_tabular_data_requested: + await stream.send_message(TabularDataByFilterResponse(data=None)) + return + self.filter = request.data_request.filter + self.order = request.data_request.sort_order + self.limit = request.data_request.limit + self.count_only = request.count_only + self.last = request.data_request.last + tabular_response_structs = [] + tabular_metadata = [data.metadata for data in self.tabular_response] + for idx, tabular_data in enumerate(self.tabular_response): + tabular_response_structs.append( + TabularData( + data=dict_to_struct(tabular_data.data), + metadata_index=idx, + time_requested=datetime_to_timestamp(tabular_data.time_requested), + time_received=datetime_to_timestamp(tabular_data.time_received), + ) + ) + await stream.send_message( + TabularDataByFilterResponse( + data=tabular_response_structs, + metadata=tabular_metadata, + count=len(tabular_response_structs), + last="LAST_TABULAR_DATA_PAGE_ID", + ) + ) + self.was_tabular_data_requested = True - async def GetClassifierNames(self, stream: Stream[GetClassifierNamesRequest, GetClassifierNamesResponse]) -> None: + async def BinaryDataByFilter(self, stream: Stream[BinaryDataByFilterRequest, BinaryDataByFilterResponse]) -> None: request = await stream.recv_message() assert request is not None - response = GetClassifierNamesResponse(classifier_names=self.classifiers) - await stream.send_message(response) + if self.was_binary_data_requested: + await stream.send_message(BinaryDataByFilterResponse()) + return + self.filter = request.data_request.filter + self.order = request.data_request.sort_order + self.limit = request.data_request.limit + self.include_binary = request.include_binary + self.count_only = request.count_only + self.include_internal_data = request.include_internal_data + self.last = request.data_request.last + await stream.send_message( + BinaryDataByFilterResponse( + data=self.binary_response, + count=len(self.binary_response), + last="LAST_BINARY_DATA_PAGE_ID", + ) + ) + self.was_binary_data_requested = True - async def AddClassifier(self, stream: Stream[AddClassifierRequest, AddClassifierResponse]) -> None: + async def BinaryDataByIDs(self, stream: Stream[BinaryDataByIDsRequest, BinaryDataByIDsResponse]) -> None: request = await stream.recv_message() assert request is not None - self.classifiers.append(request.classifier_name) - await stream.send_message(AddClassifierResponse()) + self.binary_ids = request.binary_ids + self.binary_data_ids = request.binary_data_ids + await stream.send_message(BinaryDataByIDsResponse(data=self.binary_response)) - async def RemoveClassifier(self, stream: Stream[RemoveClassifierRequest, RemoveClassifierResponse]) -> None: + async def DeleteTabularData(self, stream: Stream[DeleteTabularDataRequest, DeleteTabularDataResponse]) -> None: request = await stream.recv_message() assert request is not None - self.classifiers.remove(request.classifier_name) - await stream.send_message(RemoveClassifierResponse()) + self.organization_id = request.organization_id + self.delete_older_than_days = request.delete_older_than_days + await stream.send_message(DeleteTabularDataResponse(deleted_count=self.delete_remove_response)) - async def GetClassificationsFromCamera( - self, stream: Stream[GetClassificationsFromCameraRequest, GetClassificationsFromCameraResponse] + async def DeleteBinaryDataByFilter(self, stream: Stream[DeleteBinaryDataByFilterRequest, DeleteBinaryDataByFilterResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.filter = request.filter + await stream.send_message(DeleteBinaryDataByFilterResponse(deleted_count=self.delete_remove_response)) + + async def DeleteBinaryDataByIDs(self, stream: Stream[DeleteBinaryDataByIDsRequest, DeleteBinaryDataByIDsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.binary_ids = request.binary_ids + self.binary_data_ids = request.binary_data_ids + await stream.send_message(DeleteBinaryDataByIDsResponse(deleted_count=self.delete_remove_response)) + + async def AddTagsToBinaryDataByIDs(self, stream: Stream[AddTagsToBinaryDataByIDsRequest, AddTagsToBinaryDataByIDsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.binary_ids = request.binary_ids + self.binary_data_ids = request.binary_data_ids + self.tags = request.tags + await stream.send_message(AddTagsToBinaryDataByIDsResponse()) + + async def AddTagsToBinaryDataByFilter( + self, stream: Stream[AddTagsToBinaryDataByFilterRequest, AddTagsToBinaryDataByFilterResponse] ) -> None: request = await stream.recv_message() assert request is not None - response = GetClassificationsFromCameraResponse(classifications=self.classifications) - await stream.send_message(response) + self.filter = request.filter + self.tags = request.tags + await stream.send_message(AddTagsToBinaryDataByFilterResponse()) + + async def RemoveTagsFromBinaryDataByIDs( + self, stream: Stream[RemoveTagsFromBinaryDataByIDsRequest, RemoveTagsFromBinaryDataByIDsResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + self.binary_ids = request.binary_ids + self.binary_data_ids = request.binary_data_ids + self.tags = request.tags + await stream.send_message(RemoveTagsFromBinaryDataByIDsResponse(deleted_count=self.delete_remove_response)) - async def GetClassifications(self, stream: Stream[GetClassificationsRequest, GetClassificationsResponse]) -> None: + async def RemoveTagsFromBinaryDataByFilter( + self, stream: Stream[RemoveTagsFromBinaryDataByFilterRequest, RemoveTagsFromBinaryDataByFilterResponse] + ) -> None: request = await stream.recv_message() assert request is not None - response = GetClassificationsResponse(classifications=self.classifications) - await stream.send_message(response) + self.filter = request.filter + self.tags = request.tags + await stream.send_message(RemoveTagsFromBinaryDataByFilterResponse(deleted_count=len(request.tags))) - async def GetObjectPointClouds(self, stream: Stream[GetObjectPointCloudsRequest, GetObjectPointCloudsResponse]) -> None: + async def TagsByFilter(self, stream: Stream[TagsByFilterRequest, TagsByFilterResponse]) -> None: request = await stream.recv_message() assert request is not None - response = GetObjectPointCloudsResponse(mime_type=CameraMimeType.PCD.value, objects=self.point_clouds) - await stream.send_message(response) + self.filter = request.filter + await stream.send_message(TagsByFilterResponse(tags=self.tags_response)) - async def GetModelParameterSchema(self, stream: Stream[GetModelParameterSchemaRequest, GetModelParameterSchemaResponse]) -> None: + async def AddBoundingBoxToImageByID(self, stream: Stream[AddBoundingBoxToImageByIDRequest, AddBoundingBoxToImageByIDResponse]) -> None: request = await stream.recv_message() assert request is not None - schema = self.model_schema[request.model_type] - response = GetModelParameterSchemaResponse(model_parameter_schema=json.dumps(schema).encode("utf-8")) - await stream.send_message(response) + await stream.send_message(AddBoundingBoxToImageByIDResponse(bbox_id=self.bbox_labels_response[0])) + + async def RemoveBoundingBoxFromImageByID( + self, stream: Stream[RemoveBoundingBoxFromImageByIDRequest, RemoveBoundingBoxFromImageByIDResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + self.removed_label = request.bbox_id + self.removed_id = request.binary_id + self.removed_binary_data_id = request.binary_data_id + await stream.send_message(RemoveBoundingBoxFromImageByIDResponse()) + + async def BoundingBoxLabelsByFilter(self, stream: Stream[BoundingBoxLabelsByFilterRequest, BoundingBoxLabelsByFilterResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.filter = request.filter + await stream.send_message(BoundingBoxLabelsByFilterResponse(labels=self.bbox_labels_response)) + + async def GetDatabaseConnection(self, stream: Stream[GetDatabaseConnectionRequest, GetDatabaseConnectionResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.organization_id = request.organization_id + await stream.send_message(GetDatabaseConnectionResponse(hostname=self.hostname_response)) + + async def ConfigureDatabaseUser(self, stream: Stream[ConfigureDatabaseUserRequest, ConfigureDatabaseUserResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.organization_id = request.organization_id + self.password = request.password + await stream.send_message(ConfigureDatabaseUserResponse()) + + async def AddBinaryDataToDatasetByIDs( + self, stream: Stream[AddBinaryDataToDatasetByIDsRequest, AddBinaryDataToDatasetByIDsResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + self.added_data_ids = request.binary_ids + self.added_binary_data_ids = request.binary_data_ids + self.dataset_id = request.dataset_id + await stream.send_message(AddBinaryDataToDatasetByIDsResponse()) + + async def RemoveBinaryDataFromDatasetByIDs( + self, stream: Stream[RemoveBinaryDataFromDatasetByIDsRequest, RemoveBinaryDataFromDatasetByIDsResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + self.removed_data_ids = request.binary_ids + self.removed_binary_data_ids = request.binary_data_ids + self.dataset_id = request.dataset_id + await stream.send_message(RemoveBinaryDataFromDatasetByIDsResponse()) + + async def TabularDataBySQL(self, stream: Stream[TabularDataBySQLRequest, TabularDataBySQLResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(TabularDataBySQLResponse(raw_data=[bson.encode(dict) for dict in self.tabular_query_response])) + + async def TabularDataByMQL(self, stream: Stream[TabularDataByMQLRequest, TabularDataByMQLResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(TabularDataByMQLResponse(raw_data=[bson.encode(dict) for dict in self.tabular_query_response])) + + async def GetLatestTabularData(self, stream: Stream[GetLatestTabularDataRequest, GetLatestTabularDataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.part_id = request.part_id + self.resource_name = request.resource_name + self.resource_subtype = request.resource_subtype + self.method_name = request.method_name + timestamp = datetime_to_timestamp(datetime(2024, 12, 25)) + data = dict_to_struct(self.tabular_response[0].data) + await stream.send_message(GetLatestTabularDataResponse(time_captured=timestamp, time_synced=timestamp, payload=data)) + + async def ExportTabularData(self, stream: Stream[ExportTabularDataRequest, ExportTabularDataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.part_id = request.part_id + self.resource_name = request.resource_name + self.resource_subtype = request.resource_subtype + self.method_name = request.method_name + self.interval = request.interval + for tabular_data in self.tabular_export_response: + await stream.send_message(tabular_data) + + +class MockDataset(DatasetServiceBase): + def __init__(self, create_response: str, datasets_response: Sequence[Dataset]): + self.create_response = create_response + self.datasets_response = datasets_response + + async def CreateDataset(self, stream: Stream[CreateDatasetRequest, CreateDatasetResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.name = request.name + self.org_id = request.organization_id + await stream.send_message(CreateDatasetResponse(id=self.create_response)) + + async def DeleteDataset(self, stream: Stream[DeleteDatasetRequest, DeleteDatasetResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.deleted_id = request.id + await stream.send_message(DeleteDatasetResponse()) + + async def ListDatasetsByIDs(self, stream: Stream[ListDatasetsByIDsRequest, ListDatasetsByIDsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.ids = request.ids + await stream.send_message(ListDatasetsByIDsResponse(datasets=self.datasets_response)) + + async def ListDatasetsByOrganizationID( + self, stream: Stream[ListDatasetsByOrganizationIDRequest, ListDatasetsByOrganizationIDResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + self.org_id = request.organization_id + await stream.send_message(ListDatasetsByOrganizationIDResponse(datasets=self.datasets_response)) + + async def RenameDataset(self, stream: Stream[RenameDatasetRequest, RenameDatasetResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.id = request.id + self.name = request.name + await stream.send_message((RenameDatasetResponse())) + + +class MockDataSync(DataSyncServiceBase): + def __init__(self, file_upload_response: str): + self.file_upload_response = file_upload_response + + async def DataCaptureUpload(self, stream: Stream[DataCaptureUploadRequest, DataCaptureUploadResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.metadata = request.metadata + self.sensor_contents = request.sensor_contents + await stream.send_message(DataCaptureUploadResponse(binary_data_id=self.file_upload_response, file_id=self.file_upload_response)) + + async def FileUpload(self, stream: Stream[FileUploadRequest, FileUploadResponse]) -> None: + request_metadata = await stream.recv_message() + assert request_metadata is not None + self.metadata = request_metadata.metadata + request_file_contents = await stream.recv_message() + assert request_file_contents is not None + self.binary_data = request_file_contents.file_contents.data + await stream.send_message(FileUploadResponse(binary_data_id=self.file_upload_response)) + + async def StreamingDataCaptureUpload( + self, stream: Stream[StreamingDataCaptureUploadRequest, StreamingDataCaptureUploadResponse] + ) -> None: + request_metadata = await stream.recv_message() + assert request_metadata is not None + self.metadata = request_metadata.metadata.upload_metadata + request_data_contents = await stream.recv_message() + assert request_data_contents is not None + self.binary_data = request_data_contents.data + await stream.send_message(StreamingDataCaptureUploadResponse(binary_data_id=self.file_upload_response)) + + +class MockMLTraining(UnimplementedMLTrainingServiceBase): + def __init__(self, job_id: str, training_metadata: TrainingJobMetadata): + self.job_id = job_id + self.training_metadata = training_metadata + + async def SubmitTrainingJob(self, stream: Stream[SubmitTrainingJobRequest, SubmitTrainingJobResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.dataset_id = request.dataset_id + self.org_id = request.organization_id + self.model_name = request.model_name + self.model_version = request.model_version + self.model_type = request.model_type + self.tags = request.tags + await stream.send_message(SubmitTrainingJobResponse(id=self.job_id)) + + async def SubmitCustomTrainingJob(self, stream: Stream[SubmitCustomTrainingJobRequest, SubmitCustomTrainingJobResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.dataset_id = request.dataset_id + self.registry_item_id = request.registry_item_id + self.org_id = request.organization_id + self.model_name = request.model_name + self.model_version = request.model_version + await stream.send_message(SubmitCustomTrainingJobResponse(id=self.job_id)) - async def GetSegmenterNames(self, stream: Stream[GetSegmenterNamesRequest, GetSegmenterNamesResponse]) -> None: + async def GetTrainingJob(self, stream: Stream[GetTrainingJobRequest, GetTrainingJobResponse]) -> None: request = await stream.recv_message() assert request is not None - response = GetSegmenterNamesResponse(segmenter_names=self.segmenters) + self.training_job_id = request.id + await stream.send_message(GetTrainingJobResponse(metadata=self.training_metadata)) + + async def ListTrainingJobs(self, stream: Stream[ListTrainingJobsRequest, ListTrainingJobsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.training_status = request.status + self.org_id = request.organization_id + await stream.send_message(ListTrainingJobsResponse(jobs=[self.training_metadata])) + + async def CancelTrainingJob(self, stream: Stream[CancelTrainingJobRequest, CancelTrainingJobResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.cancel_job_id = request.id + await stream.send_message(CancelTrainingJobResponse()) + + async def DeleteCompletedTrainingJob( + self, stream: Stream[DeleteCompletedTrainingJobRequest, DeleteCompletedTrainingJobResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + self.delete_id = request.id + await stream.send_message(DeleteCompletedTrainingJobResponse()) + + +class MockBilling(UnimplementedBillingServiceBase): + def __init__( + self, + pdf: bytes, + curr_month_usage: GetCurrentMonthUsageResponse, + invoices_summary: GetInvoicesSummaryResponse, + billing_info: GetOrgBillingInformationResponse, + ): + self.pdf = pdf + self.curr_month_usage = curr_month_usage + self.invoices_summary = invoices_summary + self.billing_info = billing_info + + async def GetCurrentMonthUsage(self, stream: Stream[GetCurrentMonthUsageRequest, GetCurrentMonthUsageResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.org_id = request.org_id + await stream.send_message(self.curr_month_usage) + + async def GetInvoicePdf(self, stream: Stream[GetInvoicePdfRequest, GetInvoicePdfResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.org_id = request.org_id + self.invoice_id = request.id + response = GetInvoicePdfResponse(chunk=self.pdf) await stream.send_message(response) - async def AddSegmenter(self, stream: Stream[AddSegmenterRequest, AddSegmenterResponse]) -> None: + async def GetInvoicesSummary(self, stream: Stream[GetInvoicesSummaryRequest, GetInvoicesSummaryResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.org_id = request.org_id + await stream.send_message(self.invoices_summary) + + async def GetOrgBillingInformation(self, stream: Stream[GetOrgBillingInformationRequest, GetOrgBillingInformationResponse]) -> None: request = await stream.recv_message() assert request is not None - self.segmenters.append(request.segmenter_name) - await stream.send_message(AddSegmenterResponse()) + self.org_id = request.org_id + await stream.send_message(self.billing_info) + + +class MockApp(UnimplementedAppServiceBase): + def __init__( + self, + organizations: List[Organization], + location: Location, + robot: Robot, + robot_part: RobotPart, + log_entry: LogEntry, + id: str, + name: str, + fragment: Fragment, + available: bool, + location_auth: LocationAuth, + robot_part_history: List[RobotPartHistoryEntry], + fragment_history: List[FragmentHistoryEntry], + authorizations: List[Authorization], + url: str, + module: Module, + members: List[OrganizationMember], + invite: OrganizationInvite, + rover_rental_robots: List[RoverRentalRobot], + api_key: str, + api_keys_with_authorizations: List[APIKeyWithAuthorizations], + items: List[RegistryItem], + package_type: PackageType.ValueType, + ): + self.organizations = organizations + self.location = location + self.robot = robot + self.robot_part = robot_part + self.log_entry = log_entry + self.id = id + self.name = name + self.fragment = fragment + self.available = available + self.location_auth = location_auth + self.robot_part_history = robot_part_history + self.fragment_history = fragment_history + self.authorizations = authorizations + self.url = url + self.module = module + self.members = members + self.invite = invite + self.rover_rental_robots = rover_rental_robots + self.api_key = api_key + self.api_keys_with_authorizations = api_keys_with_authorizations + self.items = items + self.package_type = package_type + self.send_email_invite = False + self.organization_metadata = {} + self.location_metadata = {} + self.robot_metadata = {} + self.robot_part_metadata = {} - async def RemoveSegmenter(self, stream: Stream[RemoveSegmenterRequest, RemoveSegmenterResponse]) -> None: + async def GetUserIDByEmail(self, stream: Stream[GetUserIDByEmailRequest, GetUserIDByEmailResponse]) -> None: request = await stream.recv_message() assert request is not None - self.segmenters.remove(request.segmenter_name) - await stream.send_message(RemoveSegmenterResponse()) + await stream.send_message(GetUserIDByEmailResponse(user_id=self.id)) + + async def CreateOrganization(self, stream: Stream[CreateOrganizationRequest, CreateOrganizationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(CreateOrganizationResponse(organization=self.organizations[0])) + + async def ListOrganizations(self, stream: Stream[ListOrganizationsRequest, ListOrganizationsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(ListOrganizationsResponse(organizations=self.organizations)) + + async def ListOrganizationsByUser(self, stream: Stream[ListOrganizationsByUserRequest, ListOrganizationsByUserResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(ListOrganizationsByUserResponse(orgs=[OrgDetails(org_id=self.id, org_name=self.name)])) + + async def GetOrganization(self, stream: Stream[GetOrganizationRequest, GetOrganizationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(GetOrganizationResponse(organization=self.organizations[0])) + + async def GetOrganizationNamespaceAvailability( + self, stream: Stream[GetOrganizationNamespaceAvailabilityRequest, GetOrganizationNamespaceAvailabilityResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + self.namespace = request.public_namespace + await stream.send_message(GetOrganizationNamespaceAvailabilityResponse(available=self.available)) + + async def UpdateOrganization(self, stream: Stream[UpdateOrganizationRequest, UpdateOrganizationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.update_region = request.region + self.update_cid = request.cid + self.update_name = request.name + self.update_namespace = request.public_namespace + await stream.send_message(UpdateOrganizationResponse(organization=self.organizations[0])) + + async def DeleteOrganization(self, stream: Stream[DeleteOrganizationRequest, DeleteOrganizationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.delete_org_called = True + await stream.send_message(DeleteOrganizationResponse()) + + async def ListOrganizationMembers(self, stream: Stream[ListOrganizationMembersRequest, ListOrganizationMembersResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(ListOrganizationMembersResponse(members=self.members, invites=[self.invite])) + + async def CreateOrganizationInvite(self, stream: Stream[CreateOrganizationInviteRequest, CreateOrganizationInviteResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.send_email_invite = request.send_email_invite + await stream.send_message(CreateOrganizationInviteResponse(invite=self.invite)) + + async def UpdateOrganizationInviteAuthorizations( + self, stream: Stream[UpdateOrganizationInviteAuthorizationsRequest, UpdateOrganizationInviteAuthorizationsResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + self.email = request.email + self.add_authorizations = request.add_authorizations + self.remove_authorizations = request.remove_authorizations + await stream.send_message(UpdateOrganizationInviteAuthorizationsResponse(invite=self.invite)) + + async def DeleteOrganizationMember(self, stream: Stream[DeleteOrganizationMemberRequest, DeleteOrganizationMemberResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.deleted_member_id = request.user_id + await stream.send_message(DeleteOrganizationMemberResponse()) + + async def DeleteOrganizationInvite(self, stream: Stream[DeleteOrganizationInviteRequest, DeleteOrganizationInviteResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.deleted_invite_email = request.email + await stream.send_message(DeleteOrganizationInviteResponse()) + + async def ResendOrganizationInvite(self, stream: Stream[ResendOrganizationInviteRequest, ResendOrganizationInviteResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.resent_invite_email = request.email + await stream.send_message(ResendOrganizationInviteResponse()) + + async def CreateLocation(self, stream: Stream[CreateLocationRequest, CreateLocationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.name = request.name + self.parent_location_id = request.parent_location_id + await stream.send_message(CreateLocationResponse(location=self.location)) + + async def GetLocation(self, stream: Stream[GetLocationRequest, GetLocationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.location_id = request.location_id + await stream.send_message(GetLocationResponse(location=self.location)) + + async def UpdateLocation(self, stream: Stream[UpdateLocationRequest, UpdateLocationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.location_id = request.location_id + self.name = request.name + self.parent_location_id = request.parent_location_id + await stream.send_message(UpdateLocationResponse(location=self.location)) + + async def DeleteLocation(self, stream: Stream[DeleteLocationRequest, DeleteLocationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.location_id = request.location_id + await stream.send_message(DeleteLocationResponse()) + + async def ListLocations(self, stream: Stream[ListLocationsRequest, ListLocationsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(ListLocationsResponse(locations=[self.location])) + + async def ShareLocation(self, stream: Stream[ShareLocationRequest, ShareLocationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.organization_id = request.organization_id + self.location_id = request.location_id + await stream.send_message(ShareLocationResponse()) + + async def UnshareLocation(self, stream: Stream[UnshareLocationRequest, UnshareLocationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.organization_id = request.organization_id + self.location_id = request.location_id + await stream.send_message(UnshareLocationResponse()) + + async def LocationAuth(self, stream: Stream[LocationAuthRequest, LocationAuthResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.location_id = request.location_id + await stream.send_message(LocationAuthResponse(auth=self.location_auth)) + + async def CreateLocationSecret(self, stream: Stream[CreateLocationSecretRequest, CreateLocationSecretResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.location_id = request.location_id + await stream.send_message(CreateLocationSecretResponse(auth=self.location_auth)) + + async def DeleteLocationSecret(self, stream: Stream[DeleteLocationSecretRequest, DeleteLocationSecretResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.location_id = request.location_id + self.secret_id = request.secret_id + await stream.send_message(DeleteLocationSecretResponse()) + + async def GetRobot(self, stream: Stream[GetRobotRequest, GetRobotResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_id = request.id + await stream.send_message(GetRobotResponse(robot=self.robot)) + + async def GetRoverRentalRobots(self, stream: Stream[GetRoverRentalRobotsRequest, GetRoverRentalRobotsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(GetRoverRentalRobotsResponse(robots=self.rover_rental_robots)) + + async def GetRobotParts(self, stream: Stream[GetRobotPartsRequest, GetRobotPartsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_id = request.robot_id + await stream.send_message(GetRobotPartsResponse(parts=[self.robot_part])) + + async def GetRobotPart(self, stream: Stream[GetRobotPartRequest, GetRobotPartResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_id = request.id + await stream.send_message(GetRobotPartResponse(part=self.robot_part)) + + async def GetRobotPartLogs(self, stream: Stream[GetRobotPartLogsRequest, GetRobotPartLogsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_id = request.id + self.filter = request.filter + self.errors_only = request.errors_only + self.levels = request.levels + await stream.send_message(GetRobotPartLogsResponse(logs=[self.log_entry])) + + async def TailRobotPartLogs(self, stream: Stream[TailRobotPartLogsRequest, TailRobotPartLogsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_id = request.id + self.errors_only = request.errors_only + self.filter = request.filter + await stream.send_message(TailRobotPartLogsResponse(logs=[])) + + async def GetRobotPartHistory(self, stream: Stream[GetRobotPartHistoryRequest, GetRobotPartHistoryResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_id = request.id + await stream.send_message(GetRobotPartHistoryResponse(history=self.robot_part_history)) + + async def UpdateRobotPart(self, stream: Stream[UpdateRobotPartRequest, UpdateRobotPartResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_id = request.id + self.name = request.name + self.robot_config = request.robot_config + self.last_known_update = request.last_known_update + await stream.send_message(UpdateRobotPartResponse(part=self.robot_part)) + + async def NewRobotPart(self, stream: Stream[NewRobotPartRequest, NewRobotPartResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_id = request.robot_id + self.part_name = request.part_name + await stream.send_message(NewRobotPartResponse(part_id=self.id)) + + async def DeleteRobotPart(self, stream: Stream[DeleteRobotPartRequest, DeleteRobotPartResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_id = request.part_id + await stream.send_message(DeleteRobotPartResponse()) + + async def MarkPartAsMain(self, stream: Stream[MarkPartAsMainRequest, MarkPartAsMainResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_id = request.part_id + await stream.send_message(MarkPartAsMainResponse()) + + async def MarkPartForRestart(self, stream: Stream[MarkPartForRestartRequest, MarkPartForRestartResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_id = request.part_id + await stream.send_message(MarkPartForRestartResponse()) + + async def CreateRobotPartSecret(self, stream: Stream[CreateRobotPartSecretRequest, CreateRobotPartSecretResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_id = request.part_id + await stream.send_message(CreateRobotPartSecretResponse(part=self.robot_part)) + + async def DeleteRobotPartSecret(self, stream: Stream[DeleteRobotPartSecretRequest, DeleteRobotPartSecretResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_id = request.part_id + self.secret_id = request.secret_id + await stream.send_message(DeleteRobotPartSecretResponse()) + + async def ListRobots(self, stream: Stream[ListRobotsRequest, ListRobotsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.location_id = request.location_id + await stream.send_message(ListRobotsResponse(robots=[self.robot])) + + async def NewRobot(self, stream: Stream[NewRobotRequest, NewRobotResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.name = request.name + self.location_id = request.location + await stream.send_message(NewRobotResponse(id=self.id)) + + async def UpdateRobot(self, stream: Stream[UpdateRobotRequest, UpdateRobotResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_id = request.id + self.name = request.name + self.location_id = request.location + await stream.send_message(UpdateRobotResponse(robot=self.robot)) + + async def DeleteRobot(self, stream: Stream[DeleteRobotRequest, DeleteRobotResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_id = request.id + await stream.send_message(DeleteRobotResponse()) + + async def ListFragments(self, stream: Stream[ListFragmentsRequest, ListFragmentsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.fragment_visibility = request.fragment_visibility + await stream.send_message(ListFragmentsResponse(fragments=[self.fragment])) + + async def GetFragment(self, stream: Stream[GetFragmentRequest, GetFragmentResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.fragment_id = request.id + await stream.send_message(GetFragmentResponse(fragment=self.fragment)) + + async def GetFragmentHistory(self, stream: Stream[GetFragmentHistoryRequest, GetFragmentHistoryResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.id = request.id + self.page_token = request.page_token + self.page_limit = request.page_limit + await stream.send_message(GetFragmentHistoryResponse(history=self.fragment_history)) + + async def CreateFragment(self, stream: Stream[CreateFragmentRequest, CreateFragmentResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.name = request.name + await stream.send_message(CreateFragmentResponse(fragment=self.fragment)) + + async def UpdateFragment(self, stream: Stream[UpdateFragmentRequest, UpdateFragmentResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.fragment_id = request.id + self.name = request.name + self.public = request.public + self.last_known_update = request.last_known_update + await stream.send_message(UpdateFragmentResponse(fragment=self.fragment)) + + async def DeleteFragment(self, stream: Stream[DeleteFragmentRequest, DeleteFragmentResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.fragment_id = request.id + await stream.send_message(DeleteFragmentResponse()) + + async def AddRole(self, stream: Stream[AddRoleRequest, AddRoleResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.identity_id = request.authorization.identity_id + self.role = request.authorization.authorization_id.split("_")[-1] + self.resource_type = request.authorization.resource_type + self.resource_id = request.authorization.resource_id + await stream.send_message(AddRoleResponse()) + + async def RemoveRole(self, stream: Stream[RemoveRoleRequest, RemoveRoleResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.identity_id = request.authorization.identity_id + self.role = request.authorization.authorization_id.split("_")[-1] + self.resource_type = request.authorization.resource_type + self.resource_id = request.authorization.resource_id + await stream.send_message(RemoveRoleResponse()) + + async def ChangeRole(self, stream: Stream[ChangeRoleRequest, ChangeRoleResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.change_role_called = True + await stream.send_message(ChangeRoleResponse()) + + async def ListAuthorizations(self, stream: Stream[ListAuthorizationsRequest, ListAuthorizationsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.resource_ids = request.resource_ids + await stream.send_message(ListAuthorizationsResponse(authorizations=self.authorizations)) + + async def CheckPermissions(self, stream: Stream[CheckPermissionsRequest, CheckPermissionsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(CheckPermissionsResponse(authorized_permissions=request.permissions)) + + async def CreateModule(self, stream: Stream[CreateModuleRequest, CreateModuleResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.name = request.name + await stream.send_message(CreateModuleResponse(module_id=self.id, url=self.url)) + + async def UpdateModule(self, stream: Stream[UpdateModuleRequest, UpdateModuleResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.module_id = request.module_id + self.module_url = request.url + self.description = request.description + self.models = request.models + self.entrypoint = request.entrypoint + self.visibility = request.visibility + await stream.send_message(UpdateModuleResponse(url=self.url)) + + async def UploadModuleFile(self, stream: Stream[UploadModuleFileRequest, UploadModuleFileResponse]) -> None: + request_file_info = await stream.recv_message() + assert request_file_info is not None + self.module_file_info = request_file_info.module_file_info + request_file = await stream.recv_message() + assert request_file is not None + self.file = request_file.file + await stream.send_message(UploadModuleFileResponse(url=self.id)) + + async def GetModule(self, stream: Stream[GetModuleRequest, GetModuleResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.module_id = request.module_id + await stream.send_message(GetModuleResponse(module=self.module)) + + async def ListModules(self, stream: Stream[ListModulesRequest, ListModulesResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(ListModulesResponse(modules=[self.module])) + + async def CreateKey(self, stream: Stream[CreateKeyRequest, CreateKeyResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(CreateKeyResponse(key=self.api_key, id=self.id)) + + async def GetRobotAPIKeys(self, stream: Stream[GetRobotAPIKeysRequest, GetRobotAPIKeysResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(GetRobotAPIKeysResponse(api_keys=self.api_keys_with_authorizations)) + + async def DeleteKey(self, stream: Stream[DeleteKeyRequest, DeleteKeyResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.id = request.id + self.delete_key_called = True + await stream.send_message((DeleteKeyResponse())) + + async def ListKeys(self, stream: Stream[ListKeysRequest, ListKeysResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(ListKeysResponse(api_keys=self.api_keys_with_authorizations)) + + async def RotateKey(self, stream: Stream[RotateKeyRequest, RotateKeyResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(RotateKeyResponse(id=self.id, key=self.api_key)) + + async def CreateKeyFromExistingKeyAuthorizations( + self, stream: Stream[CreateKeyFromExistingKeyAuthorizationsRequest, CreateKeyFromExistingKeyAuthorizationsResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(CreateKeyFromExistingKeyAuthorizationsResponse(key=self.api_key, id=self.id)) + + async def CreateRegistryItem(self, stream: Stream[CreateRegistryItemRequest, CreateRegistryItemResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.name = request.name + self.package_type = request.type + self.organization_id = request.organization_id + await stream.send_message(CreateRegistryItemResponse()) + + async def GetOrganizationsWithAccessToLocation( + self, stream: Stream[GetOrganizationsWithAccessToLocationRequest, GetOrganizationsWithAccessToLocationResponse] + ) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message( + GetOrganizationsWithAccessToLocationResponse(organization_identities=[OrganizationIdentity(id=self.id, name=self.name)]) + ) + + async def ListRegistryItems(self, stream: Stream[ListRegistryItemsRequest, ListRegistryItemsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(ListRegistryItemsResponse(items=self.items)) + + async def UpdateRegistryItem(self, stream: Stream[UpdateRegistryItemRequest, UpdateRegistryItemResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.id = request.item_id + self.package_type = request.type + self.description = request.description + self.visibility = request.visibility + await stream.send_message(UpdateRegistryItemResponse()) + + async def DeleteRegistryItem(self, stream: Stream[DeleteRegistryItemRequest, DeleteRegistryItemResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.delete_item_called = True + await stream.send_message(DeleteRegistryItemResponse()) + + async def GetRegistryItem(self, stream: Stream[GetRegistryItemRequest, GetRegistryItemResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.include_markdown_documentation = request.include_markdown_documentation + await stream.send_message(GetRegistryItemResponse(item=self.items[0])) + + async def GetOrganizationMetadata(self, stream: Stream[GetOrganizationMetadataRequest, GetOrganizationMetadataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(GetOrganizationMetadataResponse(data=self.organization_metadata.get(request.organization_id, dict_to_struct({})))) + + async def UpdateOrganizationMetadata(self, stream: Stream[UpdateOrganizationMetadataRequest, UpdateOrganizationMetadataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.organization_metadata[request.organization_id] = request.data + await stream.send_message(UpdateOrganizationMetadataResponse()) + + async def GetLocationMetadata(self, stream: Stream[GetLocationMetadataRequest, GetLocationMetadataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(GetLocationMetadataResponse(data=self.location_metadata.get(request.location_id, dict_to_struct({})))) + + async def UpdateLocationMetadata(self, stream: Stream[UpdateLocationMetadataRequest, UpdateLocationMetadataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.location_metadata[request.location_id] = request.data + await stream.send_message(UpdateLocationMetadataResponse()) + + async def GetRobotMetadata(self, stream: Stream[GetRobotMetadataRequest, GetRobotMetadataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(GetRobotMetadataResponse(data=self.robot_metadata.get(request.id, dict_to_struct({})))) + + async def UpdateRobotMetadata(self, stream: Stream[UpdateRobotMetadataRequest, UpdateRobotMetadataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_metadata[request.id] = request.data + await stream.send_message(UpdateRobotMetadataResponse()) + + async def GetRobotPartMetadata(self, stream: Stream[GetRobotPartMetadataRequest, GetRobotPartMetadataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(GetRobotPartMetadataResponse(data=self.robot_part_metadata.get(request.id, dict_to_struct({})))) + + async def UpdateRobotPartMetadata(self, stream: Stream[UpdateRobotPartMetadataRequest, UpdateRobotPartMetadataResponse]) -> None: + request = await stream.recv_message() + assert request is not None + self.robot_part_metadata[request.id] = request.data + await stream.send_message(UpdateRobotPartMetadataResponse()) + +class MockGenericService(GenericService): + timeout: Optional[float] = None + + async def do_command(self, command: Mapping[str, ValueTypes], *, timeout: Optional[float] = None, **kwargs) -> Mapping[str, ValueTypes]: + self.timeout = timeout + return {key: True for key in command.keys()} diff --git a/tests/test_app_client.py b/tests/test_app_client.py new file mode 100644 index 000000000..73c03f5ed --- /dev/null +++ b/tests/test_app_client.py @@ -0,0 +1,829 @@ +from datetime import datetime + +import pytest +from grpclib.testing import ChannelFor + +from viam.app.app_client import APIKeyAuthorization, AppClient, Fragment, FragmentVisibilityPB +from viam.proto.app import ( + APIKey, + APIKeyWithAuthorizations, + AuthenticatorInfo, + Authorization, + AuthorizationDetails, + AuthorizedPermissions, + FragmentHistoryEntry, + Location, + LocationAuth, + Model, + Module, + ModuleFileInfo, + ModuleMetadata, + Organization, + OrganizationInvite, + OrganizationMember, + RegistryItem, + RegistryItemStatus, + Robot, + RobotPart, + RobotPartHistoryEntry, + RoverRentalRobot, + Visibility, +) +from viam.proto.app import Fragment as FragmentPB +from viam.proto.app.packages import PackageType +from viam.proto.common import LogEntry +from viam.utils import datetime_to_timestamp, struct_to_dict + +from .mocks.services import MockApp + +METADATA = {"key": "value"} + +ID = "id" +IDS = [ID] +NAME = "name" +CID = "cid" +PAGE_TOKEN = "123" +PAGE_LIMIT = 20 +TIME = datetime_to_timestamp(datetime.now()) +PUBLIC_NAMESPACE = "public_namespace" +DEFAULT_REGION = "default_region" +ORGANIZATION = Organization( + id=ID, + name=NAME, + created_on=TIME, + public_namespace=PUBLIC_NAMESPACE, + default_region=DEFAULT_REGION, +) +ORGANIZATIONS = [ORGANIZATION] +NUM = 1 +LOCATION = Location( + id=ID, + name=NAME, + parent_location_id=ID, + auth=None, + organizations=None, + created_on=TIME, + robot_count=NUM, + config=None, +) +ROBOT = Robot(id=ID, name=NAME, location=ID, last_access=TIME, created_on=TIME) +DNS_NAME = "dns_name" +SECRET = "secret" +MAIN_PART = True +FQDN = "fqdn" +LOCAL_FQDN = "local_fqdn" +ROBOT_PART = RobotPart( + id=ID, + name=NAME, + dns_name=DNS_NAME, + secret=SECRET, + robot=ID, + location_id=ID, + robot_config=None, + last_access=TIME, + user_supplied_info=None, + main_part=MAIN_PART, + fqdn=FQDN, + local_fqdn=LOCAL_FQDN, + created_on=TIME, + secrets=None, + last_updated=TIME, +) +ROBOT_PARTS = [ROBOT_PART] +ROVER_RENTAL_ROBOT = RoverRentalRobot( + robot_id=ID, + location_id="location", + robot_name=NAME, + robot_main_part_id=ID, +) +ROVER_RENTAL_ROBOTS = [ROVER_RENTAL_ROBOT] +FILTER = "filter" +ERRORS_ONLY = True +LOG_LEVELS = ["error", "warn"] +HOST = "host" +LEVEL = "level" +LOGGER_NAME = "logger_name" +MESSAGE = "message" +STACK = "stack" +LOG_ENTRY = LogEntry(host=HOST, level=LEVEL, time=TIME, logger_name=LOGGER_NAME, message=MESSAGE, caller=None, stack=STACK, fields=None) +LOG_ENTRIES = [LOG_ENTRY] +ROBOT_CONFIG = {"key": "value"} +FRAGMENT_VISIBILITY = [Fragment.Visibility.PUBLIC] +FRAGMENT_VISIBILITY_PB = [FragmentVisibilityPB.FRAGMENT_VISIBILITY_PUBLIC] +ORGANIZATION_OWNER = "organization_owner" +PUBLIC = True +ORGANIZATION_NAME = "organization_name" +ONLY_USED_BY_OWNER = True +FRAGMENT = FragmentPB( + id=ID, + name=NAME, + fragment=None, + organization_owner=ORGANIZATION_OWNER, + public=PUBLIC, + created_on=TIME, + organization_name=ORGANIZATION_NAME, + robot_part_count=NUM, + only_used_by_owner=ONLY_USED_BY_OWNER, + last_updated=TIME, +) +NAMESPACE = "namespace" +AVAILABLE = True +LOCATION_AUTH = LocationAuth(secret=SECRET, location_id=ID, secrets=None) +PART = "part" +ROBOT_PART_HISTORY_ENTRY = RobotPartHistoryEntry(part=PART, robot=ID, when=TIME, old=None) +ROBOT_PART_HISTORY = [ROBOT_PART_HISTORY_ENTRY] +AUTHENTICATOR_INFO = AuthenticatorInfo(value="value", is_deactivated=True, type=1) +FRAGMENT_HISTORY_ENTRY = FragmentHistoryEntry(fragment=ID, edited_by=AUTHENTICATOR_INFO, old=FRAGMENT, edited_on=TIME) +FRAGMENT_HISTORY = [FRAGMENT_HISTORY_ENTRY] +TYPE = "robot" +ROLE = "operator" +API_KEY = "key" +API_KEY_AUTHORIZATION = APIKeyAuthorization(role=ROLE, resource_type=TYPE, resource_id=ID) +API_KEY_AUTHORIZATIONS = [API_KEY_AUTHORIZATION] +AUTHORIZATION = Authorization( + authorization_type=TYPE, authorization_id=ID, resource_type=TYPE, resource_id=ID, identity_id=ID, organization_id=ID +) +AUTHORIZATION_DETAIL = AuthorizationDetails(authorization_type=TYPE, authorization_id=ID, resource_type=TYPE, resource_id=ID, org_id=ID) +AUTHORIZATION_DETAILS = [AUTHORIZATION_DETAIL] +AUTHORIZATIONS = [AUTHORIZATION] +API_KEY_WITH_AUTHORIZATIONS = APIKeyWithAuthorizations( + api_key=APIKey(id=ID, key=API_KEY, name=NAME), + authorizations=AUTHORIZATION_DETAILS, +) +API_KEYS_WITH_AUTHORIZATIONS = [API_KEY_WITH_AUTHORIZATIONS] +PERMISSION = AuthorizedPermissions( + resource_type=TYPE, + resource_id=ID, + permissions=["control_robot"], +) +PERMISSIONS = [PERMISSION] +VISIBILITY = Visibility.VISIBILITY_PUBLIC +URL = "url" +DESCRIPTION = "description" +USAGE = 100 +MODULE_METADATA = ModuleMetadata() +MODEL = "model" +API = "api" +MODELS = [Model(api=API, model=MODEL)] +ENTRYPOINT = "entrypoint" +PACKAGE_TYPE = PackageType.PACKAGE_TYPE_UNSPECIFIED +STATUS = RegistryItemStatus.REGISTRY_ITEM_STATUS_UNSPECIFIED +ITEM = RegistryItem( + item_id=ID, + organization_id=ID, + public_namespace=PUBLIC_NAMESPACE, + name=NAME, + type=PACKAGE_TYPE, + visibility=VISIBILITY, + url=URL, + description=DESCRIPTION, + total_robot_usage=USAGE, + total_external_robot_usage=USAGE, + total_organization_usage=USAGE, + total_external_organization_usage=USAGE, +) +ITEMS = [ITEM] +MODULE = Module( + module_id=ID, + organization_id=ID, + name=NAME, + visibility=VISIBILITY, + versions=None, + url=URL, + description=DESCRIPTION, + models=MODELS, + entrypoint=ENTRYPOINT, + total_robot_usage=NUM, + total_organization_usage=NUM, +) +MODULES = [MODULE] +EMAIL = "email" +EMAILS = [EMAIL] +MEMBER = OrganizationMember(user_id=ID, emails=EMAILS, date_added=TIME) +MEMBERS = [MEMBER] +INVITE = OrganizationInvite(organization_id=ID, email=EMAIL, created_on=TIME) +INVITES = [INVITE] +VERSION = "version" +PLATFORM = "platform" +MODULE_FILE_INFO = ModuleFileInfo(module_id=ID, version=VERSION, platform=PLATFORM) +FILE = b"file" +USER_DEFINED_METADATA = {"number": 0, "string": "string"} + + +@pytest.fixture(scope="function") +def service() -> MockApp: + return MockApp( + organizations=ORGANIZATIONS, + location=LOCATION, + robot=ROBOT, + robot_part=ROBOT_PART, + log_entry=LOG_ENTRY, + id=ID, + name=NAME, + fragment=FRAGMENT, + available=AVAILABLE, + location_auth=LOCATION_AUTH, + robot_part_history=ROBOT_PART_HISTORY, + fragment_history=FRAGMENT_HISTORY, + authorizations=AUTHORIZATIONS, + url=URL, + module=MODULE, + members=MEMBERS, + invite=INVITE, + rover_rental_robots=ROVER_RENTAL_ROBOTS, + api_key=API_KEY, + api_keys_with_authorizations=API_KEYS_WITH_AUTHORIZATIONS, + items=[ITEM], + package_type=PACKAGE_TYPE, + ) + + +class TestClient: + async def test_get_user_id_by_email(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + id = await client.get_user_id_by_email(EMAIL) + assert id == ID + + async def test_create_organization(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + org = await client.create_organization(NAME) + assert org == ORGANIZATION + + async def test_get_organizations_with_access_to_location(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + orgs = await client.get_organizations_with_access_to_location(ID) + assert orgs[0].name == NAME + assert orgs[0].id == ID + + async def test_list_organizations_by_user(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + orgs = await client.list_organizations_by_user(ID) + assert orgs[0].org_name == NAME + assert orgs[0].org_id == ID + + async def test_get_organization(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + org = await client.get_organization(org_id=ID) + assert org == ORGANIZATION + available = await client.get_organization_namespace_availability(public_namespace=NAMESPACE) + assert available == AVAILABLE + assert service.namespace == NAMESPACE + + async def test_get_organization_namespace_availability(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + available = await client.get_organization_namespace_availability(public_namespace=NAMESPACE) + assert available == AVAILABLE + assert service.namespace == NAMESPACE + + async def test_list_organization_members(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + members, invites = await client.list_organization_members(org_id=ID) + assert members == MEMBERS + assert invites == INVITES + + async def test_list_organizations(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + organizations = await client.list_organizations() + assert organizations == ORGANIZATIONS + + async def test_update_organization(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + org = await client.update_organization(org_id=ID, name=NAME, public_namespace=PUBLIC_NAMESPACE, region=DEFAULT_REGION, cid=CID) + assert org == ORGANIZATION + assert service.update_region == DEFAULT_REGION + assert service.update_cid == CID + assert service.update_name == NAME + assert service.update_namespace == PUBLIC_NAMESPACE + + async def test_update_organization_invite_authorizations(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + invite = await client.update_organization_invite_authorizations( + org_id=ID, email=EMAIL, add_authorizations=AUTHORIZATIONS, remove_authorizations=AUTHORIZATIONS + ) + assert invite == INVITE + assert service.email == EMAIL + assert service.add_authorizations == AUTHORIZATIONS + assert service.remove_authorizations == AUTHORIZATIONS + + async def test_delete_organization(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_organization(ID) + assert service.delete_org_called is True + + async def test_delete_organization_member(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_organization_member(org_id=ID, user_id=ID) + assert service.deleted_member_id == ID + + async def test_create_organization_invite(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + invite = await client.create_organization_invite(org_id=ID, email=EMAIL, authorizations=AUTHORIZATIONS) + assert invite == INVITE + assert service.send_email_invite is True + + await client.create_organization_invite(org_id=ID, email=EMAIL, authorizations=AUTHORIZATIONS, send_email_invite=False) + assert service.send_email_invite is False + + async def test_delete_organization_invite(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_organization_invite(org_id=ID, email=EMAIL) + assert service.deleted_invite_email == EMAIL + + async def test_resend_organization_invite(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.resend_organization_invite(org_id=ID, email=EMAIL) + assert service.resent_invite_email == EMAIL + + async def test_create_location(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + new_location = await client.create_location(org_id=ID, name=NAME, parent_location_id=ID) + assert service.name == NAME + assert service.parent_location_id == ID + assert new_location == LOCATION + + async def test_get_location(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + location = await client.get_location(location_id=ID) + assert service.location_id == ID + assert location == LOCATION + + async def test_update_location(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + updated_location = await client.update_location(location_id=ID, name=NAME, parent_location_id=ID) + assert service.location_id == ID + assert service.name == NAME + assert service.parent_location_id == ID + assert updated_location == LOCATION + + async def test_delete_location(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_location(location_id=ID) + assert service.location_id == ID + + async def test_list_locations(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + locations = await client.list_locations(org_id=ID) + assert locations == locations + + async def test_share_location(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.share_location(organization_id=ID, location_id=ID) + assert service.location_id == ID + assert service.organization_id == ID + + async def test_unshare_location(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.unshare_location(organization_id=ID, location_id=ID) + assert service.location_id == ID + assert service.organization_id == ID + + async def test_location_auth(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + location_auth = await client.location_auth(location_id=ID) + assert location_auth == LOCATION_AUTH + assert service.location_id == ID + + async def test_create_location_secret(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + location_auth = await client.create_location_secret(location_id=ID) + assert location_auth == LOCATION_AUTH + assert service.location_id == ID + + async def test_delete_location_secret(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_location_secret(secret_id=ID, location_id=ID) + assert service.secret_id == ID + assert service.location_id == ID + + async def test_get_robot(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + robot = await client.get_robot(robot_id=ID) + assert service.robot_id == ID + assert robot == ROBOT + + async def test_get_rover_rental_robots(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + robots = await client.get_rover_rental_robots(org_id=ID) + assert robots == ROVER_RENTAL_ROBOTS + + async def test_get_robot_parts(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + robot_parts = await client.get_robot_parts(robot_id=ID) + assert service.robot_id == ID + assert [robot_part.proto for robot_part in robot_parts] == ROBOT_PARTS + + async def test_get_robot_part(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + robot_part = await client.get_robot_part(robot_part_id=ID, indent=NUM) + assert service.robot_part_id == ID + assert robot_part.proto == ROBOT_PART + + async def test_get_robot_part_logs(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + log_entries = await client.get_robot_part_logs(robot_part_id=ID, filter=FILTER, log_levels=LOG_LEVELS, num_log_entries=NUM) + assert service.robot_part_id == ID + assert service.filter == FILTER + assert service.levels == LOG_LEVELS + assert [log_entry.proto for log_entry in log_entries] == LOG_ENTRIES + + async def test_tail_robot_part_logs(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + logs_stream = await client.tail_robot_part_logs(robot_part_id=ID, errors_only=ERRORS_ONLY, filter=FILTER) + [logs async for logs in logs_stream] # Iterate over returned value to implicitly call __anext__() so server runs properly. + assert service.robot_part_id == ID + assert service.errors_only == ERRORS_ONLY + assert service.filter == FILTER + + async def test_get_robot_part_history(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + robot_part_history = await client.get_robot_part_history(robot_part_id=ID) + assert service.robot_part_id == ID + assert len(robot_part_history) == len(ROBOT_PART_HISTORY) + for i in range(len(ROBOT_PART_HISTORY)): + assert robot_part_history[i].proto == ROBOT_PART_HISTORY[i] + + async def test_update_robot_part(self, service: MockApp): + async with ChannelFor([service]) as channel: + last_known_update = datetime.now() + client = AppClient(channel, METADATA, ID) + updated_robot_part = await client.update_robot_part(robot_part_id=ID, name=NAME, robot_config=ROBOT_CONFIG, + last_known_update=last_known_update) + assert service.robot_part_id == ID + assert service.name == NAME + assert struct_to_dict(service.robot_config) == ROBOT_CONFIG + assert updated_robot_part.proto == ROBOT_PART + assert service.last_known_update == datetime_to_timestamp(last_known_update) + + async def test_new_robot_part(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + new_robot_part_id = await client.new_robot_part(robot_id=ID, part_name=NAME) + assert service.robot_id == ID + assert service.part_name == NAME + assert new_robot_part_id == ID + + async def test_delete_robot_part(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_robot_part(robot_part_id=ID) + assert service.robot_part_id == ID + + async def test_get_robot_api_keys(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + keys = await client.get_robot_api_keys(ID) + assert keys[0].api_key == API_KEY_WITH_AUTHORIZATIONS.api_key + assert keys[0].authorizations[0] == AUTHORIZATION_DETAIL + + async def test_mark_part_as_main(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.mark_part_as_main(robot_part_id=ID) + assert service.robot_part_id == ID + + async def test_mark_part_for_restart(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.mark_part_for_restart(robot_part_id=ID) + assert service.robot_part_id == ID + + async def test_create_robot_part_secret(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + robot_part = await client.create_robot_part_secret(robot_part_id=ID) + assert service.robot_part_id == ID + assert robot_part.proto == ROBOT_PART + + async def test_delete_robot_part_secret(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_robot_part_secret(robot_part_id=ID, secret_id=ID) + assert service.robot_part_id == ID + assert service.secret_id == ID + + async def test_list_robots(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + robots = await client.list_robots(location_id=ID) + assert service.location_id == ID + assert robots == [ROBOT] + + async def test_new_robot(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + new_robot_id = await client.new_robot(name=NAME, location_id=ID) + assert service.name == NAME + assert service.location_id == ID + assert new_robot_id == ID + + async def test_update_robot(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + updated_robot = await client.update_robot(robot_id=ID, name=NAME, location_id=ID) + assert service.robot_id == ID + assert service.name == NAME + assert service.location_id == ID + assert updated_robot == ROBOT + + async def test_delete_robot(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_robot(robot_id=ID) + assert service.robot_id == ID + + async def test_list_fragments(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + fragments = await client.list_fragments(org_id=ID, visibilities=FRAGMENT_VISIBILITY) + assert service.fragment_visibility == FRAGMENT_VISIBILITY_PB + assert [fragment.proto for fragment in fragments] == [FRAGMENT] + + async def test_get_fragment(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + fragment = await client.get_fragment(fragment_id=ID) + assert service.fragment_id == ID + assert fragment.proto == FRAGMENT + + async def test_create_fragment(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + fragment = await client.create_fragment(org_id=ID, name=NAME) + assert service.name == NAME + assert fragment.proto == FRAGMENT + + async def test_update_fragment(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + last_known_update = datetime.now() + fragment = await client.update_fragment(fragment_id=ID, name=NAME, public=PUBLIC, last_known_update=last_known_update) + assert service.fragment_id == ID + assert service.name == NAME + assert service.public == PUBLIC + assert fragment.proto == FRAGMENT + assert service.last_known_update == datetime_to_timestamp(last_known_update) + + async def test_delete_fragment(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_fragment(fragment_id=ID) + assert service.id == ID + + async def test_get_fragment_history(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + fragment_history = await client.get_fragment_history(id=ID, page_token=PAGE_TOKEN, page_limit=PAGE_LIMIT) + assert service.fragment.id == ID + assert len(fragment_history) == len(FRAGMENT_HISTORY) + assert service.id == ID + assert service.page_token == PAGE_TOKEN + assert service.page_limit == PAGE_LIMIT + for i in range(len(FRAGMENT_HISTORY)): + assert fragment_history[i].proto == FRAGMENT_HISTORY[i] + + async def test_add_role(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.add_role(org_id=ID, identity_id=ID, role=ROLE, resource_type=TYPE, resource_id=ID) + assert service.identity_id == ID + assert service.role == ROLE + assert service.resource_type == TYPE + assert service.resource_id == ID + + async def test_remove_role(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.remove_role(org_id=ID, identity_id=ID, role=ROLE, resource_type=TYPE, resource_id=ID) + assert service.identity_id == ID + assert service.role == ROLE + assert service.resource_type == TYPE + assert service.resource_id == ID + + async def test_change_role(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.change_role( + organization_id=ID, + old_identity_id=ID, + old_role=ROLE, + old_resource_type=TYPE, + old_resource_id=ID, + new_identity_id=ID, + new_role=ROLE, + new_resource_type=TYPE, + new_resource_id=ID, + ) + assert service.change_role_called is True + + async def test_check_permissions(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + permissions = await client.check_permissions(permissions=PERMISSIONS) + assert permissions == PERMISSIONS + + async def test_list_authorizations(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + authorizations = await client.list_authorizations(org_id=ID, resource_ids=IDS) + assert service.resource_ids == IDS + assert authorizations == AUTHORIZATIONS + + async def test_get_registry_item(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + item = await client.get_registry_item(ID, include_markdown_documentation=True) + assert service.include_markdown_documentation is True + assert item.item_id == ITEM.item_id + assert item.name == ITEM.name + assert item.visibility == ITEM.visibility + assert item.total_robot_usage == ITEM.total_robot_usage + + async def test_create_registry_item(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.create_registry_item(ID, NAME, PACKAGE_TYPE) + assert service.name == NAME + assert service.package_type == PACKAGE_TYPE + assert service.organization_id == ID + + async def test_update_registry_item(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.update_registry_item(ID, PACKAGE_TYPE, DESCRIPTION, VISIBILITY) + assert service.id == ID + assert service.package_type == PACKAGE_TYPE + assert service.description == DESCRIPTION + assert service.visibility == VISIBILITY + + async def test_list_registry_items(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + items = await client.list_registry_items(ID, [PACKAGE_TYPE], [VISIBILITY], [PLATFORM], [STATUS]) + assert items[0].item_id == ID + assert items[0].public_namespace == PUBLIC_NAMESPACE + assert items[0].total_external_organization_usage == USAGE + + async def test_delete_registry_item(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_registry_item(ID) + assert service.id == ID + assert service.delete_item_called is True + + async def test_create_module(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + id, url = await client.create_module(org_id=ID, name=NAME) + assert service.name == NAME + assert id == ID + assert url == URL + + async def test_update_module(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + url = await client.update_module( + module_id=ID, url=URL, description=DESCRIPTION, models=MODELS, entrypoint=ENTRYPOINT, public=PUBLIC + ) + assert url == URL + assert service.module_id == ID + assert service.module_url == URL + assert service.description == DESCRIPTION + assert service.models == MODELS + assert service.entrypoint == ENTRYPOINT + assert service.visibility == VISIBILITY + + async def test_created_module(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + url = await client.update_module(module_id=ID, url=URL, description=DESCRIPTION, models=MODELS, entrypoint=ENTRYPOINT) + assert service.module_id == ID + assert service.description == DESCRIPTION + assert service.models == MODELS + assert service.entrypoint == ENTRYPOINT + assert url == URL + + async def test_upload_module_file(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + id = await client.upload_module_file(module_file_info=MODULE_FILE_INFO, file=FILE) + assert id == ID + assert service.module_file_info == MODULE_FILE_INFO + assert service.file == FILE + + async def test_get_module(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + module = await client.get_module(module_id=ID) + assert service.module_id == ID + assert module == MODULE + + async def test_list_modules(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + modules = await client.list_modules(org_id=ID) + assert modules == MODULES + + async def test_create_key(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + api_key = await client.create_key(org_id=ID, authorizations=API_KEY_AUTHORIZATIONS, name=NAME) + assert (API_KEY, ID) == api_key + + async def test_delete_key(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + await client.delete_key(ID) + assert service.id == ID + assert service.delete_key_called is True + + async def test_create_key_from_existing_key_authorizations(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + api_key = await client.create_key_from_existing_key_authorizations(id=ID) + assert (API_KEY, ID) == api_key + + async def list_keys(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + api_keys = await client.list_keys(org_id=ID) + assert api_keys == API_KEYS_WITH_AUTHORIZATIONS + + async def test_rotate_key(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + key, id = await client.rotate_key(ID) + assert key == API_KEY + assert id == ID + + async def test_get_and_update_organization_metadata(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + user_defined_metadata = await client.get_organization_metadata(ID) + assert len(user_defined_metadata) == 0 + + await client.update_organization_metadata(ID, USER_DEFINED_METADATA) + user_defined_metadata = await client.get_organization_metadata(ID) + assert user_defined_metadata == USER_DEFINED_METADATA + + async def test_get_and_update_location_metadata(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + user_defined_metadata = await client.get_location_metadata(ID) + assert len(user_defined_metadata) == 0 + + + await client.update_location_metadata(ID, USER_DEFINED_METADATA) + user_defined_metadata = await client.get_location_metadata(ID) + assert user_defined_metadata == USER_DEFINED_METADATA + + async def test_get_and_update_robot_metadata(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + user_defined_metadata = await client.get_robot_metadata(ID) + assert len(user_defined_metadata) == 0 + + await client.update_robot_metadata(ID, USER_DEFINED_METADATA) + user_defined_metadata = await client.get_robot_metadata(ID) + assert user_defined_metadata == USER_DEFINED_METADATA + + async def test_get_and_update_robot_part_metadata(self, service: MockApp): + async with ChannelFor([service]) as channel: + client = AppClient(channel, METADATA, ID) + user_defined_metadata = await client.get_robot_part_metadata(ID) + assert len(user_defined_metadata) == 0 + + await client.update_robot_part_metadata(ID, USER_DEFINED_METADATA) + user_defined_metadata = await client.get_robot_part_metadata(ID) + assert user_defined_metadata == USER_DEFINED_METADATA diff --git a/tests/test_arm.py b/tests/test_arm.py index 1754af502..9a2bfd07c 100644 --- a/tests/test_arm.py +++ b/tests/test_arm.py @@ -1,94 +1,99 @@ -import pytest from grpclib.testing import ChannelFor -from viam.components.arm import ArmClient, ArmStatus, create_status -from viam.components.arm.service import ArmService -from viam.components.generic.service import GenericService -from viam.components.resource_manager import ResourceManager -from viam.errors import NotSupportedError -from viam.proto.common import Pose + +from viam.components.arm import ArmClient, KinematicsFileFormat +from viam.components.arm.service import ArmRPCService +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, + GetGeometriesRequest, + GetGeometriesResponse, + GetKinematicsRequest, + GetKinematicsResponse, + Pose, +) from viam.proto.component.arm import ( ArmServiceStub, GetEndPositionRequest, GetEndPositionResponse, GetJointPositionsRequest, GetJointPositionsResponse, + IsMovingRequest, + IsMovingResponse, JointPositions, MoveToJointPositionsRequest, MoveToPositionRequest, StopRequest, ) -from viam.utils import dict_to_struct, message_to_struct +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict -from .mocks.components import MockArm +from . import loose_approx +from .mocks.components import GEOMETRIES, MockArm class TestArm: - arm = MockArm(name="arm") pose = Pose(x=5, y=5, z=5, o_x=5, o_y=5, o_z=5, theta=20) joint_pos = JointPositions(values=[1, 8, 2]) + kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02") - @pytest.mark.asyncio async def test_move_to_position(self): await self.arm.move_to_position(self.pose) assert self.arm.position == self.pose - @pytest.mark.asyncio async def test_get_end_position(self): pos = await self.arm.get_end_position() assert pos == self.pose - @pytest.mark.asyncio async def test_move_to_joint_positions(self): await self.arm.move_to_joint_positions(self.joint_pos) assert self.arm.joint_positions == self.joint_pos - @pytest.mark.asyncio async def test_get_joint_positions(self): jp = await self.arm.get_joint_positions() assert jp == self.joint_pos - @pytest.mark.asyncio async def test_stop(self): assert self.arm.is_stopped is False await self.arm.stop() assert self.arm.is_stopped is True - @pytest.mark.asyncio async def test_is_moving(self): await self.arm.move_to_position(self.pose) assert await self.arm.is_moving() await self.arm.stop() assert not await self.arm.is_moving() - @pytest.mark.asyncio - async def test_do(self): - with pytest.raises(NotImplementedError): - await self.arm.do_command({"command": "args"}) + async def test_get_kinematics(self): + kd = await self.arm.get_kinematics(extra={"1": "2"}) + assert kd == self.kinematics + assert self.arm.extra == {"1": "2"} - @pytest.mark.asyncio - async def test_status(self): - await self.arm.move_to_position(self.pose) - status = await create_status(self.arm) - assert status.name == MockArm.get_resource_name(self.arm.name) - assert status.status == message_to_struct(ArmStatus(end_position=self.pose, joint_positions=self.joint_pos, is_moving=True)) + async def test_get_geometries(self): + geometries = await self.arm.get_geometries() + assert geometries == GEOMETRIES + + async def test_do(self): + command = {"command": "args"} + resp = await self.arm.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio async def test_extra(self): await self.arm.get_end_position(extra={"foo": "bar"}) assert self.arm.extra == {"foo": "bar"} class TestService: + @classmethod + def setup_class(cls): + cls.name = "arm" + cls.arm = MockArm(name=cls.name) + cls.manager = ResourceManager([cls.arm]) + cls.service = ArmRPCService(cls.manager) + cls.pose = Pose(x=5, y=5, z=5, o_x=5, o_y=5, o_z=5, theta=20) + cls.joint_pos = JointPositions(values=[1, 8, 2]) + cls.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02") - name = "arm" - arm = MockArm(name=name) - manager = ResourceManager([arm]) - service = ArmService(manager) - pose = Pose(x=5, y=5, z=5, o_x=5, o_y=5, o_z=5, theta=20) - joint_pos = JointPositions(values=[1, 8, 2]) - - @pytest.mark.asyncio async def test_move_to_position(self): async with ChannelFor([self.service]) as channel: client = ArmServiceStub(channel) @@ -96,7 +101,6 @@ async def test_move_to_position(self): await client.MoveToPosition(request) assert self.arm.position == self.pose - @pytest.mark.asyncio async def test_get_end_position(self): async with ChannelFor([self.service]) as channel: client = ArmServiceStub(channel) @@ -104,7 +108,6 @@ async def test_get_end_position(self): response: GetEndPositionResponse = await client.GetEndPosition(request) assert response.pose == self.pose - @pytest.mark.asyncio async def test_move_to_joint_positions(self): async with ChannelFor([self.service]) as channel: client = ArmServiceStub(channel) @@ -112,7 +115,6 @@ async def test_move_to_joint_positions(self): await client.MoveToJointPositions(request) assert self.arm.joint_positions == self.joint_pos - @pytest.mark.asyncio async def test_get_joint_positions(self): async with ChannelFor([self.service]) as channel: client = ArmServiceStub(channel) @@ -120,16 +122,48 @@ async def test_get_joint_positions(self): response: GetJointPositionsResponse = await client.GetJointPositions(request) assert response.positions == self.joint_pos - @pytest.mark.asyncio async def test_stop(self): async with ChannelFor([self.service]) as channel: assert self.arm.is_stopped is False + assert self.arm.timeout is None client = ArmServiceStub(channel) request = StopRequest(name=self.name) - await client.Stop(request) + await client.Stop(request, timeout=4.4) assert self.arm.is_stopped is True + assert self.arm.timeout == loose_approx(4.4) + + async def test_is_moving(self): + async with ChannelFor([self.service]) as channel: + assert self.arm.is_stopped is True + self.arm.is_stopped = False + client = ArmServiceStub(channel) + request = IsMovingRequest(name=self.arm.name) + response: IsMovingResponse = await client.IsMoving(request) + assert response.is_moving is True + + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = ArmServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + async def test_get_kinematics(self): + async with ChannelFor([self.service]) as channel: + client = ArmServiceStub(channel) + request = GetKinematicsRequest(name=self.name) + response: GetKinematicsResponse = await client.GetKinematics(request) + assert (response.format, response.kinematics_data) == self.kinematics + + async def test_get_geometries(self): + async with ChannelFor([self.service]) as channel: + client = ArmServiceStub(channel) + request = GetGeometriesRequest(name=self.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES - @pytest.mark.asyncio async def test_extra(self): async with ChannelFor([self.service]) as channel: client = ArmServiceStub(channel) @@ -140,72 +174,76 @@ async def test_extra(self): class TestClient: + @classmethod + def setup_class(cls): + cls.name = "arm" + cls.arm = MockArm(name=cls.name) + cls.manager = ResourceManager([cls.arm]) + cls.service = ArmRPCService(cls.manager) + cls.pose = Pose(x=5, y=5, z=5, o_x=5, o_y=5, o_z=5, theta=20) + cls.joint_pos = JointPositions(values=[1, 8, 2]) + cls.kinematics = (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA, b"\x00\x01\x02") - name = "arm" - arm = MockArm(name=name) - manager = ResourceManager([arm]) - service = ArmService(manager) - pose = Pose(x=5, y=5, z=5, o_x=5, o_y=5, o_z=5, theta=20) - joint_pos = JointPositions(values=[1, 8, 2]) - - @pytest.mark.asyncio async def test_move_to_position(self): async with ChannelFor([self.service]) as channel: client = ArmClient(self.name, channel) await client.move_to_position(self.pose) assert self.arm.position == self.pose - @pytest.mark.asyncio async def test_get_end_position(self): async with ChannelFor([self.service]) as channel: client = ArmClient(self.name, channel) pos = await client.get_end_position() assert pos == self.pose - @pytest.mark.asyncio async def test_move_to_joint_positions(self): async with ChannelFor([self.service]) as channel: client = ArmClient(self.name, channel) await client.move_to_joint_positions(self.joint_pos) assert self.arm.joint_positions == self.joint_pos - @pytest.mark.asyncio async def test_get_joint_positions(self): async with ChannelFor([self.service]) as channel: client = ArmClient(self.name, channel) jp = await client.get_joint_positions() assert jp == self.joint_pos - @pytest.mark.asyncio async def test_stop(self): async with ChannelFor([self.service]) as channel: assert self.arm.is_stopped is False + assert self.arm.timeout is None client = ArmClient(self.name, channel) - await client.stop() + await client.stop(timeout=1.82) assert self.arm.is_stopped is True + assert self.arm.timeout == loose_approx(1.82) - @pytest.mark.asyncio async def test_is_moving(self): async with ChannelFor([self.service]) as channel: client = ArmClient(self.name, channel) - with pytest.raises(NotSupportedError): - await client.is_moving() + assert self.arm.is_stopped is True + self.arm.is_stopped = False + assert await client.is_moving() is True - @pytest.mark.asyncio - async def test_do(self): - async with ChannelFor([self.service, GenericService(self.manager)]) as channel: + async def test_get_kinematics(self): + async with ChannelFor([self.service]) as channel: client = ArmClient(self.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + kd = await client.get_kinematics(extra={"1": "2"}) + assert kd == self.kinematics + assert self.arm.extra == {"1": "2"} - @pytest.mark.asyncio - async def test_status(self): + async def test_get_geometries(self): + async with ChannelFor([self.service]) as channel: + client = ArmClient(self.name, channel) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES + + async def test_do(self): async with ChannelFor([self.service]) as channel: client = ArmClient(self.name, channel) - with pytest.raises(NotSupportedError): - await create_status(client) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio async def test_extra(self): async with ChannelFor([self.service]) as channel: client = ArmClient(self.name, channel) diff --git a/tests/test_audio_input.py b/tests/test_audio_input.py new file mode 100644 index 000000000..298a0f66f --- /dev/null +++ b/tests/test_audio_input.py @@ -0,0 +1,170 @@ +import sys +from datetime import timedelta +from typing import Union + +import pytest +from grpclib import GRPCError +from grpclib.testing import ChannelFor + +from viam.components.audio_input import AudioInput, AudioInputClient, AudioInputRPCService +from viam.components.generic.service import GenericRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse +from viam.proto.component.audioinput import ( + AudioInputServiceStub, + ChunksRequest, + ChunksResponse, + PropertiesRequest, + PropertiesResponse, + RecordRequest, + SampleFormat, +) +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict + +from . import loose_approx +from .mocks.components import GEOMETRIES, MockAudioInput + +PROPERTIES = AudioInput.Properties( + channel_count=2, + latency=timedelta(milliseconds=20), + sample_rate=41400, + sample_size=2, + is_big_endian=sys.byteorder != "little", + is_float=True, + is_interleaved=True, +) + + +@pytest.fixture(scope="function") +def audio_input() -> MockAudioInput: + return MockAudioInput(name="audio_input", properties=PROPERTIES) + + +@pytest.fixture(scope="function") +def service(audio_input: MockAudioInput) -> AudioInputRPCService: + manager = ResourceManager([audio_input]) + return AudioInputRPCService(manager) + + +@pytest.fixture(scope="function") +def generic_service(audio_input: MockAudioInput) -> GenericRPCService: + manager = ResourceManager([audio_input]) + return GenericRPCService(manager) + + +class TestAudioInput: + async def test_stream(self, audio_input: AudioInput): + idx = 0 + async for audio in await audio_input.stream(): + assert audio.info.channels == PROPERTIES.channel_count + assert audio.info.sample_format == SampleFormat.SAMPLE_FORMAT_FLOAT32_INTERLEAVED + assert audio.info.sampling_rate == PROPERTIES.sample_rate + + assert audio.chunk.data == f"{idx}".encode("utf-8") + assert audio.chunk.length == 182 + + idx += 1 + + async def test_get_properties(self, audio_input: AudioInput): + assert await audio_input.get_properties() == PROPERTIES + + async def test_do(self, audio_input: AudioInput): + command = {"command": "args"} + resp = await audio_input.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self, audio_input: AudioInput): + geometries = await audio_input.get_geometries() + assert geometries == GEOMETRIES + + +class TestService: + async def test_chunks(self, audio_input: AudioInput, service: AudioInputRPCService): + async with ChannelFor([service]) as channel: + client = AudioInputServiceStub(channel) + async with client.Chunks.open() as stream: + await stream.send_message( + ChunksRequest(name=audio_input.name, sample_format=SampleFormat.SAMPLE_FORMAT_FLOAT32_INTERLEAVED) + ) + response: Union[ChunksResponse, None] = await stream.recv_message() + assert response is not None and response.HasField("info") + assert response.info.channels == PROPERTIES.channel_count + assert response.info.sample_format == SampleFormat.SAMPLE_FORMAT_FLOAT32_INTERLEAVED + assert response.info.sampling_rate == PROPERTIES.sample_rate + + idx = 0 + while True: + response = await stream.recv_message() + if response is None: + break + assert response.HasField("chunk") + assert response.chunk.data == f"{idx}".encode("utf-8") + assert response.chunk.length == 182 + idx += 1 + + async def test_properties(self, audio_input: MockAudioInput, service: AudioInputRPCService): + assert audio_input.timeout is None + async with ChannelFor([service]) as channel: + client = AudioInputServiceStub(channel) + response: PropertiesResponse = await client.Properties(PropertiesRequest(name=audio_input.name), timeout=1.82) + assert AudioInput.Properties.from_proto(response) == PROPERTIES + assert audio_input.timeout == loose_approx(1.82) + + async def test_record(self, service: AudioInputRPCService): + async with ChannelFor([service]) as channel: + client = AudioInputServiceStub(channel) + with pytest.raises(GRPCError, match=r".*Status.UNIMPLEMENTED.*"): + await client.Record(RecordRequest()) + + async def test_do(self, audio_input: MockAudioInput, service: AudioInputRPCService): + async with ChannelFor([service]) as channel: + client = AudioInputServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=audio_input.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + async def test_get_geometries(self, audio_input: MockAudioInput, service: AudioInputRPCService): + async with ChannelFor([service]) as channel: + client = AudioInputServiceStub(channel) + request = GetGeometriesRequest(name=audio_input.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES + + +class TestClient: + async def test_stream(self, audio_input: AudioInput, service: AudioInputRPCService): + async with ChannelFor([service]) as channel: + client = AudioInputClient(audio_input.name, channel) + + idx = 0 + async for audio in await client.stream(): + assert audio.info.channels == PROPERTIES.channel_count + assert audio.info.sample_format == SampleFormat.SAMPLE_FORMAT_FLOAT32_INTERLEAVED + assert audio.info.sampling_rate == PROPERTIES.sample_rate + + assert audio.chunk.data == f"{idx}".encode("utf-8") + assert audio.chunk.length == 182 + + idx += 1 + + async def test_get_properties(self, audio_input: MockAudioInput, service: AudioInputRPCService): + assert audio_input.timeout is None + async with ChannelFor([service]) as channel: + client = AudioInputClient(audio_input.name, channel) + assert await client.get_properties(timeout=4.4) == PROPERTIES + assert audio_input.timeout == loose_approx(4.4) + + async def test_do(self, audio_input: AudioInput, service: AudioInputRPCService): + async with ChannelFor([service]) as channel: + client = AudioInputClient(audio_input.name, channel) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self, audio_input: AudioInput, service: AudioInputRPCService): + async with ChannelFor([service]) as channel: + client = AudioInputClient(audio_input.name, channel) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_base.py b/tests/test_base.py index 5823f652d..21a0db622 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -2,23 +2,26 @@ import pytest from grpclib.testing import ChannelFor -from viam.components.base import BaseClient, Vector3, create_status -from viam.components.base.service import BaseService -from viam.components.generic.service import GenericService -from viam.components.resource_manager import ResourceManager -from viam.errors import NotSupportedError -from viam.proto.common import ActuatorStatus + +from viam.components.base import BaseClient, Vector3 +from viam.components.base.service import BaseRPCService +from viam.components.generic.service import GenericRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.base import ( BaseServiceStub, + IsMovingRequest, + IsMovingResponse, MoveStraightRequest, SetPowerRequest, SetVelocityRequest, SpinRequest, StopRequest, ) -from viam.utils import dict_to_struct, message_to_struct +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict -from .mocks.components import MockBase +from . import loose_approx +from .mocks.components import GEOMETRIES, MockBase @pytest.fixture(scope="function") @@ -27,37 +30,34 @@ def base() -> MockBase: @pytest.fixture(scope="function") -def service(base: MockBase) -> BaseService: +def service(base: MockBase) -> BaseRPCService: manager = ResourceManager([base]) - return BaseService(manager) + return BaseRPCService(manager) @pytest.fixture(scope="function") -def generic_service(base: MockBase) -> GenericService: +def generic_service(base: MockBase) -> GenericRPCService: manager = ResourceManager([base]) - return GenericService(manager) + return GenericRPCService(manager) class TestBase: - @pytest.mark.asyncio async def test_move_straight(self, base: MockBase): distances = [randint(-50, 50) for _ in range(4)] velocities = [random() + 1 for _ in range(4)] - for (i, (d, v)) in enumerate(zip(distances, velocities)): + for i, (d, v) in enumerate(zip(distances, velocities)): await base.move_straight(d, v) assert base.position == sum(distances[: i + 1]) - @pytest.mark.asyncio async def test_spin(self, base: MockBase): angles = [randint(-180, 180) for _ in range(4)] velocities = [random() + 1 for _ in range(4)] - for (i, (a, v)) in enumerate(zip(angles, velocities)): + for i, (a, v) in enumerate(zip(angles, velocities)): await base.spin(a, v) assert base.angle == sum(angles[: i + 1]) - @pytest.mark.asyncio async def test_stop(self, base: MockBase): assert base.stopped is True @@ -71,17 +71,11 @@ async def test_stop(self, base: MockBase): await base.move_straight(0, 0) assert base.stopped is True - await base.move_arc(1, 1, 1) - assert base.stopped is False - await base.move_arc(0, 0, 0) - assert base.stopped is True - await base.spin(1, 1) assert base.stopped is False await base.spin(0, 0) assert base.stopped is True - @pytest.mark.asyncio async def test_set_power(self, base: MockBase): assert base.linear_pwr == Vector3(x=0, y=0, z=0) assert base.angular_pwr == Vector3(x=0, y=0, z=0) @@ -91,7 +85,6 @@ async def test_set_power(self, base: MockBase): assert base.linear_pwr == Vector3(x=1, y=2, z=3) assert base.angular_pwr == Vector3(x=4, y=5, z=6) - @pytest.mark.asyncio async def test_velocity(self, base: MockBase): assert base.linear_vel == Vector3(x=0, y=0, z=0) assert base.angular_vel == Vector3(x=0, y=0, z=0) @@ -101,7 +94,6 @@ async def test_velocity(self, base: MockBase): assert base.linear_vel == Vector3(x=1, y=2, z=3) assert base.angular_vel == Vector3(x=4, y=5, z=6) - @pytest.mark.asyncio async def test_is_moving(self, base: MockBase): await base.move_straight(1, 1) assert await base.is_moving() @@ -109,35 +101,36 @@ async def test_is_moving(self, base: MockBase): assert base.stopped is True assert not await base.is_moving() - @pytest.mark.asyncio - async def test_do(self, base: MockBase): - with pytest.raises(NotImplementedError): - await base.do_command({"command": "args"}) + async def test_get_properties(self, base: MockBase): + properties = await base.get_properties() + assert properties.width_meters == 1.0 + assert properties.turning_radius_meters == 2.0 + assert properties.wheel_circumference_meters == 3.0 - @pytest.mark.asyncio - async def test_status(self, base: MockBase): - await base.move_straight(1, 1) - status = await create_status(base) - assert status.name == base.get_resource_name(base.name) - assert status.status == message_to_struct(ActuatorStatus(is_moving=True)) + async def test_do(self, base: MockBase): + command = {"command": "args"} + resp = await base.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio async def test_extra(self, base: MockBase): assert base.extra is None extra = {"foo": "bar", "baz": [1, 2, 3]} - await base.move_straight(1, 1, extra) + await base.move_straight(1, 1, extra=extra) assert base.extra == extra + async def test_get_geometries(self, base: MockBase): + geometries = await base.get_geometries() + assert geometries == GEOMETRIES + class TestService: - @pytest.mark.asyncio - async def test_move_straight(self, base: MockBase, service: BaseService): + async def test_move_straight(self, base: MockBase, service: BaseRPCService): distances = [randint(-50, 50) for _ in range(4)] velocities = [random() + 1 for _ in range(4)] async with ChannelFor([service]) as channel: client = BaseServiceStub(channel) - for (i, (d, v)) in enumerate(zip(distances, velocities)): + for i, (d, v) in enumerate(zip(distances, velocities)): request = MoveStraightRequest( name=base.name, distance_mm=d, @@ -146,14 +139,13 @@ async def test_move_straight(self, base: MockBase, service: BaseService): await client.MoveStraight(request) assert base.position == sum(distances[: i + 1]) - @pytest.mark.asyncio - async def test_spin(self, base: MockBase, service: BaseService): + async def test_spin(self, base: MockBase, service: BaseRPCService): angles = [randint(-180, 180) for _ in range(4)] velocities = [random() + 1 for _ in range(4)] async with ChannelFor([service]) as channel: client = BaseServiceStub(channel) - for (i, (a, v)) in enumerate(zip(angles, velocities)): + for i, (a, v) in enumerate(zip(angles, velocities)): request = SpinRequest( name=base.name, angle_deg=a, @@ -162,8 +154,7 @@ async def test_spin(self, base: MockBase, service: BaseService): await client.Spin(request) assert base.angle == sum(angles[: i + 1]) - @pytest.mark.asyncio - async def test_set_power(self, base: MockBase, service: BaseService): + async def test_set_power(self, base: MockBase, service: BaseRPCService): async with ChannelFor([service]) as channel: client = BaseServiceStub(channel) assert base.linear_pwr == Vector3(x=0, y=0, z=0) @@ -175,8 +166,7 @@ async def test_set_power(self, base: MockBase, service: BaseService): assert base.linear_pwr == Vector3(x=1, y=2, z=3) assert base.angular_pwr == Vector3(x=4, y=5, z=6) - @pytest.mark.asyncio - async def test_set_velocity(self, base: MockBase, service: BaseService): + async def test_set_velocity(self, base: MockBase, service: BaseRPCService): async with ChannelFor([service]) as channel: client = BaseServiceStub(channel) assert base.linear_vel == Vector3(x=0, y=0, z=0) @@ -188,12 +178,12 @@ async def test_set_velocity(self, base: MockBase, service: BaseService): assert base.linear_vel == Vector3(x=1, y=2, z=3) assert base.angular_vel == Vector3(x=4, y=5, z=6) - @pytest.mark.asyncio - async def test_stop(self, base: MockBase, service: BaseService): + async def test_stop(self, base: MockBase, service: BaseRPCService): async with ChannelFor([service]) as channel: client = BaseServiceStub(channel) assert base.stopped is True + assert base.timeout is None request = MoveStraightRequest( name=base.name, @@ -202,8 +192,9 @@ async def test_stop(self, base: MockBase, service: BaseService): ) await client.MoveStraight(request) assert base.stopped is False - await client.Stop(StopRequest(name=base.name)) + await client.Stop(StopRequest(name=base.name), timeout=1.82) assert base.stopped is True + assert base.timeout == loose_approx(1.82) request = MoveStraightRequest( name=base.name, @@ -235,8 +226,16 @@ async def test_stop(self, base: MockBase, service: BaseService): await client.Spin(request) assert base.stopped is True - @pytest.mark.asyncio - async def test_extra(self, base: MockBase, service: BaseService): + async def test_is_moving(self, base: MockBase, service: BaseRPCService): + async with ChannelFor([service]) as channel: + assert base.stopped is True + base.stopped = False + client = BaseServiceStub(channel) + request = IsMovingRequest(name=base.name) + response: IsMovingResponse = await client.IsMoving(request) + assert response.is_moving is True + + async def test_extra(self, base: MockBase, service: BaseRPCService): async with ChannelFor([service]) as channel: assert base.extra is None client = BaseServiceStub(channel) @@ -245,32 +244,45 @@ async def test_extra(self, base: MockBase, service: BaseService): await client.MoveStraight(request) assert base.extra == extra + async def test_do(self, base: MockBase, service: BaseRPCService): + async with ChannelFor([service]) as channel: + client = BaseServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=base.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + async def test_get_geometries(self, base: MockBase, service: BaseRPCService): + async with ChannelFor([service]) as channel: + client = BaseServiceStub(channel) + request = GetGeometriesRequest(name=base.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES + class TestClient: - @pytest.mark.asyncio - async def test_move_straight(self, base: MockBase, service: BaseService): + async def test_move_straight(self, base: MockBase, service: BaseRPCService): distances = [randint(-50, 50) for _ in range(4)] velocities = [random() + 1 for _ in range(4)] async with ChannelFor([service]) as channel: client = BaseClient(base.name, channel) - for (i, (d, v)) in enumerate(zip(distances, velocities)): + for i, (d, v) in enumerate(zip(distances, velocities)): await client.move_straight(d, v) assert base.position == sum(distances[: i + 1]) - @pytest.mark.asyncio - async def test_spin(self, base: MockBase, service: BaseService): + async def test_spin(self, base: MockBase, service: BaseRPCService): angles = [randint(-180, 180) for _ in range(4)] velocities = [random() + 1 for _ in range(4)] async with ChannelFor([service]) as channel: client = BaseClient(base.name, channel) - for (i, (a, v)) in enumerate(zip(angles, velocities)): + for i, (a, v) in enumerate(zip(angles, velocities)): await client.spin(a, v) assert base.angle == sum(angles[: i + 1]) - @pytest.mark.asyncio - async def test_set_power(self, base: MockBase, service: BaseService): + async def test_set_power(self, base: MockBase, service: BaseRPCService): async with ChannelFor([service]) as channel: client = BaseClient(base.name, channel) assert base.linear_pwr == Vector3(x=0, y=0, z=0) @@ -281,8 +293,7 @@ async def test_set_power(self, base: MockBase, service: BaseService): assert base.linear_pwr == Vector3(x=1, y=2, z=3) assert base.angular_pwr == Vector3(x=4, y=5, z=6) - @pytest.mark.asyncio - async def test_set_velocity(self, base: MockBase, service: BaseService): + async def test_set_velocity(self, base: MockBase, service: BaseRPCService): async with ChannelFor([service]) as channel: client = BaseClient(base.name, channel) assert base.linear_vel == Vector3(x=0, y=0, z=0) @@ -293,8 +304,8 @@ async def test_set_velocity(self, base: MockBase, service: BaseService): assert base.linear_vel == Vector3(x=1, y=2, z=3) assert base.angular_vel == Vector3(x=4, y=5, z=6) - @pytest.mark.asyncio - async def test_stop(self, base: MockBase, service: BaseService): + async def test_stop(self, base: MockBase, service: BaseRPCService): + assert base.timeout is None async with ChannelFor([service]) as channel: client = BaseClient(base.name, channel) @@ -302,8 +313,9 @@ async def test_stop(self, base: MockBase, service: BaseService): await client.move_straight(1, 1) assert base.stopped is False - await client.stop() + await client.stop(timeout=4.4) assert base.stopped is True + assert base.timeout == loose_approx(4.4) await client.move_straight(1, 1) assert base.stopped is False @@ -315,32 +327,30 @@ async def test_stop(self, base: MockBase, service: BaseService): await client.spin(0, 0) assert base.stopped is True - @pytest.mark.asyncio - async def test_is_moving(self, base: MockBase, service: BaseService): + async def test_is_moving(self, base: MockBase, service: BaseRPCService): async with ChannelFor([service]) as channel: + assert base.stopped is True + base.stopped = False client = BaseClient(base.name, channel) - with pytest.raises(NotSupportedError): - await client.is_moving() - - @pytest.mark.asyncio - async def test_do(self, base: MockBase, service: BaseService, generic_service: GenericService): - async with ChannelFor([service, generic_service]) as channel: - client = BaseClient(base.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + assert await client.is_moving() is True - @pytest.mark.asyncio - async def test_status(self, base: MockBase, service: BaseService): + async def test_do(self, base: MockBase, service: BaseRPCService): async with ChannelFor([service]) as channel: client = BaseClient(base.name, channel) - with pytest.raises(NotSupportedError): - await create_status(client) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio - async def test_extra(self, base: MockBase, service: BaseService): + async def test_extra(self, base: MockBase, service: BaseRPCService): async with ChannelFor([service]) as channel: assert base.extra is None client = BaseClient(base.name, channel) extra = {"foo": "bar", "baz": [1, 2, 3]} - await client.move_straight(1, 1, extra) + await client.move_straight(1, 1, extra=extra) assert base.extra == extra + + async def test_get_geometries(self, base: MockBase, service: BaseRPCService): + async with ChannelFor([service]) as channel: + client = BaseClient(base.name, channel) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_billing_client.py b/tests/test_billing_client.py new file mode 100644 index 000000000..2d5c093cc --- /dev/null +++ b/tests/test_billing_client.py @@ -0,0 +1,107 @@ +import pytest +from google.protobuf.timestamp_pb2 import Timestamp +from grpclib.testing import ChannelFor + +from viam.app.billing_client import BillingClient +from viam.proto.app.billing import ( + GetCurrentMonthUsageResponse, + GetInvoicesSummaryResponse, + GetOrgBillingInformationResponse, + InvoiceSummary, +) + +from .mocks.services import MockBilling + +PDF = b"abc123" +CLOUD_STORAGE_USAGE_COST = 100.0 +DATA_UPLOAD_USAGE_COST = 101.0 +DATA_EGRES_USAGE_COST = 102.0 +REMOTE_CONTROL_USAGE_COST = 103.0 +STANDARD_COMPUTE_USAGE_COST = 104.0 +DISCOUNT_AMOUNT = 0.0 +TOTAL_USAGE_WITH_DISCOUNT = 105.0 +TOTAL_USAGE_WITHOUT_DISCOUNT = 106.0 +OUTSTANDING_BALANCE = 1000.0 +SECONDS_START = 978310861 +NANOS_START = 0 +SECONDS_END = 998310861 +NANOS_END = 0 +SECONDS_PAID = 988310861 +NANOS_PAID = 0 +START_TS = Timestamp(seconds=SECONDS_START, nanos=NANOS_END) +PAID_DATE_TS = Timestamp(seconds=SECONDS_PAID, nanos=NANOS_PAID) +END_TS = Timestamp(seconds=SECONDS_END, nanos=NANOS_END) +INVOICE_ID = "invoice" +STATUS = "status" +PAYMENT_TYPE = 1 +EMAIL = "email@fake.com" +BILLING_TIER = "tier" +INVOICE = InvoiceSummary( + id=INVOICE_ID, + invoice_date=START_TS, + invoice_amount=OUTSTANDING_BALANCE, + status=STATUS, + due_date=END_TS, + paid_date=PAID_DATE_TS, +) +INVOICES = [INVOICE] +CURR_MONTH_USAGE = GetCurrentMonthUsageResponse( + start_date=START_TS, + end_date=END_TS, + cloud_storage_usage_cost=CLOUD_STORAGE_USAGE_COST, + data_upload_usage_cost=DATA_UPLOAD_USAGE_COST, + data_egres_usage_cost=DATA_EGRES_USAGE_COST, + remote_control_usage_cost=REMOTE_CONTROL_USAGE_COST, + standard_compute_usage_cost=STANDARD_COMPUTE_USAGE_COST, + discount_amount=DISCOUNT_AMOUNT, + total_usage_with_discount=TOTAL_USAGE_WITH_DISCOUNT, + total_usage_without_discount=TOTAL_USAGE_WITHOUT_DISCOUNT, +) +INVOICES_SUMMARY = GetInvoicesSummaryResponse(outstanding_balance=OUTSTANDING_BALANCE, invoices=INVOICES) +ORG_BILLING_INFO = GetOrgBillingInformationResponse( + type=PAYMENT_TYPE, + billing_email=EMAIL, + billing_tier=BILLING_TIER, +) + +AUTH_TOKEN = "auth_token" +BILLING_SERVICE_METADATA = {"authorization": f"Bearer {AUTH_TOKEN}"} + + +@pytest.fixture(scope="function") +def service() -> MockBilling: + return MockBilling( + pdf=PDF, + curr_month_usage=CURR_MONTH_USAGE, + invoices_summary=INVOICES_SUMMARY, + billing_info=ORG_BILLING_INFO, + ) + + +class TestClient: + async def test_get_current_month_usage(self, service: MockBilling): + async with ChannelFor([service]) as channel: + org_id = "foo" + client = BillingClient(channel, BILLING_SERVICE_METADATA) + curr_month_usage = await client.get_current_month_usage(org_id=org_id) + assert curr_month_usage == CURR_MONTH_USAGE + assert service.org_id == org_id + + async def test_get_invoice_pdf(self, service: MockBilling): + assert True + + async def test_get_invoices_summary(self, service: MockBilling): + async with ChannelFor([service]) as channel: + org_id = "bar" + client = BillingClient(channel, BILLING_SERVICE_METADATA) + invoices_summary = await client.get_invoices_summary(org_id=org_id) + assert invoices_summary == INVOICES_SUMMARY + assert service.org_id == org_id + + async def test_get_org_billing_information(self, service: MockBilling): + async with ChannelFor([service]) as channel: + org_id = "baz" + client = BillingClient(channel, BILLING_SERVICE_METADATA) + org_billing_info = await client.get_org_billing_information(org_id=org_id) + assert org_billing_info == ORG_BILLING_INFO + assert service.org_id == org_id diff --git a/tests/test_board.py b/tests/test_board.py index a816fdc2b..175eb8a12 100644 --- a/tests/test_board.py +++ b/tests/test_board.py @@ -1,19 +1,23 @@ +from datetime import timedelta from typing import cast + import pytest +from google.protobuf.duration_pb2 import Duration from grpclib import GRPCError from grpclib.testing import ChannelFor + from viam.components.board import Board, BoardClient -from viam.components.board.service import BoardService -from viam.components.generic.service import GenericService -from viam.components.resource_manager import ResourceManager -from viam.errors import ComponentNotFoundError -from viam.proto.common import AnalogStatus, BoardStatus, DigitalInterruptStatus +from viam.components.board.service import BoardRPCService +from viam.components.generic.service import GenericRPCService +from viam.errors import ResourceNotFoundError +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.board import ( BoardServiceStub, GetDigitalInterruptValueRequest, GetDigitalInterruptValueResponse, GetGPIORequest, GetGPIOResponse, + PowerMode, PWMFrequencyRequest, PWMFrequencyResponse, PWMRequest, @@ -21,101 +25,121 @@ ReadAnalogReaderRequest, ReadAnalogReaderResponse, SetGPIORequest, + SetPowerModeRequest, + SetPowerModeResponse, SetPWMFrequencyRequest, SetPWMRequest, - StatusRequest, - StatusResponse, + StreamTicksRequest, + WriteAnalogRequest, + WriteAnalogResponse, ) -from viam.utils import dict_to_struct +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict + +from . import loose_approx +from .mocks.components import GEOMETRIES, MockAnalog, MockBoard, MockDigitalInterrupt, MockGPIOPin + + +@pytest.fixture(scope="function") +def analog() -> MockAnalog: + return MockAnalog("analog1", 3, 0.0, 1.0, 0.1) + + +@pytest.fixture(scope="function") +def interrupt() -> MockDigitalInterrupt: + return MockDigitalInterrupt("interrupt1") + -from .mocks.components import MockAnalogReader, MockBoard, MockDigitalInterrupt, MockGPIOPin +@pytest.fixture(scope="function") +def gpio_pin() -> MockGPIOPin: + return MockGPIOPin("pin1") @pytest.fixture(scope="function") -def board() -> MockBoard: +def board(analog: Board.Analog, interrupt: Board.DigitalInterrupt, gpio_pin: Board.GPIOPin) -> MockBoard: return MockBoard( name="board", - analog_readers={ - "reader1": MockAnalogReader("reader1", 3), - }, - digital_interrupts={ - "interrupt1": MockDigitalInterrupt("interrupt1"), - }, - gpio_pins={"pin1": MockGPIOPin("pin1")}, + analogs={analog.name: analog}, + digital_interrupts={interrupt.name: interrupt}, + gpio_pins={gpio_pin.name: gpio_pin}, ) @pytest.fixture(scope="function") -def service(board: MockBoard) -> BoardService: +def service(board: MockBoard) -> BoardRPCService: manager = ResourceManager([board]) - return BoardService(manager) + return BoardRPCService(manager) @pytest.fixture(scope="function") -def generic_service(board: MockBoard) -> GenericService: +def generic_service(board: MockBoard) -> GenericRPCService: manager = ResourceManager([board]) - return GenericService(manager) + return GenericRPCService(manager) class TestBoard: - @pytest.mark.asyncio - async def test_analog_reader_by_name(self, board: MockBoard): - with pytest.raises(ComponentNotFoundError): - await board.analog_reader_by_name("does not exist") + class TestAnalog: + async def test_read(self, analog: MockAnalog): + value = await analog.read() + assert value == analog.value + + async def test_write(self, analog: MockAnalog): + await analog.write(8) + assert analog.value.value == 8 + + class TestDigitalInterrupt: + async def test_value(self, interrupt: MockDigitalInterrupt): + value = await interrupt.value() + assert value == interrupt.val - reader = await board.analog_reader_by_name("reader1") - assert reader.name == "reader1" + async def test_analog_by_name(self, board: MockBoard): + with pytest.raises(ResourceNotFoundError): + await board.analog_by_name("does not exist") + + reader = await board.analog_by_name("analog1") + assert reader.name == "analog1" - @pytest.mark.asyncio async def test_digital_interrupt_by_name(self, board: MockBoard): - with pytest.raises(ComponentNotFoundError): + with pytest.raises(ResourceNotFoundError): await board.digital_interrupt_by_name("does not exist") interrupt = await board.digital_interrupt_by_name("interrupt1") assert interrupt.name == "interrupt1" - @pytest.mark.asyncio async def test_gpio_pin_by_name(self, board: MockBoard): - with pytest.raises(ComponentNotFoundError): + with pytest.raises(ResourceNotFoundError): await board.digital_interrupt_by_name("does not exist") pin = await board.gpio_pin_by_name("pin1") assert pin.name == "pin1" - @pytest.mark.asyncio - async def test_analog_reader_names(self, board: MockBoard): - names = await board.analog_reader_names() - assert names == ["reader1"] - - @pytest.mark.asyncio - async def test_digital_interrupt_names(self, board: MockBoard): - names = await board.digital_interrupt_names() - assert names == ["interrupt1"] - - @pytest.mark.asyncio - async def test_status(self, board: MockBoard): - extra = {"foo": "bar", "baz": [1, 2, 3]} - status = await board.status(extra) - assert status == BoardStatus( - analogs={"reader1": AnalogStatus(value=3)}, - digital_interrupts={"interrupt1": DigitalInterruptStatus(value=0)}, - ) - assert board.extra == extra - - @pytest.mark.asyncio - async def test_model_attributes(self, board: MockBoard): - attrs = await board.model_attributes() - assert attrs == Board.Attributes(remote=True) - - @pytest.mark.asyncio async def test_do(self, board: MockBoard): - with pytest.raises(NotImplementedError): - await board.do_command({"command": "args"}) + command = {"command": "args"} + resp = await board.do_command(command) + assert resp == {"command": command} + + async def test_set_power_mode(self, board: MockBoard): + pm_mode = PowerMode.POWER_MODE_OFFLINE_DEEP + pm_duration = timedelta(minutes=1) + await board.set_power_mode(mode=pm_mode, duration=pm_duration, timeout=1.11) + assert board.timeout == loose_approx(1.11) + assert board.power_mode == pm_mode + assert board.power_mode_duration == pm_duration + + async def test_get_geometries(self, board: MockBoard): + geometries = await board.get_geometries() + assert geometries == GEOMETRIES + + async def test_stream_ticks(self, board: MockBoard): + int1 = board.digital_interrupts["interrupt1"] + async for tick in await board.stream_ticks([int1]): + assert tick.pin_name == "interrupt1" + assert tick.time == 1000 + assert tick.high is True class TestService: - @pytest.mark.asyncio - async def test_read_analog_reader(self, board: MockBoard, service: BoardService): + async def test_read_analog(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardServiceStub(channel) @@ -124,47 +148,43 @@ async def test_read_analog_reader(self, board: MockBoard, service: BoardService) await client.ReadAnalogReader(request) extra = {"foo": "bar", "baz": [1, 2, 3]} - request = ReadAnalogReaderRequest(board_name=board.name, analog_reader_name="reader1", extra=dict_to_struct(extra)) - response: ReadAnalogReaderResponse = await client.ReadAnalogReader(request) + request = ReadAnalogReaderRequest(board_name=board.name, analog_reader_name="analog1", extra=dict_to_struct(extra)) + response: ReadAnalogReaderResponse = await client.ReadAnalogReader(request, timeout=4.4) assert response.value == 3 + assert response.min_range == 0.0 + assert response.max_range == 1.0 + assert response.step_size == pytest.approx(0.1) - reader = cast(MockAnalogReader, board.analog_readers["reader1"]) + reader = cast(MockAnalog, board.analogs["analog1"]) assert reader.extra == extra + assert reader.timeout == loose_approx(4.4) - @pytest.mark.asyncio - async def test_get_digital_interrupt_value(self, board: MockBoard, service: BoardService): + async def test_get_digital_interrupt_value(self, board: MockBoard, service: BoardRPCService, interrupt: MockDigitalInterrupt): async with ChannelFor([service]) as channel: client = BoardServiceStub(channel) + request = GetDigitalInterruptValueRequest(board_name=board.name, digital_interrupt_name="dne") with pytest.raises(GRPCError, match=r".*Status.NOT_FOUND.*"): - request = GetDigitalInterruptValueRequest(board_name=board.name, digital_interrupt_name="dne") await client.GetDigitalInterruptValue(request) - extra = {"foo": "bar", "baz": [1, 2, 3]} - request = GetDigitalInterruptValueRequest( - board_name=board.name, digital_interrupt_name="interrupt1", extra=dict_to_struct(extra) - ) + request = GetDigitalInterruptValueRequest(board_name=board.name, digital_interrupt_name="interrupt1") response: GetDigitalInterruptValueResponse = await client.GetDigitalInterruptValue(request) - assert response.value == 0 + assert response.value == interrupt.val - interrupt = cast(MockDigitalInterrupt, board.digital_interrupts["interrupt1"]) - assert interrupt.extra == extra - - @pytest.mark.asyncio - async def test_set_gpio(self, board: MockBoard, service: BoardService): + async def test_set_gpio(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardServiceStub(channel) extra = {"foo": "bar", "baz": [1, 2, 3]} request = SetGPIORequest(name=board.name, pin="pin1", high=True, extra=dict_to_struct(extra)) - await client.SetGPIO(request) + await client.SetGPIO(request, timeout=4.1) pin = cast(MockGPIOPin, board.gpios["pin1"]) assert pin.high is True assert pin.extra == extra + assert pin.timeout == loose_approx(4.1) - @pytest.mark.asyncio - async def test_get_gpio(self, board: MockBoard, service: BoardService): + async def test_get_gpio(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardServiceStub(channel) @@ -174,53 +194,53 @@ async def test_get_gpio(self, board: MockBoard, service: BoardService): extra = {"foo": "bar", "baz": [1, 2, 3]} request = GetGPIORequest(name=board.name, pin="pin1", extra=dict_to_struct(extra)) - response: GetGPIOResponse = await client.GetGPIO(request) + response: GetGPIOResponse = await client.GetGPIO(request, timeout=1.82) assert response.high is False pin = cast(MockGPIOPin, board.gpios["pin1"]) assert pin.extra == extra + assert pin.timeout == loose_approx(1.82) - @pytest.mark.asyncio - async def test_pwm(self, board: MockBoard, service: BoardService): + async def test_pwm(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardServiceStub(channel) extra = {"foo": "bar", "baz": [1, 2, 3]} request = PWMRequest(name=board.name, pin="pin1", extra=dict_to_struct(extra)) - response: PWMResponse = await client.PWM(request) + response: PWMResponse = await client.PWM(request, timeout=7.86) assert response.duty_cycle_pct == 0.0 pin = cast(MockGPIOPin, board.gpios["pin1"]) assert pin.extra == extra + assert pin.timeout == loose_approx(7.86) - @pytest.mark.asyncio - async def test_set_pwm(self, board: MockBoard, service: BoardService): + async def test_set_pwm(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardServiceStub(channel) extra = {"foo": "bar", "baz": [1, 2, 3]} request = SetPWMRequest(name=board.name, pin="pin1", duty_cycle_pct=12.3, extra=dict_to_struct(extra)) - await client.SetPWM(request) + await client.SetPWM(request, timeout=1.213) pin = cast(MockGPIOPin, board.gpios["pin1"]) assert pin.pwm == 12.3 assert pin.extra == extra + assert pin.timeout == loose_approx(1.213) - @pytest.mark.asyncio - async def test_pwm_frequency(self, board: MockBoard, service: BoardService): + async def test_pwm_frequency(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardServiceStub(channel) extra = {"foo": "bar", "baz": [1, 2, 3]} request = PWMFrequencyRequest(name=board.name, pin="pin1", extra=dict_to_struct(extra)) - response: PWMFrequencyResponse = await client.PWMFrequency(request) + response: PWMFrequencyResponse = await client.PWMFrequency(request, timeout=182) assert response.frequency_hz == 0 pin = cast(MockGPIOPin, board.gpios["pin1"]) assert pin.extra == extra + assert pin.timeout == loose_approx(182) - @pytest.mark.asyncio - async def test_set_pwm_freq(self, board: MockBoard, service: BoardService): + async def test_set_pwm_freq(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardServiceStub(channel) @@ -231,39 +251,81 @@ async def test_set_pwm_freq(self, board: MockBoard, service: BoardService): pin = cast(MockGPIOPin, board.gpios["pin1"]) assert pin.pwm_freq == 123 assert pin.extra == extra + assert pin.timeout is None - @pytest.mark.asyncio - async def test_status(self, board: MockBoard, service: BoardService): + async def test_do(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=board.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} - extra = {"foo": "bar", "baz": [1, 2, 3]} - request = StatusRequest(name=board.name, extra=dict_to_struct(extra)) - response: StatusResponse = await client.Status(request) + async def test_get_geometries(self, board: MockBoard, service: BoardRPCService): + async with ChannelFor([service]) as channel: + client = BoardServiceStub(channel) + request = GetGeometriesRequest(name=board.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES + + async def test_set_power_mode(self, board: MockBoard, service: BoardRPCService): + async with ChannelFor([service]) as channel: + client = BoardServiceStub(channel) + pm_mode = PowerMode.POWER_MODE_OFFLINE_DEEP + pm_duration = Duration() + pm_duration.FromTimedelta(timedelta(minutes=1)) + request = SetPowerModeRequest(name=board.name, power_mode=pm_mode, duration=pm_duration) + response: SetPowerModeResponse = await client.SetPowerMode(request, timeout=6.66) + assert response == SetPowerModeResponse() + assert board.timeout == loose_approx(6.66) + assert board.power_mode == PowerMode.POWER_MODE_OFFLINE_DEEP + assert board.power_mode_duration == pm_duration.ToTimedelta() + + async def test_write_analog(self, board: MockBoard, service: BoardRPCService): + async with ChannelFor([service]) as channel: + client = BoardServiceStub(channel) + pin = "analog1" + value = 10 + request = WriteAnalogRequest(name=board.name, pin=pin, value=value) + response: WriteAnalogResponse = await client.WriteAnalog(request, timeout=6.66) + assert response == WriteAnalogResponse() + mock_analog = cast(MockAnalog, board.analogs["analog1"]) + assert mock_analog.timeout == loose_approx(6.66) + assert mock_analog.value.value == value + assert mock_analog.name == pin + + # + async def test_stream_ticks(self, board: MockBoard, service: BoardRPCService): + async with ChannelFor([service]) as channel: + client = BoardServiceStub(channel) + interrupts = ["interrupt1"] + extra = {"foo": "stream_ticks"} + request = StreamTicksRequest(name=board.name, pin_names=interrupts, extra=dict_to_struct(extra)) - assert response.status == BoardStatus( - analogs={"reader1": AnalogStatus(value=3)}, - digital_interrupts={"interrupt1": DigitalInterruptStatus(value=0)}, - ) - assert board.extra == extra + async with client.StreamTicks.open(timeout=1) as stream: + await stream.send_message(request, end=True) + resp = await stream.recv_message() + assert resp is not None + assert resp.pin_name == "interrupt1" + assert resp.high is True + assert resp.time == 1000 class TestClient: - @pytest.mark.asyncio - async def test_analog_reader_by_name(self, board: MockBoard, service: BoardService): + async def test_analog_by_name(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardClient(name=board.name, channel=channel) - reader = await client.analog_reader_by_name("does not exist") + reader = await client.analog_by_name("does not exist") assert reader.name == "does not exist" with pytest.raises(GRPCError, match=r".*Status.NOT_FOUND.*"): await reader.read() - reader = await client.analog_reader_by_name("reader1") - assert reader.name == "reader1" + reader = await client.analog_by_name("analog1") + assert reader.name == "analog1" - @pytest.mark.asyncio - async def test_digital_interrupt_by_name(self, board: MockBoard, service: BoardService): + async def test_digital_interrupt_by_name(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardClient(name=board.name, channel=channel) @@ -275,8 +337,7 @@ async def test_digital_interrupt_by_name(self, board: MockBoard, service: BoardS interrupt = await client.digital_interrupt_by_name("interrupt1") assert interrupt.name == "interrupt1" - @pytest.mark.asyncio - async def test_gpio_pin_by_name(self, board: MockBoard, service: BoardService): + async def test_gpio_pin_by_name(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardClient(name=board.name, channel=channel) @@ -288,115 +349,133 @@ async def test_gpio_pin_by_name(self, board: MockBoard, service: BoardService): pin = await client.gpio_pin_by_name("pin1") assert pin.name == "pin1" - @pytest.mark.asyncio - async def test_analog_reader_names(self, board: MockBoard, service: BoardService): + async def test_do(self, board: MockBoard, service: BoardRPCService): + async with ChannelFor([service]) as channel: + client = BoardClient(board.name, channel) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} + + async def test_set_power_mode(self, board: MockBoard, service: BoardRPCService): + async with ChannelFor([service]) as channel: + client = BoardClient(name=board.name, channel=channel) + pm_mode = PowerMode.POWER_MODE_OFFLINE_DEEP + pm_timedelta = timedelta(minutes=1) + await client.set_power_mode(mode=pm_mode, duration=pm_timedelta, timeout=1.1) + assert board.timeout == loose_approx(1.1) + assert board.power_mode == pm_mode + pm_duration = Duration() + pm_duration.FromTimedelta(pm_timedelta) + assert board.power_mode_duration == pm_duration.ToTimedelta() + + async def test_stream_ticks(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardClient(name=board.name, channel=channel) + di = await client.digital_interrupt_by_name("interrupt1") - names = await client.analog_reader_names() - assert names == ["reader1"] + tick_stream = await client.stream_ticks(interrupts=[di]) + async for tick in tick_stream: + assert tick.pin_name == "interrupt1" + assert tick.high is True + assert tick.time == 1000 + break - @pytest.mark.asyncio - async def test_digital_interrupt_names(self, board: MockBoard, service: BoardService): + async def test_extra(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: - client = BoardClient(name=board.name, channel=channel) + client = BoardClient(board.name, channel) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES - names = await client.digital_interrupt_names() - assert names == ["interrupt1"] - @pytest.mark.asyncio - async def test_status(self, board: MockBoard, service: BoardService): +class TestAnalogClient: + async def test_read(self, analog: MockAnalog, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: - client = BoardClient(name=board.name, channel=channel) + board_client = BoardClient(name=board.name, channel=channel) + analog_client = await board_client.analog_by_name("analog1") + value = await analog_client.read() + assert value == analog.value - extra = {"foo": "bar", "baz": [1, 2, 3]} - status = await client.status(extra) - assert status == BoardStatus( - analogs={"reader1": AnalogStatus(value=3)}, - digital_interrupts={"interrupt1": DigitalInterruptStatus(value=0)}, - ) - assert board.extra == extra - - @pytest.mark.asyncio - async def test_model_attributes(self, board: MockBoard, service: BoardService): + async def test_write(self, analog: MockAnalog, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: - client = BoardClient(name=board.name, channel=channel) + board_client = BoardClient(name=board.name, channel=channel) + analog_client = await board_client.analog_by_name("analog1") + await analog_client.write(45) + assert analog.value.value == 45 + - attrs = await client.model_attributes() - assert attrs == Board.Attributes(remote=True) +class TestDigitalInterrupt: + async def test_value(self, interrupt: MockDigitalInterrupt, board: MockBoard, service: BoardRPCService): + async with ChannelFor([service]) as channel: + board_client = BoardClient(name=board.name, channel=channel) + interrupt_client = await board_client.digital_interrupt_by_name("interrupt1") + value = await interrupt_client.value() + assert value == interrupt.val class TestGPIOPinClient: - @pytest.mark.asyncio - async def test_set(self, board: MockBoard, service: BoardService): + async def test_set(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardClient(name=board.name, channel=channel) pin = await client.gpio_pin_by_name("pin1") extra = {"foo": "bar", "baz": [1, 2, 3]} - await pin.set(True, extra) + await pin.set(True, extra=extra, timeout=1.82) mock_pin = cast(MockGPIOPin, board.gpios["pin1"]) assert mock_pin.high is True assert mock_pin.extra == extra + assert mock_pin.timeout == loose_approx(1.82) - @pytest.mark.asyncio - async def test_get(self, board: MockBoard, service: BoardService): + async def test_get(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardClient(name=board.name, channel=channel) pin = await client.gpio_pin_by_name("pin1") extra = {"foo": "bar", "baz": [1, 2, 3]} - high = await pin.get(extra) + high = await pin.get(extra=extra) assert high is False mock_pin = cast(MockGPIOPin, board.gpios["pin1"]) assert mock_pin.extra == extra + assert mock_pin.timeout is None - @pytest.mark.asyncio - async def test_set_pwm(self, board: MockBoard, service: BoardService): + async def test_set_pwm(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardClient(name=board.name, channel=channel) pin = await client.gpio_pin_by_name("pin1") extra = {"foo": "bar", "baz": [1, 2, 3]} - await pin.set_pwm(12.3, extra) + await pin.set_pwm(12.3, extra=extra, timeout=3.23) mock_pin = cast(MockGPIOPin, board.gpios["pin1"]) assert mock_pin.pwm == 12.3 assert mock_pin.extra == extra + assert mock_pin.timeout == loose_approx(3.23) - @pytest.mark.asyncio - async def test_get_pwm(self, board: MockBoard, service: BoardService): + async def test_get_pwm(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardClient(name=board.name, channel=channel) pin = await client.gpio_pin_by_name("pin1") extra = {"foo": "bar", "baz": [1, 2, 3]} - pwm = await pin.get_pwm(extra) + pwm = await pin.get_pwm(extra=extra, timeout=1.2345) assert pwm == 0.0 mock_pin = cast(MockGPIOPin, board.gpios["pin1"]) assert mock_pin.extra == extra + assert mock_pin.timeout == loose_approx(1.2345) - @pytest.mark.asyncio - async def test_set_pwm_frequency(self, board: MockBoard, service: BoardService): + async def test_set_pwm_frequency(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardClient(name=board.name, channel=channel) pin = await client.gpio_pin_by_name("pin1") extra = {"foo": "bar", "baz": [1, 2, 3]} - await pin.set_pwm_frequency(123, extra) + await pin.set_pwm_frequency(123, extra=extra, timeout=4.341) mock_pin = cast(MockGPIOPin, board.gpios["pin1"]) assert mock_pin.pwm_freq == 123 assert mock_pin.extra == extra + assert mock_pin.timeout == loose_approx(4.341) - @pytest.mark.asyncio - async def test_get_pwm_freq(self, board: MockBoard, service: BoardService): + async def test_get_pwm_freq(self, board: MockBoard, service: BoardRPCService): async with ChannelFor([service]) as channel: client = BoardClient(name=board.name, channel=channel) pin = await client.gpio_pin_by_name("pin1") extra = {"foo": "bar", "baz": [1, 2, 3]} - freq = await pin.get_pwm_frequency(extra) + freq = await pin.get_pwm_frequency(extra=extra) assert freq == 0 mock_pin = cast(MockGPIOPin, board.gpios["pin1"]) assert mock_pin.extra == extra - - @pytest.mark.asyncio - async def test_do(self, board: MockBoard, service: BoardService, generic_service: GenericService): - async with ChannelFor([service, generic_service]) as channel: - client = BoardClient(board.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + assert mock_pin.timeout is None diff --git a/tests/test_button.py b/tests/test_button.py new file mode 100644 index 000000000..146303c7c --- /dev/null +++ b/tests/test_button.py @@ -0,0 +1,85 @@ +import pytest +from grpclib.testing import ChannelFor + +from viam.components.button import ButtonClient +from viam.components.button.service import ButtonRPCService +from viam.gen.component.button.v1.button_pb2 import PushRequest +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, +) +from viam.proto.component.button import ButtonServiceStub +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict + +from . import loose_approx +from .mocks.components import MockButton + +EXTRA_PARAMS = {"foo": "bar", "baz": [1, 2, 3]} + + +@pytest.fixture(scope="function") +def button() -> MockButton: + return MockButton(name="button") + + +class TestButton: + async def test_push(self, button): + await button.push(timeout=1.23, extra=EXTRA_PARAMS) + assert button.pushed is True + assert button.timeout == loose_approx(1.23) + assert button.extra == EXTRA_PARAMS + + async def test_do(self, button): + command = {"command": "args"} + resp = await button.do_command(command) + assert resp == {"command": command} + + +@pytest.fixture(scope="function") +def manager(button) -> ResourceManager: + return ResourceManager([button]) + + +@pytest.fixture(scope="function") +def service(manager) -> ButtonRPCService: + return ButtonRPCService(manager) + + +class TestService: + async def test_push(self, button, service): + async with ChannelFor([service]) as channel: + client = ButtonServiceStub(channel) + request = PushRequest(name=button.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert button.extra is None + await client.Push(request, timeout=1.23) + assert button.pushed is True + assert button.extra == EXTRA_PARAMS + assert button.timeout == loose_approx(1.23) + + async def test_do(self, button: MockButton, service: ButtonRPCService): + async with ChannelFor([service]) as channel: + client = ButtonServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=button.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + +class TestClient: + async def test_push(self, button, service): + async with ChannelFor([service]) as channel: + client = ButtonClient(button.name, channel) + assert button.extra is None + await client.push(timeout=3.45, extra=EXTRA_PARAMS) + assert button.pushed is True + assert button.extra == EXTRA_PARAMS + assert button.timeout == loose_approx(3.45) + + async def test_do(self, button, manager, service): + async with ChannelFor([service]) as channel: + client = ButtonClient(button.name, channel) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} diff --git a/tests/test_camera.py b/tests/test_camera.py index 5586c5e01..11820412a 100644 --- a/tests/test_camera.py +++ b/tests/test_camera.py @@ -1,20 +1,23 @@ -from io import BytesIO +from datetime import datetime import pytest from google.api.httpbody_pb2 import HttpBody +from google.protobuf.timestamp_pb2 import Timestamp from grpclib.testing import ChannelFor -from PIL import Image from viam.components.camera import Camera, CameraClient -from viam.components.camera.service import CameraService -from viam.components.generic.service import GenericService -from viam.components.resource_manager import ResourceManager -from viam.components.types import CameraMimeType, RawImage +from viam.components.camera.service import CameraRPCService +from viam.components.generic.service import GenericRPCService +from viam.media.video import CameraMimeType, NamedImage, ViamImage +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse, ResponseMetadata from viam.proto.component.camera import ( CameraServiceStub, DistortionParameters, + Format, GetImageRequest, GetImageResponse, + GetImagesRequest, + GetImagesResponse, GetPointCloudRequest, GetPointCloudResponse, GetPropertiesRequest, @@ -22,8 +25,11 @@ IntrinsicParameters, RenderFrameRequest, ) +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict -from .mocks.components import MockCamera +from . import loose_approx +from .mocks.components import GEOMETRIES, MockCamera # ################################ NB ################################# # # These test values have to be fixtures and must match the values in # @@ -33,8 +39,15 @@ @pytest.fixture(scope="function") -def image() -> Image.Image: - return Image.new("RGBA", (100, 100), "#AABBCCDD") +def image() -> ViamImage: + return ViamImage(b"data", CameraMimeType.PNG) + + +@pytest.fixture(scope="function") +def metadata() -> ResponseMetadata: + ts = Timestamp() + ts.FromDatetime(datetime(1970, 1, 1)) + return ResponseMetadata(captured_at=ts) @pytest.fixture(scope="function") @@ -45,154 +58,206 @@ def point_cloud() -> bytes: @pytest.fixture(scope="function") def properties() -> Camera.Properties: return Camera.Properties( - True, - IntrinsicParameters(width_px=1, height_px=2, focal_x_px=3, focal_y_px=4, center_x_px=5, center_y_px=6), - DistortionParameters(model="no_distortion"), + supports_pcd=False, + intrinsic_parameters=IntrinsicParameters(width_px=1, height_px=2, focal_x_px=3, focal_y_px=4, center_x_px=5, center_y_px=6), + distortion_parameters=DistortionParameters(model="no_distortion"), + mime_types=[CameraMimeType.PNG, CameraMimeType.JPEG], + frame_rate=10.0, ) @pytest.fixture(scope="function") -def camera() -> Camera: +def camera() -> MockCamera: return MockCamera("camera") @pytest.fixture(scope="function") -def service(camera: Camera) -> CameraService: +def service(camera: Camera) -> CameraRPCService: rm = ResourceManager([camera]) - return CameraService(rm) + return CameraRPCService(rm) @pytest.fixture(scope="function") -def generic_service(camera: Camera) -> GenericService: +def generic_service(camera: Camera) -> GenericRPCService: manager = ResourceManager([camera]) - return GenericService(manager) + return GenericRPCService(manager) class TestCamera: - @pytest.mark.asyncio - async def test_get_frame(self, camera: Camera, image: Image.Image): + async def test_get_image(self, camera: MockCamera, image: ViamImage): img = await camera.get_image(CameraMimeType.PNG) - assert img == image + assert img.data == image.data + assert img.mime_type == image.mime_type - img = await camera.get_image(CameraMimeType.RAW) - assert isinstance(img, RawImage) + img = await camera.get_image(CameraMimeType.PNG, {"1": 1}) + assert camera.extra == {"1": 1} - @pytest.mark.asyncio - async def test_get_point_cloud(self, camera: Camera, point_cloud: bytes): + async def test_get_images(self, camera: Camera, image: ViamImage, metadata: ResponseMetadata): + imgs, md = await camera.get_images() + assert isinstance(imgs[0], NamedImage) + assert imgs[0].name == camera.name + assert imgs[0].data == image.data + assert md == metadata + + async def test_get_point_cloud(self, camera: MockCamera, point_cloud: bytes): pc, _ = await camera.get_point_cloud() assert pc == point_cloud - @pytest.mark.asyncio + await camera.get_point_cloud(extra={"1": 1}) + assert camera.extra == {"1": 1} + async def test_get_properties(self, camera: Camera, properties: Camera.Properties): props = await camera.get_properties() assert props == properties - @pytest.mark.asyncio async def test_do(self, camera: Camera): - with pytest.raises(NotImplementedError): - await camera.do_command({"command": "args"}) + command = {"command": "args"} + resp = await camera.do_command(command) + assert resp == {"command": command} + + async def test_timeout(self, camera: MockCamera): + assert camera.timeout is None + + await camera.get_image(timeout=1.82) + assert camera.timeout == loose_approx(1.82) + + await camera.get_point_cloud(timeout=4.4) + assert camera.timeout == loose_approx(4.4) + + await camera.get_properties(timeout=7.86) + assert camera.timeout == loose_approx(7.86) + + async def test_get_geometries(self, camera: MockCamera): + geometries = await camera.get_geometries() + assert geometries == GEOMETRIES class TestService: - @pytest.mark.asyncio - async def test_get_frame(self, service: CameraService, image: Image.Image): + async def test_get_image(self, camera: MockCamera, service: CameraRPCService, image: ViamImage): + assert camera.timeout is None async with ChannelFor([service]) as channel: client = CameraServiceStub(channel) # Test known mime type request = GetImageRequest(name="camera", mime_type=CameraMimeType.PNG) - response: GetImageResponse = await client.GetImage(request) - img = Image.open(BytesIO(response.image), formats=["PNG"]) - assert img.tobytes() == image.tobytes() + response: GetImageResponse = await client.GetImage(request, timeout=18.2) + assert response.image == image.data + assert camera.timeout == loose_approx(18.2) - # Test raw mime type - request = GetImageRequest(name="camera", mime_type=CameraMimeType.RAW) + # Test empty mime type. Empty mime type should default to response mime type + request = GetImageRequest(name="camera") response: GetImageResponse = await client.GetImage(request) - img = Image.frombytes("RGBA", (response.width_px, response.height_px), response.image, "raw") - assert img == image - assert response.mime_type == CameraMimeType.RAW + assert response.image == image.data + assert response.mime_type == image.mime_type - # Test unknown mime type - request = GetImageRequest(name="camera", mime_type="unknown") - response: GetImageResponse = await client.GetImage(request) - img = Image.frombytes("RGBA", (response.width_px, response.height_px), response.image, "raw") - assert img == image - assert response.mime_type == "unknown" + async def test_get_images(self, camera: MockCamera, service: CameraRPCService, metadata: ResponseMetadata): + assert camera.timeout is None + async with ChannelFor([service]) as channel: + client = CameraServiceStub(channel) + + request = GetImagesRequest(name="camera") + response: GetImagesResponse = await client.GetImages(request, timeout=18.2) + raw_img = response.images[0] + assert raw_img.format == Format.FORMAT_PNG + assert raw_img.source_name == camera.name + assert response.response_metadata == metadata + assert camera.timeout == loose_approx(18.2) - @pytest.mark.asyncio - async def test_render_frame(self, service: CameraService, image: Image.Image): + async def test_render_frame(self, camera: MockCamera, service: CameraRPCService, image: ViamImage): + assert camera.timeout is None async with ChannelFor([service]) as channel: client = CameraServiceStub(channel) request = RenderFrameRequest(name="camera", mime_type=CameraMimeType.PNG) - response: HttpBody = await client.RenderFrame(request) + response: HttpBody = await client.RenderFrame(request, timeout=4.4) assert response.content_type == CameraMimeType.PNG - buf = BytesIO(response.data) - img = Image.open(buf, formats=["JPEG", "PNG"]) - assert img.tobytes() == image.tobytes() + assert response.data == image.data + assert camera.timeout == loose_approx(4.4) - @pytest.mark.asyncio - async def test_get_point_cloud(self, service: CameraService, point_cloud: bytes): + async def test_get_point_cloud(self, camera: MockCamera, service: CameraRPCService, point_cloud: bytes): + assert camera.timeout is None async with ChannelFor([service]) as channel: client = CameraServiceStub(channel) request = GetPointCloudRequest(name="camera", mime_type=CameraMimeType.PCD) - response: GetPointCloudResponse = await client.GetPointCloud(request) + response: GetPointCloudResponse = await client.GetPointCloud(request, timeout=7.86) assert response.point_cloud == point_cloud + assert camera.timeout == loose_approx(7.86) - @pytest.mark.asyncio - async def test_get_properties(self, service: CameraService, properties: Camera.Properties): + async def test_get_properties(self, camera: MockCamera, service: CameraRPCService, properties: Camera.Properties): + assert camera.timeout is None async with ChannelFor([service]) as channel: client = CameraServiceStub(channel) request = GetPropertiesRequest(name="camera") - response: GetPropertiesResponse = await client.GetProperties(request) + response: GetPropertiesResponse = await client.GetProperties(request, timeout=5.43) assert response.supports_pcd == properties.supports_pcd assert response.intrinsic_parameters == properties.intrinsic_parameters + assert response.mime_types == properties.mime_types + assert response.frame_rate == properties.frame_rate + assert camera.timeout == loose_approx(5.43) + + async def test_do(self, camera: MockCamera, service: CameraRPCService): + async with ChannelFor([service]) as channel: + client = CameraServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=camera.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + async def test_get_geometries(self, camera: MockCamera, service: CameraRPCService): + async with ChannelFor([service]) as channel: + client = CameraServiceStub(channel) + request = GetGeometriesRequest(name=camera.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES class TestClient: - @pytest.mark.asyncio - async def test_get_frame(self, service: CameraService, image: Image.Image): + async def test_get_image(self, camera: MockCamera, service: CameraRPCService, image: ViamImage): + assert camera.timeout is None async with ChannelFor([service]) as channel: client = CameraClient("camera", channel) - # Test known mime type - img = await client.get_image() - assert isinstance(img, Image.Image) - assert img.convert("RGBA") == image.convert("RGBA") - assert img.tobytes() == image.tobytes() - - # Test raw mime type - img = await client.get_image(CameraMimeType.RAW) - assert isinstance(img, RawImage) - assert img.data == image.tobytes() - assert img.width == image.width - assert img.height == image.height - assert img.mime_type == CameraMimeType.RAW - - # Test unknown mime type - img = await client.get_image("unknown") - assert isinstance(img, RawImage) - assert img.data == image.tobytes() - assert img.width == image.width - assert img.height == image.height - assert img.mime_type == "unknown" - - @pytest.mark.asyncio - async def test_get_point_cloud(self, service: CameraService, point_cloud: bytes): + img = await client.get_image(timeout=1.82, mime_type=CameraMimeType.PNG) + assert img.data == image.data + assert img.mime_type == image.mime_type + + async def test_get_images(self, camera: MockCamera, service: CameraRPCService, image: ViamImage, metadata: ResponseMetadata): + assert camera.timeout is None + async with ChannelFor([service]) as channel: + client = CameraClient("camera", channel) + + imgs, md = await client.get_images(timeout=1.82) + assert isinstance(imgs[0], NamedImage) + assert imgs[0].name == camera.name + assert imgs[0].data == image.data + assert md == metadata + assert camera.timeout == loose_approx(1.82) + + async def test_get_point_cloud(self, camera: MockCamera, service: CameraRPCService, point_cloud: bytes): + assert camera.timeout is None async with ChannelFor([service]) as channel: - camera = CameraClient("camera", channel) - pc, _ = await camera.get_point_cloud() + client = CameraClient("camera", channel) + pc, _ = await client.get_point_cloud(timeout=4.4) assert pc == point_cloud + assert camera.timeout == loose_approx(4.4) - @pytest.mark.asyncio - async def test_get_properties(self, service: CameraService, properties: Camera.Properties): + async def test_get_properties(self, camera: MockCamera, service: CameraRPCService, properties: Camera.Properties): + assert camera.timeout is None async with ChannelFor([service]) as channel: - camera = CameraClient("camera", channel) - props = await camera.get_properties() + client = CameraClient("camera", channel) + props = await client.get_properties(timeout=7.86) assert props == properties + assert camera.timeout == loose_approx(7.86) - @pytest.mark.asyncio - async def test_do(self, service: CameraService, generic_service: GenericService): - async with ChannelFor([service, generic_service]) as channel: + async def test_do(self, service: CameraRPCService): + async with ChannelFor([service]) as channel: + client = CameraClient("camera", channel) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self, service: CameraRPCService): + async with ChannelFor([service]) as channel: client = CameraClient("camera", channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_component_service_base.py b/tests/test_component_service_base.py new file mode 100644 index 000000000..73984f038 --- /dev/null +++ b/tests/test_component_service_base.py @@ -0,0 +1,74 @@ +import asyncio +import time +from typing import Mapping + +import pytest +from grpclib import const +from grpclib.client import Channel + +from viam.components.component_base import ComponentBase +from viam.errors import ResourceNotFoundError +from viam.operations import run_with_operation +from viam.resource.manager import ResourceManager +from viam.resource.registry import Registry, ResourceRegistration +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.resource.types import API + + +async def test_cancellation_propagation(): + class TestComponent(ComponentBase): + API = API("test", "test", "test") + + long_running_task_cancelled = False + + @run_with_operation + async def long_running(self, **kwargs) -> bool: + operation = self.get_operation(kwargs) + for _ in range(5): + time.sleep(0.01) + if await operation.is_cancelled(): + self.long_running_task_cancelled = True + return self.long_running_task_cancelled + self.long_running_task_cancelled = False + return self.long_running_task_cancelled + + class TestClient(TestComponent, ReconfigurableResourceRPCClientBase): + def __init__(self, name: str, channel: Channel): ... + + class TestService(ResourceRPCServiceBase): + RESOURCE_TYPE = TestComponent + + async def long_running(self) -> bool: + component = self.get_resource("test") + return await component.long_running() + + def __mapping__(self) -> Mapping[str, const.Handler]: + return { + "/TestService/long_running": const.Handler(self.long_running, const.Cardinality.UNARY_UNARY, "TestRequest", "TestResponse") + } + + component = TestComponent("test") + with pytest.raises(ResourceNotFoundError): + service = TestService(ResourceManager([component])) + + Registry.register_api(ResourceRegistration(TestComponent, TestService, lambda name, channel: TestClient(name, channel))) + service = TestService(ResourceManager([component])) + + # Test bare functions + result = await component.long_running() + assert result is False + + # Test from service + result = await service.long_running() + assert result is False + + # Test cancelled from service + with pytest.raises(asyncio.CancelledError): + task = asyncio.create_task(service.long_running()) + + asyncio.get_event_loop().call_later(0.02, task.cancel) + + result = await task + + assert component.long_running_task_cancelled is True diff --git a/tests/test_data_client.py b/tests/test_data_client.py new file mode 100644 index 000000000..381143ad7 --- /dev/null +++ b/tests/test_data_client.py @@ -0,0 +1,445 @@ +from datetime import datetime +from typing import List + +import pytest +from google.protobuf.struct_pb2 import Struct +from google.protobuf.timestamp_pb2 import Timestamp +from grpclib.testing import ChannelFor + +from viam.app.data_client import DataClient +from viam.proto.app.data import ( + Annotations, + BinaryData, + BinaryID, + BinaryMetadata, + BoundingBox, + CaptureInterval, + CaptureMetadata, + ExportTabularDataResponse, + Filter, + Order, +) +from viam.utils import create_filter, dict_to_struct + +from .mocks.services import MockData + +INCLUDE_BINARY = True +COMPONENT_NAME = "component_name" +COMPONENT_TYPE = "component_type" +METHOD = "method" +ROBOT_NAME = "robot_name" +ROBOT_ID = "robot_id" +PART_NAME = "part_name" +PART_ID = "part_id" +LOCATION_ID = "location_id" +LOCATION_IDS = [LOCATION_ID] +ORG_ID = "organization_id" +ORG_IDS = [ORG_ID] +PASSWORD = "password" +MIME_TYPE = "mime_type" +MIME_TYPES = [MIME_TYPE] +URI = "some.robot.uri" +SECONDS_START = 978310861 +NANOS_START = 0 +SECONDS_END = 978310861 +NANOS_END = 0 +START_TS = Timestamp(seconds=SECONDS_START, nanos=NANOS_START) +END_TS = Timestamp(seconds=SECONDS_END, nanos=NANOS_END) +START_DATETIME = START_TS.ToDatetime() +END_DATETIME = END_TS.ToDatetime() +TAGS = ["tag"] +BBOX_LABEL = "bbox_label" +BBOX_LABELS = [BBOX_LABEL] +DATASET_ID = "VIAM_DATASET_1" +FILTER = create_filter( + component_name=COMPONENT_NAME, + component_type=COMPONENT_TYPE, + method=METHOD, + robot_name=ROBOT_NAME, + robot_id=ROBOT_ID, + part_name=PART_NAME, + part_id=PART_ID, + location_ids=LOCATION_IDS, + organization_ids=ORG_IDS, + mime_type=MIME_TYPES, + start_time=START_DATETIME, + end_time=END_DATETIME, + tags=TAGS, + bbox_labels=BBOX_LABELS, + dataset_id=DATASET_ID, +) +INTERVAL = CaptureInterval(start=START_TS, end=END_TS) + +FILE_ID = "file_id" +BINARY_ID = BinaryID(file_id=FILE_ID, organization_id=ORG_ID, location_id=LOCATION_ID) +BINARY_IDS = [BINARY_ID] +BINARY_DATA_ID = "binary_data_id" +BINARY_DATA_IDS = [BINARY_DATA_ID] +BINARY_DATA = b"binary_data" +FILE_NAME = "file_name" +FILE_EXT = "file_extension" +BBOX = BoundingBox( + id="id", + label=BBOX_LABEL, + x_min_normalized=0, + y_min_normalized=0.1, + x_max_normalized=0.2, + y_max_normalized=0.3, +) +BBOXES = [BBOX] +SQL_QUERY = "sql_query" +MQL_BINARY = [{"binary": "mql_binary"}] +TABULAR_DATA = {"key": "value"} +TABULAR_METADATA = CaptureMetadata( + organization_id=ORG_ID, + location_id=LOCATION_ID, + robot_name=ROBOT_NAME, + robot_id=ROBOT_ID, + part_name=PART_NAME, + part_id=PART_ID, + component_type=COMPONENT_TYPE, + component_name=COMPONENT_NAME, + method_name=METHOD, + method_parameters={}, + tags=TAGS, + mime_type=MIME_TYPE, +) +BINARY_METADATA = BinaryMetadata( + id="id", + binary_data_id=BINARY_DATA_ID, + capture_metadata=TABULAR_METADATA, + time_requested=START_TS, + time_received=END_TS, + file_name=FILE_NAME, + file_ext=FILE_EXT, + uri=URI, + annotations=Annotations(bboxes=BBOXES), +) + +TABULAR_RESPONSE = [DataClient.TabularData(TABULAR_DATA, TABULAR_METADATA, START_DATETIME, END_DATETIME)] +TABULAR_EXPORT_RESPONSE = [ + ExportTabularDataResponse( + part_id=TABULAR_METADATA.part_id, + resource_name=TABULAR_METADATA.component_name, + resource_subtype=TABULAR_METADATA.component_type, + time_captured=END_TS, + organization_id=TABULAR_METADATA.organization_id, + location_id=TABULAR_METADATA.location_id, + robot_name=TABULAR_METADATA.robot_name, + robot_id=TABULAR_METADATA.robot_id, + part_name=TABULAR_METADATA.part_name, + method_parameters=Struct(), + tags=TABULAR_METADATA.tags, + payload=dict_to_struct(TABULAR_DATA), + ) +] +TABULAR_QUERY_RESPONSE = [ + {"key1": START_DATETIME, "key2": "2", "key3": [1, 2, 3], "key4": {"key4sub1": END_DATETIME}}, +] +BINARY_RESPONSE = [BinaryData(binary=BINARY_DATA, metadata=BINARY_METADATA)] +DELETE_REMOVE_RESPONSE = 1 +TAGS_RESPONSE = ["tag"] +HOSTNAME_RESPONSE = "host" + +AUTH_TOKEN = "auth_token" +DATA_SERVICE_METADATA = {"authorization": f"Bearer {AUTH_TOKEN}"} + + +@pytest.fixture(scope="function") +def service() -> MockData: + return MockData( + tabular_response=TABULAR_RESPONSE, + tabular_export_response=TABULAR_EXPORT_RESPONSE, + tabular_query_response=TABULAR_QUERY_RESPONSE, + binary_response=BINARY_RESPONSE, + delete_remove_response=DELETE_REMOVE_RESPONSE, + tags_response=TAGS_RESPONSE, + bbox_labels_response=BBOX_LABELS, + hostname_response=HOSTNAME_RESPONSE, + ) + + +class TestClient: + async def test_tabular_data_by_filter(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + sort_order = Order.ORDER_ASCENDING + limit = 100 + last = "LAST_TABULAR_ID" + tabular_data, count, last_response = await client.tabular_data_by_filter( + filter=FILTER, + sort_order=sort_order, + limit=100, + last=last, + count_only=True, + ) + assert service.filter == FILTER + assert service.order == sort_order + assert service.limit == limit + assert service.last == last + assert service.count_only is True + assert tabular_data == TABULAR_RESPONSE + assert count == len(tabular_data) + assert last_response != "" + self.assert_filter(filter=service.filter) + + async def test_tabular_data_by_sql(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + response = await client.tabular_data_by_sql(ORG_ID, SQL_QUERY) + assert isinstance(response[0]["key1"], datetime) + assert response == TABULAR_QUERY_RESPONSE + + async def test_tabular_data_by_mql(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + response = await client.tabular_data_by_mql(ORG_ID, MQL_BINARY) + assert isinstance(response[0]["key1"], datetime) + assert response == TABULAR_QUERY_RESPONSE + response = await client.tabular_data_by_mql(ORG_ID, mql_binary=[b"mql_binary"]) + assert isinstance(response[0]["key1"], datetime) + assert response == TABULAR_QUERY_RESPONSE + + async def test_get_latest_tabular_data(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + time = datetime(2024, 12, 25) + response = await client.get_latest_tabular_data(PART_ID, COMPONENT_NAME, COMPONENT_TYPE, METHOD) + assert response is not None + time_captured, time_synced, payload = response + assert service.part_id == PART_ID + assert service.resource_name == COMPONENT_NAME + assert service.resource_subtype == COMPONENT_TYPE + assert service.method_name == METHOD + assert payload == TABULAR_DATA + assert time_captured == time + assert time_synced == time + + async def test_export_tabular_data(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + tabular_data = await client.export_tabular_data(PART_ID, COMPONENT_NAME, COMPONENT_TYPE, METHOD, START_DATETIME, END_DATETIME) + assert tabular_data is not None + for tabular_datum in tabular_data: + assert tabular_datum is not None + assert tabular_datum.part_id == TABULAR_METADATA.part_id + assert tabular_datum.resource_name == TABULAR_METADATA.component_name + assert tabular_datum.resource_api == TABULAR_METADATA.component_type + assert tabular_datum.time_captured == END_DATETIME + assert tabular_datum.organization_id == TABULAR_METADATA.organization_id + assert tabular_datum.location_id == TABULAR_METADATA.location_id + assert tabular_datum.robot_name == TABULAR_METADATA.robot_name + assert tabular_datum.robot_id == TABULAR_METADATA.robot_id + assert tabular_datum.part_name == TABULAR_METADATA.part_name + assert tabular_datum.method_parameters == TABULAR_METADATA.method_parameters + assert tabular_datum.tags == TABULAR_METADATA.tags + assert tabular_datum.payload == TABULAR_DATA + assert service.part_id == PART_ID + assert service.resource_name == COMPONENT_NAME + assert service.resource_subtype == COMPONENT_TYPE + assert service.method_name == METHOD + assert service.interval == INTERVAL + + async def test_binary_data_by_filter(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + sort_order = Order.ORDER_DESCENDING + limit = 25 + last = "LAST_BINARY_ID" + binary_data, count, last_response = await client.binary_data_by_filter( + filter=FILTER, + include_binary_data=INCLUDE_BINARY, + sort_order=sort_order, + limit=limit, + count_only=False, + include_internal_data=True, + last=last, + ) + assert service.filter == FILTER + assert service.order == sort_order + assert service.limit == limit + assert service.include_binary == INCLUDE_BINARY + assert service.count_only is False + assert service.include_internal_data is True + assert service.last == last + assert binary_data == BINARY_RESPONSE + assert count == len(binary_data) + assert last_response != "" + self.assert_filter(filter=service.filter) + + async def test_binary_data_by_ids(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + binary_data = await client.binary_data_by_ids(binary_ids=BINARY_IDS) + assert binary_data == BINARY_RESPONSE + self.assert_binary_ids(binary_ids=list(service.binary_ids)) + binary_data = await client.binary_data_by_ids(binary_ids=BINARY_DATA_IDS) + assert binary_data == BINARY_RESPONSE + self.assert_binary_data_ids(binary_data_ids=list(service.binary_data_ids)) + + async def test_delete_tabular_data(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + deleted_count = await client.delete_tabular_data(organization_id=ORG_ID, delete_older_than_days=0) + assert deleted_count == DELETE_REMOVE_RESPONSE + + async def test_delete_binary_data_by_filter(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + deleted_count = await client.delete_binary_data_by_filter(filter=FILTER) + assert deleted_count == DELETE_REMOVE_RESPONSE + self.assert_filter(filter=service.filter) + + async def test_delete_binary_data_by_ids(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + deleted_count = await client.delete_binary_data_by_ids(binary_ids=BINARY_IDS) + assert deleted_count == DELETE_REMOVE_RESPONSE + self.assert_binary_ids(binary_ids=list(service.binary_ids)) + deleted_count = await client.delete_binary_data_by_ids(binary_ids=BINARY_DATA_IDS) + assert deleted_count == DELETE_REMOVE_RESPONSE + self.assert_binary_data_ids(binary_data_ids=list(service.binary_data_ids)) + + async def test_add_tags_to_binary_data_by_ids(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + await client.add_tags_to_binary_data_by_ids(tags=TAGS, binary_ids=BINARY_IDS) + assert service.tags == TAGS + self.assert_binary_ids(binary_ids=list(service.binary_ids)) + await client.add_tags_to_binary_data_by_ids(tags=TAGS, binary_ids=BINARY_DATA_IDS) + assert service.tags == TAGS + self.assert_binary_data_ids(binary_data_ids=list(service.binary_data_ids)) + + async def test_add_tags_to_binary_data_by_filter(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + await client.add_tags_to_binary_data_by_filter(tags=TAGS, filter=FILTER) + assert service.tags == TAGS + self.assert_filter(filter=service.filter) + + async def test_remove_tags_from_binary_data_by_ids(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + deleted_count = await client.remove_tags_from_binary_data_by_ids(tags=TAGS, binary_ids=BINARY_IDS) + assert deleted_count == DELETE_REMOVE_RESPONSE + assert service.tags == TAGS + self.assert_binary_ids(binary_ids=list(service.binary_ids)) + deleted_count = await client.remove_tags_from_binary_data_by_ids(tags=TAGS, binary_ids=BINARY_DATA_IDS) + assert deleted_count == DELETE_REMOVE_RESPONSE + assert service.tags == TAGS + self.assert_binary_data_ids(binary_data_ids=list(service.binary_data_ids)) + + async def test_remove_tags_from_binary_data_by_filter(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + deleted_count = await client.remove_tags_from_binary_data_by_filter(tags=TAGS, filter=FILTER) + assert deleted_count == DELETE_REMOVE_RESPONSE + assert service.tags == TAGS + self.assert_filter(filter=service.filter) + + async def test_tags_by_filter(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + tags = await client.tags_by_filter(filter=FILTER) + assert tags == TAGS_RESPONSE + self.assert_filter(filter=service.filter) + + async def test_add_bounding_box_to_image_by_id(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + bbox_label = await client.add_bounding_box_to_image_by_id( + binary_id=BINARY_ID, + label="label", + x_min_normalized=0, + y_min_normalized=0.1, + x_max_normalized=0.2, + y_max_normalized=0.3, + ) + assert bbox_label == BBOX_LABEL + bbox_label = await client.add_bounding_box_to_image_by_id( + binary_id=BINARY_DATA_ID, + label="label", + x_min_normalized=0, + y_min_normalized=0.1, + x_max_normalized=0.2, + y_max_normalized=0.3, + ) + assert bbox_label == BBOX_LABEL + + async def test_remove_bounding_box_from_image_by_id(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + await client.remove_bounding_box_from_image_by_id(BBOX_LABEL, BINARY_ID) + assert service.removed_label == BBOX_LABEL + assert service.removed_id == BINARY_ID + await client.remove_bounding_box_from_image_by_id(BBOX_LABEL, BINARY_DATA_ID) + assert service.removed_label == BBOX_LABEL + assert service.removed_binary_data_id == BINARY_DATA_ID + + async def test_bounding_box_labels_by_filter(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + bbox_labels = await client.bounding_box_labels_by_filter(filter=FILTER) + assert bbox_labels == BBOX_LABELS + self.assert_filter(filter=service.filter) + + async def test_get_database_connection(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + hostname = await client.get_database_connection(organization_id=ORG_ID) + assert hostname == HOSTNAME_RESPONSE + + async def test_configure_database_user(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + await client.configure_database_user(ORG_ID, PASSWORD) + assert service.organization_id == ORG_ID + assert service.password == PASSWORD + + async def test_add_binary_data_to_dataset_by_ids(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + await client.add_binary_data_to_dataset_by_ids(binary_ids=BINARY_IDS, dataset_id=DATASET_ID) + assert service.added_data_ids == BINARY_IDS + assert service.dataset_id == DATASET_ID + await client.add_binary_data_to_dataset_by_ids(binary_ids=BINARY_DATA_IDS, dataset_id=DATASET_ID) + assert service.added_binary_data_ids == BINARY_DATA_IDS + assert service.dataset_id == DATASET_ID + + async def test_remove_binary_data_to_dataset_by_ids(self, service: MockData): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + await client.remove_binary_data_from_dataset_by_ids(binary_ids=BINARY_IDS, dataset_id=DATASET_ID) + assert service.removed_data_ids == BINARY_IDS + assert service.dataset_id == DATASET_ID + await client.remove_binary_data_from_dataset_by_ids(binary_ids=BINARY_DATA_IDS, dataset_id=DATASET_ID) + assert service.removed_binary_data_ids == BINARY_DATA_IDS + assert service.dataset_id == DATASET_ID + + def assert_filter(self, filter: Filter) -> None: + assert filter.component_name == COMPONENT_NAME + assert filter.component_type == COMPONENT_TYPE + assert filter.method == METHOD + assert filter.robot_name == ROBOT_NAME + assert filter.robot_id == ROBOT_ID + assert filter.part_name == PART_NAME + assert filter.part_id == PART_ID + assert filter.location_ids == LOCATION_IDS + assert filter.organization_ids == ORG_IDS + assert filter.mime_type == MIME_TYPES + assert filter.interval.start.seconds == SECONDS_START + assert filter.interval.start.nanos == NANOS_START + assert filter.interval.end.seconds == SECONDS_END + assert filter.interval.end.nanos == NANOS_END + assert filter.tags_filter.tags == TAGS + assert filter.bbox_labels == BBOX_LABELS + + def assert_binary_ids(self, binary_ids: List[BinaryID]) -> None: + for binary_id in binary_ids: + assert binary_id.file_id == FILE_ID + assert binary_id.organization_id == ORG_ID + assert binary_id.location_id == LOCATION_ID + + def assert_binary_data_ids(self, binary_data_ids: List[str]) -> None: + for binary_data_id in binary_data_ids: + assert binary_data_id == BINARY_DATA_ID diff --git a/tests/test_data_sync_client.py b/tests/test_data_sync_client.py new file mode 100644 index 000000000..aaada156d --- /dev/null +++ b/tests/test_data_sync_client.py @@ -0,0 +1,168 @@ +from datetime import datetime +from typing import Any, List, Mapping, Tuple, cast + +import pytest +from google.protobuf.timestamp_pb2 import Timestamp +from grpclib.testing import ChannelFor + +from viam.app.data_client import DataClient +from viam.proto.app.datasync import SensorData, UploadMetadata +from viam.utils import datetime_to_timestamp, struct_to_dict + +from .mocks.services import MockDataSync + +COMPONENT_NAME = "component_name" +COMPONENT_TYPE = "component_type" +PART_ID = "part_id" +SECONDS_START = 1689256710 +NANOS_START = 10 +SECONDS_END = 1689256810 +NANOS_END = 10 +TAGS = ["tag"] +BINARY_DATA = b"binary_data" +METHOD_NAME = "method_name" +DATETIMES = (datetime.now(), datetime.now()) +TIMESTAMPS = cast(Tuple[Timestamp, Timestamp], (datetime_to_timestamp(DATETIMES[0]), datetime_to_timestamp(DATETIMES[1]))) +METHOD_PARAMETERS = {} +TABULAR_DATA = [{"key": "value"}] +FILE_NAME = "file_name" +FILE_EXT = ".file_extension" +FILE_UPLOAD_RESPONSE = "ID" + +AUTH_TOKEN = "auth_token" +DATA_SERVICE_METADATA = {"authorization": f"Bearer {AUTH_TOKEN}"} + + +@pytest.fixture(scope="function") +def service() -> MockDataSync: + return MockDataSync(file_upload_response=FILE_UPLOAD_RESPONSE) + + +class TestClient: + async def test_binary_data_capture_upload(self, service: MockDataSync): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + file_id = await client.binary_data_capture_upload( + part_id=PART_ID, + component_type=COMPONENT_TYPE, + component_name=COMPONENT_NAME, + method_name=METHOD_NAME, + method_parameters=METHOD_PARAMETERS, + tags=TAGS, + data_request_times=DATETIMES, + binary_data=BINARY_DATA, + file_extension=".txt", + ) + self.assert_sensor_contents(sensor_contents=list(service.sensor_contents), is_binary=True) + self.assert_metadata(metadata=service.metadata) + assert service.metadata.file_extension == ".txt" + assert file_id == FILE_UPLOAD_RESPONSE + + # Test extension dot prepend + file_id = await client.binary_data_capture_upload( + part_id=PART_ID, + component_type=COMPONENT_TYPE, + component_name=COMPONENT_NAME, + method_name=METHOD_NAME, + method_parameters=METHOD_PARAMETERS, + tags=TAGS, + data_request_times=DATETIMES, + binary_data=BINARY_DATA, + file_extension="txt", + ) + assert service.metadata.file_extension == ".txt" + + async def test_tabular_data_capture_upload(self, service: MockDataSync): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + file_id = await client.tabular_data_capture_upload( + part_id=PART_ID, + component_type=COMPONENT_TYPE, + component_name=COMPONENT_NAME, + method_name=METHOD_NAME, + method_parameters=METHOD_PARAMETERS, + tags=TAGS, + data_request_times=[DATETIMES], + tabular_data=cast(List[Mapping[str, Any]], TABULAR_DATA), + ) + self.assert_sensor_contents(sensor_contents=list(service.sensor_contents), is_binary=False) + self.assert_metadata(metadata=service.metadata) + assert file_id == FILE_UPLOAD_RESPONSE + + async def test_file_upload(self, service: MockDataSync): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + file_id = await client.file_upload( + part_id=PART_ID, + component_type=COMPONENT_TYPE, + component_name=COMPONENT_NAME, + method_name=METHOD_NAME, + file_name=FILE_NAME, + method_parameters=METHOD_PARAMETERS, + file_extension=FILE_EXT, + tags=TAGS, + data=BINARY_DATA, + ) + assert file_id == FILE_UPLOAD_RESPONSE + self.assert_metadata(service.metadata) + assert service.metadata.file_name == FILE_NAME + assert service.metadata.file_extension == FILE_EXT + assert service.binary_data == BINARY_DATA + + async def test_file_upload_from_path(self, service: MockDataSync, tmp_path): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + path = tmp_path / (FILE_NAME + FILE_EXT) + path.write_bytes(BINARY_DATA) + file_id = await client.file_upload_from_path( + part_id=PART_ID, + component_type=COMPONENT_TYPE, + component_name=COMPONENT_NAME, + method_name=METHOD_NAME, + method_parameters=METHOD_PARAMETERS, + tags=TAGS, + filepath=path.resolve(), + ) + assert file_id == FILE_UPLOAD_RESPONSE + self.assert_metadata(service.metadata) + assert service.metadata.file_name == FILE_NAME + assert service.metadata.file_extension == FILE_EXT + assert service.binary_data == BINARY_DATA + + async def test_streaming_data_capture_upload(self, service: MockDataSync): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + file_id = await client.streaming_data_capture_upload( + data=BINARY_DATA, + part_id=PART_ID, + file_ext=FILE_EXT, + component_type=COMPONENT_TYPE, + component_name=COMPONENT_NAME, + method_name=METHOD_NAME, + method_parameters=METHOD_PARAMETERS, + data_request_times=DATETIMES, + tags=TAGS, + ) + assert file_id == FILE_UPLOAD_RESPONSE + self.assert_metadata(service.metadata) + assert service.metadata.file_extension == FILE_EXT + assert service.binary_data == BINARY_DATA + + def assert_sensor_contents(self, sensor_contents: List[SensorData], is_binary: bool): + for idx, sensor_content in enumerate(sensor_contents): + assert sensor_content.metadata.time_requested.seconds == TIMESTAMPS[0].seconds + assert sensor_content.metadata.time_requested.nanos == TIMESTAMPS[0].nanos + assert sensor_content.metadata.time_received.seconds == TIMESTAMPS[1].seconds + assert sensor_content.metadata.time_received.nanos == TIMESTAMPS[1].nanos + if is_binary: + assert sensor_content.binary == BINARY_DATA + else: + assert struct_to_dict(sensor_content.struct) == TABULAR_DATA[idx] + + def assert_metadata(self, metadata: UploadMetadata) -> None: + assert metadata.part_id == PART_ID + assert metadata.component_type == COMPONENT_TYPE + assert metadata.component_name == COMPONENT_NAME + assert metadata.method_name == METHOD_NAME + assert metadata.method_parameters == METHOD_PARAMETERS + assert metadata.tags == TAGS diff --git a/tests/test_dataset.py b/tests/test_dataset.py new file mode 100644 index 000000000..86e0840ac --- /dev/null +++ b/tests/test_dataset.py @@ -0,0 +1,62 @@ +import pytest +from google.protobuf.timestamp_pb2 import Timestamp +from grpclib.testing import ChannelFor + +from viam.app.data_client import DataClient +from viam.proto.app.dataset import Dataset + +from .mocks.services import MockDataset + +CREATED_ID = "VIAM_DATASET_0" +ID = "VIAM_DATASET_1" +NAME = "dataset" +ORG_ID = "org_id" +SECONDS = 978310861 +NANOS = 0 +TIME = Timestamp(seconds=SECONDS, nanos=NANOS) +DATASET = Dataset(id=ID, name=NAME, organization_id=ORG_ID, time_created=TIME) +DATASETS = [DATASET] +AUTH_TOKEN = "auth_token" +DATA_SERVICE_METADATA = {"authorization": f"Bearer {AUTH_TOKEN}"} + + +@pytest.fixture(scope="function") +def service() -> MockDataset: + return MockDataset(CREATED_ID, DATASETS) + + +class TestClient: + async def test_create_dataset(self, service: MockDataset): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + id = await client.create_dataset(NAME, ORG_ID) + assert service.name == NAME + assert service.org_id == ORG_ID + assert id == CREATED_ID + + async def test_delete_dataset(self, service: MockDataset): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + await client.delete_dataset(ID) + assert service.deleted_id == ID + + async def test_list_datasets_by_ids(self, service: MockDataset): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + datasets = await client.list_dataset_by_ids([ID]) + assert service.ids == [ID] + assert datasets == DATASETS + + async def test_list_datasets_by_organization_id(self, service: MockDataset): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + datasets = await client.list_datasets_by_organization_id(ORG_ID) + assert service.org_id == ORG_ID + assert datasets == DATASETS + + async def test_rename_dataset(self, service: MockDataset): + async with ChannelFor([service]) as channel: + client = DataClient(channel, DATA_SERVICE_METADATA) + await client.rename_dataset(ID, NAME) + assert service.id == ID + assert service.name == NAME diff --git a/tests/test_discovery_service.py b/tests/test_discovery_service.py new file mode 100644 index 000000000..1622d2727 --- /dev/null +++ b/tests/test_discovery_service.py @@ -0,0 +1,86 @@ +import pytest +from grpclib.testing import ChannelFor + +from viam.proto.app.robot import ComponentConfig +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, +) +from viam.proto.service.discovery import ( + DiscoverResourcesRequest, + DiscoverResourcesResponse, + DiscoveryServiceStub, +) +from viam.resource.manager import ResourceManager +from viam.services.discovery import DiscoveryClient +from viam.services.discovery.service import DiscoveryRPCService +from viam.utils import dict_to_struct, struct_to_dict + +from .mocks.services import MockDiscovery + +DISCOVERIES = [ComponentConfig()] + + +DISCOVERY_SERVICE_NAME = "discovery1" + + +@pytest.fixture(scope="function") +def discovery() -> MockDiscovery: + return MockDiscovery( + DISCOVERY_SERVICE_NAME, + ) + + +@pytest.fixture(scope="function") +def service(discovery: MockDiscovery) -> DiscoveryRPCService: + rm = ResourceManager([discovery]) + return DiscoveryRPCService(rm) + + +class TestDiscovery: + async def test_discover_resources(self, discovery: MockDiscovery): + extra = {"foo": "discovery"} + response = await discovery.discover_resources(extra=extra) + assert discovery.extra == extra + assert response == DISCOVERIES + + async def test_do(self, discovery: MockDiscovery): + command = {"command": "args"} + response = await discovery.do_command(command) + assert response["cmd"] == command + + +class TestService: + async def test_discover_resources(self, discovery: MockDiscovery, service: DiscoveryRPCService): + async with ChannelFor([service]) as channel: + client = DiscoveryServiceStub(channel) + extra = {"cmd": "discovery"} + request = DiscoverResourcesRequest(name=discovery.name, extra=dict_to_struct(extra)) + response: DiscoverResourcesResponse = await client.DiscoverResources(request) + assert discovery.extra == extra + assert response.discoveries == DISCOVERIES + + async def test_do(self, discovery: MockDiscovery, service: DiscoveryRPCService): + async with ChannelFor([service]) as channel: + client = DiscoveryServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=discovery.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + assert struct_to_dict(response.result)["cmd"] == command + + +class TestClient: + async def test_discover_resources(self, discovery: MockDiscovery, service: DiscoveryRPCService): + async with ChannelFor([service]) as channel: + client = DiscoveryClient(DISCOVERY_SERVICE_NAME, channel) + extra = {"foo": "discovery"} + response = await client.discover_resources(name=DISCOVERY_SERVICE_NAME, extra=extra) + assert response == DISCOVERIES + assert discovery.extra == extra + + async def test_do(self, service: DiscoveryRPCService): + async with ChannelFor([service]) as channel: + client = DiscoveryClient(DISCOVERY_SERVICE_NAME, channel) + command = {"command": "args"} + response = await client.do_command(command) + assert response["cmd"] == command diff --git a/tests/test_easy_resource.py b/tests/test_easy_resource.py new file mode 100644 index 000000000..7ff52bd4b --- /dev/null +++ b/tests/test_easy_resource.py @@ -0,0 +1,46 @@ +import pytest + +from viam.components.generic import Generic +from viam.components.motor import Motor +from viam.proto.app.robot import ComponentConfig +from viam.resource.easy_resource import EasyResource, _parse_model, stub_model +from viam.resource.registry import Registry +from viam.resource.types import Model, ModelFamily + + +@pytest.fixture +def clear_registry(monkeypatch): + "helper to patch registry global state for duration of test" + monkeypatch.setattr(Registry, "_APIS", {}) + monkeypatch.setattr(Registry, "_RESOURCES", {}) + + +class TestEasyResource: + def test_parse_model(self): + model = Model(ModelFamily("viam", "type"), "name") + assert _parse_model("viam:type:name") == model + assert _parse_model(model) == model + with pytest.raises(ValueError): + _parse_model("not parseable") + + def test_subclass(self, clear_registry): + class SubclassTest(Generic, EasyResource): + MODEL = "org:type:name" + + # did it register correctly: + assert set(Registry._RESOURCES.keys()) == {f"rdk:component:generic/{SubclassTest.MODEL}"} + + # can it be instantiated: + resource = SubclassTest.new(ComponentConfig(name="hello"), {}) + assert resource.name == "hello" + + def test_stubs(self, clear_registry): + class MyMotor(Motor, EasyResource): + MODEL = "org:type:name" + + with pytest.raises(TypeError): + # this has unimplemented abstract methods and should fail with TypeError + MyMotor("name") + + MyMotor = stub_model(MyMotor) + MyMotor("name") # this has been stubbed and should now succeed diff --git a/tests/test_encoder.py b/tests/test_encoder.py new file mode 100644 index 000000000..f8f010b54 --- /dev/null +++ b/tests/test_encoder.py @@ -0,0 +1,148 @@ +import pytest +from grpclib.testing import ChannelFor + +from viam.components.encoder import EncoderClient +from viam.components.encoder.service import EncoderRPCService +from viam.components.generic.service import GenericRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse +from viam.proto.component.encoder import ( + EncoderServiceStub, + GetPositionRequest, + GetPositionResponse, + GetPropertiesRequest, + GetPropertiesResponse, + PositionType, + ResetPositionRequest, +) +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict + +from . import loose_approx +from .mocks.components import GEOMETRIES, MockEncoder + + +@pytest.fixture(scope="function") +def encoder() -> MockEncoder: + return MockEncoder(name="encoder") + + +@pytest.fixture(scope="function") +def service(encoder: MockEncoder) -> EncoderRPCService: + manager = ResourceManager([encoder]) + return EncoderRPCService(manager) + + +@pytest.fixture(scope="function") +def generic_service(encoder: MockEncoder) -> GenericRPCService: + manager = ResourceManager([encoder]) + return GenericRPCService(manager) + + +class TestEncoder: + async def test_get_position(self, encoder: MockEncoder): + pos, pos_type = await encoder.get_position(timeout=2.34) + assert pos == 0 + assert pos_type == PositionType.POSITION_TYPE_TICKS_COUNT + assert encoder.timeout == loose_approx(2.34) + + async def test_reset_position(self, encoder: MockEncoder): + await encoder.reset_position(timeout=5.67) + assert encoder.position == 0 + assert encoder.timeout == loose_approx(5.67) + + async def test_get_properties(self, encoder: MockEncoder): + properties = await encoder.get_properties(timeout=6.78) + assert properties.ticks_count_supported is True + assert properties.angle_degrees_supported is False + assert encoder.timeout == loose_approx(6.78) + + async def test_do(self, encoder: MockEncoder): + command = {"command": "args"} + resp = await encoder.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self, encoder: MockEncoder): + geometries = await encoder.get_geometries() + assert geometries == GEOMETRIES + + +class TestService: + async def test_get_position(self, encoder: MockEncoder, service: EncoderRPCService): + async with ChannelFor([service]) as channel: + client = EncoderServiceStub(channel) + request = GetPositionRequest(name=encoder.name) + response: GetPositionResponse = await client.GetPosition(request, timeout=2.34) + assert response.value == 0 + assert response.position_type == PositionType.POSITION_TYPE_TICKS_COUNT + assert encoder.timeout == loose_approx(2.34) + + async def test_reset_position(self, encoder: MockEncoder, service: EncoderRPCService): + async with ChannelFor([service]) as channel: + client = EncoderServiceStub(channel) + request = ResetPositionRequest(name=encoder.name) + await client.ResetPosition(request, timeout=5.67) + assert encoder.position == 0 + assert encoder.timeout == loose_approx(5.67) + + async def test_get_properties(self, encoder: MockEncoder, service: EncoderRPCService): + async with ChannelFor([service]) as channel: + client = EncoderServiceStub(channel) + request = GetPropertiesRequest(name=encoder.name) + response: GetPropertiesResponse = await client.GetProperties(request, timeout=6.78) + assert response.ticks_count_supported is True + assert response.angle_degrees_supported is False + assert encoder.timeout == loose_approx(6.78) + + async def test_do(self, encoder: MockEncoder, service: EncoderRPCService): + async with ChannelFor([service]) as channel: + client = EncoderServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=encoder.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + async def test_get_geometries(self, encoder: MockEncoder, service: EncoderRPCService): + async with ChannelFor([service]) as channel: + client = EncoderServiceStub(channel) + request = GetGeometriesRequest(name=encoder.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES + + +class TestClient: + async def test_get_position(self, encoder: MockEncoder, service: EncoderRPCService): + async with ChannelFor([service]) as channel: + client = EncoderClient(encoder.name, channel) + position, pos_type = await client.get_position(timeout=2.34) + assert position == 0 + assert pos_type == PositionType.POSITION_TYPE_TICKS_COUNT + assert encoder.timeout == loose_approx(2.34) + + async def test_reset_position(self, encoder: MockEncoder, service: EncoderRPCService): + async with ChannelFor([service]) as channel: + client = EncoderClient(encoder.name, channel) + await client.reset_position(timeout=5.67) + assert encoder.timeout == loose_approx(5.67) + assert encoder.position == 0 + + async def test_get_properties(self, encoder: MockEncoder, service: EncoderRPCService): + async with ChannelFor([service]) as channel: + client = EncoderClient(encoder.name, channel) + properties = await client.get_properties(timeout=6.78) + assert properties.ticks_count_supported is True + assert properties.angle_degrees_supported is False + assert encoder.timeout == loose_approx(6.78) + + async def test_do(self, encoder: MockEncoder, service: EncoderRPCService): + async with ChannelFor([service]) as channel: + client = EncoderClient(encoder.name, channel) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self, encoder: MockEncoder, service: EncoderRPCService): + async with ChannelFor([service]) as channel: + client = EncoderClient(encoder.name, channel) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_gantry.py b/tests/test_gantry.py index 1941d0b86..6f039f00c 100644 --- a/tests/test_gantry.py +++ b/tests/test_gantry.py @@ -1,116 +1,147 @@ -import pytest from grpclib.testing import ChannelFor -from viam.components.gantry import GantryClient, GantryStatus, create_status -from viam.components.gantry.service import GantryService -from viam.components.generic.service import GenericService -from viam.components.resource_manager import ResourceManager -from viam.errors import NotSupportedError + +from viam.components.gantry import GantryClient +from viam.components.gantry.service import GantryRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.gantry import ( GantryServiceStub, GetLengthsRequest, GetLengthsResponse, GetPositionRequest, GetPositionResponse, + HomeRequest, + HomeResponse, + IsMovingRequest, + IsMovingResponse, MoveToPositionRequest, StopRequest, ) -from viam.utils import dict_to_struct, message_to_struct +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict -from .mocks.components import MockGantry +from . import loose_approx +from .mocks.components import GEOMETRIES, MockGantry class TestGantry: - gantry = MockGantry("gantry", [1, 2, 3], [4, 5, 6]) - @pytest.mark.asyncio async def test_get_position(self): pos = await self.gantry.get_position() assert pos == [1, 2, 3] - @pytest.mark.asyncio async def test_move_to_position(self): - await self.gantry.move_to_position([1, 8, 2]) + await self.gantry.move_to_position([1, 8, 2], [3, 9, 12]) assert self.gantry.position == [1, 8, 2] - @pytest.mark.asyncio async def test_get_lengths(self): lengths = await self.gantry.get_lengths() assert lengths == [4, 5, 6] - @pytest.mark.asyncio + async def test_home(self): + homed = await self.gantry.home() + assert homed is True + async def test_do(self): - with pytest.raises(NotImplementedError): - await self.gantry.do_command({"command": "args"}) + command = {"command": "args"} + resp = await self.gantry.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio async def test_stop(self): assert self.gantry.is_stopped is False await self.gantry.stop() assert self.gantry.is_stopped is True - @pytest.mark.asyncio async def test_is_moving(self): - await self.gantry.move_to_position([1, 8, 2]) + await self.gantry.move_to_position([1, 8, 2], [3, 9, 12]) assert await self.gantry.is_moving() await self.gantry.stop() assert not await self.gantry.is_moving() - @pytest.mark.asyncio - async def test_status(self): - await self.gantry.move_to_position([1, 2, 3]) - status = await create_status(self.gantry) - assert status.name == MockGantry.get_resource_name(self.gantry.name) - assert status.status == message_to_struct(GantryStatus(lengths_mm=[4, 5, 6], positions_mm=[1, 2, 3], is_moving=True)) - - @pytest.mark.asyncio async def test_extra(self): assert self.gantry.extra is None or len(self.gantry.extra) == 0 extra = {"foo": "bar", "baz": [1, 2, 3]} - await self.gantry.move_to_position([1, 2, 3], extra=extra) + await self.gantry.move_to_position([1, 2, 3], [4, 5, 6], extra=extra) assert self.gantry.extra == extra + async def test_timeout(self): + assert self.gantry.timeout is None -class TestService: + await self.gantry.get_position(timeout=5.5) + assert self.gantry.timeout == loose_approx(5.5) - gantry = MockGantry("gantry", [1, 2, 3], [4, 5, 6]) - manager = ResourceManager([gantry]) - service = GantryService(manager) + await self.gantry.move_to_position([1, 2, 3], [4, 5, 6], timeout=1.82) + assert self.gantry.timeout == loose_approx(1.82) + + await self.gantry.get_lengths(timeout=7.86) + assert self.gantry.timeout == loose_approx(7.86) + + await self.gantry.stop(timeout=4.4) + assert self.gantry.timeout == loose_approx(4.4) + + async def test_get_geometries(self): + geometries = await self.gantry.get_geometries() + assert geometries == GEOMETRIES + + +class TestService: + @classmethod + def setup_class(cls): + cls.gantry = MockGantry("gantry", [1, 2, 3], [4, 5, 6]) + cls.manager = ResourceManager([cls.gantry]) + cls.service = GantryRPCService(cls.manager) - @pytest.mark.asyncio async def test_get_position(self): async with ChannelFor([self.service]) as channel: client = GantryServiceStub(channel) request = GetPositionRequest(name=self.gantry.name) - response: GetPositionResponse = await client.GetPosition(request) + response: GetPositionResponse = await client.GetPosition(request, timeout=9.87) assert list(response.positions_mm) == [1, 2, 3] + assert self.gantry.timeout == loose_approx(9.87) - @pytest.mark.asyncio async def test_move_to_position(self): async with ChannelFor([self.service]) as channel: client = GantryServiceStub(channel) - request = MoveToPositionRequest(name=self.gantry.name, positions_mm=[1, 8, 2]) - await client.MoveToPosition(request) + request = MoveToPositionRequest(name=self.gantry.name, positions_mm=[1, 8, 2], speeds_mm_per_sec=[3, 9, 12]) + await client.MoveToPosition(request, timeout=18.2) assert self.gantry.position == [1, 8, 2] + assert self.gantry.speeds == [3, 9, 12] + assert self.gantry.timeout == loose_approx(18.2) + + async def test_home(self): + async with ChannelFor([self.service]) as channel: + client = GantryServiceStub(channel) + request = HomeRequest(name=self.gantry.name) + response: HomeResponse = await client.Home(request, timeout=18.2) + assert response.homed is True + assert self.gantry.timeout == loose_approx(18.2) - @pytest.mark.asyncio async def test_get_lengths(self): async with ChannelFor([self.service]) as channel: client = GantryServiceStub(channel) request = GetLengthsRequest(name=self.gantry.name) - response: GetLengthsResponse = await client.GetLengths(request) + response: GetLengthsResponse = await client.GetLengths(request, timeout=3.3) assert list(response.lengths_mm) == [4, 5, 6] + assert self.gantry.timeout == loose_approx(3.3) - @pytest.mark.asyncio async def test_stop(self): async with ChannelFor([self.service]) as channel: assert self.gantry.is_stopped is False client = GantryServiceStub(channel) request = StopRequest(name=self.gantry.name) - await client.Stop(request) + await client.Stop(request, timeout=1.1) assert self.gantry.is_stopped is True + assert self.gantry.timeout == loose_approx(1.1) + + async def test_is_moving(self): + async with ChannelFor([self.service]) as channel: + assert self.gantry.is_stopped is True + self.gantry.is_stopped = False + client = GantryServiceStub(channel) + request = IsMovingRequest(name=self.gantry.name) + response: IsMovingResponse = await client.IsMoving(request) + assert response.is_moving is True - @pytest.mark.asyncio async def test_extra(self): async with ChannelFor([self.service]) as channel: assert self.gantry.extra is None or len(self.gantry.extra) == 0 @@ -120,68 +151,91 @@ async def test_extra(self): await client.Stop(request) assert self.gantry.extra == extra + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = GantryServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=self.gantry.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} -class TestClient: + async def test_get_geometries(self): + async with ChannelFor([self.service]) as channel: + client = GantryServiceStub(channel) + request = GetGeometriesRequest(name=self.gantry.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES - gantry = MockGantry("gantry", [1, 2, 3], [4, 5, 6]) - manager = ResourceManager([gantry]) - service = GantryService(manager) - @pytest.mark.asyncio +class TestClient: + @classmethod + def setup_class(cls): + cls.gantry = MockGantry("gantry", [1, 2, 3], [4, 5, 6]) + cls.manager = ResourceManager([cls.gantry]) + cls.service = GantryRPCService(cls.manager) + async def test_get_position(self): async with ChannelFor([self.service]) as channel: client = GantryClient(self.gantry.name, channel) - pos = await client.get_position() + pos = await client.get_position(timeout=1.82) assert pos == [1, 2, 3] + assert self.gantry.timeout == loose_approx(1.82) - @pytest.mark.asyncio async def test_move_to_position(self): async with ChannelFor([self.service]) as channel: client = GantryClient(self.gantry.name, channel) - await client.move_to_position([1, 8, 2]) + await client.move_to_position([1, 8, 2], [3, 9, 12], timeout=4.4) assert self.gantry.position == [1, 8, 2] + assert self.gantry.speeds == [3, 9, 12] + assert self.gantry.timeout == loose_approx(4.4) + + async def test_home(self): + async with ChannelFor([self.service]) as channel: + client = GantryClient(self.gantry.name, channel) + homed = await client.home(timeout=5.5) + assert homed is True + assert self.gantry.timeout == loose_approx(5.5) - @pytest.mark.asyncio async def test_get_lengths(self): async with ChannelFor([self.service]) as channel: client = GantryClient(self.gantry.name, channel) - lengths = await client.get_lengths() + lengths = await client.get_lengths(timeout=5.5) assert lengths == [4, 5, 6] + assert self.gantry.timeout == loose_approx(5.5) - @pytest.mark.asyncio async def test_do(self): - async with ChannelFor([self.service, GenericService(self.manager)]) as channel: + async with ChannelFor([self.service]) as channel: client = GantryClient(self.gantry.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio async def test_stop(self): async with ChannelFor([self.service]) as channel: assert self.gantry.is_stopped is False client = GantryClient(self.gantry.name, channel) - await client.stop() + await client.stop(timeout=None) assert self.gantry.is_stopped is True + assert self.gantry.timeout is None - @pytest.mark.asyncio async def test_is_moving(self): async with ChannelFor([self.service]) as channel: + assert self.gantry.is_stopped is True + self.gantry.is_stopped = False client = GantryClient(self.gantry.name, channel) - with pytest.raises(NotSupportedError): - await client.is_moving() - - @pytest.mark.asyncio - async def test_status(self): - async with ChannelFor([self.service]) as channel: - client = GantryClient(self.gantry.name, channel) - with pytest.raises(NotSupportedError): - await create_status(client) + assert await client.is_moving() is True - @pytest.mark.asyncio async def test_extra(self): async with ChannelFor([self.service]) as channel: assert self.gantry.extra is None or len(self.gantry.extra) == 0 client = GantryClient(self.gantry.name, channel) extra = {"foo": "bar", "baz": [1, 2, 3]} - await client.move_to_position([1, 2, 3], extra=extra) + await client.move_to_position([1, 2, 3], [4, 5, 6], extra=extra) assert self.gantry.extra == extra + + async def test_get_geometries(self): + async with ChannelFor([self.service]) as channel: + client = GantryClient(self.gantry.name, channel) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_generic.py b/tests/test_generic.py deleted file mode 100644 index 3a8a3c6e3..000000000 --- a/tests/test_generic.py +++ /dev/null @@ -1,55 +0,0 @@ -import pytest -from grpclib.testing import ChannelFor - -from viam.components.generic import GenericClient, GenericService -from viam.components.resource_manager import ResourceManager -from viam.proto.component.generic import ( - DoCommandRequest, - DoCommandResponse, - GenericServiceStub, -) -from viam.utils import dict_to_struct, struct_to_dict - -from .mocks.components import MockGeneric - - -class TestSensor: - - generic = MockGeneric(name="generic") - - @pytest.mark.asyncio - async def test_do(self): - result = await self.generic.do_command({"command": "args"}) - assert result == {"command": True} - - -class TestService: - - name = "generic" - generic = MockGeneric(name=name) - manager = ResourceManager([generic]) - service = GenericService(manager) - - @pytest.mark.asyncio - async def test_do(self): - async with ChannelFor([self.service]) as channel: - client = GenericServiceStub(channel) - request = DoCommandRequest(name=self.name, command=dict_to_struct({"command": "args"})) - response: DoCommandResponse = await client.DoCommand(request) - result = struct_to_dict(response.result) - assert result == {"command": True} - - -class TestClient: - - name = "generic" - generic = MockGeneric(name=name) - manager = ResourceManager([generic]) - service = GenericService(manager) - - @pytest.mark.asyncio - async def test_do(self): - async with ChannelFor([self.service]) as channel: - client = GenericClient(self.name, channel) - result = await client.do_command({"command": "args"}) - assert result == {"command": True} diff --git a/tests/test_generic_component.py b/tests/test_generic_component.py new file mode 100644 index 000000000..1f52922c9 --- /dev/null +++ b/tests/test_generic_component.py @@ -0,0 +1,70 @@ +from grpclib.testing import ChannelFor + +from viam.components.generic import GenericClient, GenericRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse +from viam.proto.component.generic import GenericServiceStub +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict + +from . import loose_approx +from .mocks.components import GEOMETRIES, MockGenericComponent + + +class TestGenericComponent: + generic = MockGenericComponent(name="generic") + + async def test_do(self): + result = await self.generic.do_command({"command": "args"}, timeout=1.82) + assert result == {"command": True} + assert self.generic.timeout == loose_approx(1.82) + + async def test_get_geometries(self): + geometries = await self.generic.get_geometries() + assert geometries == GEOMETRIES + + +class TestService: + @classmethod + def setup_class(cls): + cls.name = "generic" + cls.generic = MockGenericComponent(name=cls.name) + cls.manager = ResourceManager([cls.generic]) + cls.service = GenericRPCService(cls.manager) + + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = GenericServiceStub(channel) + request = DoCommandRequest(name=self.name, command=dict_to_struct({"command": "args"})) + response: DoCommandResponse = await client.DoCommand(request, timeout=4.4) + result = struct_to_dict(response.result) + assert result == {"command": True} + assert self.generic.timeout == loose_approx(4.4) + + async def test_get_geometries(self): + async with ChannelFor([self.service]) as channel: + client = GenericServiceStub(channel) + request = GetGeometriesRequest(name=self.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES + + +class TestClient: + @classmethod + def setup_class(cls): + cls.name = "generic" + cls.generic = MockGenericComponent(name=cls.name) + cls.manager = ResourceManager([cls.generic]) + cls.service = GenericRPCService(cls.manager) + + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = GenericClient(self.name, channel) + result = await client.do_command({"command": "args"}, timeout=7.86) + assert result == {"command": True} + assert self.generic.timeout == loose_approx(7.86) + + async def test_get_geometries(self): + async with ChannelFor([self.service]) as channel: + client = GenericClient(self.name, channel) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_generic_service.py b/tests/test_generic_service.py new file mode 100644 index 000000000..f22fe715b --- /dev/null +++ b/tests/test_generic_service.py @@ -0,0 +1,53 @@ +from grpclib.testing import ChannelFor + +from viam.components.generic import GenericClient, GenericRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.component.generic import GenericServiceStub +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict + +from . import loose_approx +from .mocks.services import MockGenericService + + +class TestGenericService: + generic = MockGenericService(name="generic") + + async def test_do(self): + result = await self.generic.do_command({"command": "args"}, timeout=1.82) + assert result == {"command": True} + assert self.generic.timeout == loose_approx(1.82) + + +class TestService: + @classmethod + def setup_class(cls): + cls.name = "generic" + cls.generic = MockGenericService(name=cls.name) + cls.manager = ResourceManager([cls.generic]) + cls.service = GenericRPCService(cls.manager) + + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = GenericServiceStub(channel) + request = DoCommandRequest(name=self.name, command=dict_to_struct({"command": "args"})) + response: DoCommandResponse = await client.DoCommand(request, timeout=4.4) + result = struct_to_dict(response.result) + assert result == {"command": True} + assert self.generic.timeout == loose_approx(4.4) + + +class TestClient: + @classmethod + def setup_class(cls): + cls.name = "generic" + cls.generic = MockGenericService(name=cls.name) + cls.manager = ResourceManager([cls.generic]) + cls.service = GenericRPCService(cls.manager) + + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = GenericClient(self.name, channel) + result = await client.do_command({"command": "args"}, timeout=7.86) + assert result == {"command": True} + assert self.generic.timeout == loose_approx(7.86) diff --git a/tests/test_gripper.py b/tests/test_gripper.py index 8aefcc707..84f47cf0a 100644 --- a/tests/test_gripper.py +++ b/tests/test_gripper.py @@ -1,22 +1,24 @@ import pytest from grpclib.testing import ChannelFor -from viam.components.generic.service import GenericService -from viam.components.gripper import Gripper, GripperClient, create_status -from viam.components.gripper.service import GripperService -from viam.components.resource_manager import ResourceManager -from viam.errors import NotSupportedError -from viam.proto.common import ActuatorStatus +from viam.components.generic.service import GenericRPCService +from viam.components.gripper import Gripper, GripperClient +from viam.components.gripper.service import GripperRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.gripper import ( GrabRequest, GrabResponse, GripperServiceStub, + IsMovingRequest, + IsMovingResponse, OpenRequest, StopRequest, ) -from viam.utils import message_to_struct +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict -from .mocks.components import MockGripper +from . import loose_approx +from .mocks.components import GEOMETRIES, MockGripper @pytest.fixture(scope="function") @@ -25,77 +27,78 @@ def gripper() -> MockGripper: @pytest.fixture(scope="function") -def service(gripper: Gripper) -> GripperService: +def service(gripper: Gripper) -> GripperRPCService: rm = ResourceManager([gripper]) - return GripperService(rm) + return GripperRPCService(rm) @pytest.fixture(scope="function") -def generic_service(gripper: Gripper) -> GenericService: +def generic_service(gripper: Gripper) -> GenericRPCService: manager = ResourceManager([gripper]) - return GenericService(manager) + return GenericRPCService(manager) class TestGripper: - @pytest.mark.asyncio async def test_open(self, gripper: MockGripper): - await gripper.open() + await gripper.open(timeout=1.82) assert gripper.opened is True + assert gripper.timeout == loose_approx(1.82) - @pytest.mark.asyncio async def test_grab(self, gripper: MockGripper): grabbed = await gripper.grab() assert gripper.opened is False assert isinstance(grabbed, bool) + assert gripper.timeout is None - @pytest.mark.asyncio async def test_stop(self, gripper: MockGripper): assert gripper.is_stopped is True await gripper.open() assert gripper.is_stopped is False - await gripper.stop() + await gripper.stop(timeout=7.86) assert gripper.is_stopped is True + assert gripper.timeout == loose_approx(7.86) - @pytest.mark.asyncio async def test_is_moving(self, gripper: MockGripper): await gripper.open() assert await gripper.is_moving() await gripper.stop() assert not await gripper.is_moving() - @pytest.mark.asyncio async def test_do(self, gripper: MockGripper): - with pytest.raises(NotImplementedError): - await gripper.do_command({"command": "args"}) + command = {"command": "args"} + resp = await gripper.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio - async def test_status(self, gripper: MockGripper): - await gripper.open() - status = await create_status(gripper) - assert status.name == gripper.get_resource_name(gripper.name) - assert status.status == message_to_struct(ActuatorStatus(is_moving=True)) + async def test_extra(self, gripper: MockGripper): + assert gripper.extra is None + extra = {"foo": "bar", "baz": [1, 2, 3]} + await gripper.open(timeout=1.1, extra=extra) + assert gripper.extra == extra + + async def test_get_geometries(self, gripper: MockGripper): + geometries = await gripper.get_geometries() + assert geometries == GEOMETRIES class TestService: - @pytest.mark.asyncio - async def test_open(self, gripper: MockGripper, service: GripperService): + async def test_open(self, gripper: MockGripper, service: GripperRPCService): async with ChannelFor([service]) as channel: client = GripperServiceStub(channel) request = OpenRequest(name=gripper.name) - await client.Open(request) + await client.Open(request, timeout=1.23) assert gripper.opened is True + assert gripper.timeout == loose_approx(1.23) - @pytest.mark.asyncio - async def test_grab(self, gripper: MockGripper, service: GripperService): + async def test_grab(self, gripper: MockGripper, service: GripperRPCService): async with ChannelFor([service]) as channel: client = GripperServiceStub(channel) request = GrabRequest(name=gripper.name) - response: GrabResponse = await client.Grab(request) + response: GrabResponse = await client.Grab(request, timeout=4.56) assert gripper.opened is False assert isinstance(response.success, bool) + assert gripper.timeout == loose_approx(4.56) - @pytest.mark.asyncio - async def test_stop(self, gripper: MockGripper, service: GripperService): + async def test_stop(self, gripper: MockGripper, service: GripperRPCService): async with ChannelFor([service]) as channel: assert gripper.is_stopped is True @@ -105,54 +108,96 @@ async def test_stop(self, gripper: MockGripper, service: GripperService): assert gripper.is_stopped is False request = StopRequest(name=gripper.name) - await client.Stop(request) + await client.Stop(request, timeout=7.89) assert gripper.is_stopped is True + assert gripper.timeout == loose_approx(7.89) + + async def test_is_moving(self, gripper: MockGripper, service: GripperRPCService): + async with ChannelFor([service]) as channel: + assert gripper.is_stopped is True + gripper.is_stopped = False + client = GripperServiceStub(channel) + request = IsMovingRequest(name=gripper.name) + response: IsMovingResponse = await client.IsMoving(request) + assert response.is_moving is True + + async def test_extra(self, gripper: MockGripper, service: GripperRPCService): + async with ChannelFor([service]) as channel: + assert gripper.extra is None + client = GripperServiceStub(channel) + extra = {"foo": "bar", "baz": [1, 2, 3]} + request = OpenRequest(name=gripper.name, extra=dict_to_struct(extra)) + await client.Open(request) + assert gripper.extra == extra + + async def test_do(self, gripper: MockGripper, service: GripperRPCService): + async with ChannelFor([service]) as channel: + client = GripperServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=gripper.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + async def test_get_geometries(self, gripper: MockGripper, service: GripperRPCService): + async with ChannelFor([service]) as channel: + client = GripperServiceStub(channel) + request = GetGeometriesRequest(name=gripper.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES class TestClient: - @pytest.mark.asyncio - async def test_open(self, gripper: MockGripper, service: GripperService): + async def test_open(self, gripper: MockGripper, service: GripperRPCService): async with ChannelFor([service]) as channel: client = GripperClient(gripper.name, channel) - await client.open() + await client.open(timeout=1.23) assert gripper.opened is True + assert gripper.timeout == loose_approx(1.23) - @pytest.mark.asyncio - async def test_grab(self, gripper: MockGripper, service: GripperService): + async def test_grab(self, gripper: MockGripper, service: GripperRPCService): async with ChannelFor([service]) as channel: client = GripperClient(gripper.name, channel) - grabbed = await client.grab() + grabbed = await client.grab(timeout=2.34) assert gripper.opened is False assert isinstance(grabbed, bool) + assert gripper.timeout == loose_approx(2.34) - @pytest.mark.asyncio - async def test_stop(self, gripper: MockGripper, service: GripperService): + async def test_stop(self, gripper: MockGripper, service: GripperRPCService): async with ChannelFor([service]) as channel: client = GripperClient(gripper.name, channel) assert gripper.is_stopped is True await client.open() assert gripper.is_stopped is False - await client.stop() + await client.stop(timeout=3.45) + assert gripper.is_stopped is True + assert gripper.timeout == loose_approx(3.45) + + async def test_is_moving(self, gripper: MockGripper, service: GripperRPCService): + async with ChannelFor([service]) as channel: assert gripper.is_stopped is True + gripper.is_stopped = False + client = GripperClient(gripper.name, channel) + assert await client.is_moving() is True - @pytest.mark.asyncio - async def test_is_moving(self, gripper: MockGripper, service: GripperService): + async def test_do(self, gripper: MockGripper, service: GripperRPCService): async with ChannelFor([service]) as channel: client = GripperClient(gripper.name, channel) - with pytest.raises(NotSupportedError): - await client.is_moving() + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio - async def test_do(self, gripper: MockGripper, service: GripperService, generic_service: GenericService): - async with ChannelFor([service, generic_service]) as channel: + async def test_extra(self, gripper: MockGripper, service: GripperRPCService): + async with ChannelFor([service]) as channel: + assert gripper.extra is None client = GripperClient(gripper.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + extra = {"foo": "bar", "baz": [1, 2, 3]} + await client.open(timeout=1.1, extra=extra) + assert gripper.extra == extra - @pytest.mark.asyncio - async def test_status(self, gripper: MockGripper, service: GripperService): + async def test_get_geometries(self, gripper: MockGripper, service: GripperRPCService): async with ChannelFor([service]) as channel: client = GripperClient(gripper.name, channel) - with pytest.raises(NotSupportedError): - await create_status(client) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_input.py b/tests/test_input.py index e12787f8c..f60243bb2 100644 --- a/tests/test_input.py +++ b/tests/test_input.py @@ -5,11 +5,11 @@ import pytest from grpclib.testing import ChannelFor -from viam.components.generic.service import GenericService +from viam.components.generic.service import GenericRPCService from viam.components.input import Control, Event, EventType from viam.components.input.client import ControllerClient -from viam.components.input.service import InputControllerService -from viam.components.resource_manager import ResourceManager +from viam.components.input.service import InputControllerRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.inputcontroller import ( GetControlsRequest, GetControlsResponse, @@ -20,8 +20,11 @@ StreamEventsResponse, TriggerEventRequest, ) +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict -from .mocks.components import MockInputController +from . import loose_approx +from .mocks.components import GEOMETRIES, MockInputController @pytest.fixture(scope="function") @@ -30,21 +33,21 @@ def controller() -> MockInputController: @pytest.fixture(scope="function") -def service(controller: MockInputController) -> InputControllerService: +def service(controller: MockInputController) -> InputControllerRPCService: manager = ResourceManager([controller]) - return InputControllerService(manager) + return InputControllerRPCService(manager) @pytest.fixture(scope="function") -def generic_service(controller: MockInputController) -> GenericService: +def generic_service(controller: MockInputController) -> GenericRPCService: manager = ResourceManager([controller]) - return GenericService(manager) + return GenericRPCService(manager) class TestInputController: - @pytest.mark.asyncio async def test_get_controls(self, controller: MockInputController): - controls = await controller.get_controls() + extra = {"foo": "get_detectors"} + controls = await controller.get_controls(extra=extra, timeout=4.4) assert controls == [ Control.ABSOLUTE_X, Control.ABSOLUTE_Y, @@ -68,39 +71,53 @@ async def test_get_controls(self, controller: MockInputController): Control.BUTTON_RECORD, Control.BUTTON_E_STOP, ] + assert controller.extra == extra + assert controller.timeout == loose_approx(4.4) - @pytest.mark.asyncio async def test_get_events(self, controller: MockInputController): - events = await controller.get_events() + extra = {"foo": "get_events"} + events = await controller.get_events(extra=extra, timeout=1.82) assert len(events) == 0 + assert controller.extra == extra + assert controller.timeout == loose_approx(1.82) - @pytest.mark.asyncio async def test_trigger_event(self, controller: MockInputController): assert len(controller.events) == 0 event = Event(time(), EventType.CONNECT, Control.ABSOLUTE_X, 0) - await controller.trigger_event(event) + await controller.trigger_event(event, timeout=7.86) + assert controller.timeout == loose_approx(7.86) events = await controller.get_events() assert events[Control.ABSOLUTE_X] == event + assert controller.extra is None + assert controller.timeout is None def test_register_control_callback(self, controller: MockInputController): assert len(controller.callbacks) == 0 - controller.register_control_callback(Control.ABSOLUTE_X, [EventType.BUTTON_PRESS, EventType.BUTTON_RELEASE], lambda ev: print(ev)) + extra = {"foo": "register_control_callback"} + controller.register_control_callback( + Control.ABSOLUTE_X, [EventType.BUTTON_PRESS, EventType.BUTTON_RELEASE], lambda ev: print(ev), extra=extra + ) assert len(controller.callbacks) == 1 assert len(controller.callbacks[Control.ABSOLUTE_X]) == 2 + assert controller.reg_extra == extra - @pytest.mark.asyncio async def test_do(self, controller: MockInputController): - with pytest.raises(NotImplementedError): - await controller.do_command({"command": "args"}) + command = {"command": "args"} + resp = await controller.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self, controller: MockInputController): + geometries = await controller.get_geometries() + assert geometries == GEOMETRIES class TestService: - @pytest.mark.asyncio - async def test_get_controls(self, controller: MockInputController, service: InputControllerService): + async def test_get_controls(self, controller: MockInputController, service: InputControllerRPCService): async with ChannelFor([service]) as channel: client = InputControllerServiceStub(channel) - request = GetControlsRequest(controller=controller.name) - response: GetControlsResponse = await client.GetControls(request) + extra = {"foo": "get_controls"} + request = GetControlsRequest(controller=controller.name, extra=dict_to_struct(extra)) + response: GetControlsResponse = await client.GetControls(request, timeout=2.23) controls = list(response.controls) assert controls == [ Control.ABSOLUTE_X, @@ -125,31 +142,35 @@ async def test_get_controls(self, controller: MockInputController, service: Inpu Control.BUTTON_RECORD, Control.BUTTON_E_STOP, ] + assert controller.extra == extra + assert controller.timeout == loose_approx(2.23) - @pytest.mark.asyncio - async def test_get_events(self, controller: MockInputController, service: InputControllerService): + async def test_get_events(self, controller: MockInputController, service: InputControllerRPCService): async with ChannelFor([service]) as channel: client = InputControllerServiceStub(channel) - request = GetEventsRequest(controller=controller.name) - response: GetEventsResponse = await client.GetEvents(request) + extra = {"foo": "get_events"} + request = GetEventsRequest(controller=controller.name, extra=dict_to_struct(extra)) + response: GetEventsResponse = await client.GetEvents(request, timeout=2.34) events = list(response.events) assert events == list(controller.events.values()) + assert controller.extra == extra + assert controller.timeout == loose_approx(2.34) - @pytest.mark.asyncio - async def test_trigger_event(self, controller: MockInputController, service: InputControllerService): + async def test_trigger_event(self, controller: MockInputController, service: InputControllerRPCService): event = Event(time(), EventType.CONNECT, Control.ABSOLUTE_X, 0) async with ChannelFor([service]) as channel: client = InputControllerServiceStub(channel) request = TriggerEventRequest(controller=controller.name, event=event.proto) - await client.TriggerEvent(request) + await client.TriggerEvent(request, timeout=3.45) assert controller.events[Control.ABSOLUTE_X].control == event.control assert controller.events[Control.ABSOLUTE_X].event == event.event assert controller.events[Control.ABSOLUTE_X].value == event.value # timestamp nanos conversion can result in differences in the 1e-7 scale assert abs(controller.events[Control.ABSOLUTE_X].time - event.time) < 0.000001 + assert controller.extra == {} + assert controller.timeout == loose_approx(3.45) - @pytest.mark.asyncio - async def test_stream_events(selc, controller: MockInputController, service: InputControllerService): + async def test_stream_events(selc, controller: MockInputController, service: InputControllerRPCService): async with ChannelFor([service]) as channel: def trigger_event(): @@ -159,9 +180,11 @@ def trigger_event(): asyncio.get_running_loop().call_later(0.1, trigger_event) client = InputControllerServiceStub(channel) + extra = {"foo": "stream_events"} request = StreamEventsRequest( controller=controller.name, events=[StreamEventsRequest.Events(control=Control.BUTTON_START, events=[EventType.BUTTON_RELEASE])], + extra=dict_to_struct(extra), ) async with client.StreamEvents.open(timeout=1) as stream: await stream.send_message(request, end=True) @@ -177,14 +200,31 @@ def trigger_event(): await controller.trigger_event(Event(time(), EventType.BUTTON_RELEASE, Control.BUTTON_START, 0)) await asyncio.sleep(0.1) assert controller.callbacks[Control.BUTTON_START][EventType.BUTTON_RELEASE] is None + assert controller.reg_extra == extra + + async def test_do(self, controller: MockInputController, service: InputControllerRPCService): + async with ChannelFor([service]) as channel: + client = InputControllerServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=controller.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + async def test_get_geometries(self, controller: MockInputController, service: InputControllerRPCService): + async with ChannelFor([service]) as channel: + client = InputControllerServiceStub(channel) + request = GetGeometriesRequest(name=controller.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES class TestClient: - @pytest.mark.asyncio - async def test_get_controls(self, controller: MockInputController, service: InputControllerService): + async def test_get_controls(self, controller: MockInputController, service: InputControllerRPCService): async with ChannelFor([service]) as channel: client = ControllerClient(controller.name, channel) - controls = await client.get_controls() + extra = {"foo": "get_controls"} + controls = await client.get_controls(extra=extra, timeout=4.56) assert controls == [ Control.ABSOLUTE_X, Control.ABSOLUTE_Y, @@ -208,28 +248,32 @@ async def test_get_controls(self, controller: MockInputController, service: Inpu Control.BUTTON_RECORD, Control.BUTTON_E_STOP, ] + assert controller.extra == extra + assert controller.timeout == loose_approx(4.56) - @pytest.mark.asyncio - async def test_get_events(self, controller: MockInputController, service: InputControllerService): + async def test_get_events(self, controller: MockInputController, service: InputControllerRPCService): async with ChannelFor([service]) as channel: client = ControllerClient(controller.name, channel) - events = await client.get_events() + extra = {"foo": "get_events"} + events = await client.get_events(extra=extra, timeout=5.67) assert events == controller.events + assert controller.extra == extra + assert controller.timeout == loose_approx(5.67) - @pytest.mark.asyncio - async def test_trigger_event(self, controller: MockInputController, service: InputControllerService): + async def test_trigger_event(self, controller: MockInputController, service: InputControllerRPCService): event = Event(time(), EventType.CONNECT, Control.ABSOLUTE_X, 0) async with ChannelFor([service]) as channel: client = ControllerClient(controller.name, channel) - await client.trigger_event(event) + await client.trigger_event(event, timeout=6.78) assert controller.events[Control.ABSOLUTE_X].control == event.control assert controller.events[Control.ABSOLUTE_X].event == event.event assert controller.events[Control.ABSOLUTE_X].value == event.value # timestamp nanos conversion can result in differences in the 1e-7 scale assert abs(controller.events[Control.ABSOLUTE_X].time - event.time) < 0.000001 + assert controller.timeout == loose_approx(6.78) + assert controller.extra == {} - @pytest.mark.asyncio - async def test_register_control_callback(self, controller: MockInputController, service: InputControllerService): + async def test_register_control_callback(self, controller: MockInputController, service: InputControllerRPCService): async with ChannelFor([service]) as channel: client = ControllerClient(controller.name, channel) @@ -240,7 +284,8 @@ def test_event(event: Event): self.callback_called = True self.event_equal = event.control == Control.BUTTON_START and event.event == EventType.BUTTON_RELEASE and event.value == 0 - client.register_control_callback(Control.BUTTON_START, [EventType.BUTTON_RELEASE], test_event) + extra = {"foo": "register_control_callback"} + client.register_control_callback(Control.BUTTON_START, [EventType.BUTTON_RELEASE], test_event, extra=extra) def trigger_event(): asyncio.get_running_loop().create_task( @@ -248,17 +293,42 @@ def trigger_event(): ) asyncio.get_running_loop().call_later(0.1, trigger_event) - future = datetime.now() + timedelta(seconds=0.2) while datetime.now() < future: await asyncio.sleep(0.1) assert self.callback_called is True assert self.event_equal is True + assert controller.extra is None + assert controller.reg_extra == extra - @pytest.mark.asyncio - async def test_do(self, controller: MockInputController, service: InputControllerService, generic_service: GenericService): - async with ChannelFor([service, generic_service]) as channel: + async def test_do(self, controller: MockInputController, service: InputControllerRPCService): + async with ChannelFor([service]) as channel: + client = ControllerClient(controller.name, channel) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} + + async def test_channel_rest(self, controller: MockInputController, service: InputControllerRPCService): + channel = await ChannelFor([service]).__aenter__() + client = ControllerClient(controller.name, channel) + assert client._is_streaming is False # not streaming before callback registered + + client.register_control_callback(Control.BUTTON_START, [EventType.BUTTON_RELEASE], lambda x: print(x)) + await asyncio.sleep(0.1) + assert client._is_streaming is True # registering callback should start streaming + + await channel.__aexit__(None, None, None) + await asyncio.sleep(0.1) + assert client._is_streaming is False # closing the channel should cancel the stream + + async with ChannelFor([service]) as channel2: + client.reset_channel(channel2) + await asyncio.sleep(0.1) + assert client._is_streaming is True # reset the channel should restart the callback stream + + async def test_get_geometries(self, controller: MockInputController, service: InputControllerRPCService): + async with ChannelFor([service]) as channel: client = ControllerClient(controller.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_media.py b/tests/test_media.py new file mode 100644 index 000000000..2fb1596b6 --- /dev/null +++ b/tests/test_media.py @@ -0,0 +1,80 @@ +import os +from array import array +from io import BytesIO + +import pytest +from PIL import Image + +from viam.errors import NotSupportedError +from viam.media.utils.pil import pil_to_viam_image, viam_to_pil_image +from viam.media.video import CameraMimeType, NamedImage, ViamImage + + +class TestViamImage: + def test_supported_image(self): + i = Image.new("RGBA", (100, 100), "#AABBCCDD") + b = BytesIO() + i.save(b, "PNG") + img = ViamImage(b.getvalue(), CameraMimeType.PNG) + assert img._mime_type == CameraMimeType.PNG + pil_img = viam_to_pil_image(img) + assert pil_img.tobytes() == i.tobytes() + + def test_dimensions(self): + WIDTH = 400 + HEIGHT = 300 + i = Image.new("RGBA", (WIDTH, HEIGHT), "#AABBCCDD") + + img1 = pil_to_viam_image(i, CameraMimeType.JPEG) + assert img1.width == WIDTH + assert img1.height == HEIGHT + + img2 = pil_to_viam_image(i, CameraMimeType.PNG) + assert img2.width == WIDTH + assert img2.height == HEIGHT + + img3 = pil_to_viam_image(i, CameraMimeType.VIAM_RGBA) + assert img3.width == WIDTH + assert img3.height == HEIGHT + + img4 = ViamImage(b"data", CameraMimeType.PCD) + assert img4.width is None + assert img4.height is None + + def test_bytes_to_depth_array(self): + with open(f"{os.path.dirname(__file__)}/data/fakeDM.vnd.viam.dep", "rb") as depth_map: + img = ViamImage(depth_map.read(), CameraMimeType.VIAM_RAW_DEPTH) + assert isinstance(img, ViamImage) + standard_data = img.bytes_to_depth_array() + assert len(standard_data) == 10 + assert len(standard_data[0]) == 20 + data_arr = array("H", img.data[24:]) + data_arr.byteswap() + assert len(data_arr) == 200 + assert standard_data[0][0] == data_arr[0] + assert standard_data[-1][3] == data_arr[183] + assert standard_data[-1][3] == 9 * 3 + assert standard_data[4][4] == 4 * 4 + + img2 = ViamImage(b"data", CameraMimeType.PCD) + with pytest.raises(NotSupportedError): + img2.bytes_to_depth_array() + + +class TestNamedImage: + def test_name(self): + name = "img" + img = NamedImage(name, b"123", CameraMimeType.JPEG) + assert img.name == name + + +def test_image_conversion(): + i = Image.new("RGBA", (100, 100), "#AABBCCDD") + + v_img = pil_to_viam_image(i, CameraMimeType.JPEG) + assert isinstance(v_img, ViamImage) + assert v_img.mime_type == CameraMimeType.JPEG + + pil_img = viam_to_pil_image(v_img) + v_img2 = pil_to_viam_image(pil_img, CameraMimeType.JPEG) + assert v_img2.data == v_img.data diff --git a/tests/test_ml_training_client.py b/tests/test_ml_training_client.py new file mode 100644 index 000000000..8c5ca67ee --- /dev/null +++ b/tests/test_ml_training_client.py @@ -0,0 +1,110 @@ +import pytest +from google.protobuf.timestamp_pb2 import Timestamp +from grpclib.testing import ChannelFor + +from viam.app.ml_training_client import MLTrainingClient +from viam.proto.app.mltraining import ModelType, SubmitTrainingJobRequest, TrainingJobMetadata, TrainingStatus +from viam.utils import create_filter + +from .mocks.services import MockMLTraining + +AUTH_TOKEN = "auth_token" +ML_TRAINING_SERVICE_METADATA = {"authorization": f"Bearer {AUTH_TOKEN}"} +ID = "id" +TRAINING_JOB_ID = "training-job-id" +CANCEL_ID = "cancel-id" +DELETE_ID = "delete-id" +JOB_ID = "job-id" +ORG_ID = "org-id" +DATASET_ID = "dataset-id" +REGISTRY_ITEM_ID = "registry-item-id" +REGISTRY_ITEM_VERSION = "registry-item-version" +MODEL_ID = "model-id" +MODEL_NAME = "model-name" +MODEL_VERSION = "model-version" +MODEL_TYPE = ModelType.MODEL_TYPE_UNSPECIFIED +TRAINING_STATUS = TrainingStatus.TRAINING_STATUS_UNSPECIFIED +CREATED_ON = Timestamp(seconds=100, nanos=0) +LAST_MODIFIED = Timestamp(seconds=101, nanos=0) +TAGS = ["tag"] +FILTER = create_filter( + component_name="component", + component_type="type", + method="method", + robot_name="robot", + robot_id="robo-id", + part_name="part", + part_id="part-id", + location_ids=["location-id"], + organization_ids=[ORG_ID], + mime_type=["mime-type"], + start_time=CREATED_ON.ToDatetime(), + end_time=LAST_MODIFIED.ToDatetime(), + tags=TAGS, + bbox_labels=["bbox-label"], +) +SUBMIT_JOB_REQUEST = SubmitTrainingJobRequest(organization_id=ORG_ID) +TRAINING_METADATA = TrainingJobMetadata( + status=TRAINING_STATUS, + created_on=CREATED_ON, + last_modified=LAST_MODIFIED, + synced_model_id=MODEL_ID, + id=ID, + error_status=None, +) + + +@pytest.fixture(scope="function") +def service() -> MockMLTraining: + return MockMLTraining(job_id=JOB_ID, training_metadata=TRAINING_METADATA) + + +class TestClient: + async def test_cancel_training_job(self, service: MockMLTraining): + async with ChannelFor([service]) as channel: + client = MLTrainingClient(channel, ML_TRAINING_SERVICE_METADATA) + await client.cancel_training_job(CANCEL_ID) + assert service.cancel_job_id == CANCEL_ID + + async def test_submit_training_job(self, service: MockMLTraining): + async with ChannelFor([service]) as channel: + client = MLTrainingClient(channel, ML_TRAINING_SERVICE_METADATA) + id = await client.submit_training_job( + org_id=ORG_ID, dataset_id=DATASET_ID, model_name=MODEL_NAME, model_version=MODEL_VERSION, model_type=MODEL_TYPE, tags=TAGS + ) + assert id == JOB_ID + + async def test_custom_submit_training_job(self, service: MockMLTraining): + async with ChannelFor([service]) as channel: + client = MLTrainingClient(channel, ML_TRAINING_SERVICE_METADATA) + id = await client.submit_custom_training_job( + org_id=ORG_ID, + dataset_id=DATASET_ID, + registry_item_id=REGISTRY_ITEM_ID, + registry_item_version=REGISTRY_ITEM_VERSION, + model_name=MODEL_NAME, + model_version=MODEL_VERSION, + ) + assert id == JOB_ID + + async def test_get_training_job(self, service: MockMLTraining): + async with ChannelFor([service]) as channel: + client = MLTrainingClient(channel, ML_TRAINING_SERVICE_METADATA) + training_metadata = await client.get_training_job(id=TRAINING_JOB_ID) + assert training_metadata == TRAINING_METADATA + assert service.training_job_id == TRAINING_JOB_ID + + async def test_list_training_jobs(self, service: MockMLTraining): + async with ChannelFor([service]) as channel: + client = MLTrainingClient(channel, ML_TRAINING_SERVICE_METADATA) + training_jobs = await client.list_training_jobs(ORG_ID, TRAINING_STATUS) + assert len(training_jobs) == 1 + assert training_jobs[0] == TRAINING_METADATA + assert service.training_status == TRAINING_STATUS + assert service.org_id == ORG_ID + + async def test_delete_completed_training_job(self, service: MockMLTraining): + async with ChannelFor([service]) as channel: + client = MLTrainingClient(channel, ML_TRAINING_SERVICE_METADATA) + await client.delete_completed_training_job(DELETE_ID) + assert service.delete_id == DELETE_ID diff --git a/tests/test_mlmodel.py b/tests/test_mlmodel.py new file mode 100644 index 000000000..929b38fc3 --- /dev/null +++ b/tests/test_mlmodel.py @@ -0,0 +1,132 @@ +import numpy as np +import pytest +from grpclib.testing import ChannelFor + +from viam.proto.service.mlmodel import InferRequest, InferResponse, MetadataRequest, MetadataResponse, MLModelServiceStub +from viam.resource.manager import ResourceManager +from viam.services.mlmodel import MLModelClient, MLModelRPCService + +from .mocks.services import MockMLModel + + +class TestMLModel: + name = "mlmodel" + mlmodel = MockMLModel(name=name) + + async def test_infer(self): + resp = await self.mlmodel.infer(input_tensors=MockMLModel.EMPTY_NDARRAYS) + assert resp == MockMLModel.EMPTY_NDARRAYS + + async def test_metadata(self): + resp = await self.mlmodel.metadata() + assert resp == MockMLModel.META + + +class TestService: + @classmethod + def setup_class(cls): + cls.name = "mlmodel" + cls.mlmodel = MockMLModel(name=cls.name) + cls.manager = ResourceManager([cls.mlmodel]) + cls.service = MLModelRPCService(cls.manager) + + async def test_infer(self): + async with ChannelFor([self.service]) as channel: + client = MLModelServiceStub(channel) + request = InferRequest(name=self.name) + response: InferResponse = await client.Infer(request) + assert response.output_tensors == MockMLModel.EMPTY_TENSORS + + async def test_metadata(self): + async with ChannelFor([self.service]) as channel: + client = MLModelServiceStub(channel) + request = MetadataRequest(name=self.name) + response: MetadataResponse = await client.Metadata(request) + assert response.metadata == MockMLModel.META + + +class TestClient: + @classmethod + def setup_class(cls): + cls.name = "mlmodel" + cls.mlmodel = MockMLModel(name=cls.name) + cls.manager = ResourceManager([cls.mlmodel]) + cls.service = MLModelRPCService(cls.manager) + + # ignore warning about our out-of-bound int casting (i.e. uint32 -> int16) because we don't store uint32s for int16 & uint16 tensor + # data > 2^16-1 in the first place (inherently they are int16, we just cast them to uint32 for the grpc message) + @pytest.mark.filterwarnings("ignore::DeprecationWarning") + async def test_infer(self): + async with ChannelFor([self.service]) as channel: + client = MLModelClient(self.name, channel) + response = await client.infer(MockMLModel.EMPTY_NDARRAYS) + assert not response + + response = await client.infer(MockMLModel.DOUBLE_NDARRAYS) + assert len(response.keys()) == 1 + assert "0" in response.keys() + assert np.array_equal(response["0"], MockMLModel.DOUBLE_NDARRAY) + assert response["0"].dtype == np.float64 + + response = await client.infer(MockMLModel.DOUBLE_FLOAT_NDARRAYS) + assert len(response.keys()) == 2 + assert response["0"].dtype == np.float64 + assert np.array_equal(response["0"], MockMLModel.DOUBLE_NDARRAY) + assert response["1"].dtype == np.float32 + assert np.array_equal(response["1"], MockMLModel.FLOAT_NDARRAY) + + response = await client.infer(MockMLModel.INTS_NDARRAYS) + assert len(response.keys()) == 4 + assert all(name in response.keys() for name in ["0", "1", "2", "3"]) + assert np.array_equal(response["0"], MockMLModel.INT8_NDARRAY) + assert response["0"].dtype == np.int8 + assert np.array_equal(response["1"], MockMLModel.INT16_NDARRAY) + assert response["1"].dtype == np.int16 + assert np.array_equal(response["2"], MockMLModel.INT32_NDARRAY) + assert response["2"].dtype == np.int32 + assert np.array_equal(response["3"], MockMLModel.INT64_NDARRAY) + assert response["3"].dtype == np.int64 + + response = await client.infer(MockMLModel.UINTS_NDARRAYS) + assert len(response.keys()) == 4 + assert all(name in response.keys() for name in ["0", "1", "2", "3"]) + assert np.array_equal(response["0"], MockMLModel.UINT8_NDARRAY) + assert response["0"].dtype == np.uint8 + assert np.array_equal(response["1"], MockMLModel.UINT16_NDARRAY) + assert response["1"].dtype == np.uint16 + assert np.array_equal(response["2"], MockMLModel.UINT32_NDARRAY) + assert response["2"].dtype == np.uint32 + assert np.array_equal(response["3"], MockMLModel.UINT64_NDARRAY) + assert response["3"].dtype == np.uint64 + + response = await client.infer(MockMLModel.SQUARE_INT_UINT_NDARRAYS) + assert len(response.keys()) == 2 + assert all(name in response for name in ["0", "1"]) + assert np.array_equal(response["0"], MockMLModel.SQUARE_INT16_NDARRAY) + assert response["0"].dtype == np.int16 + assert np.array_equal(response["1"], MockMLModel.UINT64_NDARRAY) + assert response["1"].dtype == np.uint64 + + async def test_metadata(self): + async with ChannelFor([self.service]) as channel: + META = MockMLModel.META + META_INPUTS = MockMLModel.META_INPUTS + META_OUTPUTS = MockMLModel.META_OUTPUTS + client = MLModelClient(self.name, channel) + response = await client.metadata() + assert response.name == META.name + assert response.type == META.type + assert response.description == META.description + assert len(response.input_info) == len(META_INPUTS) + assert response.input_info[0].shape == META_INPUTS[0].shape + assert response.input_info == META_INPUTS + output_info = response.output_info + assert len(output_info) == len(META_OUTPUTS) + assert output_info[0].name == META_OUTPUTS[0].name + assert len(output_info[0].associated_files) == len(META_OUTPUTS[0].associated_files) + assert output_info[2].name == META_OUTPUTS[2].name + assert output_info[2].associated_files[0].name == META_OUTPUTS[2].associated_files[0].name + assert output_info[3].name == META_OUTPUTS[3].name + assert output_info[3].shape == META_OUTPUTS[3].shape + assert output_info == META_OUTPUTS + assert response == META diff --git a/tests/test_mlmodel_utils.py b/tests/test_mlmodel_utils.py new file mode 100644 index 000000000..b24d8d37b --- /dev/null +++ b/tests/test_mlmodel_utils.py @@ -0,0 +1,58 @@ +import builtins + +import numpy as np +import pytest + +from viam.services.mlmodel.utils import flat_tensors_to_ndarrays, ndarrays_to_flat_tensors + +from .mocks.services import MockMLModel + + +# ignore warning about our out-of-bound int casting (i.e. uint32 -> int16) because we don't store uint32s for int16 & uint16 tensor data +# > 2^16-1 in the first place (inherently they are int16, we just cast them to uint32 for the grpc message) +@pytest.mark.filterwarnings("ignore::DeprecationWarning") +def test_flat_tensors_to_ndarrays(): + output = flat_tensors_to_ndarrays(MockMLModel.INTS_FLAT_TENSORS) + assert len(output.keys()) == 4 + assert all(name in output.keys() for name in ["0", "1", "2", "3"]) + assert np.array_equal(output["0"], MockMLModel.INT8_NDARRAY) + assert output["0"].dtype == np.int8 + assert np.array_equal(output["1"], MockMLModel.INT16_NDARRAY) + assert output["1"].dtype == np.int16 + assert np.array_equal(output["2"], MockMLModel.INT32_NDARRAY) + assert output["2"].dtype == np.int32 + assert np.array_equal(output["3"], MockMLModel.INT64_NDARRAY) + assert output["3"].dtype == np.int64 + + output = flat_tensors_to_ndarrays(MockMLModel.UINTS_FLAT_TENSORS) + assert len(output.keys()) == 4 + assert all(name in output.keys() for name in ["0", "1", "2", "3"]) + assert np.array_equal(output["0"], MockMLModel.UINT8_NDARRAY) + assert output["0"].dtype == np.uint8 + assert np.array_equal(output["1"], MockMLModel.UINT16_NDARRAY) + assert output["1"].dtype == np.uint16 + assert np.array_equal(output["2"], MockMLModel.UINT32_NDARRAY) + assert output["2"].dtype == np.uint32 + assert np.array_equal(output["3"], MockMLModel.UINT64_NDARRAY) + assert output["3"].dtype == np.uint64 + + output = flat_tensors_to_ndarrays(MockMLModel.DOUBLE_FLOAT_TENSORS) + assert len(output.keys()) == 2 + assert all(name in output.keys() for name in ["0", "1"]) + assert np.array_equal(output["0"], MockMLModel.DOUBLE_NDARRAY) + assert output["0"].dtype == np.float64 + assert np.array_equal(output["1"], MockMLModel.FLOAT_NDARRAY) + assert output["1"].dtype == np.float32 + + +@pytest.mark.filterwarnings("ignore::DeprecationWarning") +def test_ndarrays_to_flat_tensors(): + output = ndarrays_to_flat_tensors(MockMLModel.INTS_NDARRAYS) + assert len(output.tensors) == 4 + assert all(name in output.tensors.keys() for name in ["0", "1", "2", "3"]) + assert type(output.tensors["0"].int8_tensor.data) is builtins.bytes + bytes_buffer = output.tensors["0"].int8_tensor.data + assert np.array_equal(np.frombuffer(bytes_buffer, dtype=np.int8).reshape(output.tensors["0"].shape), MockMLModel.INT8_NDARRAY) + assert np.array_equal(np.array(output.tensors["1"].int16_tensor.data, dtype=np.int16), MockMLModel.INT16_NDARRAY) + assert np.array_equal(np.array(output.tensors["2"].int32_tensor.data, dtype=np.int32), MockMLModel.INT32_NDARRAY) + assert np.array_equal(np.array(output.tensors["3"].int64_tensor.data, dtype=np.int64), MockMLModel.INT64_NDARRAY) diff --git a/tests/test_module.py b/tests/test_module.py new file mode 100644 index 000000000..9f7d48c89 --- /dev/null +++ b/tests/test_module.py @@ -0,0 +1,326 @@ +from unittest import mock + +import pytest +from grpclib.testing import ChannelFor + +from viam.errors import GRPCError +from viam.module import Module +from viam.module.service import ModuleRPCService +from viam.proto.app.robot import ComponentConfig +from viam.proto.module import ( + AddResourceRequest, + ModuleServiceStub, + ReadyRequest, + ReadyResponse, + ReconfigureResourceRequest, + RemoveResourceRequest, + ValidateConfigRequest, + ValidateConfigResponse, +) +from viam.proto.robot import ResourceRPCSubtype +from viam.resource.types import API, Model +from viam.robot.client import RobotClient +from viam.robot.service import RobotService +from viam.utils import dict_to_struct + +from .mocks.module.gizmo.api import Gizmo +from .mocks.module.gizmo.my_gizmo import MyGizmo +from .mocks.module.summation.api import SummationService +from .mocks.module.summation.my_summation import MySummationService +from .test_robot import service as robot_service # noqa: F401 + + +@pytest.fixture +async def module(request): + module = Module("some_fake_address") + module.add_model_from_registry(Gizmo.API, MyGizmo.MODEL) + request.cls.module = module + yield module + await module.stop() + + +@pytest.fixture +def service(module: Module) -> ModuleRPCService: + return ModuleRPCService(module) + + +class TestModule: + async def test_add_resource(self, module: Module): + req = AddResourceRequest( + config=ComponentConfig( + name="gizmo1", + namespace="acme", + type="gizmo", + model="acme:demo:mygizmo", + attributes=dict_to_struct({"arg1": "arg1", "motor": "motor1"}), + api="acme:component:gizmo", + ) + ) + assert Gizmo.get_resource_name("gizmo1") not in module.server.resources + await module.add_resource(req) + assert Gizmo.get_resource_name("gizmo1") in module.server.resources + + req = AddResourceRequest( + config=ComponentConfig( + name="mysum1", + namespace="acme", + type="summation", + model="acme:demo:mysum", + attributes=dict_to_struct({"subtract": False}), + api="acme:service:summation", + ) + ) + assert SummationService.get_resource_name("mysum1") not in module.server.resources + await module.add_resource(req) + assert SummationService.get_resource_name("mysum1") in module.server.resources + + async def test_reconfigure_resource(self, module: Module): + await self.test_add_resource(module) + + gizmo = module.server.get_resource(MyGizmo, Gizmo.get_resource_name("gizmo1")) + assert gizmo.my_arg == "arg1" + req = ReconfigureResourceRequest( + config=ComponentConfig( + name="gizmo1", + namespace="acme", + type="gizmo", + model="acme:demo:mygizmo", + attributes=dict_to_struct({"arg1": "arg2", "motor": "motor1"}), + api="acme:component:gizmo", + ) + ) + await module.reconfigure_resource(req) + assert gizmo.my_arg == "arg2" + + summer = module.server.get_resource(MySummationService, SummationService.get_resource_name("mysum1")) + assert summer.subtract is False + req = ReconfigureResourceRequest( + config=ComponentConfig( + name="mysum1", + namespace="acme", + type="summation", + model="acme:demo:mysum", + attributes=dict_to_struct({"subtract": True}), + api="acme:service:summation", + ) + ) + await module.reconfigure_resource(req) + assert summer.subtract is True + + async def test_add_resource_with_deps(self, robot_service: RobotService, module: Module): # noqa: F811 + async with ChannelFor([robot_service]) as channel: + _ = mock.patch("viam.module.module.Module._connect_to_parent") + module.parent = await RobotClient.with_channel(channel, RobotClient.Options()) + req = AddResourceRequest( + config=ComponentConfig( + name="gizmo2", + namespace="acme", + type="gizmo", + model="acme:demo:mygizmo", + attributes=dict_to_struct({"arg1": "arg2", "motor": "motor1"}), + api="acme:component:gizmo", + ), + dependencies=["rdk:component:arm/arm1", "rdk:service:mlmodel/mlmodel1"], + ) + await module.add_resource(req) + assert Gizmo.get_resource_name("gizmo2") in module.server.resources + + req = RemoveResourceRequest(name="acme:component:gizmo/gizmo2") + await module.remove_resource(req) + assert Gizmo.get_resource_name("gizmo2") not in module.server.resources + + async def test_remove_resource(self, module: Module): + await self.test_add_resource(module) + + gizmo = module.server.get_resource(MyGizmo, Gizmo.get_resource_name("gizmo1")) + assert gizmo.closed is False + assert Gizmo.get_resource_name("gizmo1") in module.server.resources + req = RemoveResourceRequest(name="acme:component:gizmo/gizmo1") + await module.remove_resource(req) + assert Gizmo.get_resource_name("gizmo1") not in module.server.resources + assert gizmo.closed is True + + with mock.patch("tests.mocks.module.summation.MySummationService.close") as mocked: + assert SummationService.get_resource_name("mysum1") in module.server.resources + req = RemoveResourceRequest(name="acme:service:summation/mysum1") + + mocked.assert_not_called() + await module.remove_resource(req) + assert SummationService.get_resource_name("mysum1") not in module.server.resources + # test default close + mocked.assert_called_once() + + async def test_ready(self, module: Module): + with mock.patch("viam.module.Module._connect_to_parent"): + p_addr = "SOME_FAKE_ADDRESS" + assert module._parent_address != p_addr + req = ReadyRequest(parent_address=p_addr) + resp = await module.ready(req) + assert module._parent_address == p_addr + assert len(resp.handlermap.handlers) == 2 + + handler = resp.handlermap.handlers[0] + rn = Gizmo.get_resource_name("") + assert handler.subtype == ResourceRPCSubtype(subtype=rn, proto_service="acme.component.gizmo.v1.GizmoService") + assert len(handler.models) == 1 + model = handler.models[0] + assert model == "acme:demo:mygizmo" + + handler = resp.handlermap.handlers[1] + rn = SummationService.get_resource_name("") + assert handler.subtype == ResourceRPCSubtype(subtype=rn, proto_service="acme.service.summation.v1.SummationService") + assert len(handler.models) == 1 + model = handler.models[0] + assert model == "acme:demo:mysum" + + def test_add_model_from_registry(self): + mod = Module("fake") + mod.add_model_from_registry(Gizmo.API, MyGizmo.MODEL) + + with pytest.raises(ValueError): + mod.add_model_from_registry(API.from_string("fake:fake:fake"), Model.from_string("faker:faker:faker")) + + async def test_multiple_resources_same_model(self, module: Module): + req = AddResourceRequest( + config=ComponentConfig( + name="gizmo1", + namespace="acme", + type="gizmo", + model="acme:demo:mygizmo", + attributes=dict_to_struct({"arg1": "arg1", "motor": "motor1"}), + api="acme:component:gizmo", + ) + ) + await module.add_resource(req) + req = AddResourceRequest( + config=ComponentConfig( + name="gizmo2", + namespace="acme", + type="gizmo", + model="acme:demo:mygizmo", + attributes=dict_to_struct({"arg1": "arg2", "motor": "motor1"}), + api="acme:component:gizmo", + ) + ) + await module.add_resource(req) + + g1 = module.server.get_resource(Gizmo, Gizmo.get_resource_name("gizmo1")) + g2 = module.server.get_resource(Gizmo, Gizmo.get_resource_name("gizmo2")) + + assert await g1.do_one("arg1") is True + assert await g2.do_one("arg1") is False + assert await g1.do_one("arg2") is False + assert await g2.do_one("arg2") is True + + async def test_validate_config(self, module: Module): + await self.test_add_resource(module) + + req = ValidateConfigRequest( + config=( + ComponentConfig( + name="gizmo1", + namespace="acme", + type="gizmo", + model="acme:demo:mygizmo", + attributes=dict_to_struct({"arg1": "arg2", "motor": "motor1"}), + api="acme:component:gizmo", + ) + ) + ) + response = await module.validate_config(req) + assert response.dependencies == ["motor1"] + + req = ValidateConfigRequest( + config=( + ComponentConfig( + name="gizmo2", + namespace="acme", + type="gizmo", + model="acme:demo:mygizmo", + attributes=dict_to_struct({"arg1": "arg2", "invalid": "attribute", "motor": "motor1"}), + api="acme:component:gizmo", + ) + ) + ) + with pytest.raises(GRPCError, match=r".*Status.INVALID_ARGUMENT.*"): + response = await module.validate_config(req) + + req = ValidateConfigRequest( + config=( + ComponentConfig( + name="gizmo3", + namespace="acme", + type="gizmo", + model="acme:demo:mygizmo", + attributes=dict_to_struct({"arg1": "arg2"}), + api="acme:component:gizmo", + ) + ) + ) + with pytest.raises(GRPCError, match=r".*Status.INVALID_ARGUMENT.*"): + response = await module.validate_config(req) + + req = ValidateConfigRequest( + config=( + ComponentConfig( + name="gizmo3", + namespace="acme", + type="gizmo", + model="acme:demo:mygizmo", + attributes=dict_to_struct({"motor": "motor1"}), + api="acme:component:gizmo", + ) + ) + ) + with pytest.raises(GRPCError, match=r".*Status.INVALID_ARGUMENT.*"): + response = await module.validate_config(req) + + req = ValidateConfigRequest( + config=ComponentConfig( + name="mysum1", + namespace="acme", + type="summation", + model="acme:demo:mysum", + attributes=dict_to_struct({"subtract": False}), + api="acme:service:summation", + ) + ) + response = await module.validate_config(req) + assert response.dependencies == [] + + +class TestService: + async def test_add_resource(self, service: ModuleRPCService): + async with ChannelFor([service]) as channel: + client = ModuleServiceStub(channel) + with mock.patch("viam.module.module.Module.add_resource") as mocked: + await client.AddResource(AddResourceRequest()) + mocked.assert_called_once() + + async def test_reconfigure_resource(self, service: ModuleRPCService): + async with ChannelFor([service]) as channel: + client = ModuleServiceStub(channel) + with mock.patch("viam.module.module.Module.reconfigure_resource") as mocked: + await client.ReconfigureResource(ReconfigureResourceRequest()) + mocked.assert_called_once() + + async def test_remove_resource(self, service: ModuleRPCService): + async with ChannelFor([service]) as channel: + client = ModuleServiceStub(channel) + with mock.patch("viam.module.module.Module.remove_resource") as mocked: + await client.RemoveResource(RemoveResourceRequest()) + mocked.assert_called_once() + + async def test_ready(self, service: ModuleRPCService): + async with ChannelFor([service]) as channel: + client = ModuleServiceStub(channel) + with mock.patch("viam.module.module.Module.ready", return_value=ReadyResponse()) as mocked: + await client.Ready(ReadyRequest()) + mocked.assert_called_once() + + async def test_validate_config(self, service: ModuleRPCService): + async with ChannelFor([service]) as channel: + client = ModuleServiceStub(channel) + with mock.patch("viam.module.module.Module.validate_config", return_value=ValidateConfigResponse()) as mocked: + await client.ValidateConfig(ValidateConfigRequest()) + mocked.assert_called_once() diff --git a/tests/test_motion_service.py b/tests/test_motion_service.py index c8b136274..4808ea601 100644 --- a/tests/test_motion_service.py +++ b/tests/test_motion_service.py @@ -1,62 +1,333 @@ +from typing import Any, Mapping, Optional, Sequence +from unittest.mock import patch + import pytest +from google.protobuf.timestamp_pb2 import Timestamp from grpclib.testing import ChannelFor from viam.components.arm import Arm -from viam.components.gantry import Gantry -from viam.proto.common import Pose, PoseInFrame -from viam.services.motion import MotionServiceClient - -from .mocks.services import MockMotionService +from viam.components.base import Base +from viam.gen.service.motion.v1.motion_pb2 import ( + PLAN_STATE_FAILED, + PLAN_STATE_IN_PROGRESS, + PLAN_STATE_SUCCEEDED, + ComponentState, + GetPlanResponse, + ListPlanStatusesResponse, + Plan, + PlanStatus, + PlanStatusWithID, + PlanStep, + PlanWithStatus, +) +from viam.proto.common import GeoGeometry, Geometry, GeoPoint, Pose, PoseInFrame, ResourceName, Transform, WorldState +from viam.proto.service.motion import Constraints, LinearConstraint, MotionConfiguration +from viam.resource.manager import ResourceManager +from viam.services.motion import MotionClient +from viam.services.motion.motion import Motion +from viam.services.motion.service import MotionRPCService +from viam.utils import ValueTypes -MOVE_RESPONSES = {"arm": False, "gantry": True} -MOVE_SINGLE_COMPONENT_RESPONSES = {"arm": True, "gantry": False} -GET_POSE_RESPONSES = { - "arm": PoseInFrame(reference_frame="arm", pose=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)), - "gantry": PoseInFrame(reference_frame="gantry", pose=Pose(x=2, y=3, z=4, o_x=3, o_y=4, o_z=5, theta=21)), -} +from . import loose_approx MOTION_SERVICE_NAME = "motion1" @pytest.fixture(scope="function") -def service() -> MockMotionService: - return MockMotionService( - move_responses=MOVE_RESPONSES, - move_single_component_responses=MOVE_SINGLE_COMPONENT_RESPONSES, - get_pose_responses=GET_POSE_RESPONSES, - ) - - -class TestClient: - @pytest.mark.asyncio - async def test_plan_and_move(self, service: MockMotionService): - async with ChannelFor([service]) as channel: - client = MotionServiceClient(MOTION_SERVICE_NAME, channel) - success = await client.move(Arm.get_resource_name("arm"), PoseInFrame(), extra={"foo": "bar"}) - assert success == MOVE_RESPONSES["arm"] - assert service.extra == {"foo": "bar"} - success = await client.move(Gantry.get_resource_name("gantry"), PoseInFrame()) - assert success == MOVE_RESPONSES["gantry"] - assert service.extra == {} - - @pytest.mark.asyncio - async def test_move_single_component(self, service: MockMotionService): - async with ChannelFor([service]) as channel: - client = MotionServiceClient(MOTION_SERVICE_NAME, channel) - success = await client.move_single_component(Arm.get_resource_name("arm"), PoseInFrame(), extra={"foo": "bar"}) - assert success == MOVE_SINGLE_COMPONENT_RESPONSES["arm"] - assert service.extra == {"foo": "bar"} - success = await client.move_single_component(Gantry.get_resource_name("gantry"), PoseInFrame()) - assert success == MOVE_SINGLE_COMPONENT_RESPONSES["gantry"] - assert service.extra == {} - - @pytest.mark.asyncio - async def test_get_pose(self, service: MockMotionService): - async with ChannelFor([service]) as channel: - client = MotionServiceClient(MOTION_SERVICE_NAME, channel) - pose = await client.get_pose(Arm.get_resource_name("arm"), "x", extra={"foo": "bar"}) - assert pose == GET_POSE_RESPONSES["arm"] - assert service.extra == {"foo": "bar"} - pose = await client.get_pose(Gantry.get_resource_name("gantry"), "y") - assert pose == GET_POSE_RESPONSES["gantry"] - assert service.extra == {} +def motion(): + class MockMotion(Motion): + async def move( + self, + component_name: ResourceName, + destination: PoseInFrame, + world_state: Optional[WorldState] = None, + constraints: Optional[Constraints] = None, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + ) -> bool: + raise NotImplementedError + + async def move_on_globe( + self, + component_name: ResourceName, + destination: GeoPoint, + movement_sensor_name: ResourceName, + obstacles: Optional[Sequence[GeoGeometry]] = None, + heading: Optional[float] = None, + configuration: Optional[MotionConfiguration] = None, + *, + bounding_regions: Optional[Sequence[GeoGeometry]] = None, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> str: + raise NotImplementedError + + async def move_on_map( + self, + component_name: ResourceName, + destination: Pose, + slam_service_name: ResourceName, + configuration: Optional[MotionConfiguration] = None, + obstacles: Optional[Sequence[Geometry]] = None, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> str: + raise NotImplementedError + + async def stop_plan( + self, component_name: ResourceName, *, extra: Optional[Mapping[str, ValueTypes]] = None, timeout: Optional[float] = None + ): + raise NotImplementedError + + async def get_plan( + self, + component_name: ResourceName, + last_plan_only: bool = False, + execution_id: Optional[str] = None, + *, + extra: Optional[Mapping[str, ValueTypes]] = None, + timeout: Optional[float] = None, + ) -> GetPlanResponse: + raise NotImplementedError + + async def list_plan_statuses( + self, only_active_plans: bool = False, *, extra: Optional[Mapping[str, ValueTypes]] = None, timeout: Optional[float] = None + ) -> ListPlanStatusesResponse: + raise NotImplementedError + + async def get_pose( + self, + component_name: ResourceName, + destination_frame: str, + supplemental_transforms: Optional[Sequence[Transform]] = None, + *, + extra: Optional[Mapping[str, Any]] = None, + timeout: Optional[float] = None, + ) -> PoseInFrame: + raise NotImplementedError + + return MockMotion(MOTION_SERVICE_NAME) + + +@pytest.fixture(scope="function") +def service(motion: Motion) -> MotionRPCService: + return MotionRPCService(ResourceManager([motion])) + + +class TestMotionService: + async def test_move(self, motion: Motion, service: MotionRPCService): + with patch.object(motion, "move") as patched_method: + patched_method.return_value = True + async with ChannelFor([service]) as channel: + client = MotionClient(MOTION_SERVICE_NAME, channel) + resource_name = Arm.get_resource_name("arm") + destination = PoseInFrame(reference_frame="refframe") + world_state = WorldState(transforms=[Transform(reference_frame="ws_tfrm_rf")]) + constraints = Constraints(linear_constraint=[LinearConstraint(), LinearConstraint(line_tolerance_mm=2)]) + extra = {"foo": "bar"} + timeout = 2 + success = await client.move( + resource_name, + destination, + world_state, + constraints, + extra=extra, + timeout=timeout, + ) + assert success is True + patched_method.assert_called_once() + assert patched_method.call_args.args[0] == resource_name + assert patched_method.call_args.args[1] == destination + assert patched_method.call_args.args[2] == world_state + assert patched_method.call_args.args[3] == constraints + assert patched_method.call_args.kwargs["extra"] == extra + assert patched_method.call_args.kwargs["timeout"] == loose_approx(timeout) + + async def test_get_pose(self, motion: Motion, service: MotionRPCService): + with patch.object(motion, "get_pose") as patched_method: + response = PoseInFrame(reference_frame="arm", pose=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)) + patched_method.return_value = response + async with ChannelFor([service]) as channel: + client = MotionClient(MOTION_SERVICE_NAME, channel) + rn = Arm.get_resource_name("arm") + destination_frame = "x" + transforms = [Transform(reference_frame="y")] + extra = {"foo": "bar"} + timeout = 4 + pose = await client.get_pose(rn, destination_frame, transforms, extra=extra, timeout=timeout) + assert pose == response + patched_method.assert_called_once() + assert patched_method.call_args.args[0] == rn + assert patched_method.call_args.args[1] == destination_frame + assert patched_method.call_args.args[2] == transforms + assert patched_method.call_args.kwargs["extra"] == extra + assert patched_method.call_args.kwargs["timeout"] == loose_approx(timeout) + + async def test_move_on_map(self, motion: Motion, service: MotionRPCService): + with patch.object(motion, "move_on_map") as patched_method: + resopnse = "Move On Map Response" + patched_method.return_value = resopnse + async with ChannelFor([service]) as channel: + client = MotionClient(MOTION_SERVICE_NAME, channel) + component_rn = Base.get_resource_name("move_on_globe_base") + destination = Pose(x=1, y=2, z=3, theta=4) + slam_rn = ResourceName(namespace="rdk", type="service", subtype="slam", name="move_on_map_slam") + configuration = MotionConfiguration(position_polling_frequency_hz=4.44) + obstacles = [Geometry(center=Pose(x=9, y=8, z=7, theta=6))] + extra = {"foo": "bar"} + timeout = 4 + execution_id = await client.move_on_map( + component_rn, + destination, + slam_service_name=slam_rn, + configuration=configuration, + obstacles=obstacles, + extra=extra, + timeout=timeout, + ) + assert execution_id == resopnse + patched_method.assert_called_once() + assert patched_method.call_args.args[0] == component_rn + assert patched_method.call_args.args[1] == destination + assert patched_method.call_args.args[2] == slam_rn + assert patched_method.call_args.args[3] == configuration + assert patched_method.call_args.args[4] == obstacles + assert patched_method.call_args.kwargs["extra"] == extra + assert patched_method.call_args.kwargs["timeout"] == loose_approx(timeout) + + async def test_move_on_globe(self, motion: Motion, service: MotionRPCService): + with patch.object(motion, "move_on_globe") as patched_method: + response = "Move On Globe Response" + patched_method.return_value = response + async with ChannelFor([service]) as channel: + client = MotionClient(MOTION_SERVICE_NAME, channel) + component_rn = Base.get_resource_name("move_on_globe_base") + destination = GeoPoint(latitude=123, longitude=456) + movement_rn = ResourceName(namespace="rdk", type="component", subtype="movement_sensor", name="move_on_globe_ms") + obstacles = [GeoGeometry(location=GeoPoint(latitude=44, longitude=786))] + heading = 5.55 + configuration = MotionConfiguration(position_polling_frequency_hz=4.44) + bounding_regions = [GeoGeometry(location=GeoPoint(latitude=182, longitude=41))] + extra = {"foo": "bar"} + timeout = 3 + execution_id = await client.move_on_globe( + component_rn, + destination, + movement_rn, + obstacles, + heading, + configuration, + bounding_regions=bounding_regions, + extra=extra, + timeout=timeout, + ) + assert execution_id == response + patched_method.assert_called_once() + assert patched_method.call_args.args[0] == component_rn + assert patched_method.call_args.args[1] == destination + assert patched_method.call_args.args[2] == movement_rn + assert patched_method.call_args.args[3] == obstacles + assert patched_method.call_args.args[4] == heading + assert patched_method.call_args.args[5] == configuration + assert patched_method.call_args.kwargs["bounding_regions"] == bounding_regions + assert patched_method.call_args.kwargs["extra"] == extra + assert patched_method.call_args.kwargs["timeout"] == loose_approx(timeout) + + async def test_stop_plan(self, motion: Motion, service: MotionRPCService): + with patch.object(motion, "stop_plan") as patched_method: + async with ChannelFor([service]) as channel: + client = MotionClient(MOTION_SERVICE_NAME, channel) + component_rn = Base.get_resource_name("stop_plan_base") + extra = {"foo": "bar"} + timeout = 4 + await client.stop_plan(component_rn, extra=extra, timeout=timeout) + patched_method.assert_called_once() + assert patched_method.call_args.args[0] == component_rn + assert patched_method.call_args.kwargs["extra"] == extra + assert patched_method.call_args.kwargs["timeout"] == loose_approx(timeout) + + async def test_get_plan(self, motion: Motion, service: MotionRPCService): + with patch.object(motion, "get_plan") as patched_method: + response = GetPlanResponse( + current_plan_with_status=PlanWithStatus( + plan=Plan( + id="plan_id2", + component_name=Base.get_resource_name("get_plan_base"), + execution_id="execution_id", + steps=[ + PlanStep(step={"get_plan_base": ComponentState(pose=Pose(x=2, y=3, z=4, o_x=3, o_y=4, o_z=5, theta=21))}), + PlanStep(step={"get_plan_base": ComponentState(pose=Pose(x=6, y=7, z=8, o_x=9, o_y=10, o_z=11, theta=23))}), + ], + ), + status=PlanStatus(state=PLAN_STATE_SUCCEEDED, timestamp=Timestamp(seconds=4)), + status_history=[PlanStatus(state=PLAN_STATE_IN_PROGRESS, timestamp=Timestamp(seconds=3))], + ), + replan_history=[ + PlanWithStatus( + plan=Plan( + id="plan_id1", + component_name=Base.get_resource_name("get_plan_base"), + execution_id="execution_id", + steps=[ + PlanStep(step={"get_plan_base": ComponentState(pose=Pose(x=1, y=1, z=1, o_x=1, o_y=1, o_z=1, theta=20))}), + PlanStep(step={"get_plan_base": ComponentState(pose=Pose(x=2, y=3, z=4, o_x=3, o_y=4, o_z=5, theta=21))}), + PlanStep(step={"get_plan_base": ComponentState(pose=Pose(x=6, y=7, z=8, o_x=9, o_y=10, o_z=11, theta=23))}), + ], + ), + status=PlanStatus(state=PLAN_STATE_FAILED, reason="failure reason", timestamp=Timestamp(seconds=2)), + status_history=[PlanStatus(state=PLAN_STATE_IN_PROGRESS, timestamp=Timestamp(seconds=1))], + ) + ], + ) + patched_method.return_value = response + async with ChannelFor([service]) as channel: + client = MotionClient(MOTION_SERVICE_NAME, channel) + component_rn = Base.get_resource_name("get_plan_base") + last_plan_only = True + execution_id = "ex_id" + extra = {"foo": "bar"} + timeout = 4 + + plan = await client.get_plan(component_rn, last_plan_only, execution_id, extra=extra, timeout=timeout) + assert plan == response + patched_method.assert_called_once() + assert patched_method.call_args.args[0] == component_rn + assert patched_method.call_args.args[1] == last_plan_only + assert patched_method.call_args.args[2] == execution_id + assert patched_method.call_args.kwargs["extra"] == extra + assert patched_method.call_args.kwargs["timeout"] == loose_approx(timeout) + + async def test_list_plan_statuses(self, motion: Motion, service: MotionRPCService): + with patch.object(motion, "list_plan_statuses") as patched_method: + response = [PlanStatusWithID(plan_id="lpsr_pswid")] + patched_method.return_value = response + async with ChannelFor([service]) as channel: + client = MotionClient(MOTION_SERVICE_NAME, channel) + + only_active_plans = True + extra = {"foo": "bar"} + timeout = 5 + + plan_statuses = await client.list_plan_statuses(only_active_plans, extra=extra, timeout=timeout) + + assert plan_statuses == response + patched_method.assert_called_once() + assert patched_method.call_args.args[0] == only_active_plans + assert patched_method.call_args.kwargs["extra"] == extra + assert patched_method.call_args.kwargs["timeout"] == loose_approx(timeout) + + async def test_do(self, motion: Motion, service: MotionRPCService): + with patch.object(motion, "do_command") as patched_method: + response = {"do": "command"} + patched_method.return_value = response + async with ChannelFor([service]) as channel: + client = MotionClient(MOTION_SERVICE_NAME, channel) + command = {"command": "args"} + timeout = 5 + dc_response = await client.do_command(command, timeout=timeout) + assert dc_response == response + patched_method.assert_called_once() + assert patched_method.call_args.args[0] == command + assert patched_method.call_args.kwargs["timeout"] == loose_approx(timeout) diff --git a/tests/test_motor.py b/tests/test_motor.py index 9dc1260ad..ba13c3a95 100644 --- a/tests/test_motor.py +++ b/tests/test_motor.py @@ -1,11 +1,10 @@ import pytest from grpclib.testing import ChannelFor -from viam.components.generic.service import GenericService -from viam.components.motor import MotorClient, MotorStatus, create_status -from viam.components.motor.service import MotorService -from viam.components.resource_manager import ResourceManager -from viam.errors import NotSupportedError +from viam.components.generic.service import GenericRPCService +from viam.components.motor import MotorClient +from viam.components.motor.service import MotorRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.motor import ( GetPositionRequest, GetPositionResponse, @@ -13,16 +12,21 @@ GetPropertiesResponse, GoForRequest, GoToRequest, + IsMovingRequest, + IsMovingResponse, IsPoweredRequest, IsPoweredResponse, MotorServiceStub, ResetZeroPositionRequest, SetPowerRequest, + SetRPMRequest, StopRequest, ) -from viam.utils import dict_to_struct, message_to_struct +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict -from .mocks.components import MockMotor +from . import loose_approx +from .mocks.components import GEOMETRIES, MockMotor @pytest.fixture(scope="function") @@ -31,32 +35,32 @@ def motor() -> MockMotor: @pytest.fixture(scope="function") -def service(motor: MockMotor) -> MotorService: +def service(motor: MockMotor) -> MotorRPCService: manager = ResourceManager([motor]) - return MotorService(manager) + return MotorRPCService(manager) @pytest.fixture(scope="function") -def generic_service(motor: MockMotor) -> GenericService: +def generic_service(motor: MockMotor) -> GenericRPCService: manager = ResourceManager([motor]) - return GenericService(manager) + return GenericRPCService(manager) class TestMotor: - @pytest.mark.asyncio async def test_set_power(self, motor: MockMotor): power = 0.32 - await motor.set_power(power) + await motor.set_power(power, timeout=1.23) assert motor.power == power + assert motor.timeout == loose_approx(1.23) - @pytest.mark.asyncio async def test_get_position(self, motor: MockMotor): - pos = await motor.get_position() + pos = await motor.get_position(timeout=2.34) assert pos == 0 + assert motor.timeout == loose_approx(2.34) - @pytest.mark.asyncio async def test_go_for(self, motor: MockMotor): - await motor.go_for(30, 20) + await motor.go_for(30, 20, timeout=3.45) + assert motor.timeout == loose_approx(3.45) assert await motor.get_position() == 20 await motor.go_for(-10, 10) @@ -68,94 +72,101 @@ async def test_go_for(self, motor: MockMotor): await motor.go_for(-10, -10) assert await motor.get_position() == 15 - @pytest.mark.asyncio async def test_go_to(self, motor: MockMotor): - await motor.go_to(30, 20) + await motor.go_to(30, 20, timeout=4.56) + assert motor.timeout == loose_approx(4.56) assert await motor.get_position() == 20 await motor.go_to(-10, 50) assert await motor.get_position() == 50 - @pytest.mark.asyncio + async def test_set_rpm(self, motor: MockMotor): + await motor.set_rpm(30, timeout=4.56) + assert motor.timeout == loose_approx(4.56) + moving = await motor.is_moving() + assert moving is True + + await motor.set_rpm(-30) + moving = await motor.is_moving() + assert moving is True + async def test_reset_zero(self, motor: MockMotor): - await motor.reset_zero_position(20) + await motor.reset_zero_position(20, timeout=5.67) assert motor.offset == 20 + assert motor.timeout == loose_approx(5.67) - @pytest.mark.asyncio async def test_get_properties(self, motor: MockMotor): - properties = await motor.get_properties() + properties = await motor.get_properties(timeout=6.78) assert properties.position_reporting is True + assert motor.timeout == loose_approx(6.78) - @pytest.mark.asyncio async def test_stop(self, motor: MockMotor): await motor.set_power(10) assert motor.powered is True - await motor.stop() + await motor.stop(timeout=7.89) assert motor.powered is False assert motor.power == 0 + assert motor.timeout == loose_approx(7.89) - @pytest.mark.asyncio async def test_is_powered(self, motor: MockMotor): await motor.set_power(10) - is_powered = await motor.is_powered() + is_powered, power_pct = await motor.is_powered(timeout=8.90) assert is_powered is True + assert power_pct == 10 + assert motor.timeout == loose_approx(8.90) await motor.set_power(0) - is_powered = await motor.is_powered() + is_powered, power_pct = await motor.is_powered() assert is_powered is False + assert is_powered == 0 - @pytest.mark.asyncio async def test_is_moving(self, motor: MockMotor): await motor.set_power(10) assert await motor.is_moving() await motor.set_power(0) assert not await motor.is_moving() - @pytest.mark.asyncio async def test_do(self, motor: MockMotor): - with pytest.raises(NotImplementedError): - await motor.do_command({"command": "args"}) + command = {"command": "args"} + resp = await motor.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio - async def test_status(self, motor: MockMotor): - await motor.set_power(10) - status = await create_status(motor) - assert status.name == motor.get_resource_name(motor.name) - assert status.status == message_to_struct(MotorStatus(is_powered=True, position=0, position_reporting=True, is_moving=True)) - - @pytest.mark.asyncio async def test_extra(self, motor: MockMotor): assert motor.extra is None extra = {"foo": "bar", "baz": [1, 2, 3]} await motor.is_powered(extra=extra) assert motor.extra == extra + async def test_get_geometries(self, motor: MockMotor): + geometries = await motor.get_geometries() + assert geometries == GEOMETRIES + class TestService: - @pytest.mark.asyncio - async def test_set_power(self, motor: MockMotor, service: MotorService): + async def test_set_power(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorServiceStub(channel) request = SetPowerRequest(name=motor.name, power_pct=13) - await client.SetPower(request) + await client.SetPower(request, timeout=2.34) assert motor.power == 13 + assert motor.timeout == loose_approx(2.34) - @pytest.mark.asyncio - async def test_get_position(self, motor: MockMotor, service: MotorService): + async def test_get_position(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorServiceStub(channel) request = GetPositionRequest(name=motor.name) - response: GetPositionResponse = await client.GetPosition(request) + response: GetPositionResponse = await client.GetPosition(request, timeout=2.34) assert response.position == 0 + assert motor.timeout == loose_approx(2.34) - @pytest.mark.asyncio - async def test_go_for(self, motor: MockMotor, service: MotorService): + async def test_go_for(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorServiceStub(channel) request = GoForRequest(name=motor.name, rpm=30, revolutions=20) - await client.GoFor(request) + await client.GoFor(request, timeout=3.45) assert motor.position == 20 + assert motor.timeout == loose_approx(3.45) request = GoForRequest(name=motor.name, rpm=-10, revolutions=10) await client.GoFor(request) @@ -169,63 +180,88 @@ async def test_go_for(self, motor: MockMotor, service: MotorService): await client.GoFor(request) assert motor.position == 15 - @pytest.mark.asyncio - async def test_go_to(self, motor: MockMotor, service: MotorService): + async def test_go_to(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorServiceStub(channel) request = GoToRequest(name=motor.name, rpm=30, position_revolutions=20) - await client.GoTo(request) + await client.GoTo(request, timeout=4.56) assert motor.position == 20 + assert motor.timeout == loose_approx(4.56) request = GoToRequest(name=motor.name, rpm=-10, position_revolutions=50) await client.GoTo(request) assert motor.position == 50 - @pytest.mark.asyncio - async def test_reset_zero(self, motor: MockMotor, service: MotorService): + async def test_set_rpm(self, motor: MockMotor, service: MotorRPCService): + async with ChannelFor([service]) as channel: + client = MotorServiceStub(channel) + + request = SetRPMRequest(name=motor.name, rpm=30) + await client.SetRPM(request, timeout=4.56) + moving = await motor.is_moving() + assert moving is True + assert motor.timeout == loose_approx(4.56) + + request = SetRPMRequest(name=motor.name, rpm=-10) + await client.SetRPM(request) + moving = await motor.is_moving() + assert moving is True + + async def test_reset_zero(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorServiceStub(channel) request = ResetZeroPositionRequest(name=motor.name, offset=20) - await client.ResetZeroPosition(request) + await client.ResetZeroPosition(request, timeout=5.67) assert motor.offset == 20 + assert motor.timeout == loose_approx(5.67) - @pytest.mark.asyncio - async def test_get_properties(self, motor: MockMotor, service: MotorService): + async def test_get_properties(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorServiceStub(channel) request = GetPropertiesRequest(name=motor.name) - response: GetPropertiesResponse = await client.GetProperties(request) + response: GetPropertiesResponse = await client.GetProperties(request, timeout=6.78) assert response.position_reporting is True + assert motor.timeout == loose_approx(6.78) - @pytest.mark.asyncio - async def test_stop(self, motor: MockMotor, service: MotorService): + async def test_stop(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorServiceStub(channel) request = StopRequest(name=motor.name) - await client.Stop(request) + await client.Stop(request, timeout=7.89) assert motor.powered is False assert motor.power == 0 + assert motor.timeout == loose_approx(7.89) - @pytest.mark.asyncio - async def test_is_powered(self, motor: MockMotor, service: MotorService): + async def test_is_powered(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorServiceStub(channel) sp_request = SetPowerRequest(name=motor.name, power_pct=13) await client.SetPower(sp_request) request = IsPoweredRequest(name=motor.name) - response: IsPoweredResponse = await client.IsPowered(request) + response: IsPoweredResponse = await client.IsPowered(request, timeout=8.90) assert response.is_on is True + assert response.power_pct == 13 + assert motor.timeout == loose_approx(8.90) sp_request = SetPowerRequest(name=motor.name, power_pct=0) await client.SetPower(sp_request) request = IsPoweredRequest(name=motor.name) response: IsPoweredResponse = await client.IsPowered(request) assert response.is_on is False + assert response.power_pct == 0 - @pytest.mark.asyncio - async def test_extra(self, motor: MockMotor, service: MotorService): + async def test_is_moving(self, motor: MockMotor, service: MotorRPCService): + async with ChannelFor([service]) as channel: + assert motor.powered is False + motor.powered = True + client = MotorServiceStub(channel) + request = IsMovingRequest(name=motor.name) + response: IsMovingResponse = await client.IsMoving(request) + assert response.is_moving is True + + async def test_extra(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorServiceStub(channel) @@ -235,29 +271,45 @@ async def test_extra(self, motor: MockMotor, service: MotorService): await client.IsPowered(request) assert motor.extra == extra + async def test_do(self, motor: MockMotor, service: MotorRPCService): + async with ChannelFor([service]) as channel: + client = MotorServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=motor.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + async def test_get_geometries(self, motor: MockMotor, service: MotorRPCService): + async with ChannelFor([service]) as channel: + client = MotorServiceStub(channel) + request = GetGeometriesRequest(name=motor.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES + class TestClient: - @pytest.mark.asyncio - async def test_set_power(self, motor: MockMotor, service: MotorService): + async def test_set_power(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) - await client.set_power(13) + await client.set_power(13, timeout=9.10) assert motor.power == 13 + assert motor.timeout == loose_approx(9.10) - @pytest.mark.asyncio - async def test_get_position(self, motor: MockMotor, service: MotorService): + async def test_get_position(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) - position = await client.get_position() + position = await client.get_position(timeout=2.34) assert position == 0 + assert motor.timeout == loose_approx(2.34) - @pytest.mark.asyncio - async def test_go_for(self, motor: MockMotor, service: MotorService): + async def test_go_for(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) - await client.go_for(30, 20) + await client.go_for(30, 20, timeout=3.45) assert motor.position == 20 + assert motor.timeout == loose_approx(3.45) await client.go_for(-10, 10) assert motor.position == 10 @@ -268,75 +320,82 @@ async def test_go_for(self, motor: MockMotor, service: MotorService): await client.go_for(-10, -10) assert motor.position == 15 - @pytest.mark.asyncio - async def test_go_to(self, motor: MockMotor, service: MotorService): + async def test_go_to(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) - await client.go_to(30, 20) + await client.go_to(30, 20, timeout=4.56) assert motor.position == 20 + assert motor.timeout == loose_approx(4.56) await client.go_to(-10, 50) assert motor.position == 50 - @pytest.mark.asyncio - async def test_reset_zero(self, motor: MockMotor, service: MotorService): + async def test_set_rpm(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) - await client.reset_zero_position(20) + + await client.set_rpm(30, timeout=4.56) + moving = await motor.is_moving() + assert motor.timeout == loose_approx(4.56) + assert moving is True + + await client.set_rpm(-10) + moving = await motor.is_moving() + assert moving is True + + async def test_reset_zero(self, motor: MockMotor, service: MotorRPCService): + async with ChannelFor([service]) as channel: + client = MotorClient(motor.name, channel) + await client.reset_zero_position(20, timeout=5.67) + assert motor.timeout == loose_approx(5.67) assert motor.offset == 20 - @pytest.mark.asyncio - async def test_get_properties(self, motor: MockMotor, service: MotorService): + async def test_get_properties(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) - properties = await client.get_properties() + properties = await client.get_properties(timeout=6.78) assert properties.position_reporting is True + assert motor.timeout == loose_approx(6.78) - @pytest.mark.asyncio - async def test_stop(self, motor: MockMotor, service: MotorService): + async def test_stop(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) - await client.stop() + await client.stop(timeout=7.89) assert motor.powered is False assert motor.power == 0 + assert motor.timeout == loose_approx(7.89) - @pytest.mark.asyncio - async def test_is_powered(self, motor: MockMotor, service: MotorService): + async def test_is_powered(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) await client.set_power(13) - is_on = await client.is_powered() + is_on, power_pct = await client.is_powered(timeout=8.90) assert is_on is True + assert power_pct == 13 + assert motor.timeout == loose_approx(8.90) await client.set_power(0) - is_on = await client.is_powered() + is_on, power_pct = await client.is_powered() assert is_on is False + assert power_pct == 0 - @pytest.mark.asyncio - async def test_is_moving(self, motor: MockMotor, service: MotorService): + async def test_is_moving(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) - with pytest.raises(NotSupportedError): - await client.is_moving() - - @pytest.mark.asyncio - async def test_do(self, motor: MockMotor, service: MotorService, generic_service: GenericService): - async with ChannelFor([service, generic_service]) as channel: - client = MotorClient(motor.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + assert motor.powered is False + motor.powered = True + assert await client.is_moving() is True - @pytest.mark.asyncio - async def test_status(self, motor: MockMotor, service: MotorService): + async def test_do(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) - with pytest.raises(NotSupportedError): - await create_status(client) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio - async def test_extra(self, motor: MockMotor, service: MotorService): + async def test_extra(self, motor: MockMotor, service: MotorRPCService): async with ChannelFor([service]) as channel: client = MotorClient(motor.name, channel) @@ -344,3 +403,9 @@ async def test_extra(self, motor: MockMotor, service: MotorService): extra = {"foo": "bar", "baz": [1, 2, 3]} await client.is_powered(extra=extra) assert motor.extra == extra + + async def test_get_geometries(self, motor: MockMotor, service: MotorRPCService): + async with ChannelFor([service]) as channel: + client = MotorClient(motor.name, channel) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_movement_sensor.py b/tests/test_movement_sensor.py index aa7ca3df7..1048f02fb 100644 --- a/tests/test_movement_sensor.py +++ b/tests/test_movement_sensor.py @@ -1,14 +1,19 @@ import pytest from grpclib.testing import ChannelFor -from viam.components.generic.service import GenericService -from viam.components.movement_sensor import ( - MovementSensor, - MovementSensorClient, - MovementSensorService, +from viam.components.generic.service import GenericRPCService +from viam.components.movement_sensor import MovementSensor, MovementSensorClient, MovementSensorRPCService +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, + GeoPoint, + GetGeometriesRequest, + GetGeometriesResponse, + GetReadingsRequest, + GetReadingsResponse, + Orientation, + Vector3, ) -from viam.components.resource_manager import ResourceManager -from viam.proto.common import GeoPoint, Orientation, Vector3 from viam.proto.component.movementsensor import ( GetAccuracyRequest, GetAccuracyResponse, @@ -16,6 +21,8 @@ GetAngularVelocityResponse, GetCompassHeadingRequest, GetCompassHeadingResponse, + GetLinearAccelerationRequest, + GetLinearAccelerationResponse, GetLinearVelocityRequest, GetLinearVelocityResponse, GetOrientationRequest, @@ -26,226 +33,372 @@ GetPropertiesResponse, MovementSensorServiceStub, ) +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, sensor_readings_value_to_native, struct_to_dict -from .mocks.components import MockMovementSensor +from . import loose_approx +from .mocks.components import GEOMETRIES, MockMovementSensor COORDINATE = GeoPoint(latitude=40.664679865782624, longitude=-73.97668056188789) ALTITUDE = 15 LINEAR_VELOCITY = Vector3(x=1, y=2, z=3) ANGULAR_VELOCITY = Vector3(x=1, y=2, z=3) +LINEAR_ACCELERATION = Vector3(x=1, y=2, z=3) HEADING = 182 ORIENTATION = Orientation(o_x=1, o_y=2, o_z=3, theta=5) PROPERTIES = MovementSensor.Properties( + linear_acceleration_supported=False, linear_velocity_supported=False, angular_velocity_supported=True, orientation_supported=False, position_supported=True, compass_heading_supported=False, ) -ACCURACY = {"foo": 0.1, "bar": 2, "baz": 3.14} +ACCURACY = MovementSensor.Accuracy( + accuracy={"foo": 0.1, "bar": 2, "baz": 3.14}, + position_hdop=0.0, + position_vdop=0.0, + position_nmea_gga_fix=0, + compass_degrees_error=0.0, +) +EXTRA_PARAMS = {"foo": "bar", "baz": [1, 2, 3]} +READINGS = {"a": 1, "b": 2, "c": 3} @pytest.fixture(scope="function") def movement_sensor() -> MovementSensor: return MockMovementSensor( - "movement_sensor", COORDINATE, ALTITUDE, LINEAR_VELOCITY, ANGULAR_VELOCITY, HEADING, ORIENTATION, PROPERTIES, ACCURACY + "movement_sensor", + COORDINATE, + ALTITUDE, + LINEAR_VELOCITY, + ANGULAR_VELOCITY, + LINEAR_ACCELERATION, + HEADING, + ORIENTATION, + PROPERTIES, + ACCURACY, + READINGS, ) @pytest.fixture(scope="function") -def service(movement_sensor: MovementSensor) -> MovementSensorService: +def service(movement_sensor: MovementSensor) -> MovementSensorRPCService: manager = ResourceManager([movement_sensor]) - return MovementSensorService(manager) + return MovementSensorRPCService(manager) @pytest.fixture(scope="function") -def generic_service(movement_sensor: MovementSensor) -> GenericService: +def generic_service(movement_sensor: MovementSensor) -> GenericRPCService: manager = ResourceManager([movement_sensor]) - return GenericService(manager) + return GenericRPCService(manager) class TestMovementSensor: - @pytest.mark.asyncio - async def test_get_position(self, movement_sensor: MovementSensor): - (coord, alt) = await movement_sensor.get_position() + async def test_get_position(self, movement_sensor: MockMovementSensor): + assert movement_sensor.extra is None + (coord, alt) = await movement_sensor.get_position(extra=EXTRA_PARAMS) assert coord == COORDINATE assert alt == ALTITUDE + assert movement_sensor.extra == EXTRA_PARAMS - @pytest.mark.asyncio - async def test_get_linear_velocity(self, movement_sensor: MovementSensor): - value = await movement_sensor.get_linear_velocity() + async def test_get_linear_velocity(self, movement_sensor: MockMovementSensor): + assert movement_sensor.extra is None + value = await movement_sensor.get_linear_velocity(extra=EXTRA_PARAMS) assert value == LINEAR_VELOCITY + assert movement_sensor.extra == EXTRA_PARAMS + + async def test_get_linear_acceleration(self, movement_sensor: MockMovementSensor): + assert movement_sensor.extra is None + value = await movement_sensor.get_linear_acceleration(extra=EXTRA_PARAMS) + assert value == LINEAR_ACCELERATION + assert movement_sensor.extra == EXTRA_PARAMS - @pytest.mark.asyncio - async def test_get_angular_velocity(self, movement_sensor: MovementSensor): - value = await movement_sensor.get_angular_velocity() + async def test_get_angular_velocity(self, movement_sensor: MockMovementSensor): + assert movement_sensor.extra is None + value = await movement_sensor.get_angular_velocity(extra=EXTRA_PARAMS) assert value == ANGULAR_VELOCITY + assert movement_sensor.extra == EXTRA_PARAMS - @pytest.mark.asyncio - async def test_get_compass_heading(self, movement_sensor: MovementSensor): - value = await movement_sensor.get_compass_heading() + async def test_get_compass_heading(self, movement_sensor: MockMovementSensor): + assert movement_sensor.extra is None + value = await movement_sensor.get_compass_heading(extra=EXTRA_PARAMS) assert value == HEADING + assert movement_sensor.extra == EXTRA_PARAMS - @pytest.mark.asyncio - async def test_get_orientation(self, movement_sensor: MovementSensor): - value = await movement_sensor.get_orientation() + async def test_get_orientation(self, movement_sensor: MockMovementSensor): + assert movement_sensor.extra is None + value = await movement_sensor.get_orientation(extra=EXTRA_PARAMS) assert value == ORIENTATION + assert movement_sensor.extra == EXTRA_PARAMS - @pytest.mark.asyncio - async def test_get_properties(self, movement_sensor: MovementSensor): - value = await movement_sensor.get_properties() + async def test_get_properties(self, movement_sensor: MockMovementSensor): + assert movement_sensor.extra is None + value = await movement_sensor.get_properties(extra=EXTRA_PARAMS) assert value == PROPERTIES + assert movement_sensor.extra == EXTRA_PARAMS + + async def test_get_accuracy(self, movement_sensor: MockMovementSensor): + assert movement_sensor.extra is None + value = await movement_sensor.get_accuracy(extra=EXTRA_PARAMS) + assert value.accuracy == pytest.approx(ACCURACY.accuracy) + assert value.position_hdop == pytest.approx(ACCURACY.position_hdop) + assert value.position_vdop == pytest.approx(ACCURACY.position_vdop) + assert value.position_nmea_gga_fix == pytest.approx(ACCURACY.position_nmea_gga_fix) + assert value.compass_degrees_error == pytest.approx(ACCURACY.compass_degrees_error) + assert movement_sensor.extra == EXTRA_PARAMS + + async def test_get_readings(self, movement_sensor: MockMovementSensor): + assert movement_sensor.extra is None + readings = await movement_sensor.get_readings(extra=EXTRA_PARAMS) + assert readings == READINGS + assert movement_sensor.extra == EXTRA_PARAMS + + async def test_timeout(self, movement_sensor: MockMovementSensor): + assert movement_sensor.timeout is None + + await movement_sensor.get_position(timeout=1.23) + assert movement_sensor.timeout == loose_approx(1.23) + + await movement_sensor.get_linear_velocity(timeout=2.34) + assert movement_sensor.timeout == loose_approx(2.34) + + await movement_sensor.get_angular_velocity(timeout=3.45) + assert movement_sensor.timeout == loose_approx(3.45) + + await movement_sensor.get_linear_acceleration(timeout=9.01) + assert movement_sensor.timeout == loose_approx(9.01) + + await movement_sensor.get_compass_heading(timeout=4.56) + assert movement_sensor.timeout == loose_approx(4.56) + + await movement_sensor.get_orientation(timeout=5.67) + assert movement_sensor.timeout == loose_approx(5.67) + + await movement_sensor.get_properties(timeout=6.78) + assert movement_sensor.timeout == loose_approx(6.78) + + await movement_sensor.get_accuracy(timeout=7.89) + assert movement_sensor.timeout == loose_approx(7.89) + + await movement_sensor.get_readings(timeout=8.90) + assert movement_sensor.timeout == loose_approx(8.90) - @pytest.mark.asyncio - async def test_get_accuracy(self, movement_sensor: MovementSensor): - value = await movement_sensor.get_accuracy() - assert value == ACCURACY - - @pytest.mark.asyncio - async def test_get_readings(self, movement_sensor: MovementSensor): - value = await movement_sensor.get_readings() - assert value == { - "position": COORDINATE, - "altitude": ALTITUDE, - "linear_velocity": LINEAR_VELOCITY, - "angular_velocity": ANGULAR_VELOCITY, - "compass": HEADING, - "orientation": ORIENTATION, - } - - @pytest.mark.asyncio async def test_do(self, movement_sensor: MovementSensor): - with pytest.raises(NotImplementedError): - await movement_sensor.do_command({"command": "args"}) + command = {"command": "args"} + resp = await movement_sensor.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self, movement_sensor: MockMovementSensor): + geometries = await movement_sensor.get_geometries() + assert geometries == GEOMETRIES class TestService: - @pytest.mark.asyncio - async def test_get_position(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_position(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorServiceStub(channel) - request = GetPositionRequest(name=movement_sensor.name) - response: GetPositionResponse = await client.GetPosition(request) + request = GetPositionRequest(name=movement_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert movement_sensor.extra is None + response: GetPositionResponse = await client.GetPosition(request, timeout=1.23) assert response.coordinate == COORDINATE - assert response.altitude_mm == ALTITUDE + assert response.altitude_m == ALTITUDE + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(1.23) - @pytest.mark.asyncio - async def test_get_linear_velocity(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_linear_velocity(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorServiceStub(channel) - request = GetLinearVelocityRequest(name=movement_sensor.name) - response: GetLinearVelocityResponse = await client.GetLinearVelocity(request) + request = GetLinearVelocityRequest(name=movement_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert movement_sensor.extra is None + response: GetLinearVelocityResponse = await client.GetLinearVelocity(request, timeout=2.34) assert response.linear_velocity == LINEAR_VELOCITY + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(2.34) - @pytest.mark.asyncio - async def test_get_angular_velocity(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_angular_velocity(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorServiceStub(channel) - request = GetAngularVelocityRequest(name=movement_sensor.name) - response: GetAngularVelocityResponse = await client.GetAngularVelocity(request) + request = GetAngularVelocityRequest(name=movement_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert movement_sensor.extra is None + response: GetAngularVelocityResponse = await client.GetAngularVelocity(request, timeout=3.45) assert response.angular_velocity == ANGULAR_VELOCITY + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(3.45) - @pytest.mark.asyncio - async def test_get_compass_heading(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_linear_acceleration(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorServiceStub(channel) - request = GetCompassHeadingRequest(name=movement_sensor.name) - response: GetCompassHeadingResponse = await client.GetCompassHeading(request) + request = GetLinearAccelerationRequest(name=movement_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert movement_sensor.extra is None + response: GetLinearAccelerationResponse = await client.GetLinearAcceleration(request, timeout=2.34) + assert response.linear_acceleration == LINEAR_ACCELERATION + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(2.34) + + async def test_get_compass_heading(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): + async with ChannelFor([service]) as channel: + client = MovementSensorServiceStub(channel) + request = GetCompassHeadingRequest(name=movement_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + response: GetCompassHeadingResponse = await client.GetCompassHeading(request, timeout=4.56) assert response.value == HEADING + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(4.56) - @pytest.mark.asyncio - async def test_get_orientation(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_orientation(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorServiceStub(channel) - request = GetOrientationRequest(name=movement_sensor.name) - response: GetOrientationResponse = await client.GetOrientation(request) + request = GetOrientationRequest(name=movement_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert movement_sensor.extra is None + response: GetOrientationResponse = await client.GetOrientation(request, timeout=5.67) assert response.orientation == ORIENTATION + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(5.67) + + async def test_get_properties(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): + async with ChannelFor([service]) as channel: + client = MovementSensorServiceStub(channel) + request = GetPropertiesRequest(name=movement_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + response: GetPropertiesResponse = await client.GetProperties(request, timeout=6.78) + assert MovementSensor.Properties.from_proto(response) == PROPERTIES + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(6.78) - @pytest.mark.asyncio - async def test_get_properties(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_accuracy(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): + async with ChannelFor([service]) as channel: + client = MovementSensorServiceStub(channel) + request = GetAccuracyRequest(name=movement_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert movement_sensor.extra is None + response: GetAccuracyResponse = await client.GetAccuracy(request, timeout=7.89) + assert response.accuracy == pytest.approx(ACCURACY.accuracy) + assert response.position_hdop == pytest.approx(ACCURACY.position_hdop) + assert response.position_vdop == pytest.approx(ACCURACY.position_vdop) + assert response.position_nmea_gga_fix == pytest.approx(ACCURACY.position_nmea_gga_fix) + assert response.compass_degrees_error == pytest.approx(ACCURACY.compass_degrees_error) + assert movement_sensor.timeout == loose_approx(7.89) + + async def test_get_readings(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): + async with ChannelFor([service]) as channel: + client = MovementSensorServiceStub(channel) + request = GetReadingsRequest(name=movement_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert movement_sensor.extra is None + response: GetReadingsResponse = await client.GetReadings(request, timeout=8.90) + assert sensor_readings_value_to_native(response.readings) == READINGS + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(8.90) + + async def test_do(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorServiceStub(channel) - request = GetPropertiesRequest(name=movement_sensor.name) - response: GetPropertiesResponse = await client.GetProperties(request) - assert response == PROPERTIES + command = {"command": "args"} + request = DoCommandRequest(name=movement_sensor.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} - @pytest.mark.asyncio - async def test_get_accuracy(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_geometries(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorServiceStub(channel) - request = GetAccuracyRequest(name=movement_sensor.name) - response: GetAccuracyResponse = await client.GetAccuracy(request) - assert response.accuracy_mm == pytest.approx(ACCURACY) + request = GetGeometriesRequest(name=movement_sensor.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES class TestClient: - @pytest.mark.asyncio - async def test_get_position(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_position(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorClient(movement_sensor.name, channel) - (coord, alt) = await client.get_position() + assert movement_sensor.extra is None + (coord, alt) = await client.get_position(extra=EXTRA_PARAMS, timeout=1.45) assert coord == COORDINATE assert alt == ALTITUDE + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(1.45) - @pytest.mark.asyncio - async def test_get_linear_velocity(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_linear_velocity(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorClient(movement_sensor.name, channel) - value = await client.get_linear_velocity() + assert movement_sensor.extra is None + value = await client.get_linear_velocity(extra=EXTRA_PARAMS, timeout=2.34) assert value == LINEAR_VELOCITY + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(2.34) - @pytest.mark.asyncio - async def test_get_angular_velocity(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_angular_velocity(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorClient(movement_sensor.name, channel) - value = await client.get_angular_velocity() + assert movement_sensor.extra is None + value = await client.get_angular_velocity(extra=EXTRA_PARAMS, timeout=3.45) assert value == ANGULAR_VELOCITY + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(3.45) - @pytest.mark.asyncio - async def test_get_compass_heading(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_linear_acceleration(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorClient(movement_sensor.name, channel) - value = await client.get_compass_heading() + assert movement_sensor.extra is None + value = await client.get_linear_acceleration(extra=EXTRA_PARAMS, timeout=2.34) + assert value == LINEAR_ACCELERATION + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(2.34) + + async def test_get_compass_heading(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): + async with ChannelFor([service]) as channel: + client = MovementSensorClient(movement_sensor.name, channel) + assert movement_sensor.extra is None + value = await client.get_compass_heading(extra=EXTRA_PARAMS, timeout=4.56) assert value == HEADING + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(4.56) - @pytest.mark.asyncio - async def test_get_orientation(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_orientation(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorClient(movement_sensor.name, channel) - value = await client.get_orientation() + assert movement_sensor.extra is None + value = await client.get_orientation(extra=EXTRA_PARAMS, timeout=5.67) assert value == ORIENTATION + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(5.67) - @pytest.mark.asyncio - async def test_get_properties(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_properties(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorClient(movement_sensor.name, channel) - value = await client.get_properties() + assert movement_sensor.extra is None + value = await client.get_properties(extra=EXTRA_PARAMS, timeout=6.78) assert value == PROPERTIES + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(6.78) - @pytest.mark.asyncio - async def test_get_accuracy(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_get_accuracy(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorClient(movement_sensor.name, channel) - value = await client.get_accuracy() - assert value == pytest.approx(ACCURACY) + assert movement_sensor.extra is None + value = await client.get_accuracy(extra=EXTRA_PARAMS, timeout=7.89) + assert value.accuracy == pytest.approx(ACCURACY.accuracy) + assert value.position_hdop == pytest.approx(ACCURACY.position_hdop) + assert value.position_vdop == pytest.approx(ACCURACY.position_vdop) + assert value.position_nmea_gga_fix == pytest.approx(ACCURACY.position_nmea_gga_fix) + assert value.compass_degrees_error == pytest.approx(ACCURACY.compass_degrees_error) + assert movement_sensor.timeout == loose_approx(7.89) + + async def test_get_readings(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): + async with ChannelFor([service]) as channel: + client = MovementSensorClient(movement_sensor.name, channel) + assert movement_sensor.extra is None + value = await client.get_readings(extra=EXTRA_PARAMS, timeout=2.34) + assert value == READINGS + assert movement_sensor.extra == EXTRA_PARAMS + assert movement_sensor.timeout == loose_approx(2.34) - @pytest.mark.asyncio - async def test_get_readings(self, movement_sensor: MovementSensor, service: MovementSensorService): + async def test_do(self, movement_sensor: MovementSensor, service: MovementSensorRPCService): async with ChannelFor([service]) as channel: client = MovementSensorClient(movement_sensor.name, channel) - value = await client.get_readings() - assert value == { - "position": COORDINATE, - "altitude": ALTITUDE, - "linear_velocity": LINEAR_VELOCITY, - "angular_velocity": ANGULAR_VELOCITY, - "compass": HEADING, - "orientation": ORIENTATION, - } - - @pytest.mark.asyncio - async def test_do(self, movement_sensor: MovementSensor, service: MovementSensorService, generic_service: GenericService): - async with ChannelFor([service, generic_service]) as channel: + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self, movement_sensor: MockMovementSensor, service: MovementSensorRPCService): + async with ChannelFor([service]) as channel: client = MovementSensorClient(movement_sensor.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_navigation.py b/tests/test_navigation.py new file mode 100644 index 000000000..992009170 --- /dev/null +++ b/tests/test_navigation.py @@ -0,0 +1,231 @@ +from grpclib.testing import ChannelFor + +from viam.proto.common import DoCommandRequest, DoCommandResponse, GeoPoint +from viam.proto.service.navigation import ( + AddWaypointRequest, + GetLocationRequest, + GetLocationResponse, + GetModeRequest, + GetModeResponse, + GetObstaclesRequest, + GetObstaclesResponse, + GetPropertiesRequest, + GetPropertiesResponse, + GetWaypointsRequest, + GetWaypointsResponse, + MapType, + Mode, + NavigationServiceStub, + RemoveWaypointRequest, + SetModeRequest, +) +from viam.resource.manager import ResourceManager +from viam.services.navigation import NavigationClient, NavigationRPCService +from viam.utils import dict_to_struct, struct_to_dict + +from .mocks.services import MockNavigation + + +class TestNavigationService: + name = "navigation" + navigation = MockNavigation(name="navigation") + + async def test_get_location(self): + result = await self.navigation.get_location() + assert result == MockNavigation.LOCATION + + async def test_get_obstacles(self): + result = await self.navigation.get_obstacles() + assert result == MockNavigation.OBSTACLES + + async def test_get_waypoints(self): + result = await self.navigation.get_waypoints() + assert result == self.navigation.WAYPOINTS + + async def test_add_waypoint(self): + assert self.navigation.add_waypoints == [] + point = GeoPoint(latitude=100.0, longitude=200.0) + await self.navigation.add_waypoint(point) + assert self.navigation.add_waypoints == [point] + + async def test_remove_waypoint(self): + assert self.navigation.remove_waypoints == [] + id = "xyz" + await self.navigation.remove_waypoint(id) + assert self.navigation.remove_waypoints == [id] + + async def test_get_mode(self): + result = await self.navigation.get_mode() + assert result == Mode.MODE_UNSPECIFIED + + async def test_set_mode(self): + assert self.navigation.mode == Mode.MODE_UNSPECIFIED + mode = Mode.MODE_MANUAL + await self.navigation.set_mode(mode) + assert self.navigation.mode == mode + + async def test_get_properties(self): + assert self.navigation.map_type == MapType.MAP_TYPE_UNSPECIFIED + result = await self.navigation.get_properties() + assert self.navigation.map_type == result + + async def test_do(self): + command = {"command": "args"} + result = await self.navigation.do_command(command) + assert result == {"command": command} + + +class TestService: + @classmethod + def setup_class(cls): + cls.name = "navigation" + cls.navigation = MockNavigation(name=cls.name) + cls.manager = ResourceManager([cls.navigation]) + cls.service = NavigationRPCService(cls.manager) + + async def test_get_location(self): + async with ChannelFor([self.service]) as channel: + client = NavigationServiceStub(channel) + request = GetLocationRequest(name=self.name) + response: GetLocationResponse = await client.GetLocation(request) + result = response.location + assert result == self.navigation.LOCATION + + async def test_get_obstacles(self): + async with ChannelFor([self.service]) as channel: + client = NavigationServiceStub(channel) + request = GetObstaclesRequest(name=self.name) + response: GetObstaclesResponse = await client.GetObstacles(request) + result = response.obstacles + assert result == self.navigation.OBSTACLES + + async def test_get_waypoints(self): + async with ChannelFor([self.service]) as channel: + client = NavigationServiceStub(channel) + request = GetWaypointsRequest(name=self.name) + response: GetWaypointsResponse = await client.GetWaypoints(request) + result = response.waypoints + assert result == self.navigation.WAYPOINTS + + async def test_add_waypoint(self): + async with ChannelFor([self.service]) as channel: + assert self.navigation.add_waypoints == [] + client = NavigationServiceStub(channel) + point = GeoPoint(latitude=100.0, longitude=200.0) + request = AddWaypointRequest(name=self.name, location=point) + await client.AddWaypoint(request) + assert self.navigation.add_waypoints == [point] + + async def test_remove_waypoint(self): + async with ChannelFor([self.service]) as channel: + assert self.navigation.remove_waypoints == [] + client = NavigationServiceStub(channel) + id = "xyz" + request = RemoveWaypointRequest(name=self.name, id=id) + await client.RemoveWaypoint(request) + assert self.navigation.remove_waypoints == [id] + + async def test_get_mode(self): + async with ChannelFor([self.service]) as channel: + client = NavigationServiceStub(channel) + request = GetModeRequest(name=self.name) + response: GetModeResponse = await client.GetMode(request) + result = response.mode + assert result == Mode.MODE_UNSPECIFIED + + async def test_set_mode(self): + async with ChannelFor([self.service]) as channel: + assert self.navigation.mode == Mode.MODE_UNSPECIFIED + client = NavigationServiceStub(channel) + mode = Mode.MODE_MANUAL + request = SetModeRequest(name=self.name, mode=mode) + await client.SetMode(request) + assert self.navigation.mode == Mode.MODE_MANUAL + + async def test_get_properties(self): + async with ChannelFor([self.service]) as channel: + client = NavigationServiceStub(channel) + request = GetPropertiesRequest(name=self.name) + response: GetPropertiesResponse = await client.GetProperties(request) + result = response.map_type + assert result == MapType.MAP_TYPE_UNSPECIFIED + + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = NavigationServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + +class TestClient: + @classmethod + def setup_class(cls): + cls.name = "navigation" + cls.navigation = MockNavigation(name=cls.name) + cls.manager = ResourceManager([cls.navigation]) + cls.service = NavigationRPCService(cls.manager) + + async def test_get_location(self): + async with ChannelFor([self.service]) as channel: + client = NavigationClient(self.name, channel) + result = await client.get_location() + assert result == self.navigation.LOCATION + + async def test_get_obstacles(self): + async with ChannelFor([self.service]) as channel: + client = NavigationClient(self.name, channel) + result = await client.get_obstacles() + assert result == self.navigation.OBSTACLES + + async def test_get_waypoints(self): + async with ChannelFor([self.service]) as channel: + client = NavigationClient(self.name, channel) + result = await client.get_waypoints() + assert result == self.navigation.WAYPOINTS + + async def test_add_waypoint(self): + async with ChannelFor([self.service]) as channel: + assert self.navigation.add_waypoints == [] + client = NavigationClient(self.name, channel) + point = GeoPoint(latitude=100.0, longitude=200.0) + await client.add_waypoint(point) + assert self.navigation.add_waypoints == [point] + + async def test_remove_waypoint(self): + async with ChannelFor([self.service]) as channel: + assert self.navigation.remove_waypoints == [] + client = NavigationClient(self.name, channel) + id = "xyz" + await client.remove_waypoint(id) + assert self.navigation.remove_waypoints == [id] + + async def test_get_mode(self): + async with ChannelFor([self.service]) as channel: + client = NavigationClient(self.name, channel) + result = await client.get_mode() + assert result == Mode.MODE_UNSPECIFIED + + async def test_set_mode(self): + async with ChannelFor([self.service]) as channel: + assert self.navigation.mode == Mode.MODE_UNSPECIFIED + client = NavigationClient(self.name, channel) + mode = Mode.MODE_MANUAL + await client.set_mode(mode) + assert self.navigation.mode == Mode.MODE_MANUAL + + async def test_get_properties(self): + async with ChannelFor([self.service]) as channel: + assert self.navigation.map_type == MapType.MAP_TYPE_UNSPECIFIED + client = NavigationClient(self.name, channel) + result = await client.get_properties() + assert self.navigation.map_type == result + + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = NavigationClient(self.name, channel) + command = {"command": "args"} + response = await client.do_command(command) + assert response == {"command": command} diff --git a/tests/test_operations.py b/tests/test_operations.py new file mode 100644 index 000000000..69ace63c2 --- /dev/null +++ b/tests/test_operations.py @@ -0,0 +1,74 @@ +import asyncio +import time +from uuid import UUID + +import pytest + +from viam.operations import METADATA_KEY, Operation, run_with_operation + + +async def test_is_cancelled(): + event = asyncio.Event() + opid = Operation("test-opid-0", event) + + # Test start condition + assert await opid.is_cancelled() is False + + # Test that setting an event will cancel + event.set() + assert await opid.is_cancelled() is True + + # Test that once set, cancelled is not unset + event.clear() + assert await opid.is_cancelled() is True + + +async def test_wrapper(): + class TestWrapperClass: + long_running_task_cancelled = False + + @run_with_operation + async def long_running(self, **kwargs) -> bool: + operation: Operation = kwargs.get(Operation.ARG_NAME, Operation._noop()) + for _ in range(5): + time.sleep(0.01) + if await operation.is_cancelled(): + self.long_running_task_cancelled = True + return self.long_running_task_cancelled + self.long_running_task_cancelled = False + return self.long_running_task_cancelled + + test_obj = TestWrapperClass() + + # Test not cancelled + result = await test_obj.long_running() + assert result is False + + # Test cancelled + with pytest.raises(asyncio.CancelledError): + task = asyncio.create_task(test_obj.long_running()) + asyncio.get_event_loop().call_later(0.02, task.cancel) + result = await task + + assert test_obj.long_running_task_cancelled is True + + # Test timed out + test_obj.long_running_task_cancelled = False + assert test_obj.long_running_task_cancelled is False + assert await asyncio.create_task(test_obj.long_running(timeout=0.02)) is True + + +async def test_wrapper_with_metadata(): + test_metadata_opid = "11111111-1111-1111-1111-111111111111" + + class TestWrapperClass: + @run_with_operation + async def run(self, **kwargs) -> bool: + operation: Operation = kwargs.get(Operation.ARG_NAME, Operation._noop()) + assert operation.id == UUID(test_metadata_opid) + return False + + test_obj = TestWrapperClass() + metadata = {METADATA_KEY: test_metadata_opid} + result = await test_obj.run(metadata=metadata) + assert result is False diff --git a/tests/test_pose_tracker.py b/tests/test_pose_tracker.py index 593e13ce8..b7de24d2c 100644 --- a/tests/test_pose_tracker.py +++ b/tests/test_pose_tracker.py @@ -1,18 +1,14 @@ -import pytest from grpclib.testing import ChannelFor -from viam.components.generic.service import GenericService from viam.components.pose_tracker import PoseTrackerClient -from viam.components.pose_tracker.service import PoseTrackerService -from viam.components.resource_manager import ResourceManager -from viam.proto.common import Pose, PoseInFrame -from viam.proto.component.posetracker import ( - GetPosesRequest, - GetPosesResponse, - PoseTrackerServiceStub, -) +from viam.components.pose_tracker.service import PoseTrackerRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse, Pose, PoseInFrame +from viam.proto.component.posetracker import GetPosesRequest, GetPosesResponse, PoseTrackerServiceStub +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict -from .mocks.components import MockPose, MockPoseTracker +from . import loose_approx +from .mocks.components import GEOMETRIES, MockPose, MockPoseTracker POSES = [ MockPose(X=1, Y=2, Z=3, o_X=2, o_Y=3, o_Z=4, theta=20), @@ -21,56 +17,90 @@ class TestPoseTracker: - mock_pose_tracker = MockPoseTracker(name="pt", poses=POSES) - @pytest.mark.asyncio async def test_get_poses(self): - received_poses = await self.mock_pose_tracker.get_poses([]) + assert self.mock_pose_tracker.extra is None + received_poses = await self.mock_pose_tracker.get_poses([], extra={"foo": "get_poses"}, timeout=1.23) assert received_poses["0"] == PoseInFrame(reference_frame="0", pose=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)) assert received_poses["1"] == PoseInFrame(reference_frame="1", pose=Pose(x=5, y=5, z=5, o_x=5, o_y=3, o_z=4, theta=30)) + assert self.mock_pose_tracker.timeout == loose_approx(1.23) + assert self.mock_pose_tracker.extra == {"foo": "get_poses"} - @pytest.mark.asyncio async def test_do(self): - with pytest.raises(NotImplementedError): - await self.mock_pose_tracker.do_command({"command": "args"}) + command = {"command": "args"} + resp = await self.mock_pose_tracker.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self): + geometries = await self.mock_pose_tracker.get_geometries() + assert geometries == GEOMETRIES class TestService: - name = "pose_tracker" - pose_tracker = MockPoseTracker(name=name, poses=POSES) - manager = ResourceManager([pose_tracker]) - service = PoseTrackerService(manager) + @classmethod + def setup_class(cls): + cls.name = "pose_tracker" + cls.pose_tracker = MockPoseTracker(name=cls.name, poses=POSES) + cls.manager = ResourceManager([cls.pose_tracker]) + cls.service = PoseTrackerRPCService(cls.manager) - @pytest.mark.asyncio async def test_get_poses(self): + assert self.pose_tracker.extra is None async with ChannelFor([self.service]) as channel: client = PoseTrackerServiceStub(channel) - request = GetPosesRequest(name=self.name) - response: GetPosesResponse = await client.GetPoses(request) + request = GetPosesRequest(name=self.name, extra=dict_to_struct({"foo": "get_poses"})) + response: GetPosesResponse = await client.GetPoses(request, timeout=2.34) received_poses = response.body_poses assert received_poses["0"] == PoseInFrame(reference_frame="0", pose=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)) assert received_poses["1"] == PoseInFrame(reference_frame="1", pose=Pose(x=5, y=5, z=5, o_x=5, o_y=3, o_z=4, theta=30)) + assert self.pose_tracker.timeout == loose_approx(2.34) + assert self.pose_tracker.extra == {"foo": "get_poses"} + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = PoseTrackerServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + async def test_get_geometries(self): + async with ChannelFor([self.service]) as channel: + client = PoseTrackerServiceStub(channel) + request = GetGeometriesRequest(name=self.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES -class TestClient: - name = "pose_tracker" - pose_tracker = MockPoseTracker(name=name, poses=POSES) - manager = ResourceManager([pose_tracker]) - service = PoseTrackerService(manager) +class TestClient: + @classmethod + def setup_class(cls): + cls.name = "pose_tracker" + cls.pose_tracker = MockPoseTracker(name=cls.name, poses=POSES) + cls.manager = ResourceManager([cls.pose_tracker]) + cls.service = PoseTrackerRPCService(cls.manager) - @pytest.mark.asyncio async def test_get_poses(self): + assert self.pose_tracker.extra is None async with ChannelFor([self.service]) as channel: client = PoseTrackerClient(self.name, channel) - received_poses = await client.get_poses([]) + received_poses = await client.get_poses([], extra={"foo": "get_poses"}, timeout=3.45) assert received_poses["0"] == PoseInFrame(reference_frame="0", pose=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)) assert received_poses["1"] == PoseInFrame(reference_frame="1", pose=Pose(x=5, y=5, z=5, o_x=5, o_y=3, o_z=4, theta=30)) + assert self.pose_tracker.timeout == loose_approx(3.45) + assert self.pose_tracker.extra == {"foo": "get_poses"} - @pytest.mark.asyncio async def test_do(self): - async with ChannelFor([self.service, GenericService(self.manager)]) as channel: + async with ChannelFor([self.service]) as channel: + client = PoseTrackerClient(self.name, channel) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self): + async with ChannelFor([self.service]) as channel: client = PoseTrackerClient(self.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_power_sensor.py b/tests/test_power_sensor.py new file mode 100644 index 000000000..b52688ef3 --- /dev/null +++ b/tests/test_power_sensor.py @@ -0,0 +1,192 @@ +import pytest +from grpclib.testing import ChannelFor + +from viam.components.generic.service import GenericRPCService +from viam.components.power_sensor import PowerSensor, PowerSensorClient, PowerSensorRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetReadingsRequest, GetReadingsResponse +from viam.proto.component.powersensor import ( + GetCurrentRequest, + GetCurrentResponse, + GetPowerRequest, + GetPowerResponse, + GetVoltageRequest, + GetVoltageResponse, + PowerSensorServiceStub, +) +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, sensor_readings_value_to_native, struct_to_dict + +from . import loose_approx +from .mocks.components import MockPowerSensor + +VOLTS = 3.2 +AMPERES = 2.0 +WATTS = 1.0 +IS_AC = True +READINGS = {"a": 1, "b": 2, "c": 3} +EXTRA_PARAMS = {"foo": "bar", "baz": [1, 2, 3]} + + +@pytest.fixture(scope="function") +def power_sensor() -> PowerSensor: + return MockPowerSensor("power_sensor", VOLTS, AMPERES, IS_AC, WATTS, READINGS) + + +@pytest.fixture(scope="function") +def service(power_sensor: PowerSensor) -> PowerSensorRPCService: + manager = ResourceManager([power_sensor]) + return PowerSensorRPCService(manager) + + +@pytest.fixture(scope="function") +def generic_service(power_sensor: PowerSensor) -> GenericRPCService: + manager = ResourceManager([power_sensor]) + return GenericRPCService(manager) + + +class TestPowerSensor: + async def test_get_voltage(self, power_sensor: MockPowerSensor): + assert power_sensor.extra is None + (volts, is_ac) = await power_sensor.get_voltage(extra=EXTRA_PARAMS) + assert volts == VOLTS + assert is_ac == IS_AC + assert power_sensor.extra == EXTRA_PARAMS + + async def test_get_current(self, power_sensor: MockPowerSensor): + assert power_sensor.extra is None + (amperes, is_ac) = await power_sensor.get_current(extra=EXTRA_PARAMS) + assert amperes == AMPERES + assert is_ac == IS_AC + assert power_sensor.extra == EXTRA_PARAMS + + async def test_get_power(self, power_sensor: MockPowerSensor): + assert power_sensor.extra is None + watts = await power_sensor.get_power(extra=EXTRA_PARAMS) + assert watts == WATTS + assert power_sensor.extra == EXTRA_PARAMS + + async def test_get_readings(self, power_sensor: MockPowerSensor): + assert power_sensor.extra is None + value = await power_sensor.get_readings(extra=EXTRA_PARAMS) + assert value == READINGS + assert power_sensor.extra == EXTRA_PARAMS + + async def test_timeout(self, power_sensor: MockPowerSensor): + assert power_sensor.timeout is None + + await power_sensor.get_voltage(timeout=8.90) + assert power_sensor.timeout == loose_approx(8.90) + + await power_sensor.get_current(timeout=2.34) + assert power_sensor.timeout == loose_approx(2.34) + + await power_sensor.get_power(timeout=3.45) + assert power_sensor.timeout == loose_approx(3.45) + + await power_sensor.get_readings(timeout=8.90) + assert power_sensor.timeout == loose_approx(8.90) + + async def test_do(self, power_sensor: PowerSensor): + command = {"command": "args"} + resp = await power_sensor.do_command(command) + assert resp == {"command": command} + + +class TestService: + async def test_get_voltage(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): + async with ChannelFor([service]) as channel: + client = PowerSensorServiceStub(channel) + request = GetVoltageRequest(name=power_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert power_sensor.extra is None + response: GetVoltageResponse = await client.GetVoltage(request, timeout=8.90) + assert response.volts == VOLTS + assert response.is_ac == IS_AC + assert power_sensor.extra == EXTRA_PARAMS + assert power_sensor.timeout == loose_approx(8.90) + + async def test_get_current(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): + async with ChannelFor([service]) as channel: + client = PowerSensorServiceStub(channel) + request = GetCurrentRequest(name=power_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert power_sensor.extra is None + response: GetCurrentResponse = await client.GetCurrent(request, timeout=8.90) + assert response.amperes == AMPERES + assert response.is_ac == IS_AC + assert power_sensor.extra == EXTRA_PARAMS + assert power_sensor.timeout == loose_approx(8.90) + + async def test_get_power(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): + async with ChannelFor([service]) as channel: + client = PowerSensorServiceStub(channel) + request = GetPowerRequest(name=power_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert power_sensor.extra is None + response: GetPowerResponse = await client.GetPower(request, timeout=8.90) + assert response.watts == WATTS + assert power_sensor.extra == EXTRA_PARAMS + assert power_sensor.timeout == loose_approx(8.90) + + async def test_get_readings(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): + async with ChannelFor([service]) as channel: + client = PowerSensorServiceStub(channel) + request = GetReadingsRequest(name=power_sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert power_sensor.extra is None + response: GetReadingsResponse = await client.GetReadings(request, timeout=8.90) + assert sensor_readings_value_to_native(response.readings) == READINGS + assert power_sensor.extra == EXTRA_PARAMS + assert power_sensor.timeout == loose_approx(8.90) + + async def test_do(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): + async with ChannelFor([service]) as channel: + client = PowerSensorServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=power_sensor.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + +class TestClient: + async def test_get_voltage(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): + async with ChannelFor([service]) as channel: + client = PowerSensorClient(power_sensor.name, channel) + assert power_sensor.extra is None + (vol, is_ac) = await client.get_voltage(extra=EXTRA_PARAMS, timeout=1.45) + assert vol == VOLTS + assert is_ac == IS_AC + assert power_sensor.extra == EXTRA_PARAMS + assert power_sensor.timeout == loose_approx(1.45) + + async def test_get_current(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): + async with ChannelFor([service]) as channel: + client = PowerSensorClient(power_sensor.name, channel) + assert power_sensor.extra is None + (vol, is_ac) = await client.get_voltage(extra=EXTRA_PARAMS, timeout=1.45) + assert vol == VOLTS + assert is_ac == IS_AC + assert power_sensor.extra == EXTRA_PARAMS + assert power_sensor.timeout == loose_approx(1.45) + + async def test_get_power(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): + async with ChannelFor([service]) as channel: + client = PowerSensorClient(power_sensor.name, channel) + assert power_sensor.extra is None + watts = await client.get_power(extra=EXTRA_PARAMS, timeout=1.45) + assert watts == WATTS + assert power_sensor.extra == EXTRA_PARAMS + assert power_sensor.timeout == loose_approx(1.45) + + async def test_get_readings(self, power_sensor: MockPowerSensor, service: PowerSensorRPCService): + async with ChannelFor([service]) as channel: + client = PowerSensorClient(power_sensor.name, channel) + assert power_sensor.extra is None + value = await client.get_readings(extra=EXTRA_PARAMS, timeout=2.34) + assert value == READINGS + assert power_sensor.extra == EXTRA_PARAMS + assert power_sensor.timeout == loose_approx(2.34) + + async def test_do(self, power_sensor: PowerSensor, service: PowerSensorRPCService): + async with ChannelFor([service]) as channel: + client = PowerSensorClient(power_sensor.name, channel) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} diff --git a/tests/test_provisioning_client.py b/tests/test_provisioning_client.py new file mode 100644 index 000000000..ebc8cb27a --- /dev/null +++ b/tests/test_provisioning_client.py @@ -0,0 +1,74 @@ +import pytest +from grpclib.testing import ChannelFor + +from viam.app.provisioning_client import ProvisioningClient +from viam.proto.provisioning import CloudConfig, GetSmartMachineStatusResponse, NetworkInfo, ProvisioningInfo + +from .mocks.services import MockProvisioning + +ID = "id" +MODEL = "model" +MANUFACTURER = "acme" +PROVISIONING_INFO = ProvisioningInfo(fragment_id=ID, model=MODEL, manufacturer=MANUFACTURER) +HAS_CREDENTIALS = True +IS_ONLINE = True +NETWORK_TYPE = "type" +SSID = "ssid" +ERROR = "error" +ERRORS = [ERROR] +PSK = "psk" +SECRET = "secret" +APP_ADDRESS = "address" +NETWORK_INFO_LATEST = NetworkInfo( + type=NETWORK_TYPE, + ssid=SSID, + security="security", + signal=12, + connected=IS_ONLINE, + last_error=ERROR, +) +NETWORK_INFO = [NETWORK_INFO_LATEST] +SMART_MACHINE_STATUS_RESPONSE = GetSmartMachineStatusResponse( + provisioning_info=PROVISIONING_INFO, + has_smart_machine_credentials=HAS_CREDENTIALS, + is_online=IS_ONLINE, + latest_connection_attempt=NETWORK_INFO_LATEST, + errors=ERRORS, +) +CLOUD_CONFIG = CloudConfig(id=ID, secret=SECRET, app_address=APP_ADDRESS) + +AUTH_TOKEN = "auth_token" +PROVISIONING_SERVICE_METADATA = {"authorization": f"Bearer {AUTH_TOKEN}"} + + +@pytest.fixture(scope="function") +def service() -> MockProvisioning: + return MockProvisioning(smart_machine_status=SMART_MACHINE_STATUS_RESPONSE, network_info=NETWORK_INFO) + + +class TestClient: + async def test_get_network_list(self, service: MockProvisioning): + async with ChannelFor([service]) as channel: + client = ProvisioningClient(channel, PROVISIONING_SERVICE_METADATA) + network_info = await client.get_network_list() + assert network_info == NETWORK_INFO + + async def test_get_smart_machine_status(self, service: MockProvisioning): + async with ChannelFor([service]) as channel: + client = ProvisioningClient(channel, PROVISIONING_SERVICE_METADATA) + smart_machine_status = await client.get_smart_machine_status() + assert smart_machine_status == SMART_MACHINE_STATUS_RESPONSE + + async def test_set_network_credentials(self, service: MockProvisioning): + async with ChannelFor([service]) as channel: + client = ProvisioningClient(channel, PROVISIONING_SERVICE_METADATA) + await client.set_network_credentials(network_type=NETWORK_TYPE, ssid=SSID, psk=PSK) + assert service.network_type == NETWORK_TYPE + assert service.ssid == SSID + assert service.psk == PSK + + async def test_set_smart_machine_credentials(self, service: MockProvisioning): + async with ChannelFor([service]) as channel: + client = ProvisioningClient(channel, PROVISIONING_SERVICE_METADATA) + await client.set_smart_machine_credentials(cloud_config=CLOUD_CONFIG) + assert service.cloud_config == CLOUD_CONFIG diff --git a/tests/test_registry.py b/tests/test_registry.py index a711c7e04..3b36d8bb8 100644 --- a/tests/test_registry.py +++ b/tests/test_registry.py @@ -1,17 +1,19 @@ import pytest from grpclib.client import Channel +from viam.components.arm import Arm from viam.components.component_base import ComponentBase -from viam.components.service_base import ComponentServiceBase -from viam.errors import ComponentNotFoundError, DuplicateComponentError -from viam.registry import ComponentRegistration, Registry +from viam.errors import DuplicateResourceError, ResourceNotFoundError +from viam.resource.registry import Registry, ResourceRegistration +from viam.resource.rpc_service_base import ResourceRPCServiceBase +from viam.resource.types import API, RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, Model, ModelFamily, resource_name_from_string class FakeComponent(ComponentBase): - pass + API = API("fake", "fake", "fake") -class FakeComponentService(ComponentServiceBase[FakeComponent]): +class FakeComponentService(ResourceRPCServiceBase): pass @@ -21,42 +23,121 @@ def __init__(self, name: str, channel: Channel): def test_components_register_themselves_correctly(): - assert "arm" in Registry.REGISTERED_COMPONENTS - assert "base" in Registry.REGISTERED_COMPONENTS - assert "board" in Registry.REGISTERED_COMPONENTS - assert "camera" in Registry.REGISTERED_COMPONENTS - assert "gantry" in Registry.REGISTERED_COMPONENTS - assert "gripper" in Registry.REGISTERED_COMPONENTS - assert "motor" in Registry.REGISTERED_COMPONENTS - assert "movement_sensor" in Registry.REGISTERED_COMPONENTS - assert "pose_tracker" in Registry.REGISTERED_COMPONENTS - assert "sensor" in Registry.REGISTERED_COMPONENTS - assert "servo" in Registry.REGISTERED_COMPONENTS + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "arm") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "audio_input") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "base") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "board") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "camera") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "gantry") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "gripper") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "motor") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "movement_sensor") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "pose_tracker") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "sensor") in Registry.REGISTERED_APIS() + assert API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "servo") in Registry.REGISTERED_APIS() def test_lookup(): - with pytest.raises(ComponentNotFoundError): - Registry.lookup("fake_component") + with pytest.raises(ResourceNotFoundError): + Registry.lookup_api(API("fake", "fake", "fake")) - component = Registry.lookup("arm") - assert component.name == "arm" + component = Registry.lookup_api(API(RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, "arm")) + assert component.resource_type.API == Arm.API def test_registration(): - assert "fake_component" not in Registry.REGISTERED_COMPONENTS + assert FakeComponent.API not in Registry.REGISTERED_APIS() - Registry.register( - ComponentRegistration( - FakeComponent, "fake_component", FakeComponentService, lambda name, channel: FakeComponentClient(name, channel) - ) + Registry.register_api( + ResourceRegistration(FakeComponent, FakeComponentService, lambda name, channel: FakeComponentClient(name, channel)) ) - assert "fake_component" in Registry.REGISTERED_COMPONENTS - component = Registry.lookup("fake_component") + assert FakeComponent.API in Registry.REGISTERED_APIS() + component = Registry.lookup_api(FakeComponent.API) assert component is not None - with pytest.raises(DuplicateComponentError): - Registry.register( - ComponentRegistration( - FakeComponent, "fake_component", FakeComponentService, lambda name, channel: FakeComponentClient(name, channel) - ) + with pytest.raises(DuplicateResourceError): + Registry.register_api( + ResourceRegistration(FakeComponent, FakeComponentService, lambda name, channel: FakeComponentClient(name, channel)) ) + + +def test_api(): + # test fields should always be lowercase + api = API("test", "TEST", "TeSt") + assert api.namespace == "test" + assert api.resource_type != "test" + assert api.resource_subtype != "test" + + # test from resource name + rn = Arm.get_resource_name("test_arm") + api = API.from_resource_name(rn) + assert api == Arm.API + + # test from string + api = API.from_string("TEST:tester:TESTerson") + assert api.namespace != "test" + assert api.resource_type == "tester" + assert api.resource_subtype != "testerson" + + with pytest.raises(ValueError): + API.from_string("this:should:not:work") + + # test str + assert str(api) != "test:tester:testerson" + + +def test_model_family(): + # test fields should always be lowercase + mf = ModelFamily("nameSPACE", "FAMily") + assert mf.namespace != "namespace" + assert mf.family != "family" + + # test str + assert str(mf) != "namespace:family" + + # test default + assert ModelFamily.DEFAULT.namespace == RESOURCE_NAMESPACE_RDK + assert ModelFamily.DEFAULT.family == ModelFamily.DEFAULT_FAMILY_NAME + + +def test_model(): + # test fields should always be lowercase + model = Model(ModelFamily("nameSPACE", "FAMily"), "naME") + assert model.name != "name" + + # test str + assert str(model) != "namespace:family:name" + + # test from_string + model = Model.from_string("f_namespace:f_family:name") + assert model.model_family.namespace == "f_namespace" + assert model.model_family.family == "f_family" + assert model.name == "name" + + # test from string with errors + with pytest.raises(ValueError): + Model.from_string("!nv@lid:1nval!d:name") + + # test from string without errors + model = Model.from_string("!nv@lid:1nval!d:name", ignore_errors=True) + assert model.model_family == ModelFamily("", "") + + +def test_resource_name_from_string(): + # normal + rn = resource_name_from_string("namespace:type:subtype/name") + assert rn.namespace == "namespace" + assert rn.type == "type" + assert rn.subtype == "subtype" + assert rn.name == "name" + + # remote + rn = resource_name_from_string("ns:tp:st/remote:nm") + assert rn.namespace == "ns" + assert rn.type == "tp" + assert rn.subtype == "st" + assert rn.name == "remote:nm" + + # error + with pytest.raises(ValueError): + resource_name_from_string("this:shouldn't:work") diff --git a/tests/test_resource_manager.py b/tests/test_resource_manager.py index 8a135f1e4..2c75a3428 100644 --- a/tests/test_resource_manager.py +++ b/tests/test_resource_manager.py @@ -1,7 +1,9 @@ +from unittest import mock + import pytest -from viam.components.resource_manager import ResourceManager -from viam.errors import ComponentNotFoundError, DuplicateComponentError +from viam.errors import DuplicateResourceError, ResourceNotFoundError +from viam.resource.manager import ResourceManager from .mocks.components import MockArm, MockServo @@ -11,9 +13,9 @@ def test_init(self): servo1 = MockServo(name="servo1") servo2 = MockServo(name="servo2") manager = ResourceManager([servo1, servo2]) - assert len(manager.components) == 2 - assert manager.components["servo1"] == servo1 - assert manager.components["servo2"] == servo2 + assert len(manager.resources) == 2 + assert manager.resources[MockServo.get_resource_name("servo1")] == servo1 + assert manager.resources[MockServo.get_resource_name("servo2")] == servo2 def test_registers_correctly(self): servo1 = MockServo(name="servo1") @@ -21,20 +23,21 @@ def test_registers_correctly(self): manager = ResourceManager([]) manager.register(servo1) - assert len(manager.components) == 1 + assert len(manager.resources) == 1 manager.register(servo2) - assert len(manager.components) == 2 + assert len(manager.resources) == 2 def test_raises_error_on_duplicate_name(self): servo1 = MockServo(name="servo1") servo2 = MockServo(name="servo2") manager = ResourceManager([servo1, servo2]) - with pytest.raises(DuplicateComponentError): + # No duplicate name/subtype allowed + with pytest.raises(DuplicateResourceError): manager.register(MockServo(name="servo2")) - with pytest.raises(DuplicateComponentError): - manager.register(MockArm(name="servo2")) + # No error because name/subtype is different + manager.register(MockArm(name="servo2")) class TestGetComponent: @@ -43,7 +46,7 @@ def test_get_component(self): arm = MockArm(name="arm") manager = ResourceManager([servo, arm]) - component = manager.get_component(MockServo, "servo") + component = manager.get_resource(MockServo, MockServo.get_resource_name("servo")) assert component.name == "servo" assert isinstance(component, MockServo) @@ -52,5 +55,72 @@ def test_not_found(self): arm = MockArm(name="arm") manager = ResourceManager([servo, arm]) - with pytest.raises(ComponentNotFoundError): - manager.get_component(MockArm, "servo") + with pytest.raises(ResourceNotFoundError): + manager.get_resource(MockArm, MockArm.get_resource_name("servo")) + + with pytest.raises(ResourceNotFoundError): + manager.get_resource(MockArm, MockServo.get_resource_name("servo")) + + def test_get_short_name_component(self): + servo = MockServo(name="servo") + r1_servo = MockServo(name="remote1:servo") + r1_arm = MockArm(name="remote1:arm") + r2_arm = MockArm(name="remote2:arm") + r2_arm2 = MockArm(name="remote2:arm1") + r32_arm = MockArm(name="remote3:remote2:arm") + r321_arm3 = MockArm(name="remote3:remote2:remote1:arm3") + manager = ResourceManager([servo, r1_servo, r1_arm, r2_arm, r2_arm2, r32_arm, r321_arm3]) + + component = manager.get_resource(MockServo, MockServo.get_resource_name("servo")) + assert component.name == "servo" + component = manager.get_resource(MockServo, MockServo.get_resource_name("remote1:servo")) + assert component.name == "remote1:servo" + + component = manager.get_resource(MockArm, MockArm.get_resource_name("arm1")) + assert component.name == "remote2:arm1" + component = manager.get_resource(MockArm, MockArm.get_resource_name("remote2:arm1")) + assert component.name == "remote2:arm1" + + component = manager.get_resource(MockArm, MockArm.get_resource_name("arm3")) + assert component.name == "remote3:remote2:remote1:arm3" + + with pytest.raises(ResourceNotFoundError): + component = manager.get_resource(MockArm, MockArm.get_resource_name("arm")) + component = manager.get_resource(MockArm, MockArm.get_resource_name("remote1:arm")) + assert component.name == "remote1:arm" + component = manager.get_resource(MockArm, MockArm.get_resource_name("remote2:arm")) + assert component.name == "remote2:arm" + component = manager.get_resource(MockArm, MockArm.get_resource_name("remote3:remote2:arm")) + assert component.name == "remote3:remote2:arm" + + +class TestClose: + async def test_close(self): + servo1 = MockServo(name="servo1") + arm1 = MockArm(name="arm1") + manager = ResourceManager([servo1, arm1]) + with mock.patch("tests.mocks.components.MockServo.close") as mockedServo: + mockedServo.assert_not_called() + rn = MockServo.get_resource_name(servo1.name) + assert rn in manager.resources.keys() + await manager.remove_resource(rn) + assert rn not in manager.resources.keys() + mockedServo.assert_called() + with mock.patch("tests.mocks.components.MockArm.close") as mockedArm: + mockedArm.assert_not_called() + rn = MockArm.get_resource_name(arm1.name) + assert rn in manager.resources.keys() + await manager.remove_resource(rn) + assert rn not in manager.resources.keys() + mockedArm.assert_called() + + # test to see if the resource is gone from manager even after failed close function + async def test_failed_close(self): + servo1 = MockServo(name="servo1") + manager = ResourceManager([servo1]) + rn = MockServo.get_resource_name(servo1.name) + assert rn in manager.resources.keys() + rn.name = "bad_name" + with pytest.raises(Exception): + await manager.remove_resource(rn) + assert rn not in manager.resources.keys() diff --git a/tests/test_robot.py b/tests/test_robot.py index e62df9bb3..8c9535b83 100644 --- a/tests/test_robot.py +++ b/tests/test_robot.py @@ -1,108 +1,111 @@ import asyncio -from typing import Any, Dict, Optional +from typing import Any, Dict, List, Optional, Tuple +from unittest import mock import pytest -from google.protobuf.struct_pb2 import Struct, Value +from grpclib.client import Channel from grpclib.exceptions import GRPCError from grpclib.server import Stream from grpclib.testing import ChannelFor -from viam.components.arm import Arm + +from viam.components.arm import Arm, KinematicsFileFormat +from viam.components.arm.client import ArmClient from viam.components.motor import Motor -from viam.components.resource_manager import ResourceManager -from viam.errors import ComponentNotFoundError, ServiceNotImplementedError, ViamError -from viam.proto.common import Pose, PoseInFrame, ResourceName +from viam.components.movement_sensor import MovementSensor +from viam.errors import ResourceNotFoundError +from viam.proto.common import Geometry, GeoPoint, Orientation, Pose, PoseInFrame, ResourceName, Transform, Vector3 from viam.proto.component.arm import JointPositions -from viam.proto.component.arm import Status as ArmStatus -from viam.proto.component.motor import Status as MotorStatus from viam.proto.robot import ( - DiscoverComponentsRequest, - DiscoverComponentsResponse, - Discovery, - DiscoveryQuery, + BlockForOperationRequest, + BlockForOperationResponse, + CancelOperationRequest, + CancelOperationResponse, FrameSystemConfig, FrameSystemConfigRequest, FrameSystemConfigResponse, - GetStatusRequest, - GetStatusResponse, + GetCloudMetadataRequest, + GetCloudMetadataResponse, + GetMachineStatusRequest, + GetMachineStatusResponse, + GetOperationsRequest, + GetOperationsResponse, + GetVersionRequest, + GetVersionResponse, + Operation, ResourceNamesRequest, ResourceNamesResponse, + ResourceStatus, RobotServiceStub, - Status, + ShutdownRequest, + ShutdownResponse, StopAllRequest, StopExtraParameters, TransformPoseRequest, TransformPoseResponse, ) +from viam.resource.manager import ResourceManager +from viam.resource.registry import Registry +from viam.resource.rpc_client_base import ResourceRPCClientBase +from viam.resource.types import RESOURCE_NAMESPACE_RDK, RESOURCE_TYPE_COMPONENT, RESOURCE_TYPE_SERVICE from viam.robot.client import RobotClient from viam.robot.service import RobotService -from viam.services.vision import VisionServiceClient -from viam.utils import dict_to_struct, message_to_struct, struct_to_message +from viam.services.mlmodel.client import MLModelClient +from viam.utils import dict_to_struct -from .mocks.components import MockArm, MockCamera, MockMotor, MockSensor +from .mocks.components import MockArm, MockCamera, MockMotor, MockMovementSensor, MockSensor +from .mocks.services import MockMLModel RESOURCE_NAMES = [ - ResourceName(namespace="rdk", type="component", subtype="arm", name="arm1"), - ResourceName(namespace="rdk", type="component", subtype="camera", name="camera1"), - ResourceName(namespace="rdk", type="component", subtype="motor", name="motor1"), -] - -ARM_STATUS = ArmStatus( - end_position=Pose( - x=1, - y=2, - z=3, - o_x=2, - o_y=3, - o_z=4, - theta=20, - ), - joint_positions=JointPositions(values=[0, 0, 0, 0, 0, 0]), - is_moving=False, -) - -ARM_STATUS_MSG = Status( - name=ResourceName(namespace="rdk", type="component", subtype="arm", name="arm1"), status=message_to_struct(ARM_STATUS) -) - -CAMERA_STATUS_MSG = Status(name=ResourceName(namespace="rdk", type="component", subtype="camera", name="camera1"), status=Struct()) - -STATUSES = [ - ARM_STATUS_MSG, - CAMERA_STATUS_MSG, - Status( - name=ResourceName(namespace="rdk", type="component", subtype="motor", name="motor1"), - status=message_to_struct(MotorStatus(is_powered=False, position=0, position_reporting=True, is_moving=False)), - ), + ResourceName(namespace=RESOURCE_NAMESPACE_RDK, type=RESOURCE_TYPE_COMPONENT, subtype="arm", name="arm1"), + ResourceName(namespace=RESOURCE_NAMESPACE_RDK, type=RESOURCE_TYPE_COMPONENT, subtype="camera", name="camera1"), + ResourceName(namespace=RESOURCE_NAMESPACE_RDK, type=RESOURCE_TYPE_COMPONENT, subtype="motor", name="motor1"), + ResourceName(namespace=RESOURCE_NAMESPACE_RDK, type=RESOURCE_TYPE_COMPONENT, subtype="movement_sensor", name="movement_sensor1"), + ResourceName(namespace=RESOURCE_NAMESPACE_RDK, type=RESOURCE_TYPE_SERVICE, subtype="mlmodel", name="mlmodel1"), ] CONFIG_RESPONSE = [ FrameSystemConfig( - name="config0", - pose_in_parent_frame=PoseInFrame(reference_frame="reference0", pose=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)), - model_json=b"some fake json", + frame=Transform( + reference_frame="frame0", + pose_in_observer_frame=PoseInFrame(reference_frame="reference0", pose=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)), + ) ), FrameSystemConfig( - name="config1", - pose_in_parent_frame=PoseInFrame(reference_frame="reference1", pose=Pose(x=2, y=3, z=4, o_x=3, o_y=4, o_z=5, theta=21)), - model_json=b"some fake json part 2", + frame=Transform( + reference_frame="frame1", + pose_in_observer_frame=PoseInFrame(reference_frame="reference1", pose=Pose(x=2, y=3, z=4, o_x=3, o_y=4, o_z=5, theta=21)), + ) ), ] TRANSFORM_RESPONSE = PoseInFrame(reference_frame="arm", pose=Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)) -DISCOVERY_QUERY = DiscoveryQuery(subtype="camera", model="webcam") +OPERATION_ID = "abc" -DISCOVERY_RESPONSE = [ - Discovery( - query=DISCOVERY_QUERY, - results=Struct( - fields={ - "foo": Value(string_value="bar"), - "one": Value(number_value=1), - } - ), - ) -] +OPERATIONS_RESPONSE = [Operation(id=OPERATION_ID)] + +GET_CLOUD_METADATA_RESPONSE = GetCloudMetadataResponse( + robot_part_id="the-machine-id", + primary_org_id="the-primary-org", + location_id="the-location", + machine_id="the-machine-id", + machine_part_id="the-machine-part-id", +) + +GET_VERVSION_RESPONSE = GetVersionResponse( + platform="rdk", + version="0.2.0", + api_version="0.3.0", +) + +GET_MACHINE_STATUS_RESPONSE = GetMachineStatusResponse( + resources=[ + ResourceStatus( + name=ResourceName(namespace=RESOURCE_NAMESPACE_RDK, type=RESOURCE_TYPE_COMPONENT, subtype="arm", name="arm1"), + state=ResourceStatus.State.STATE_READY, + ) + ] +) @pytest.fixture(scope="function") @@ -111,6 +114,27 @@ def service() -> RobotService: MockArm(name="arm1"), MockCamera(name="camera1"), MockMotor(name="motor1"), + MockMovementSensor( + name="movement_sensor1", + coordinates=GeoPoint(latitude=40.664679865782624, longitude=-73.97668056188789), + altitude=15, + lin_vel=Vector3(x=1, y=2, z=3), + ang_vel=Vector3(x=1, y=2, z=3), + lin_acc=Vector3(x=1, y=2, z=3), + heading=182, + orientation=Orientation(o_x=1, o_y=2, o_z=3, theta=5), + properties=MovementSensor.Properties( + linear_acceleration_supported=False, + linear_velocity_supported=False, + angular_velocity_supported=True, + orientation_supported=False, + position_supported=True, + compass_heading_supported=False, + ), + accuracy=MovementSensor.Accuracy(accuracy={"foo": 0.1, "bar": 2, "baz": 3.14}), + readings={"a": 1, "b": 2, "c": 3}, + ), + MockMLModel("mlmodel1"), ] async def Config(stream: Stream[FrameSystemConfigRequest, FrameSystemConfigResponse]) -> None: @@ -125,47 +149,61 @@ async def TransformPose(stream: Stream[TransformPoseRequest, TransformPoseRespon response = TransformPoseResponse(pose=TRANSFORM_RESPONSE) await stream.send_message(response) - async def DiscoverComponents(stream: Stream[DiscoverComponentsRequest, DiscoverComponentsResponse]) -> None: + async def GetOperations(stream: Stream[GetOperationsRequest, GetOperationsResponse]) -> None: + request = await stream.recv_message() + assert request is not None + response = GetOperationsResponse(operations=OPERATIONS_RESPONSE) + await stream.send_message(response) + + async def GetCloudMetadata(stream: Stream[GetCloudMetadataRequest, GetCloudMetadataResponse]) -> None: request = await stream.recv_message() assert request is not None - response = DiscoverComponentsResponse(discovery=DISCOVERY_RESPONSE) + await stream.send_message(GET_CLOUD_METADATA_RESPONSE) + + async def GetVersion(stream: Stream[GetVersionRequest, GetVersionResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(GET_VERVSION_RESPONSE) + + async def GetMachineStatus(stream: Stream[GetMachineStatusRequest, GetMachineStatusResponse]) -> None: + request = await stream.recv_message() + assert request is not None + await stream.send_message(GET_MACHINE_STATUS_RESPONSE) + + async def Shutdown(stream: Stream[ShutdownRequest, ShutdownResponse]) -> None: + request = await stream.recv_message() + assert request is not None + response = ShutdownResponse() await stream.send_message(response) manager = ResourceManager(resources) service = RobotService(manager) service.FrameSystemConfig = Config service.TransformPose = TransformPose - service.DiscoverComponents = DiscoverComponents + service.GetOperations = GetOperations + service.GetCloudMetadata = GetCloudMetadata + service.Shutdown = Shutdown + service.GetVersion = GetVersion + service.GetMachineStatus = GetMachineStatus + return service class TestRobotService: - @pytest.mark.asyncio async def test_resource_names(self, service: RobotService): async with ChannelFor([service]) as channel: client = RobotServiceStub(channel) response: ResourceNamesResponse = await client.ResourceNames(ResourceNamesRequest()) - assert list(response.resources) == RESOURCE_NAMES - - @pytest.mark.asyncio - async def test_get_status(self, service: RobotService): - async with ChannelFor([service]) as channel: - client = RobotServiceStub(channel) - response: GetStatusResponse = await client.GetStatus(GetStatusRequest()) - assert list(response.status) == STATUSES - - request = GetStatusRequest(resource_names=[MockArm.get_resource_name("arm1"), MockCamera.get_resource_name("camera1")]) - response: GetStatusResponse = await client.GetStatus(request) - assert list(response.status) == [ARM_STATUS_MSG, CAMERA_STATUS_MSG] + assert len(response.resources) == len(RESOURCE_NAMES) + assert set(response.resources) == set(RESOURCE_NAMES) - @pytest.mark.asyncio async def test_stop_all(self, service: RobotService): async with ChannelFor([service]) as channel: client = RobotServiceStub(channel) - arm = service.manager.get_component(Arm, "arm1") + arm = service.manager.get_resource(Arm, Arm.get_resource_name("arm1")) assert isinstance(arm, MockArm) - motor = service.manager.get_component(Motor, "motor1") + motor = service.manager.get_resource(Motor, Motor.get_resource_name("motor1")) assert isinstance(motor, MockMotor) # Test one with extra, one without @@ -184,7 +222,7 @@ async def test_stop_all(self, service: RobotService): assert await motor.is_moving() is False - # Test passing extra where component's `stop` function doesn't take extra + # Test passing extra where component's ``stop`` function doesn't take extra await arm.move_to_position(Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)) assert await arm.is_moving() is True await motor.set_power(1) @@ -207,7 +245,7 @@ async def test_stop_all(self, service: RobotService): await motor.set_power(1) assert await motor.is_moving() is True - async def error_stop(extra: Optional[Dict[str, Any]] = None): + async def error_stop(extra: Optional[Dict[str, Any]] = None, **kwargs): raise Exception("Some random error") arm.stop = error_stop @@ -218,27 +256,27 @@ async def error_stop(extra: Optional[Dict[str, Any]] = None): class TestRobotClient: - @pytest.mark.asyncio async def test_refresh(self, service: RobotService): async with ChannelFor([service]) as channel: client = await RobotClient.with_channel(channel, RobotClient.Options()) - assert client._resource_names == RESOURCE_NAMES + assert len(client._resource_names) == len(RESOURCE_NAMES) + assert set(client._resource_names) == set(RESOURCE_NAMES) service.manager.register(MockSensor("sensor1")) await client.refresh() - assert client._resource_names == RESOURCE_NAMES + [MockSensor.get_resource_name("sensor1")] + assert set(client._resource_names) == set(RESOURCE_NAMES + [MockSensor.get_resource_name("sensor1")]) + await client.close() - @pytest.mark.asyncio async def test_refresh_task(self, service: RobotService): async with ChannelFor([service]) as channel: client = await RobotClient.with_channel(channel, RobotClient.Options()) assert client._refresh_task is None + await client.close() client = await RobotClient.with_channel(channel, RobotClient.Options(refresh_interval=100)) assert client._refresh_task is not None await client.close() - @pytest.mark.asyncio async def test_close(self, service: RobotService): async with ChannelFor([service]) as channel: client = await RobotClient.with_channel(channel, RobotClient.Options(refresh_interval=100)) @@ -246,34 +284,34 @@ async def test_close(self, service: RobotService): assert client._refresh_task is not None and client._refresh_task.cancelled() is False await client.close() - assert client._channel._connected is True # Robots created with `with_channel` do not close Channels automatically + assert client._channel._connected is True # Robots created with ``with_channel`` do not close Channels automatically with pytest.raises(asyncio.CancelledError): await client._refresh_task - @pytest.mark.asyncio async def test_resource_names(self, service: RobotService): async with ChannelFor([service]) as channel: client = await RobotClient.with_channel(channel, RobotClient.Options()) - assert client.resource_names == RESOURCE_NAMES + assert len(client._resource_names) == len(RESOURCE_NAMES) + assert set(client._resource_names) == set(RESOURCE_NAMES) service.manager.register(MockSensor("sensor1")) await client.refresh() - assert client._resource_names == RESOURCE_NAMES + [MockSensor.get_resource_name("sensor1")] + assert set(client._resource_names) == set(RESOURCE_NAMES + [MockSensor.get_resource_name("sensor1")]) + await client.close() - @pytest.mark.asyncio async def test_get_component(self, service: RobotService): async with ChannelFor([service]) as channel: client = await RobotClient.with_channel(channel, RobotClient.Options()) component = client.get_component(MockArm.get_resource_name("arm1")) assert isinstance(component, Arm) - with pytest.raises(ComponentNotFoundError): + with pytest.raises(ResourceNotFoundError): client.get_component(MockArm.get_resource_name("arm2")) - with pytest.raises(ViamError): + with pytest.raises(ValueError): client.get_component( ResourceName( - namespace="rdk", + namespace=RESOURCE_NAMESPACE_RDK, type="service", subtype="vision", ) @@ -283,60 +321,102 @@ async def test_get_component(self, service: RobotService): component = MockArm.from_robot(client, "arm1") assert isinstance(component, Arm) - with pytest.raises(ComponentNotFoundError): + with pytest.raises(ResourceNotFoundError): MockArm.from_robot(client, "arm2") - @pytest.mark.asyncio - async def test_get_status(self, service: RobotService): - async with ChannelFor([service]) as channel: - client = await RobotClient.with_channel(channel, RobotClient.Options()) - statuses = await client.get_status() - assert statuses == STATUSES - - statuses = await client.get_status([MockArm.get_resource_name("arm1"), MockCamera.get_resource_name("camera1")]) - assert statuses == [ARM_STATUS_MSG, CAMERA_STATUS_MSG] - - arm_status = statuses[0].status - assert struct_to_message(arm_status, ArmStatus) == ARM_STATUS + await client.close() - @pytest.mark.asyncio async def test_get_service(self, service: RobotService): async with ChannelFor([service]) as channel: client = await RobotClient.with_channel(channel, RobotClient.Options()) - client._resource_names.append(ResourceName(namespace="rdk", type="service", subtype="vision", name="vision1")) - with pytest.raises(ServiceNotImplementedError): - VisionServiceClient.from_robot(client) - VisionServiceClient.from_robot(client, "vision1") + with pytest.raises(ResourceNotFoundError): + MLModelClient.from_robot(client, "mlmodel") + MLModelClient.from_robot(client, "mlmodel1") + await client.close() - @pytest.mark.asyncio async def test_get_frame_system_config(self, service: RobotService): async with ChannelFor([service]) as channel: client = await RobotClient.with_channel(channel, RobotClient.Options()) configs = await client.get_frame_system_config() assert configs == CONFIG_RESPONSE + await client.close() - @pytest.mark.asyncio async def test_transform_pose(self, service: RobotService): async with ChannelFor([service]) as channel: client = await RobotClient.with_channel(channel, RobotClient.Options()) pose = await client.transform_pose(PoseInFrame(), "some dest") assert pose == TRANSFORM_RESPONSE + await client.close() + + async def test_get_cloud_metadata(self, service: RobotService): + async with ChannelFor([service]) as channel: + client = await RobotClient.with_channel(channel, RobotClient.Options()) + md = await client.get_cloud_metadata() + assert md == GET_CLOUD_METADATA_RESPONSE + await client.close() + + async def test_get_version(self, service: RobotService): + async with ChannelFor([service]) as channel: + client = await RobotClient.with_channel(channel, RobotClient.Options()) + md = await client.get_version() + assert md == GET_VERVSION_RESPONSE + await client.close() + + async def test_get_machine_status(self, service: RobotService): + async with ChannelFor([service]) as channel: + client = await RobotClient.with_channel(channel, RobotClient.Options()) + statuses = await client.get_machine_status() + assert statuses == GET_MACHINE_STATUS_RESPONSE + await client.close() + + async def test_get_operations(self, service: RobotService): + async with ChannelFor([service]) as channel: + client = await RobotClient.with_channel(channel, RobotClient.Options()) + ops = await client.get_operations() + assert ops == OPERATIONS_RESPONSE + await client.close() + + async def test_cancel_operation(self, service: RobotService): + cancelled = None - @pytest.mark.asyncio - async def test_discover_components(self, service: RobotService): + async def CancelOperation(stream: Stream[CancelOperationRequest, CancelOperationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + nonlocal cancelled + cancelled = request.id + await stream.send_message(CancelOperationResponse()) + + service.CancelOperation = CancelOperation async with ChannelFor([service]) as channel: client = await RobotClient.with_channel(channel, RobotClient.Options()) - discoveries = await client.discover_components([DISCOVERY_QUERY]) - assert discoveries == DISCOVERY_RESPONSE + await client.cancel_operation(id=OPERATION_ID) + assert cancelled == OPERATION_ID + await client.close() + + async def test_block_for_operation(self, service: RobotService): + blocked = None + + async def BlockForOperation(stream: Stream[BlockForOperationRequest, BlockForOperationResponse]) -> None: + request = await stream.recv_message() + assert request is not None + nonlocal blocked + blocked = request.id + await stream.send_message(BlockForOperationResponse()) + + service.BlockForOperation = BlockForOperation + async with ChannelFor([service]) as channel: + client = await RobotClient.with_channel(channel, RobotClient.Options()) + await client.block_for_operation(id=OPERATION_ID) + assert blocked == OPERATION_ID + await client.close() - @pytest.mark.asyncio async def test_stop_all(self, service: RobotService): async with ChannelFor([service]) as channel: client = await RobotClient.with_channel(channel, RobotClient.Options()) - arm = service.manager.get_component(Arm, "arm1") + arm = service.manager.get_resource(Arm, Arm.get_resource_name("arm1")) assert isinstance(arm, MockArm) - motor = service.manager.get_component(Motor, "motor1") + motor = service.manager.get_resource(Motor, Motor.get_resource_name("motor1")) assert isinstance(motor, MockMotor) await arm.move_to_position(Pose(x=1, y=2, z=3, o_x=2, o_y=3, o_z=4, theta=20)) @@ -345,9 +425,127 @@ async def test_stop_all(self, service: RobotService): assert await motor.is_moving() is True extra = {"foo": "bar", "baz": [1, 2, 3]} - await client.stop_all({arm.get_resource_name(arm.name): extra}) + await client.stop_all({arm.get_resource_name(arm.name): extra}) # type: ignore assert await arm.is_moving() is False assert arm.extra == extra assert await motor.is_moving() is False + await client.close() + + async def test_create_or_reset_client(self, service: RobotService): + async with ChannelFor([service]) as channel: + client = await RobotClient.with_channel(channel, RobotClient.Options()) + + # No change in channel + arm_client = ArmClient.from_robot(client, "arm1") + assert arm_client.channel is client._channel + await client._create_or_reset_client(Arm.get_resource_name(arm_client.name)) + assert arm_client is client.get_component(Arm.get_resource_name(arm_client.name)) + assert arm_client.channel is client._channel + + # Yes change in channel + async with ChannelFor([service]) as channel2: + arm_client.reset_channel(channel2) + assert arm_client.channel is not client._channel + await client._create_or_reset_client(Arm.get_resource_name(arm_client.name)) + assert arm_client is client.get_component(Arm.get_resource_name(arm_client.name)) + assert arm_client.channel is client._channel + + await client.close() + + # Change in client + class FakeArmClient(Arm, ResourceRPCClientBase): + actual_client: ArmClient + + def __init__(self, name: str, channel: Channel): + super().__init__(name) + self.channel = channel + self.actual_client = ArmClient(name, channel) + self.client = self.actual_client.client + + async def get_end_position( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + ) -> Pose: + return await self.actual_client.get_end_position(extra=extra, timeout=timeout) + + async def move_to_position( + self, + pose: Pose, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + ): + return await self.actual_client.move_to_position(pose, extra=extra, timeout=timeout) + + async def move_to_joint_positions( + self, + positions: JointPositions, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + ): + return await self.actual_client.move_to_joint_positions(positions, extra=extra, timeout=timeout) + + async def get_joint_positions( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + ) -> JointPositions: + return await self.actual_client.get_joint_positions(extra=extra, timeout=timeout) + + async def stop( + self, + *, + extra: Optional[Dict[str, Any]] = None, + timeout: Optional[float] = None, + ): + return await self.actual_client.stop(extra=extra, timeout=timeout) + + async def is_moving(self) -> bool: + return await self.actual_client.is_moving() + + async def get_kinematics( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None + ) -> Tuple[KinematicsFileFormat.ValueType, bytes]: + return await self.actual_client.get_kinematics(timeout=timeout) + + async def get_geometries( + self, *, extra: Optional[Dict[str, Any]] = None, timeout: Optional[float] = None + ) -> List[Geometry]: + return await self.actual_client.get_geometries(timeout=timeout) + + old_create_client = Registry._APIS[Arm.API].create_rpc_client + Registry._APIS[Arm.API].create_rpc_client = lambda name, channel: FakeArmClient(name, channel) + + client = await RobotClient.with_channel(channel, RobotClient.Options()) + + async with ChannelFor([service]) as channel2: + arm_client.channel = channel2 + assert arm_client.channel is not client._channel + await client._create_or_reset_client(Arm.get_resource_name(arm_client.name)) + assert arm_client is not client.get_component(Arm.get_resource_name(arm_client.name)) # Should be a new client now + + await client.close() + Registry._APIS[Arm.API].create_rpc_client = old_create_client + + async def test_shutdown(self, service: RobotService): + async with ChannelFor([service]) as channel: + + async def shutdown_client_mock(self): + return await self._client.Shutdown(ShutdownRequest()) + + client = await RobotClient.with_channel(channel, RobotClient.Options()) + + with mock.patch("viam.robot.client.RobotClient.shutdown") as shutdown_mock: + shutdown_mock.return_value = await shutdown_client_mock(client) + shutdown_response = await client.shutdown() + + assert shutdown_response == ShutdownResponse() + shutdown_mock.assert_called_once() + + await client.close() diff --git a/tests/test_rpc.py b/tests/test_rpc.py new file mode 100644 index 000000000..f7b13b40e --- /dev/null +++ b/tests/test_rpc.py @@ -0,0 +1,72 @@ +import pytest +from grpclib.client import Channel +from grpclib.testing import ChannelFor + +from viam.resource.registry import Registry +from viam.resource.rpc_client_base import ReconfigurableResourceRPCClientBase +from viam.resource.types import RESOURCE_NAMESPACE_RDK +from viam.rpc.dial import ViamChannel + +from .mocks import components # noqa: F401 + + +async def test_reset_channel(): + class TestStub: + def __init__(self, channel: Channel) -> None: + self.channel = channel + + class TestClient(ReconfigurableResourceRPCClientBase): + def __init__(self, channel: Channel) -> None: + self.channel = channel + self.client = TestStub(channel) + + async with ChannelFor([]) as channel: + client = TestClient(channel) + assert client.channel == channel + async with ChannelFor([]) as channel2: + client.reset_channel(channel2) + assert client.channel == channel2 + assert channel != channel2 + + +async def test_builtin_clients_are_reconfigurable(): + async with ChannelFor([]) as channel: + for api, registration in Registry.REGISTERED_APIS().items(): + if api.namespace == RESOURCE_NAMESPACE_RDK: + client = registration.create_rpc_client("client", channel) + assert isinstance(client, ReconfigurableResourceRPCClientBase) + + +async def test_viam_channel_closing(): + def release(): + nonlocal did_release + did_release = True + + # Non-error case + did_release = False + channel = await ChannelFor([]).__aenter__() + v_channel = ViamChannel(channel=channel, release=release) + assert v_channel._closed is False + assert did_release is False + v_channel.close() + assert v_channel._closed is True + assert did_release is True + await channel.__aexit__(None, None, None) + + # Error case + def close(): + raise Exception("FAILURE") + + did_release = False + channel = await ChannelFor([]).__aenter__() + old_close = channel.close + channel.close = close + v_channel = ViamChannel(channel=channel, release=release) + assert v_channel._closed is False + assert did_release is False + with pytest.raises(Exception): + v_channel.close() + assert v_channel._closed is True + assert did_release is True + channel.close = old_close + await channel.__aexit__(None, None, None) diff --git a/tests/test_sensor.py b/tests/test_sensor.py index b64cdb9a8..d9751fc1c 100644 --- a/tests/test_sensor.py +++ b/tests/test_sensor.py @@ -1,70 +1,107 @@ import pytest from grpclib.testing import ChannelFor -from viam.components.generic.service import GenericService -from viam.components.resource_manager import ResourceManager from viam.components.sensor import SensorClient -from viam.components.sensor.service import SensorService -from viam.proto.component.sensor import ( +from viam.components.sensor.service import SensorRPCService +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, + GetGeometriesRequest, + GetGeometriesResponse, GetReadingsRequest, GetReadingsResponse, - SensorServiceStub, ) -from viam.utils import primitive_to_value +from viam.proto.component.sensor import SensorServiceStub +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, sensor_readings_value_to_native, struct_to_dict -from .mocks.components import MockSensor +from . import loose_approx +from .mocks.components import GEOMETRIES, MockSensor + +READINGS = {"a": 1, "b": 2, "c": 3} +EXTRA_PARAMS = {"foo": "bar", "baz": [1, 2, 3]} + + +@pytest.fixture(scope="function") +def sensor() -> MockSensor: + return MockSensor(name="sensor", result=READINGS) class TestSensor: + async def test_get_readings(self, sensor): + assert sensor.extra is None + readings = await sensor.get_readings(extra=EXTRA_PARAMS, timeout=1.23) + assert readings == READINGS + assert sensor.extra == EXTRA_PARAMS + assert sensor.timeout == loose_approx(1.23) - sensor = MockSensor(name="sensor", result={"a": 1, "b": 2, "c": 3}) + async def test_do(self, sensor): + command = {"command": "args"} + resp = await sensor.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio - async def test_get_readings(self): - readings = await self.sensor.get_readings() - assert readings == {"a": 1, "b": 2, "c": 3} + async def test_get_geometries(self, sensor): + geometries = await sensor.get_geometries() + assert geometries == GEOMETRIES - @pytest.mark.asyncio - async def test_do(self): - with pytest.raises(NotImplementedError): - await self.sensor.do_command({"command": "args"}) +@pytest.fixture(scope="function") +def manager(sensor) -> ResourceManager: + return ResourceManager([sensor]) -class TestService: - name = "sensor" - readings = {"a": 1, "b": 2, "c": 3} - sensor = MockSensor(name=name, result=readings) - manager = ResourceManager([sensor]) - service = SensorService(manager) +@pytest.fixture(scope="function") +def service(manager) -> SensorRPCService: + return SensorRPCService(manager) + - @pytest.mark.asyncio - async def test_get_readings(self): - async with ChannelFor([self.service]) as channel: +class TestService: + async def test_get_readings(self, sensor, service): + async with ChannelFor([service]) as channel: + client = SensorServiceStub(channel) + request = GetReadingsRequest(name=sensor.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert sensor.extra is None + result: GetReadingsResponse = await client.GetReadings(request, timeout=2.34) + assert sensor_readings_value_to_native(result.readings) == READINGS + assert sensor.extra == EXTRA_PARAMS + assert sensor.timeout == loose_approx(2.34) + + async def test_do(self, sensor: MockSensor, service: SensorRPCService): + async with ChannelFor([service]) as channel: client = SensorServiceStub(channel) - request = GetReadingsRequest(name=self.name) - result: GetReadingsResponse = await client.GetReadings(request) - assert result.readings == {key: primitive_to_value(value) for (key, value) in self.readings.items()} + command = {"command": "args"} + request = DoCommandRequest(name=sensor.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + async def test_get_geometries(self, sensor: MockSensor, service: SensorRPCService): + async with ChannelFor([service]) as channel: + client = SensorServiceStub(channel) + request = GetGeometriesRequest(name=sensor.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES class TestClient: - - name = "sensor" - readings = {"a": 1, "b": 2, "c": 3} - sensor = MockSensor(name=name, result=readings) - manager = ResourceManager([sensor]) - service = SensorService(manager) - - @pytest.mark.asyncio - async def test_get_readings(self): - async with ChannelFor([self.service]) as channel: - client = SensorClient(self.name, channel) - value_readings = await client.get_readings() - assert self.readings == value_readings - - @pytest.mark.asyncio - async def test_do(self): - async with ChannelFor([self.service, GenericService(self.manager)]) as channel: - client = SensorClient(self.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + async def test_get_readings(self, sensor, service): + async with ChannelFor([service]) as channel: + client = SensorClient(sensor.name, channel) + assert sensor.extra is None + value_readings = await client.get_readings(timeout=3.45, extra=EXTRA_PARAMS) + assert READINGS == value_readings + assert sensor.extra == EXTRA_PARAMS + assert sensor.timeout == loose_approx(3.45) + + async def test_do(self, sensor, manager, service): + async with ChannelFor([service]) as channel: + client = SensorClient(sensor.name, channel) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} + + async def test_get_geometries(self, sensor, service): + async with ChannelFor([service]) as channel: + client = SensorClient(sensor.name, channel) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_sensors_service.py b/tests/test_sensors_service.py deleted file mode 100644 index ae5a613c0..000000000 --- a/tests/test_sensors_service.py +++ /dev/null @@ -1,83 +0,0 @@ -import pytest -from grpclib.testing import ChannelFor - -from viam.proto.common import GeoPoint, Orientation, ResourceName, Vector3 -from viam.proto.service.sensors import Readings -from viam.services.sensors import SensorsServiceClient -from viam.utils import primitive_to_value - -from .mocks.services import MockSensorsService - -SENSORS = [ - ResourceName(namespace="test", type="component", subtype="sensor", name="sensor0"), - ResourceName(namespace="test", type="component", subtype="sensor", name="sensor1"), - ResourceName(namespace="test", type="component", subtype="sensor", name="sensor2"), - ResourceName(namespace="test", type="component", subtype="sensor", name="sensor3"), -] - -ANGVEL = Vector3(x=0.1, y=2.3, z=4.5) -VEC3 = Vector3(x=6.7, y=8.9, z=10.11) -POINT = GeoPoint(latitude=123.45, longitude=678.9) -ORIENTATION = Orientation(o_x=1, o_y=2, o_z=3, theta=4) - -READINGS = [ - Readings( - name=ResourceName(namespace="test", type="component", subtype="sensor", name="sensor0"), - readings={"a": primitive_to_value({"_type": "angular_velocity", "x": ANGVEL.x, "y": ANGVEL.y, "z": ANGVEL.z})}, - ), - Readings( - name=ResourceName(namespace="test", type="component", subtype="sensor", name="sensor1"), - readings={"b": primitive_to_value({"_type": "vector3", "x": VEC3.x, "y": VEC3.y, "z": VEC3.z})}, - ), - Readings( - name=ResourceName(namespace="test", type="component", subtype="sensor", name="sensor2"), - readings={"c": primitive_to_value({"_type": "geopoint", "lat": POINT.latitude, "lng": POINT.longitude})}, - ), - Readings( - name=ResourceName(namespace="test", type="component", subtype="sensor", name="sensor3"), - readings={ - "d": primitive_to_value( - { - "ox": ORIENTATION.o_x, - "oy": ORIENTATION.o_y, - "oz": ORIENTATION.o_z, - "theta": ORIENTATION.theta, - "_type": "orientation_vector_degrees", - } - ) - }, - ), -] - -SENSOR_SERVICE_NAME = "sensors1" - - -@pytest.fixture(scope="function") -def service() -> MockSensorsService: - return MockSensorsService(SENSORS, READINGS) - - -class TestClient: - @pytest.mark.asyncio - async def test_get_sensors(self, service: MockSensorsService): - async with ChannelFor([service]) as channel: - client = SensorsServiceClient(SENSOR_SERVICE_NAME, channel) - sensors = await client.get_sensors() - assert sensors == SENSORS - - @pytest.mark.asyncio - async def test_get_readings(self, service: MockSensorsService): - async with ChannelFor([service]) as channel: - client = SensorsServiceClient(SENSOR_SERVICE_NAME, channel) - sensors = [ - ResourceName(namespace="test", type="component", subtype="sensor", name="sensor1"), - ResourceName(namespace="test", type="component", subtype="sensor", name="sensor2"), - ] - readings = await client.get_readings(sensors) - assert readings == { - ResourceName(namespace="test", type="component", subtype="sensor", name="sensor0"): {"a": ANGVEL}, - ResourceName(namespace="test", type="component", subtype="sensor", name="sensor1"): {"b": VEC3}, - ResourceName(namespace="test", type="component", subtype="sensor", name="sensor2"): {"c": POINT}, - ResourceName(namespace="test", type="component", subtype="sensor", name="sensor3"): {"d": ORIENTATION}, - } - assert service.sensors_for_readings == sensors diff --git a/tests/test_server.py b/tests/test_server.py new file mode 100644 index 000000000..166e689f6 --- /dev/null +++ b/tests/test_server.py @@ -0,0 +1,81 @@ +import pytest +from grpclib import Status +from grpclib.testing import ChannelFor + +from tests.mocks.components import MockArm +from viam.components.arm.service import ArmRPCService +from viam.errors import GRPCError, ViamGRPCError +from viam.gen.component.arm.v1.arm_grpc import ArmServiceStub +from viam.gen.component.arm.v1.arm_pb2 import IsMovingRequest, IsMovingResponse +from viam.resource.manager import ResourceManager +from viam.rpc.server import _grpc_error_wrapper, _patch_mappings + + +async def raise_exception(): + raise Exception("this is a fake Exception") + + +async def raise_viamgrpcerror(): + raise ViamGRPCError("this is a fake ViamGRPCError") + + +async def raise_grpcerror() -> bool: + raise GRPCError(Status.CANCELLED, "this is a fake GRPCError") + + +class TestServer: + async def test_grpc_error_wrapper(self): + with pytest.raises(Exception) as e_info: + await raise_exception() + assert e_info.value.args[0] == "this is a fake Exception" + wrapped_raise_exception = _grpc_error_wrapper(raise_exception) + with pytest.raises(GRPCError) as e_info: + await wrapped_raise_exception() + assert e_info.value.args[0] == Status.UNKNOWN + assert "Exception - this is a fake Exception" in e_info.value.args[1] + + with pytest.raises(ViamGRPCError) as e_info: + await raise_viamgrpcerror() + assert e_info.value.args[0] == "this is a fake ViamGRPCError" + wrapped_raise_viamgrpcerror = _grpc_error_wrapper(raise_viamgrpcerror) + with pytest.raises(GRPCError) as e_info: + await wrapped_raise_viamgrpcerror() + assert e_info.value.args[0] == Status.INTERNAL + assert e_info.value.args[1] == "this is a fake ViamGRPCError" + + with pytest.raises(GRPCError) as e_info: + await raise_grpcerror() + assert e_info.value.args[0] == Status.CANCELLED + assert e_info.value.args[1] == "this is a fake GRPCError" + wrapped_raise_grpcerror = _grpc_error_wrapper(raise_grpcerror) + with pytest.raises(GRPCError) as e_info: + await wrapped_raise_grpcerror() + assert e_info.value.args[0] == Status.CANCELLED + assert e_info.value.args[1] == "this is a fake GRPCError" + + async def test_patch_mappings(self): + arm = MockArm("arm0") + manager = ResourceManager([arm]) + service = ArmRPCService(manager) + patched_service = _patch_mappings([service])[0] + patched_is_moving_handler = patched_service.__mapping__()["/viam.component.arm.v1.ArmService/IsMoving"] + + async with ChannelFor([patched_service]) as channel: + client = ArmServiceStub(channel) + request = IsMovingRequest(name=arm.name) + response: IsMovingResponse = await client.IsMoving(request) + assert response.is_moving is False + + arm.is_moving = raise_viamgrpcerror + patched_service = _patch_mappings([service])[0] + patched_is_moving_error_handler = patched_service.__mapping__()["/viam.component.arm.v1.ArmService/IsMoving"] + assert patched_is_moving_handler[0] != patched_is_moving_error_handler[0] + assert patched_is_moving_handler[1:3] == patched_is_moving_error_handler[1:3] + + async with ChannelFor([patched_service]) as channel: + client = ArmServiceStub(channel) + request = IsMovingRequest(name=arm.name) + with pytest.raises(GRPCError) as e_info: + await client.IsMoving(request) + assert e_info.value.args[0] == Status.INTERNAL + assert e_info.value.args[1] == "this is a fake ViamGRPCError" diff --git a/tests/test_servo.py b/tests/test_servo.py index b94ecae5e..48ef34821 100644 --- a/tests/test_servo.py +++ b/tests/test_servo.py @@ -1,145 +1,176 @@ -import pytest from grpclib.testing import ChannelFor -from viam.components.generic.service import GenericService -from viam.components.resource_manager import ResourceManager -from viam.components.servo import ServoClient, ServoStatus, create_status -from viam.components.servo.service import ServoService -from viam.errors import NotSupportedError +from viam.components.servo import ServoClient +from viam.components.servo.service import ServoRPCService +from viam.proto.common import DoCommandRequest, DoCommandResponse, GetGeometriesRequest, GetGeometriesResponse from viam.proto.component.servo import ( GetPositionRequest, GetPositionResponse, + IsMovingRequest, + IsMovingResponse, MoveRequest, ServoServiceStub, StopRequest, ) -from viam.utils import message_to_struct +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict -from .mocks.components import MockServo +from . import loose_approx +from .mocks.components import GEOMETRIES, MockServo class TestServo: - servo = MockServo(name="servo") pos = 42 - @pytest.mark.asyncio async def test_move(self): - await self.servo.move(self.pos) + await self.servo.move(self.pos, timeout=1.23, extra={"foo": "move"}) assert self.servo.angle == self.pos + assert self.servo.timeout == loose_approx(1.23) + assert self.servo.extra == {"foo": "move"} - @pytest.mark.asyncio async def test_get_position(self): - new_pos = await self.servo.get_position() + new_pos = await self.servo.get_position(timeout=2.34, extra={"foo": "get_position"}) assert new_pos == self.pos + assert self.servo.timeout == loose_approx(2.34) + assert self.servo.extra == {"foo": "get_position"} - @pytest.mark.asyncio async def test_stop(self): assert self.servo.is_stopped is False - await self.servo.stop() + await self.servo.stop(timeout=3.45, extra={"foo": "stop"}) assert self.servo.is_stopped is True + assert self.servo.timeout == loose_approx(3.45) + assert self.servo.extra == {"foo": "stop"} - @pytest.mark.asyncio async def test_is_moving(self): await self.servo.move(self.pos) assert await self.servo.is_moving() await self.servo.stop() assert not await self.servo.is_moving() - @pytest.mark.asyncio async def test_do(self): - with pytest.raises(NotImplementedError): - await self.servo.do_command({"command": "args"}) + command = {"command": "args"} + resp = await self.servo.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio - async def test_status(self): - await self.servo.move(self.pos) - status = await create_status(self.servo) - assert status.name == self.servo.get_resource_name(self.servo.name) - assert status.status == message_to_struct(ServoStatus(position_deg=self.pos, is_moving=True)) + async def test_get_geometries(self): + geometries = await self.servo.get_geometries() + assert geometries == GEOMETRIES class TestService: + @classmethod + def setup_class(cls): + cls.name = "servo" + cls.servo = MockServo(name=cls.name) + cls.manager = ResourceManager([cls.servo]) + cls.service = ServoRPCService(cls.manager) + cls.pos = 42 - name = "servo" - servo = MockServo(name=name) - manager = ResourceManager([servo]) - service = ServoService(manager) - pos = 42 - - @pytest.mark.asyncio async def test_move(self): async with ChannelFor([self.service]) as channel: client = ServoServiceStub(channel) - request = MoveRequest(name=self.name, angle_deg=self.pos) - await client.Move(request) + request = MoveRequest(name=self.name, angle_deg=self.pos, extra=dict_to_struct({"foo": "move"})) + await client.Move(request, timeout=1.23) assert self.servo.angle == self.pos + assert self.servo.timeout == loose_approx(1.23) + assert self.servo.extra == {"foo": "move"} - @pytest.mark.asyncio async def test_get_position(self): async with ChannelFor([self.service]) as channel: client = ServoServiceStub(channel) - request = GetPositionRequest(name=self.name) - response: GetPositionResponse = await client.GetPosition(request) + request = GetPositionRequest(name=self.name, extra=dict_to_struct({"foo": "get_position"})) + response: GetPositionResponse = await client.GetPosition(request, timeout=2.34) assert response.position_deg == self.pos + assert self.servo.timeout == loose_approx(2.34) + assert self.servo.extra == {"foo": "get_position"} - @pytest.mark.asyncio async def test_stop(self): async with ChannelFor([self.service]) as channel: assert self.servo.is_stopped is False client = ServoServiceStub(channel) - request = StopRequest(name=self.name) - await client.Stop(request) + request = StopRequest(name=self.name, extra=dict_to_struct({"foo": "stop"})) + await client.Stop(request, timeout=3.45) assert self.servo.is_stopped is True + assert self.servo.timeout == loose_approx(3.45) + assert self.servo.extra == {"foo": "stop"} + async def test_is_moving(self): + async with ChannelFor([self.service]) as channel: + assert self.servo.is_stopped is True + self.servo.is_stopped = False + client = ServoServiceStub(channel) + request = IsMovingRequest(name=self.servo.name) + response: IsMovingResponse = await client.IsMoving(request) + assert response.is_moving is True -class TestClient: + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = ServoServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} - name = "servo" - servo = MockServo(name=name) - manager = ResourceManager([servo]) - service = ServoService(manager) - pos = 42 + async def test_get_geometries(self): + async with ChannelFor([self.service]) as channel: + client = ServoServiceStub(channel) + request = GetGeometriesRequest(name=self.servo.name) + response: GetGeometriesResponse = await client.GetGeometries(request) + assert [geometry for geometry in response.geometries] == GEOMETRIES + + +class TestClient: + @classmethod + def setup_class(cls): + cls.name = "servo" + cls.servo = MockServo(name=cls.name) + cls.manager = ResourceManager([cls.servo]) + cls.service = ServoRPCService(cls.manager) + cls.pos = 42 - @pytest.mark.asyncio async def test_move(self): async with ChannelFor([self.service]) as channel: client = ServoClient(self.servo.name, channel) - await client.move(self.pos) + await client.move(self.pos, timeout=1.23, extra={"foo": "move"}) assert self.servo.angle == self.pos + assert self.servo.timeout == loose_approx(1.23) + assert self.servo.extra == {"foo": "move"} - @pytest.mark.asyncio async def test_get_position(self): async with ChannelFor([self.service]) as channel: client = ServoClient(self.servo.name, channel) - new_pos = await client.get_position() + new_pos = await client.get_position(timeout=2.34, extra={"foo": "get_position"}) assert new_pos == self.pos + assert self.servo.timeout == loose_approx(2.34) + assert self.servo.extra == {"foo": "get_position"} - @pytest.mark.asyncio async def test_stop(self): async with ChannelFor([self.service]) as channel: assert self.servo.is_stopped is False client = ServoClient(self.name, channel) - await client.stop() + await client.stop(timeout=3.45, extra={"foo": "stop"}) assert self.servo.is_stopped is True + assert self.servo.timeout == loose_approx(3.45) + assert self.servo.extra == {"foo": "stop"} - @pytest.mark.asyncio async def test_is_moving(self): async with ChannelFor([self.service]) as channel: client = ServoClient(self.name, channel) - with pytest.raises(NotSupportedError): - await client.is_moving() + assert self.servo.is_stopped is True + self.servo.is_stopped = False + assert await client.is_moving() is True - @pytest.mark.asyncio async def test_do(self): - async with ChannelFor([self.service, GenericService(self.manager)]) as channel: + async with ChannelFor([self.service]) as channel: client = ServoClient(self.name, channel) - with pytest.raises(NotImplementedError): - await client.do_command({"command": "args"}) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} - @pytest.mark.asyncio - async def test_status(self): + async def test_get_geometries(self): async with ChannelFor([self.service]) as channel: client = ServoClient(self.name, channel) - with pytest.raises(NotSupportedError): - await create_status(client) + geometries = await client.get_geometries() + assert geometries == GEOMETRIES diff --git a/tests/test_sessions_client.py b/tests/test_sessions_client.py new file mode 100644 index 000000000..b6976e7d6 --- /dev/null +++ b/tests/test_sessions_client.py @@ -0,0 +1,135 @@ +import asyncio +import socket +import time +from concurrent.futures import ThreadPoolExecutor +from typing import List + +import pytest +from grpclib import GRPCError, Status +from grpclib._typing import IServable +from grpclib.server import Server as GRPCServer +from grpclib.testing import ChannelFor + +from viam.errors import MethodNotImplementedError +from viam.rpc.dial import DialOptions, dial +from viam.sessions_client import SESSION_METADATA_KEY, SessionsClient, _SupportedState + +from .mocks.robot import MockRobot + + +@pytest.fixture(scope="function") +def service() -> MockRobot: + return MockRobot() + + +@pytest.fixture(scope="function") +def service_without_session(service: MockRobot) -> MockRobot: + async def StartSession(stream) -> None: + raise MethodNotImplementedError("StartSession").grpc_error + + service.StartSession = StartSession + return service + + +@pytest.fixture(scope="function") +def service_without_heartbeat(service: MockRobot) -> MockRobot: + async def SendSessionHeartbeat(stream) -> None: + raise MethodNotImplementedError("SendSessionHeartbeat").grpc_error + + service.SendSessionHeartbeat = SendSessionHeartbeat + return service + + +async def test_init_client(): + async with ChannelFor([]) as channel: + client = SessionsClient(channel, "", None) + assert client._current_id == "" + assert client._supported == _SupportedState.UNKNOWN + + +async def test_sessions_error(): + async with ChannelFor([]) as channel: + client = SessionsClient(channel, "", None) + + with pytest.raises(GRPCError) as e_info: + assert await client.metadata == {} + + assert e_info.value.status == Status.UNKNOWN + + +async def test_sessions_not_supported(): + async with ChannelFor([]) as channel: + client = SessionsClient(channel, "", None) + client._supported = _SupportedState.FALSE + assert await client.metadata == {} + assert client._supported == _SupportedState.FALSE + + +async def test_sessions_not_implemented(service_without_session: MockRobot): + async with ChannelFor([service_without_session]) as channel: + client = SessionsClient(channel, "", None) + assert await client.metadata == {} + assert client._supported == _SupportedState.FALSE + + +async def test_sessions_heartbeat_disconnect(service_without_heartbeat: MockRobot): + async with ChannelFor([service_without_heartbeat]) as channel: + client = SessionsClient(channel, "", None) + assert await client.metadata == {} + assert client._supported == _SupportedState.UNKNOWN + + +async def _run_server(sock: socket.socket, handlers: List[IServable], shutdown_signal: asyncio.Event): + server = GRPCServer(handlers=handlers) + await server.start(sock=sock) + # shutdown_signal.wait() seems to be bugged <3.9 and blocks the thread, + # so have to do a bit of a busy wait here + while not shutdown_signal.is_set(): + await asyncio.sleep(0.1) + server.close() + + +async def test_sessions_heartbeat_thread_blocked(): + sock = socket.socket() + sock.bind(("", 0)) + + shutdown_signal = asyncio.Event() + m = MockRobot() + t = ThreadPoolExecutor() + t.submit(asyncio.run, _run_server(sock, [m], shutdown_signal)) + + await asyncio.sleep(0.5) + + port = sock.getsockname()[1] + addr = f"localhost:{port}" + options = DialOptions(disable_webrtc=True, insecure=True) + channel = await dial(address=addr, options=options) + + client = SessionsClient(channel.channel, addr, options) + assert await client.metadata == {SESSION_METADATA_KEY: MockRobot.SESSION_ID} + + assert client._supported == _SupportedState.TRUE + assert client._heartbeat_interval and client._heartbeat_interval.total_seconds() == MockRobot.HEARTBEAT_INTERVAL + + time.sleep(3) + shutdown_signal.set() + client.reset() + assert m.heartbeat_count >= 5 + + +async def test_sessions_disabled(service: MockRobot): + async with ChannelFor([service]) as channel: + client = SessionsClient(channel, "", None, disabled=True) + assert await client.metadata == {} + assert client._supported == _SupportedState.UNKNOWN + assert not client._heartbeat_interval + + +async def test_safete_heartbeat_monitored(): + async with ChannelFor([]) as channel: + client = SessionsClient(channel, "", None, disabled=True) + is_monitored = client._is_safety_heartbeat_monitored("/viam.component.arm.v1.ArmService/MoveToPosition") + assert is_monitored is True + + is_monitored = client._is_safety_heartbeat_monitored("/viam.component.camera.v1.CameraService/GetImage") + assert is_monitored is False diff --git a/tests/test_slam.py b/tests/test_slam.py new file mode 100644 index 000000000..258ecbae6 --- /dev/null +++ b/tests/test_slam.py @@ -0,0 +1,152 @@ +from typing import List + +from grpclib.testing import ChannelFor + +from viam.proto.common import DoCommandRequest, DoCommandResponse +from viam.proto.service.slam import ( + GetInternalStateRequest, + GetInternalStateResponse, + GetPointCloudMapRequest, + GetPointCloudMapResponse, + GetPositionRequest, + GetPositionResponse, + GetPropertiesRequest, + GetPropertiesResponse, + SLAMServiceStub, +) +from viam.resource.manager import ResourceManager +from viam.services.slam import SLAMClient, SLAMRPCService +from viam.utils import dict_to_struct, struct_to_dict + +from .mocks.services import MockSLAM + + +class TestSLAMService: + name = "slam" + slam = MockSLAM(name="slam") + + async def test_get_internal_state_chunks(self): + chunks = await self.slam.get_internal_state() + assert chunks == MockSLAM.INTERNAL_STATE_CHUNKS + + async def test_get_point_cloud_map(self): + chunks = await self.slam.get_point_cloud_map() + assert chunks == MockSLAM.POINT_CLOUD_PCD_CHUNKS + + async def test_get_point_cloud_map_return_edited(self): + chunks = await self.slam.get_point_cloud_map(return_edited_map=True) + assert chunks == MockSLAM.POINT_CLOUD_PCD_CHUNKS_EDITED + + async def test_get_position(self): + pos = await self.slam.get_position() + assert pos == MockSLAM.POSITION + + async def test_do(self): + command = {"command": "args"} + resp = await self.slam.do_command(command) + assert resp == {"command": command} + + async def test_get_properties(self): + properties = await self.slam.get_properties() + assert properties.cloud_slam == MockSLAM.CLOUD_SLAM + assert properties.mapping_mode == MockSLAM.MAPPING_MODE + assert properties.internal_state_file_type == MockSLAM.INTERNAL_STATE_FILE_TYPE + assert properties.sensor_info == MockSLAM.SENSOR_INFO + + +class TestService: + @classmethod + def setup_class(cls): + cls.name = "slam" + cls.slam = MockSLAM(name=cls.name) + cls.manager = ResourceManager([cls.slam]) + cls.service = SLAMRPCService(cls.manager) + + async def test_get_internal_state(self): + async with ChannelFor([self.service]) as channel: + client = SLAMServiceStub(channel) + request = GetInternalStateRequest(name=self.name) + response: List[GetInternalStateResponse] = await client.GetInternalState(request) + for i, chunk in enumerate(response): + assert chunk.internal_state_chunk == MockSLAM.INTERNAL_STATE_CHUNKS[i] + + async def test_get_point_cloud_map(self): + async with ChannelFor([self.service]) as channel: + client = SLAMServiceStub(channel) + request = GetPointCloudMapRequest(name=self.name) + response: List[GetPointCloudMapResponse] = await client.GetPointCloudMap(request) + for i, chunk in enumerate(response): + assert chunk.point_cloud_pcd_chunk == MockSLAM.POINT_CLOUD_PCD_CHUNKS[i] + + async def test_get_position(self): + async with ChannelFor([self.service]) as channel: + client = SLAMServiceStub(channel) + request = GetPositionRequest(name=self.name) + response: GetPositionResponse = await client.GetPosition(request) + assert response.pose == MockSLAM.POSITION + + async def test_get_properties(self): + async with ChannelFor([self.service]) as channel: + client = SLAMServiceStub(channel) + request = GetPropertiesRequest(name=self.name) + response: GetPropertiesResponse = await client.GetProperties(request) + assert response.cloud_slam == MockSLAM.CLOUD_SLAM + assert response.mapping_mode == MockSLAM.MAPPING_MODE + assert response.internal_state_file_type == MockSLAM.INTERNAL_STATE_FILE_TYPE + assert response.sensor_info == MockSLAM.SENSOR_INFO + + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = SLAMServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=self.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + +class TestClient: + @classmethod + def setup_class(cls): + cls.name = "slam" + cls.slam = MockSLAM(name=cls.name) + cls.manager = ResourceManager([cls.slam]) + cls.service = SLAMRPCService(cls.manager) + + async def test_get_internal_state(self): + async with ChannelFor([self.service]) as channel: + client = SLAMClient(self.name, channel) + response = await client.get_internal_state() + assert len(response) == len(MockSLAM.INTERNAL_STATE_CHUNKS) + for i, chunk in enumerate(response): + assert chunk == MockSLAM.INTERNAL_STATE_CHUNKS[i] + + async def test_get_point_cloud_map(self): + async with ChannelFor([self.service]) as channel: + client = SLAMClient(self.name, channel) + response = await client.get_point_cloud_map() + assert len(response) == len(MockSLAM.POINT_CLOUD_PCD_CHUNKS) + for i, chunk in enumerate(response): + assert chunk == MockSLAM.POINT_CLOUD_PCD_CHUNKS[i] + + async def test_get_position(self): + async with ChannelFor([self.service]) as channel: + client = SLAMClient(self.name, channel) + response = await client.get_position() + assert response == MockSLAM.POSITION + + async def test_get_properties(self): + async with ChannelFor([self.service]) as channel: + client = SLAMClient(self.name, channel) + properties = await client.get_properties() + assert properties.cloud_slam == MockSLAM.CLOUD_SLAM + assert properties.mapping_mode == MockSLAM.MAPPING_MODE + assert properties.internal_state_file_type == MockSLAM.INTERNAL_STATE_FILE_TYPE + assert properties.sensor_info == MockSLAM.SENSOR_INFO + + async def test_do(self): + async with ChannelFor([self.service]) as channel: + client = SLAMClient(self.name, channel) + command = {"command": "args"} + response = await client.do_command(command) + assert response == {"command": command} diff --git a/tests/test_switch.py b/tests/test_switch.py new file mode 100644 index 000000000..868f6f272 --- /dev/null +++ b/tests/test_switch.py @@ -0,0 +1,150 @@ +import pytest +from grpclib.testing import ChannelFor + +from viam.components.switch import SwitchClient +from viam.components.switch.service import SwitchRPCService +from viam.gen.component.switch.v1.switch_pb2 import ( + GetNumberOfPositionsRequest, + GetNumberOfPositionsResponse, + GetPositionRequest, + GetPositionResponse, + SetPositionRequest, +) +from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, +) +from viam.proto.component.switch import SwitchServiceStub +from viam.resource.manager import ResourceManager +from viam.utils import dict_to_struct, struct_to_dict + +from . import loose_approx +from .mocks.components import MockSwitch + +DEFAULT_POSITION = 0 +DEFAULT_NUMBER_OF_POSITIONS = 0 +EXTRA_PARAMS = {"foo": "bar", "baz": [1, 2, 3]} + + +@pytest.fixture(scope="function") +def switch() -> MockSwitch: + return MockSwitch(name="sensor", number_of_positions=DEFAULT_NUMBER_OF_POSITIONS, position=DEFAULT_POSITION) + + +class TestSwitch: + pos = 2 + + async def test_set_position(self, switch): + await switch.set_position(self.pos, timeout=1.23, extra=EXTRA_PARAMS) + assert switch.position == self.pos + assert switch.timeout == loose_approx(1.23) + assert switch.extra == EXTRA_PARAMS + + async def test_get_position(self, switch): + assert switch.extra is None + position = await switch.get_position(timeout=2.34, extra=EXTRA_PARAMS) + assert position == DEFAULT_NUMBER_OF_POSITIONS + assert switch.extra == EXTRA_PARAMS + assert switch.timeout == loose_approx(2.34) + + async def test_get_number_of_positions(self, switch): + number_of_positions = await switch.get_number_of_positions(timeout=3.45, extra=EXTRA_PARAMS) + assert number_of_positions == DEFAULT_NUMBER_OF_POSITIONS + assert switch.extra == EXTRA_PARAMS + assert switch.timeout == loose_approx(3.45) + + async def test_do(self, switch): + command = {"command": "args"} + resp = await switch.do_command(command) + assert resp == {"command": command} + + +@pytest.fixture(scope="function") +def manager(switch) -> ResourceManager: + return ResourceManager([switch]) + + +@pytest.fixture(scope="function") +def service(manager) -> SwitchRPCService: + return SwitchRPCService(manager) + + +class TestService: + pos = 2 + + async def test_set_position(self, switch, service): + async with ChannelFor([service]) as channel: + client = SwitchServiceStub(channel) + request = SetPositionRequest(name=switch.name, position=self.pos, extra=dict_to_struct(EXTRA_PARAMS)) + assert switch.extra is None + await client.SetPosition(request, timeout=1.23) + assert switch.position == self.pos + assert switch.extra == EXTRA_PARAMS + assert switch.timeout == loose_approx(1.23) + + async def test_get_position(self, switch, service): + async with ChannelFor([service]) as channel: + client = SwitchServiceStub(channel) + request = GetPositionRequest(name=switch.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert switch.extra is None + result: GetPositionResponse = await client.GetPosition(request, timeout=2.34) + assert result.position == DEFAULT_POSITION + assert switch.extra == EXTRA_PARAMS + assert switch.timeout == loose_approx(2.34) + + async def test_get_number_of_positions(self, switch, service): + async with ChannelFor([service]) as channel: + client = SwitchServiceStub(channel) + request = GetNumberOfPositionsRequest(name=switch.name, extra=dict_to_struct(EXTRA_PARAMS)) + assert switch.extra is None + result: GetNumberOfPositionsResponse = await client.GetNumberOfPositions(request, timeout=4.56) + assert result.number_of_positions == DEFAULT_NUMBER_OF_POSITIONS + assert switch.extra == EXTRA_PARAMS + assert switch.timeout == loose_approx(4.56) + + async def test_do(self, switch: MockSwitch, service: SwitchRPCService): + async with ChannelFor([service]) as channel: + client = SwitchServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=switch.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + result = struct_to_dict(response.result) + assert result == {"command": command} + + +class TestClient: + pos = 2 + + async def test_set_position(self, switch, service): + async with ChannelFor([service]) as channel: + client = SwitchClient(switch.name, channel) + assert switch.extra is None + await client.set_position(position=self.pos, timeout=3.45, extra=EXTRA_PARAMS) + assert switch.position == self.pos + assert switch.extra == EXTRA_PARAMS + assert switch.timeout == loose_approx(3.45) + + async def test_get_position(self, switch, service): + async with ChannelFor([service]) as channel: + client = SwitchClient(switch.name, channel) + assert switch.extra is None + value_position = await client.get_position(timeout=3.45, extra=EXTRA_PARAMS) + assert DEFAULT_POSITION == value_position + assert switch.extra == EXTRA_PARAMS + assert switch.timeout == loose_approx(3.45) + + async def test_get_number_of_positions(self, switch, service): + async with ChannelFor([service]) as channel: + client = SwitchClient(switch.name, channel) + assert switch.extra is None + value_number_of_positions = await client.get_number_of_positions(timeout=3.45, extra=EXTRA_PARAMS) + assert DEFAULT_NUMBER_OF_POSITIONS == value_number_of_positions + assert switch.extra == EXTRA_PARAMS + assert switch.timeout == loose_approx(3.45) + + async def test_do(self, switch, manager, service): + async with ChannelFor([service]) as channel: + client = SwitchClient(switch.name, channel) + command = {"command": "args"} + resp = await client.do_command(command) + assert resp == {"command": command} diff --git a/tests/test_utils.py b/tests/test_utils.py index 4e964c249..2ddef12e5 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,15 +1,14 @@ +import asyncio + import pytest from google.protobuf.json_format import ParseError from google.protobuf.struct_pb2 import ListValue, Struct, Value +from google.protobuf.timestamp_pb2 import Timestamp -from viam.proto.common import ( - ActuatorStatus, - GeoPoint, - Orientation, - ResourceName, - Vector3, -) +from viam.proto.common import ActuatorStatus, GeoPoint, Orientation, ResourceName, Vector3 from viam.utils import ( + PointerCounter, + datetime_to_timestamp, dict_to_struct, message_to_struct, primitive_to_value, @@ -76,7 +75,7 @@ class X: v = X() with pytest.raises(TypeError): - primitive_to_value(v) + primitive_to_value(v) # type: ignore def test_value_to_primitive(): @@ -190,7 +189,7 @@ def test_struct_to_message_error(): def test_dict_to_struct(): expected = {"a": 1, "b": "2", "c": [3, 4, 5], "d": True, "e": {"1": 2, "3": "4", "5": [6, 7, 8], "9": False}} - # Just testing that this doesn't error, since this calls `primitive_to_value` + # Just testing that this doesn't error, since this calls ``primitive_to_value`` _ = dict_to_struct(expected) # Unsupported type @@ -209,6 +208,15 @@ def test_struct_to_dict(): assert struct_to_dict(struct) == expected +def test_datetime_to_timestamp(): + expected = Timestamp( + seconds=1686045600, + ) + datetime = expected.ToDatetime() + + assert datetime_to_timestamp(datetime) == expected + + def test_sensor_readings(): expected = { "VEC3": Vector3(x=0.1, y=2.3, z=4.5), @@ -221,3 +229,25 @@ def test_sensor_readings(): response = sensor_readings_value_to_native(test) assert response == expected + + +async def test_pointer_counter(): + counter = PointerCounter() + + assert counter.count == 0 + + counter.increment() + assert counter.count == 1 + + async def final_test(): + assert counter.count > 0 + await counter.wait() + assert counter.count == 0 + + task = asyncio.get_running_loop().create_task(final_test()) + + await asyncio.sleep(0) # Needed to start the final_test task + + counter.decrement() + + await task diff --git a/tests/test_viam_client.py b/tests/test_viam_client.py new file mode 100644 index 000000000..83210e49e --- /dev/null +++ b/tests/test_viam_client.py @@ -0,0 +1,169 @@ +import os +import random +from unittest.mock import patch +from uuid import uuid4 + +import pytest +from grpclib.testing import ChannelFor + +from viam.app.app_client import RobotPart +from viam.app.viam_client import ViamClient +from viam.rpc.dial import Credentials, DialOptions + + +class TestViamClient: + async def test_requires_credentials(self): + with pytest.raises(ValueError): + await ViamClient.create_from_dial_options(DialOptions()) + + async def test_disallows_robot_secret(self): + with pytest.raises(ValueError): + creds = Credentials("robot-secret", "SOME_ROBOT_SECRET") + await ViamClient.create_from_dial_options(DialOptions(credentials=creds)) + + async def test_requires_auth_entity(self): + with pytest.raises(ValueError): + creds = Credentials("api-key", "SOME_API_KEY") + await ViamClient.create_from_dial_options(DialOptions(credentials=creds)) + + async def test_sets_fields(self): + async with ChannelFor([]) as channel: + with patch("viam.app.viam_client._dial_app") as patched_dial: + patched_dial.return_value = channel + with patch("viam.app.viam_client._get_access_token") as patched_auth: + ACCESS_TOKEN = "MY_ACCESS_TOKEN" + patched_auth.return_value = ACCESS_TOKEN + + creds = Credentials("robot-location-secret", "SOME_LOCATION_SECRET") + auth_entity = "SOME.AUTH.ENTITY" + DIAL_OPTIONS = DialOptions(credentials=creds, auth_entity=auth_entity) + + client = await ViamClient.create_from_dial_options(DIAL_OPTIONS) + + assert client._dial_options == DIAL_OPTIONS + assert DIAL_OPTIONS.auth_entity is not None + assert client._location_id == DIAL_OPTIONS.auth_entity.split(".")[1] + assert client._metadata == {"authorization": f"Bearer {ACCESS_TOKEN}"} + + async def test_clients(self): + async with ChannelFor([]) as channel: + with patch("viam.app.viam_client._dial_app") as patched_dial: + patched_dial.return_value = channel + with patch("viam.app.viam_client._get_access_token") as patched_auth: + ACCESS_TOKEN = "MY_ACCESS_TOKEN" + METADATA = {"authorization": f"Bearer {ACCESS_TOKEN}"} + patched_auth.return_value = ACCESS_TOKEN + + creds = Credentials("robot-location-secret", "SOME_LOCATION_SECRET") + opts = DialOptions(credentials=creds, auth_entity="SOME.AUTH.ENTITY") + + client = await ViamClient.create_from_dial_options(opts) + + assert client.data_client._channel == channel + assert client.data_client._metadata == METADATA + + assert client.app_client._channel == channel + assert client.app_client._metadata == METADATA + + assert client.ml_training_client._channel == channel + assert client.ml_training_client._metadata == METADATA + + assert client.billing_client._channel == channel + assert client.billing_client._metadata == METADATA + + assert client.provisioning_client._channel == channel + assert client.provisioning_client._metadata == METADATA + + async def test_client_from_env_vars(self): + async with ChannelFor([]) as channel: + with patch("viam.app.viam_client._dial_app") as patched_dial: + patched_dial.return_value = channel + with patch("viam.app.viam_client._get_access_token") as patched_auth: + ACCESS_TOKEN = "MY_ACCESS_TOKEN" + METADATA = {"authorization": f"Bearer {ACCESS_TOKEN}"} + patched_auth.return_value = ACCESS_TOKEN + + os.environ["VIAM_API_KEY"] = "MY_API_KEY" + os.environ["VIAM_API_KEY_ID"] = str(uuid4()) + + client = await ViamClient.create_from_env_vars() + + assert client.data_client._channel == channel + assert client.data_client._metadata == METADATA + + assert client.app_client._channel == channel + assert client.app_client._metadata == METADATA + + assert client.ml_training_client._channel == channel + assert client.ml_training_client._metadata == METADATA + + assert client.billing_client._channel == channel + assert client.billing_client._metadata == METADATA + + assert client.provisioning_client._channel == channel + assert client.provisioning_client._metadata == METADATA + + async def test_closes(self): + async with ChannelFor([]) as channel: + with patch.object(channel, "close"): + with patch("viam.app.viam_client._dial_app") as patched_dial: + patched_dial.return_value = channel + with patch("viam.app.viam_client._get_access_token") as patched_auth: + patched_auth.return_value = "MY_ACCESS_TOKEN" + + creds = Credentials("robot-location-secret", "SOME_LOCATION_SECRET") + opts = DialOptions(credentials=creds, auth_entity="SOME.AUTH.ENTITY") + + client = await ViamClient.create_from_dial_options(opts) + + assert client._closed is False + client.close() + assert client._closed is True + + class TestConnectToMachine: + @pytest.fixture + async def client(self): + async with ChannelFor([]) as channel: + with patch("viam.app.viam_client._dial_app") as patched_dial: + patched_dial.return_value = channel + with patch("viam.app.viam_client._get_access_token") as patched_auth: + patched_auth.return_value = "MY_ACCESS_TOKEN" + + creds = Credentials("api-key", "MY_API_KEY") + opts = DialOptions(credentials=creds, auth_entity=str(uuid4())) + + yield await ViamClient.create_from_dial_options(opts) + + async def test_requires_address_or_id(self, client: ViamClient): + with pytest.raises(ValueError): + await client.connect_to_machine() + + async def test_address_supersedes_id(self, client: ViamClient): + with patch("viam.app.app_client.AppClient.get_robot_parts") as get_robot_parts: + with patch("viam.app.viam_client.RobotClient.at_address") as get_robot_client: + ADDRESS = "MACHINE_ADDRESS" + await client.connect_to_machine(address=ADDRESS, id="MACHINE_ID") + + get_robot_parts.assert_not_called() + assert get_robot_client.call_args.args[0] == ADDRESS + + async def test_gets_main_part_address(self, client: ViamClient): + with patch("viam.app.app_client.AppClient.get_robot_parts") as get_robot_parts: + MAIN_PART = RobotPart() + MAIN_PART.fqdn = "main.part.fqdn" + MAIN_PART.main_part = True + + ROBOT_PARTS = [MAIN_PART] + for _ in range(10): + part = RobotPart() + part.main_part = False + ROBOT_PARTS.append(part) + random.shuffle(ROBOT_PARTS) + + get_robot_parts.return_value = ROBOT_PARTS + + with patch("viam.app.viam_client.RobotClient.at_address") as get_robot_client: + MACHINE_ID = "MACHINE_ID" + await client.connect_to_machine(id=MACHINE_ID) + get_robot_parts.assert_called_once_with(MACHINE_ID) + assert get_robot_client.call_args.args[0] == MAIN_PART.fqdn diff --git a/tests/test_vision_service.py b/tests/test_vision_service.py index 0761bbde5..72da84e76 100644 --- a/tests/test_vision_service.py +++ b/tests/test_vision_service.py @@ -1,11 +1,14 @@ from random import random -from typing import Dict, Mapping, Sequence, Union import pytest from grpclib.testing import ChannelFor from PIL import Image +from viam.media.utils.pil import pil_to_viam_image +from viam.media.video import CameraMimeType, ViamImage from viam.proto.common import ( + DoCommandRequest, + DoCommandResponse, GeometriesInFrame, Geometry, PointCloudObject, @@ -13,17 +16,32 @@ RectangularPrism, Vector3, ) -from viam.proto.service.vision import VisionServiceBase -from viam.services.vision import ( - Detection, - Classification, - VisModelConfig, - VisModelType, - VisionServiceClient, +from viam.proto.service.vision import ( + CaptureAllFromCameraRequest, + CaptureAllFromCameraResponse, + GetClassificationsFromCameraRequest, + GetClassificationsFromCameraResponse, + GetClassificationsRequest, + GetClassificationsResponse, + GetDetectionsFromCameraRequest, + GetDetectionsFromCameraResponse, + GetDetectionsRequest, + GetDetectionsResponse, + GetObjectPointCloudsRequest, + GetObjectPointCloudsResponse, + GetPropertiesRequest, + GetPropertiesResponse, + VisionServiceStub, ) +from viam.resource.manager import ResourceManager +from viam.services.vision import Classification, Detection, Vision, VisionClient +from viam.services.vision.service import VisionRPCService +from viam.utils import dict_to_struct, struct_to_dict -from .mocks.services import MockVisionService +from .mocks.services import MockVision +i = Image.new("RGBA", (100, 100), "#AABBCCDD") +IMAGE = pil_to_viam_image(i, CameraMimeType.JPEG) DETECTORS = [ "detector-0", "detector-1", @@ -58,28 +76,7 @@ "segmenter-0", "segmenter-1", ] -MODEL_SCHEMA: Dict[str, Mapping[str, Union[str, int, float, bool, Sequence, Mapping]]] = { - VisModelType.CLASSIFIER_TENSORFLOW: { - "parameter-0": "float64", - "parameter-1": "string", - }, - VisModelType.CLASSIFIER_TFLITE: { - "parameter-0": "int", - "parameter-1": "string", - }, - VisModelType.DETECTOR_COLOR: { - "parameter-0": "int", - "parameter-1": "float64", - }, - VisModelType.DETECTOR_TENSORFLOW: { - "parameter-0": "string", - "parameter-1": "string", - }, - VisModelType.DETECTOR_TF_LITE: { - "parameter-0": "string", - "parameter-1": "float64", - }, -} + POINT_CLOUDS = [ PointCloudObject( point_cloud=b"THIS IS A POINT CLOUD", @@ -107,132 +104,269 @@ ), ] +VISION_IMAGE = ViamImage(bytes([0, 100]), CameraMimeType.JPEG) + +PROPERTIES = Vision.Properties( + classifications_supported=True, + detections_supported=True, + object_point_clouds_supported=True, +) + + VISION_SERVICE_NAME = "vision1" @pytest.fixture(scope="function") -def service() -> VisionServiceBase: - return MockVisionService( +def vision() -> MockVision: + return MockVision( + VISION_SERVICE_NAME, detectors=DETECTORS, detections=DETECTIONS, classifiers=CLASSIFIERS, classifications=CLASSIFICATIONS, segmenters=SEGMENTERS, point_clouds=POINT_CLOUDS, - model_schema=MODEL_SCHEMA, + image=VISION_IMAGE, + properties=PROPERTIES, ) -class TestClient: - @pytest.mark.asyncio - async def test_get_detectors(self, service: VisionServiceBase): +@pytest.fixture(scope="function") +def service(vision: MockVision) -> VisionRPCService: + rm = ResourceManager([vision]) + return VisionRPCService(rm) + + +class TestVision: + async def test_get_properties(self, vision: MockVision): + extra = {"foo": "get_properties"} + response = await vision.get_properties(extra=extra) + assert response == PROPERTIES + assert vision.extra == extra + + async def test_capture_all_from_camera(self, vision: MockVision): + extra = {"foo": "capture_all_from_camera"} + response = await vision.capture_all_from_camera( + "fake-camera", + return_image=True, + return_detections=True, + extra=extra, + ) + assert response.image is not None + assert response.image.data == VISION_IMAGE.data + assert response.image.mime_type == VISION_IMAGE.mime_type + assert response.detections == DETECTIONS + assert response.classifications is None + assert response.objects is None + assert vision.extra == extra + + async def test_get_detections_from_camera(self, vision: MockVision): + extra = {"foo": "get_detections_from_camera"} + response = await vision.get_detections_from_camera("fake-camera", extra=extra) + assert response == DETECTIONS + assert vision.extra == extra + + async def test_get_detections(self, vision: MockVision): + extra = {"foo": "get_detections"} + response = await vision.get_detections(IMAGE, extra=extra) + assert response == DETECTIONS + assert vision.extra == extra + + async def test_get_classifications_from_camera(self, vision: MockVision): + extra = {"foo": "get_classifications_from_camera"} + response = await vision.get_classifications_from_camera("fake-camera", 1, extra=extra) + assert response == CLASSIFICATIONS + assert vision.extra == extra + + async def test_get_classifications(self, vision: MockVision): + extra = {"foo": "get_classifications"} + response = await vision.get_classifications(IMAGE, 1, extra=extra) + assert response == CLASSIFICATIONS + assert vision.extra == extra + + async def test_get_object_point_clouds(self, vision: MockVision): + extra = {"foo": "get_object_point_clouds"} + response = await vision.get_object_point_clouds("camera", extra=extra) + assert response == POINT_CLOUDS + assert vision.extra == extra + + async def test_do(self, vision: MockVision): + command = {"command": "args"} + response = await vision.do_command(command) + assert response["cmd"] == command + + +class TestService: + async def test_capture_all_from_camera(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - response = await client.get_detector_names() - assert response == DETECTORS + client = VisionServiceStub(channel) + extra = {"foo": "capture_all_from_camera"} + request = CaptureAllFromCameraRequest( + name=vision.name, camera_name="fake-camera", return_image=True, return_classifications=True, extra=dict_to_struct(extra) + ) + response: CaptureAllFromCameraResponse = await client.CaptureAllFromCamera(request) + assert response.image.image == VISION_IMAGE.data + assert response.image.format == VISION_IMAGE.mime_type.to_proto() + assert response.classifications == CLASSIFICATIONS + assert response.detections == [] + assert response.objects == [] + assert vision.extra == extra - @pytest.mark.asyncio - async def test_add_detector(self, service: VisionServiceBase): + async def test_get_properties(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - await client.add_detector(VisModelConfig("detector-2", VisModelType.DETECTOR_TENSORFLOW, {"foo": "bar"})) - response = await client.get_detector_names() - assert response[-1] == "detector-2" + client = VisionServiceStub(channel) + extra = {"foo": "get_properties"} + request = GetPropertiesRequest(name=vision.name, extra=dict_to_struct(extra)) + response: GetPropertiesResponse = await client.GetProperties(request) + assert response.classifications_supported == PROPERTIES.classifications_supported + assert response.detections_supported == PROPERTIES.detections_supported + assert response.object_point_clouds_supported == PROPERTIES.object_point_clouds_supported + assert vision.extra == extra - @pytest.mark.asyncio - async def test_remove_detector(self, service: VisionServiceBase): + async def test_get_detections_from_camera(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - await client.remove_detector("detector-1") - response = await client.get_detector_names() - assert "detector-1" not in response + client = VisionServiceStub(channel) + extra = {"foo": "get_detections_from_camera"} + request = GetDetectionsFromCameraRequest(name=vision.name, camera_name="fake-camera", extra=dict_to_struct(extra)) + response: GetDetectionsFromCameraResponse = await client.GetDetectionsFromCamera(request) + assert response.detections == DETECTIONS + assert vision.extra == extra - @pytest.mark.asyncio - async def test_get_detections_from_camera(self, service: VisionServiceBase): + async def test_get_detections(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - response = await client.get_detections_from_camera("fake-camera", "fake-detector") - assert response == DETECTIONS + client = VisionServiceStub(channel) + extra = {"foo": "get_detections"} + request = GetDetectionsRequest( + name=vision.name, + image=IMAGE.data, + width=100, + height=100, + mime_type=CameraMimeType.JPEG, + extra=dict_to_struct(extra), + ) + response: GetDetectionsResponse = await client.GetDetections(request) + assert response.detections == DETECTIONS + assert vision.extra == extra - @pytest.mark.asyncio - async def test_get_detections(self, service: VisionServiceBase): + async def test_get_classifications_from_camera(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - image = Image.new("RGB", (100, 100), "#AABBCCDD") - response = await client.get_detections(image, "fake-detector") - assert response == DETECTIONS + client = VisionServiceStub(channel) + extra = {"foo": "get_classifications_from_camera"} + request = GetClassificationsFromCameraRequest(name=vision.name, camera_name="fake-camera", n=1, extra=dict_to_struct(extra)) + response: GetClassificationsFromCameraResponse = await client.GetClassificationsFromCamera(request) + assert response.classifications == CLASSIFICATIONS + assert vision.extra == extra - @pytest.mark.asyncio - async def test_get_classifiers(self, service: VisionServiceBase): + async def test_get_classifications(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - response = await client.get_classifier_names() - assert response == CLASSIFIERS + client = VisionServiceStub(channel) + extra = {"foo": "get_classifications"} + request = GetClassificationsRequest( + name=vision.name, + image=IMAGE.data, + width=100, + height=100, + mime_type=CameraMimeType.JPEG, + n=1, + extra=dict_to_struct(extra), + ) + response: GetClassificationsResponse = await client.GetClassifications(request) + assert response.classifications == CLASSIFICATIONS + assert vision.extra == extra - @pytest.mark.asyncio - async def test_add_classifier(self, service: VisionServiceBase): + async def test_get_object_point_clouds(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - await client.add_classifier(VisModelConfig("classifier-2", VisModelType.DETECTOR_TENSORFLOW, {"foo": "bar"})) - response = await client.get_classifier_names() - assert response[-1] == "classifier-2" + client = VisionServiceStub(channel) + extra = {"foo": "get_object_point_clouds"} + request = GetObjectPointCloudsRequest( + name=vision.name, + camera_name="camera", + mime_type=CameraMimeType.PCD, + extra=dict_to_struct(extra), + ) + response: GetObjectPointCloudsResponse = await client.GetObjectPointClouds(request) + assert response.objects == POINT_CLOUDS + assert vision.extra == extra - @pytest.mark.asyncio - async def test_remove_classifier(self, service: VisionServiceBase): + async def test_do(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - await client.remove_classifier("classifier-1") - response = await client.get_classifier_names() - assert "classifier-1" not in response + client = VisionServiceStub(channel) + command = {"command": "args"} + request = DoCommandRequest(name=vision.name, command=dict_to_struct(command)) + response: DoCommandResponse = await client.DoCommand(request) + assert struct_to_dict(response.result)["cmd"] == command + - @pytest.mark.asyncio - async def test_get_classifications_from_camera(self, service: VisionServiceBase): +class TestClient: + async def test_get_properties(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - response = await client.get_classifications_from_camera("fake-camera", "fake-classifier", 1) - assert response == CLASSIFICATIONS + client = VisionClient(VISION_SERVICE_NAME, channel) + extra = {"foo": "get_properties"} + response = await client.get_properties(extra=extra) + assert response == PROPERTIES + assert vision.extra == extra - @pytest.mark.asyncio - async def test_get_classifications(self, service: VisionServiceBase): + async def test_capture_all_from_camera(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - image = Image.new("RGB", (100, 100), "#AABBCCDD") - response = await client.get_classifications(image, "fake-classifier") - assert response == CLASSIFICATIONS + client = VisionClient(VISION_SERVICE_NAME, channel) + extra = {"foo": "capture_all_from_camera"} + response = await client.capture_all_from_camera( + "fake-camera", + return_image=True, + return_object_point_clouds=True, + extra=extra, + ) + assert response.image is not None + assert response.image.data == VISION_IMAGE.data + assert response.image.mime_type == VISION_IMAGE.mime_type + assert response.detections is None + assert response.classifications is None + assert response.objects == POINT_CLOUDS + assert vision.extra == extra - @pytest.mark.asyncio - async def test_get_segmenters(self, service: VisionServiceBase): + async def test_get_detections_from_camera(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - response = await client.get_segmenter_names() - assert response == SEGMENTERS + client = VisionClient(VISION_SERVICE_NAME, channel) + extra = {"foo": "get_detections_from_camera"} + response = await client.get_detections_from_camera("fake-camera", extra=extra) + assert response == DETECTIONS + assert vision.extra == extra - @pytest.mark.asyncio - async def test_add_segmenter(self, service: VisionServiceBase): + async def test_get_detections(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - await client.add_segmenter(VisModelConfig("segmenter-2", VisModelType.DETECTOR_TENSORFLOW, {"foo": "bar"})) - response = await client.get_segmenter_names() - assert response[-1] == "segmenter-2" + client = VisionClient(VISION_SERVICE_NAME, channel) + extra = {"foo": "get_detections"} + response = await client.get_detections(IMAGE, extra=extra) + assert response == DETECTIONS + assert vision.extra == extra - @pytest.mark.asyncio - async def test_remove_segmenter(self, service: VisionServiceBase): + async def test_get_classifications_from_camera(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - await client.remove_segmenter("segmenter-1") - response = await client.get_segmenter_names() - assert "segmenter-1" not in response + client = VisionClient(VISION_SERVICE_NAME, channel) + extra = {"foo": "get_classifications_from_camera"} + response = await client.get_classifications_from_camera("fake-camera", 1, extra=extra) + assert response == CLASSIFICATIONS + assert vision.extra == extra - @pytest.mark.asyncio - async def test_get_model_parameters_schema(self, service: VisionServiceBase): + async def test_get_classifications(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - response = await client.get_model_parameters_schema(VisModelType.DETECTOR_COLOR) - assert response == MODEL_SCHEMA[VisModelType.DETECTOR_COLOR] + client = VisionClient(VISION_SERVICE_NAME, channel) + extra = {"foo": "get_classifications"} + response = await client.get_classifications(IMAGE, 1, extra=extra) + assert response == CLASSIFICATIONS + assert vision.extra == extra - @pytest.mark.asyncio - async def test_get_object_point_clouds(self, service: VisionServiceBase): + async def test_get_object_point_clouds(self, vision: MockVision, service: VisionRPCService): async with ChannelFor([service]) as channel: - client = VisionServiceClient(VISION_SERVICE_NAME, channel) - response = await client.get_object_point_clouds("camera", "segmenter", {}) + client = VisionClient(VISION_SERVICE_NAME, channel) + extra = {"foo": "get_object_point_clouds"} + response = await client.get_object_point_clouds("camera", extra=extra) assert response == POINT_CLOUDS + assert vision.extra == extra + + async def test_do(self, service: VisionRPCService): + async with ChannelFor([service]) as channel: + client = VisionClient(VISION_SERVICE_NAME, channel) + command = {"command": "args"} + response = await client.do_command(command) + assert response["cmd"] == command diff --git a/uv.lock b/uv.lock new file mode 100644 index 000000000..24ed62e33 --- /dev/null +++ b/uv.lock @@ -0,0 +1,2523 @@ +version = 1 +revision = 1 +requires-python = ">=3.8.1" +resolution-markers = [ + "python_full_version < '3.9'", + "python_full_version >= '3.9' and python_full_version < '3.12'", + "python_full_version == '3.12.*'", + "python_full_version >= '3.13'", +] + +[[package]] +name = "alabaster" +version = "0.7.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/71/a8ee96d1fd95ca04a0d2e2d9c4081dac4c2d2b12f7ddb899c8cb9bfd1532/alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2", size = 11454 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/88/c7083fc61120ab661c5d0b82cb77079fc1429d3f913a456c1c82cf4658f7/alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3", size = 13857 }, +] + +[[package]] +name = "anyascii" +version = "0.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9f/52/93b9ea99063f7cf37fb67f5e3f49480686cbe7f228c48b9d713326223b6e/anyascii-0.3.2.tar.gz", hash = "sha256:9d5d32ef844fe225b8bc7cba7f950534fae4da27a9bf3a6bea2cb0ea46ce4730", size = 214052 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4f/7b/a9a747e0632271d855da379532b05a62c58e979813814a57fa3b3afeb3a4/anyascii-0.3.2-py3-none-any.whl", hash = "sha256:3b3beef6fc43d9036d3b0529050b0c48bfad8bc960e9e562d7223cfb94fe45d4", size = 289923 }, +] + +[[package]] +name = "appnope" +version = "0.1.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/5d/752690df9ef5b76e169e68d6a129fa6d08a7100ca7f754c89495db3c6019/appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee", size = 4170 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/29/5ecc3a15d5a33e31b26c11426c45c501e439cb865d0bff96315d86443b78/appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c", size = 4321 }, +] + +[[package]] +name = "astroid" +version = "3.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/53/1067e1113ecaf58312357f2cd93063674924119d80d173adc3f6f2387aa2/astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a", size = 397576 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/96/b32bbbb46170a1c8b8b1f28c794202e25cfe743565e9d3469b8eb1e0cc05/astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25", size = 276348 }, +] + +[[package]] +name = "asttokens" +version = "2.4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/1d/f03bcb60c4a3212e15f99a56085d93093a497718adf828d050b9d675da81/asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0", size = 62284 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/45/86/4736ac618d82a20d87d2f92ae19441ebc7ac9e7a581d7e58bbe79233b24a/asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", size = 27764 }, +] + +[[package]] +name = "attrs" +version = "24.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/0f/aafca9af9315aee06a89ffde799a10a582fe8de76c563ee80bbcdc08b3fb/attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346", size = 792678 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6a/21/5b6702a7f963e95456c0de2d495f67bf5fd62840ac655dc451586d23d39a/attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2", size = 63001 }, +] + +[[package]] +name = "babel" +version = "2.16.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytz", marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/74/f1bc80f23eeba13393b7222b11d95ca3af2c1e28edca18af487137eefed9/babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316", size = 9348104 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/20/bc79bc575ba2e2a7f70e8a1155618bb1301eaa5132a8271373a6903f73f8/babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b", size = 9587599 }, +] + +[[package]] +name = "backcall" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/40/764a663805d84deee23043e1426a9175567db89c8b3287b5c2ad9f71aa93/backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e", size = 18041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/1c/ff6546b6c12603d8dd1070aa3c3d273ad4c07f5771689a7b69a550e8c951/backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255", size = 11157 }, +] + +[[package]] +name = "certifi" +version = "2024.8.30" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/ee/9b19140fe824b367c04c5e1b369942dd754c4c5462d5674002f75c4dedc1/certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9", size = 168507 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/90/3c9ff0512038035f59d279fddeb79f5f1eccd8859f06d6163c58798b9487/certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8", size = 167321 }, +] + +[[package]] +name = "cffi" +version = "1.17.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/07/f44ca684db4e4f08a3fdc6eeb9a0d15dc6883efc7b8c90357fdbf74e186c/cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", size = 182191 }, + { url = "https://files.pythonhosted.org/packages/08/fd/cc2fedbd887223f9f5d170c96e57cbf655df9831a6546c1727ae13fa977a/cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", size = 178592 }, + { url = "https://files.pythonhosted.org/packages/de/cc/4635c320081c78d6ffc2cab0a76025b691a91204f4aa317d568ff9280a2d/cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", size = 426024 }, + { url = "https://files.pythonhosted.org/packages/b6/7b/3b2b250f3aab91abe5f8a51ada1b717935fdaec53f790ad4100fe2ec64d1/cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", size = 448188 }, + { url = "https://files.pythonhosted.org/packages/d3/48/1b9283ebbf0ec065148d8de05d647a986c5f22586b18120020452fff8f5d/cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", size = 455571 }, + { url = "https://files.pythonhosted.org/packages/40/87/3b8452525437b40f39ca7ff70276679772ee7e8b394934ff60e63b7b090c/cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", size = 436687 }, + { url = "https://files.pythonhosted.org/packages/8d/fb/4da72871d177d63649ac449aec2e8a29efe0274035880c7af59101ca2232/cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", size = 446211 }, + { url = "https://files.pythonhosted.org/packages/ab/a0/62f00bcb411332106c02b663b26f3545a9ef136f80d5df746c05878f8c4b/cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", size = 461325 }, + { url = "https://files.pythonhosted.org/packages/36/83/76127035ed2e7e27b0787604d99da630ac3123bfb02d8e80c633f218a11d/cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", size = 438784 }, + { url = "https://files.pythonhosted.org/packages/21/81/a6cd025db2f08ac88b901b745c163d884641909641f9b826e8cb87645942/cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", size = 461564 }, + { url = "https://files.pythonhosted.org/packages/f8/fe/4d41c2f200c4a457933dbd98d3cf4e911870877bd94d9656cc0fcb390681/cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", size = 171804 }, + { url = "https://files.pythonhosted.org/packages/d1/b6/0b0f5ab93b0df4acc49cae758c81fe4e5ef26c3ae2e10cc69249dfd8b3ab/cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", size = 181299 }, + { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 }, + { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 }, + { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, + { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, + { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, + { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 }, + { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 }, + { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 }, + { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 }, + { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 }, + { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 }, + { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 }, + { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, + { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, + { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, + { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, + { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, + { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, + { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, + { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, + { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, + { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, + { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, + { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, + { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, + { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, + { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, + { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, + { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, + { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, + { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, + { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, + { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, + { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, + { url = "https://files.pythonhosted.org/packages/48/08/15bf6b43ae9bd06f6b00ad8a91f5a8fe1069d4c9fab550a866755402724e/cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b", size = 182457 }, + { url = "https://files.pythonhosted.org/packages/c2/5b/f1523dd545f92f7df468e5f653ffa4df30ac222f3c884e51e139878f1cb5/cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964", size = 425932 }, + { url = "https://files.pythonhosted.org/packages/53/93/7e547ab4105969cc8c93b38a667b82a835dd2cc78f3a7dad6130cfd41e1d/cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9", size = 448585 }, + { url = "https://files.pythonhosted.org/packages/56/c4/a308f2c332006206bb511de219efeff090e9d63529ba0a77aae72e82248b/cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc", size = 456268 }, + { url = "https://files.pythonhosted.org/packages/ca/5b/b63681518265f2f4060d2b60755c1c77ec89e5e045fc3773b72735ddaad5/cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c", size = 436592 }, + { url = "https://files.pythonhosted.org/packages/bb/19/b51af9f4a4faa4a8ac5a0e5d5c2522dcd9703d07fac69da34a36c4d960d3/cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1", size = 446512 }, + { url = "https://files.pythonhosted.org/packages/e2/63/2bed8323890cb613bbecda807688a31ed11a7fe7afe31f8faaae0206a9a3/cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8", size = 171576 }, + { url = "https://files.pythonhosted.org/packages/2f/70/80c33b044ebc79527447fd4fbc5455d514c3bb840dede4455de97da39b4d/cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1", size = 181229 }, + { url = "https://files.pythonhosted.org/packages/b9/ea/8bb50596b8ffbc49ddd7a1ad305035daa770202a6b782fc164647c2673ad/cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", size = 182220 }, + { url = "https://files.pythonhosted.org/packages/ae/11/e77c8cd24f58285a82c23af484cf5b124a376b32644e445960d1a4654c3a/cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", size = 178605 }, + { url = "https://files.pythonhosted.org/packages/ed/65/25a8dc32c53bf5b7b6c2686b42ae2ad58743f7ff644844af7cdb29b49361/cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", size = 424910 }, + { url = "https://files.pythonhosted.org/packages/42/7a/9d086fab7c66bd7c4d0f27c57a1b6b068ced810afc498cc8c49e0088661c/cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", size = 447200 }, + { url = "https://files.pythonhosted.org/packages/da/63/1785ced118ce92a993b0ec9e0d0ac8dc3e5dbfbcaa81135be56c69cabbb6/cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", size = 454565 }, + { url = "https://files.pythonhosted.org/packages/74/06/90b8a44abf3556599cdec107f7290277ae8901a58f75e6fe8f970cd72418/cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", size = 435635 }, + { url = "https://files.pythonhosted.org/packages/bd/62/a1f468e5708a70b1d86ead5bab5520861d9c7eacce4a885ded9faa7729c3/cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", size = 445218 }, + { url = "https://files.pythonhosted.org/packages/5b/95/b34462f3ccb09c2594aa782d90a90b045de4ff1f70148ee79c69d37a0a5a/cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", size = 460486 }, + { url = "https://files.pythonhosted.org/packages/fc/fc/a1e4bebd8d680febd29cf6c8a40067182b64f00c7d105f8f26b5bc54317b/cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", size = 437911 }, + { url = "https://files.pythonhosted.org/packages/e6/c3/21cab7a6154b6a5ea330ae80de386e7665254835b9e98ecc1340b3a7de9a/cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", size = 460632 }, + { url = "https://files.pythonhosted.org/packages/cb/b5/fd9f8b5a84010ca169ee49f4e4ad6f8c05f4e3545b72ee041dbbcb159882/cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", size = 171820 }, + { url = "https://files.pythonhosted.org/packages/8c/52/b08750ce0bce45c143e1b5d7357ee8c55341b52bdef4b0f081af1eb248c2/cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", size = 181290 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/09/c1bc53dab74b1816a00d8d030de5bf98f724c52c1635e07681d312f20be8/charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", size = 104809 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/61/095a0aa1a84d1481998b534177c8566fdc50bb1233ea9a0478cd3cc075bd/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", size = 194219 }, + { url = "https://files.pythonhosted.org/packages/cc/94/f7cf5e5134175de79ad2059edf2adce18e0685ebdb9227ff0139975d0e93/charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", size = 122521 }, + { url = "https://files.pythonhosted.org/packages/46/6a/d5c26c41c49b546860cc1acabdddf48b0b3fb2685f4f5617ac59261b44ae/charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", size = 120383 }, + { url = "https://files.pythonhosted.org/packages/b8/60/e2f67915a51be59d4539ed189eb0a2b0d292bf79270410746becb32bc2c3/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", size = 138223 }, + { url = "https://files.pythonhosted.org/packages/05/8c/eb854996d5fef5e4f33ad56927ad053d04dc820e4a3d39023f35cad72617/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", size = 148101 }, + { url = "https://files.pythonhosted.org/packages/f6/93/bb6cbeec3bf9da9b2eba458c15966658d1daa8b982c642f81c93ad9b40e1/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", size = 140699 }, + { url = "https://files.pythonhosted.org/packages/da/f1/3702ba2a7470666a62fd81c58a4c40be00670e5006a67f4d626e57f013ae/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", size = 142065 }, + { url = "https://files.pythonhosted.org/packages/3f/ba/3f5e7be00b215fa10e13d64b1f6237eb6ebea66676a41b2bcdd09fe74323/charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", size = 144505 }, + { url = "https://files.pythonhosted.org/packages/33/c3/3b96a435c5109dd5b6adc8a59ba1d678b302a97938f032e3770cc84cd354/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", size = 139425 }, + { url = "https://files.pythonhosted.org/packages/43/05/3bf613e719efe68fb3a77f9c536a389f35b95d75424b96b426a47a45ef1d/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", size = 145287 }, + { url = "https://files.pythonhosted.org/packages/58/78/a0bc646900994df12e07b4ae5c713f2b3e5998f58b9d3720cce2aa45652f/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", size = 149929 }, + { url = "https://files.pythonhosted.org/packages/eb/5c/97d97248af4920bc68687d9c3b3c0f47c910e21a8ff80af4565a576bd2f0/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", size = 141605 }, + { url = "https://files.pythonhosted.org/packages/a8/31/47d018ef89f95b8aded95c589a77c072c55e94b50a41aa99c0a2008a45a4/charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", size = 142646 }, + { url = "https://files.pythonhosted.org/packages/ae/d5/4fecf1d58bedb1340a50f165ba1c7ddc0400252d6832ff619c4568b36cc0/charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", size = 92846 }, + { url = "https://files.pythonhosted.org/packages/a2/a0/4af29e22cb5942488cf45630cbdd7cefd908768e69bdd90280842e4e8529/charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", size = 100343 }, + { url = "https://files.pythonhosted.org/packages/68/77/02839016f6fbbf808e8b38601df6e0e66c17bbab76dff4613f7511413597/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", size = 191647 }, + { url = "https://files.pythonhosted.org/packages/3e/33/21a875a61057165e92227466e54ee076b73af1e21fe1b31f1e292251aa1e/charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", size = 121434 }, + { url = "https://files.pythonhosted.org/packages/dd/51/68b61b90b24ca35495956b718f35a9756ef7d3dd4b3c1508056fa98d1a1b/charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", size = 118979 }, + { url = "https://files.pythonhosted.org/packages/e4/a6/7ee57823d46331ddc37dd00749c95b0edec2c79b15fc0d6e6efb532e89ac/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", size = 136582 }, + { url = "https://files.pythonhosted.org/packages/74/f1/0d9fe69ac441467b737ba7f48c68241487df2f4522dd7246d9426e7c690e/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", size = 146645 }, + { url = "https://files.pythonhosted.org/packages/05/31/e1f51c76db7be1d4aef220d29fbfa5dbb4a99165d9833dcbf166753b6dc0/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", size = 139398 }, + { url = "https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", size = 140273 }, + { url = "https://files.pythonhosted.org/packages/07/07/7e554f2bbce3295e191f7e653ff15d55309a9ca40d0362fcdab36f01063c/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", size = 142577 }, + { url = "https://files.pythonhosted.org/packages/d8/b5/eb705c313100defa57da79277d9207dc8d8e45931035862fa64b625bfead/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", size = 137747 }, + { url = "https://files.pythonhosted.org/packages/19/28/573147271fd041d351b438a5665be8223f1dd92f273713cb882ddafe214c/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", size = 143375 }, + { url = "https://files.pythonhosted.org/packages/cf/7c/f3b682fa053cc21373c9a839e6beba7705857075686a05c72e0f8c4980ca/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", size = 148474 }, + { url = "https://files.pythonhosted.org/packages/1e/49/7ab74d4ac537ece3bc3334ee08645e231f39f7d6df6347b29a74b0537103/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", size = 140232 }, + { url = "https://files.pythonhosted.org/packages/2d/dc/9dacba68c9ac0ae781d40e1a0c0058e26302ea0660e574ddf6797a0347f7/charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", size = 140859 }, + { url = "https://files.pythonhosted.org/packages/6c/c2/4a583f800c0708dd22096298e49f887b49d9746d0e78bfc1d7e29816614c/charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", size = 92509 }, + { url = "https://files.pythonhosted.org/packages/57/ec/80c8d48ac8b1741d5b963797b7c0c869335619e13d4744ca2f67fc11c6fc/charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", size = 99870 }, + { url = "https://files.pythonhosted.org/packages/d1/b2/fcedc8255ec42afee97f9e6f0145c734bbe104aac28300214593eb326f1d/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", size = 192892 }, + { url = "https://files.pythonhosted.org/packages/2e/7d/2259318c202f3d17f3fe6438149b3b9e706d1070fe3fcbb28049730bb25c/charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", size = 122213 }, + { url = "https://files.pythonhosted.org/packages/3a/52/9f9d17c3b54dc238de384c4cb5a2ef0e27985b42a0e5cc8e8a31d918d48d/charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", size = 119404 }, + { url = "https://files.pythonhosted.org/packages/99/b0/9c365f6d79a9f0f3c379ddb40a256a67aa69c59609608fe7feb6235896e1/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", size = 137275 }, + { url = "https://files.pythonhosted.org/packages/91/33/749df346e93d7a30cdcb90cbfdd41a06026317bfbfb62cd68307c1a3c543/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", size = 147518 }, + { url = "https://files.pythonhosted.org/packages/72/1a/641d5c9f59e6af4c7b53da463d07600a695b9824e20849cb6eea8a627761/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", size = 140182 }, + { url = "https://files.pythonhosted.org/packages/ee/fb/14d30eb4956408ee3ae09ad34299131fb383c47df355ddb428a7331cfa1e/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", size = 141869 }, + { url = "https://files.pythonhosted.org/packages/df/3e/a06b18788ca2eb6695c9b22325b6fde7dde0f1d1838b1792a0076f58fe9d/charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", size = 144042 }, + { url = "https://files.pythonhosted.org/packages/45/59/3d27019d3b447a88fe7e7d004a1e04be220227760264cc41b405e863891b/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", size = 138275 }, + { url = "https://files.pythonhosted.org/packages/7b/ef/5eb105530b4da8ae37d506ccfa25057961b7b63d581def6f99165ea89c7e/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", size = 144819 }, + { url = "https://files.pythonhosted.org/packages/a2/51/e5023f937d7f307c948ed3e5c29c4b7a3e42ed2ee0b8cdf8f3a706089bf0/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", size = 149415 }, + { url = "https://files.pythonhosted.org/packages/24/9d/2e3ef673dfd5be0154b20363c5cdcc5606f35666544381bee15af3778239/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", size = 141212 }, + { url = "https://files.pythonhosted.org/packages/5b/ae/ce2c12fcac59cb3860b2e2d76dc405253a4475436b1861d95fe75bdea520/charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", size = 142167 }, + { url = "https://files.pythonhosted.org/packages/ed/3a/a448bf035dce5da359daf9ae8a16b8a39623cc395a2ffb1620aa1bce62b0/charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", size = 93041 }, + { url = "https://files.pythonhosted.org/packages/b6/7c/8debebb4f90174074b827c63242c23851bdf00a532489fba57fef3416e40/charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", size = 100397 }, + { url = "https://files.pythonhosted.org/packages/ef/d4/a1d72a8f6aa754fdebe91b848912025d30ab7dced61e9ed8aabbf791ed65/charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", size = 191415 }, + { url = "https://files.pythonhosted.org/packages/13/82/83c188028b6f38d39538442dd127dc794c602ae6d45d66c469f4063a4c30/charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", size = 121051 }, + { url = "https://files.pythonhosted.org/packages/16/ea/a9e284aa38cccea06b7056d4cbc7adf37670b1f8a668a312864abf1ff7c6/charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", size = 119143 }, + { url = "https://files.pythonhosted.org/packages/34/2a/f392457d45e24a0c9bfc012887ed4f3c54bf5d4d05a5deb970ffec4b7fc0/charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", size = 137506 }, + { url = "https://files.pythonhosted.org/packages/be/4d/9e370f8281cec2fcc9452c4d1ac513324c32957c5f70c73dd2fa8442a21a/charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", size = 147272 }, + { url = "https://files.pythonhosted.org/packages/33/95/ef68482e4a6adf781fae8d183fb48d6f2be8facb414f49c90ba6a5149cd1/charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", size = 139734 }, + { url = "https://files.pythonhosted.org/packages/3d/09/d82fe4a34c5f0585f9ea1df090e2a71eb9bb1e469723053e1ee9f57c16f3/charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", size = 141094 }, + { url = "https://files.pythonhosted.org/packages/81/b2/160893421adfa3c45554fb418e321ed342bb10c0a4549e855b2b2a3699cb/charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", size = 144113 }, + { url = "https://files.pythonhosted.org/packages/9e/ef/cd47a63d3200b232792e361cd67530173a09eb011813478b1c0fb8aa7226/charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", size = 138555 }, + { url = "https://files.pythonhosted.org/packages/a8/6f/4ff299b97da2ed6358154b6eb3a2db67da2ae204e53d205aacb18a7e4f34/charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", size = 144944 }, + { url = "https://files.pythonhosted.org/packages/d1/2f/0d1efd07c74c52b6886c32a3b906fb8afd2fecf448650e73ecb90a5a27f1/charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", size = 148925 }, + { url = "https://files.pythonhosted.org/packages/bd/28/7ea29e73eea52c7e15b4b9108d0743fc9e4cc2cdb00d275af1df3d46d360/charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", size = 140732 }, + { url = "https://files.pythonhosted.org/packages/b3/c1/ebca8e87c714a6a561cfee063f0655f742e54b8ae6e78151f60ba8708b3a/charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", size = 141288 }, + { url = "https://files.pythonhosted.org/packages/74/20/8923a06f15eb3d7f6a306729360bd58f9ead1dc39bc7ea8831f4b407e4ae/charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", size = 92373 }, + { url = "https://files.pythonhosted.org/packages/db/fb/d29e343e7c57bbf1231275939f6e75eb740cd47a9d7cb2c52ffeb62ef869/charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", size = 99577 }, + { url = "https://files.pythonhosted.org/packages/f7/9d/bcf4a449a438ed6f19790eee543a86a740c77508fbc5ddab210ab3ba3a9a/charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", size = 194198 }, + { url = "https://files.pythonhosted.org/packages/66/fe/c7d3da40a66a6bf2920cce0f436fa1f62ee28aaf92f412f0bf3b84c8ad6c/charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", size = 122494 }, + { url = "https://files.pythonhosted.org/packages/2a/9d/a6d15bd1e3e2914af5955c8eb15f4071997e7078419328fee93dfd497eb7/charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", size = 120393 }, + { url = "https://files.pythonhosted.org/packages/3d/85/5b7416b349609d20611a64718bed383b9251b5a601044550f0c8983b8900/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", size = 138331 }, + { url = "https://files.pythonhosted.org/packages/79/66/8946baa705c588521afe10b2d7967300e49380ded089a62d38537264aece/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", size = 148097 }, + { url = "https://files.pythonhosted.org/packages/44/80/b339237b4ce635b4af1c73742459eee5f97201bd92b2371c53e11958392e/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", size = 140711 }, + { url = "https://files.pythonhosted.org/packages/98/69/5d8751b4b670d623aa7a47bef061d69c279e9f922f6705147983aa76c3ce/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", size = 142251 }, + { url = "https://files.pythonhosted.org/packages/1f/8d/33c860a7032da5b93382cbe2873261f81467e7b37f4ed91e25fed62fd49b/charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", size = 144636 }, + { url = "https://files.pythonhosted.org/packages/c2/65/52aaf47b3dd616c11a19b1052ce7fa6321250a7a0b975f48d8c366733b9f/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", size = 139514 }, + { url = "https://files.pythonhosted.org/packages/51/fd/0ee5b1c2860bb3c60236d05b6e4ac240cf702b67471138571dad91bcfed8/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", size = 145528 }, + { url = "https://files.pythonhosted.org/packages/e1/9c/60729bf15dc82e3aaf5f71e81686e42e50715a1399770bcde1a9e43d09db/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", size = 149804 }, + { url = "https://files.pythonhosted.org/packages/53/cd/aa4b8a4d82eeceb872f83237b2d27e43e637cac9ffaef19a1321c3bafb67/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", size = 141708 }, + { url = "https://files.pythonhosted.org/packages/54/7f/cad0b328759630814fcf9d804bfabaf47776816ad4ef2e9938b7e1123d04/charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561", size = 142708 }, + { url = "https://files.pythonhosted.org/packages/c1/9d/254a2f1bcb0ce9acad838e94ed05ba71a7cb1e27affaa4d9e1ca3958cdb6/charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", size = 92830 }, + { url = "https://files.pythonhosted.org/packages/2f/0e/d7303ccae9735ff8ff01e36705ad6233ad2002962e8668a970fc000c5e1b/charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", size = 100376 }, + { url = "https://files.pythonhosted.org/packages/28/76/e6222113b83e3622caa4bb41032d0b1bf785250607392e1b778aca0b8a7d/charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", size = 48543 }, +] + +[[package]] +name = "click" +version = "8.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/96/d3/f04c7bfcf5c1862a2a5b845c6b2b360488cf47af55dfa79c98f6a6bf98b5/click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de", size = 336121 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", size = 97941 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "comm" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/a8/fb783cb0abe2b5fded9f55e5703015cdf1c9c85b3669087c538dd15a6a86/comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e", size = 6210 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/75/49e5bfe642f71f272236b5b2d2691cf915a7283cc0ceda56357b61daa538/comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3", size = 7180 }, +] + +[[package]] +name = "coverage" +version = "7.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/08/7e37f82e4d1aead42a7443ff06a1e406aabf7302c4f00a546e4b320b994c/coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d", size = 798791 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/61/eb7ce5ed62bacf21beca4937a90fe32545c91a3c8a42a30c6616d48fc70d/coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16", size = 206690 }, + { url = "https://files.pythonhosted.org/packages/7d/73/041928e434442bd3afde5584bdc3f932fb4562b1597629f537387cec6f3d/coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36", size = 207127 }, + { url = "https://files.pythonhosted.org/packages/c7/c8/6ca52b5147828e45ad0242388477fdb90df2c6cbb9a441701a12b3c71bc8/coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02", size = 235654 }, + { url = "https://files.pythonhosted.org/packages/d5/da/9ac2b62557f4340270942011d6efeab9833648380109e897d48ab7c1035d/coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc", size = 233598 }, + { url = "https://files.pythonhosted.org/packages/53/23/9e2c114d0178abc42b6d8d5281f651a8e6519abfa0ef460a00a91f80879d/coverage-7.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f59d57baca39b32db42b83b2a7ba6f47ad9c394ec2076b084c3f029b7afca23", size = 234732 }, + { url = "https://files.pythonhosted.org/packages/0f/7e/a0230756fb133343a52716e8b855045f13342b70e48e8ad41d8a0d60ab98/coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34", size = 233816 }, + { url = "https://files.pythonhosted.org/packages/28/7c/3753c8b40d232b1e5eeaed798c875537cf3cb183fb5041017c1fdb7ec14e/coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c", size = 232325 }, + { url = "https://files.pythonhosted.org/packages/57/e3/818a2b2af5b7573b4b82cf3e9f137ab158c90ea750a8f053716a32f20f06/coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959", size = 233418 }, + { url = "https://files.pythonhosted.org/packages/c8/fb/4532b0b0cefb3f06d201648715e03b0feb822907edab3935112b61b885e2/coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232", size = 209343 }, + { url = "https://files.pythonhosted.org/packages/5a/25/af337cc7421eca1c187cc9c315f0a755d48e755d2853715bfe8c418a45fa/coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0", size = 210136 }, + { url = "https://files.pythonhosted.org/packages/ad/5f/67af7d60d7e8ce61a4e2ddcd1bd5fb787180c8d0ae0fbd073f903b3dd95d/coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93", size = 206796 }, + { url = "https://files.pythonhosted.org/packages/e1/0e/e52332389e057daa2e03be1fbfef25bb4d626b37d12ed42ae6281d0a274c/coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3", size = 207244 }, + { url = "https://files.pythonhosted.org/packages/aa/cd/766b45fb6e090f20f8927d9c7cb34237d41c73a939358bc881883fd3a40d/coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff", size = 239279 }, + { url = "https://files.pythonhosted.org/packages/70/6c/a9ccd6fe50ddaf13442a1e2dd519ca805cbe0f1fcd377fba6d8339b98ccb/coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d", size = 236859 }, + { url = "https://files.pythonhosted.org/packages/14/6f/8351b465febb4dbc1ca9929505202db909c5a635c6fdf33e089bbc3d7d85/coverage-7.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c0420b573964c760df9e9e86d1a9a622d0d27f417e1a949a8a66dd7bcee7bc6", size = 238549 }, + { url = "https://files.pythonhosted.org/packages/68/3c/289b81fa18ad72138e6d78c4c11a82b5378a312c0e467e2f6b495c260907/coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56", size = 237477 }, + { url = "https://files.pythonhosted.org/packages/ed/1c/aa1efa6459d822bd72c4abc0b9418cf268de3f60eeccd65dc4988553bd8d/coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234", size = 236134 }, + { url = "https://files.pythonhosted.org/packages/fb/c8/521c698f2d2796565fe9c789c2ee1ccdae610b3aa20b9b2ef980cc253640/coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133", size = 236910 }, + { url = "https://files.pythonhosted.org/packages/7d/30/033e663399ff17dca90d793ee8a2ea2890e7fdf085da58d82468b4220bf7/coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c", size = 209348 }, + { url = "https://files.pythonhosted.org/packages/20/05/0d1ccbb52727ccdadaa3ff37e4d2dc1cd4d47f0c3df9eb58d9ec8508ca88/coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6", size = 210230 }, + { url = "https://files.pythonhosted.org/packages/7e/d4/300fc921dff243cd518c7db3a4c614b7e4b2431b0d1145c1e274fd99bd70/coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778", size = 206983 }, + { url = "https://files.pythonhosted.org/packages/e1/ab/6bf00de5327ecb8db205f9ae596885417a31535eeda6e7b99463108782e1/coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391", size = 207221 }, + { url = "https://files.pythonhosted.org/packages/92/8f/2ead05e735022d1a7f3a0a683ac7f737de14850395a826192f0288703472/coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8", size = 240342 }, + { url = "https://files.pythonhosted.org/packages/0f/ef/94043e478201ffa85b8ae2d2c79b4081e5a1b73438aafafccf3e9bafb6b5/coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d", size = 237371 }, + { url = "https://files.pythonhosted.org/packages/1f/0f/c890339dd605f3ebc269543247bdd43b703cce6825b5ed42ff5f2d6122c7/coverage-7.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44fee9975f04b33331cb8eb272827111efc8930cfd582e0320613263ca849ca", size = 239455 }, + { url = "https://files.pythonhosted.org/packages/d1/04/7fd7b39ec7372a04efb0f70c70e35857a99b6a9188b5205efb4c77d6a57a/coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163", size = 238924 }, + { url = "https://files.pythonhosted.org/packages/ed/bf/73ce346a9d32a09cf369f14d2a06651329c984e106f5992c89579d25b27e/coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a", size = 237252 }, + { url = "https://files.pythonhosted.org/packages/86/74/1dc7a20969725e917b1e07fe71a955eb34bc606b938316bcc799f228374b/coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d", size = 238897 }, + { url = "https://files.pythonhosted.org/packages/b6/e9/d9cc3deceb361c491b81005c668578b0dfa51eed02cd081620e9a62f24ec/coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5", size = 209606 }, + { url = "https://files.pythonhosted.org/packages/47/c8/5a2e41922ea6740f77d555c4d47544acd7dc3f251fe14199c09c0f5958d3/coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb", size = 210373 }, + { url = "https://files.pythonhosted.org/packages/8c/f9/9aa4dfb751cb01c949c990d136a0f92027fbcc5781c6e921df1cb1563f20/coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106", size = 207007 }, + { url = "https://files.pythonhosted.org/packages/b9/67/e1413d5a8591622a46dd04ff80873b04c849268831ed5c304c16433e7e30/coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9", size = 207269 }, + { url = "https://files.pythonhosted.org/packages/14/5b/9dec847b305e44a5634d0fb8498d135ab1d88330482b74065fcec0622224/coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c", size = 239886 }, + { url = "https://files.pythonhosted.org/packages/7b/b7/35760a67c168e29f454928f51f970342d23cf75a2bb0323e0f07334c85f3/coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a", size = 237037 }, + { url = "https://files.pythonhosted.org/packages/f7/95/d2fd31f1d638df806cae59d7daea5abf2b15b5234016a5ebb502c2f3f7ee/coverage-7.6.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b260de9790fd81e69401c2dc8b17da47c8038176a79092a89cb2b7d945d060", size = 239038 }, + { url = "https://files.pythonhosted.org/packages/6e/bd/110689ff5752b67924efd5e2aedf5190cbbe245fc81b8dec1abaffba619d/coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862", size = 238690 }, + { url = "https://files.pythonhosted.org/packages/d3/a8/08d7b38e6ff8df52331c83130d0ab92d9c9a8b5462f9e99c9f051a4ae206/coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388", size = 236765 }, + { url = "https://files.pythonhosted.org/packages/d6/6a/9cf96839d3147d55ae713eb2d877f4d777e7dc5ba2bce227167d0118dfe8/coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155", size = 238611 }, + { url = "https://files.pythonhosted.org/packages/74/e4/7ff20d6a0b59eeaab40b3140a71e38cf52547ba21dbcf1d79c5a32bba61b/coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a", size = 209671 }, + { url = "https://files.pythonhosted.org/packages/35/59/1812f08a85b57c9fdb6d0b383d779e47b6f643bc278ed682859512517e83/coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129", size = 210368 }, + { url = "https://files.pythonhosted.org/packages/9c/15/08913be1c59d7562a3e39fce20661a98c0a3f59d5754312899acc6cb8a2d/coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e", size = 207758 }, + { url = "https://files.pythonhosted.org/packages/c4/ae/b5d58dff26cade02ada6ca612a76447acd69dccdbb3a478e9e088eb3d4b9/coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962", size = 208035 }, + { url = "https://files.pythonhosted.org/packages/b8/d7/62095e355ec0613b08dfb19206ce3033a0eedb6f4a67af5ed267a8800642/coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb", size = 250839 }, + { url = "https://files.pythonhosted.org/packages/7c/1e/c2967cb7991b112ba3766df0d9c21de46b476d103e32bb401b1b2adf3380/coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704", size = 246569 }, + { url = "https://files.pythonhosted.org/packages/8b/61/a7a6a55dd266007ed3b1df7a3386a0d760d014542d72f7c2c6938483b7bd/coverage-7.6.1-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13b0a73a0896988f053e4fbb7de6d93388e6dd292b0d87ee51d106f2c11b465b", size = 248927 }, + { url = "https://files.pythonhosted.org/packages/c8/fa/13a6f56d72b429f56ef612eb3bc5ce1b75b7ee12864b3bd12526ab794847/coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f", size = 248401 }, + { url = "https://files.pythonhosted.org/packages/75/06/0429c652aa0fb761fc60e8c6b291338c9173c6aa0f4e40e1902345b42830/coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223", size = 246301 }, + { url = "https://files.pythonhosted.org/packages/52/76/1766bb8b803a88f93c3a2d07e30ffa359467810e5cbc68e375ebe6906efb/coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3", size = 247598 }, + { url = "https://files.pythonhosted.org/packages/66/8b/f54f8db2ae17188be9566e8166ac6df105c1c611e25da755738025708d54/coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f", size = 210307 }, + { url = "https://files.pythonhosted.org/packages/9f/b0/e0dca6da9170aefc07515cce067b97178cefafb512d00a87a1c717d2efd5/coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657", size = 211453 }, + { url = "https://files.pythonhosted.org/packages/81/d0/d9e3d554e38beea5a2e22178ddb16587dbcbe9a1ef3211f55733924bf7fa/coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0", size = 206674 }, + { url = "https://files.pythonhosted.org/packages/38/ea/cab2dc248d9f45b2b7f9f1f596a4d75a435cb364437c61b51d2eb33ceb0e/coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a", size = 207101 }, + { url = "https://files.pythonhosted.org/packages/ca/6f/f82f9a500c7c5722368978a5390c418d2a4d083ef955309a8748ecaa8920/coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b", size = 236554 }, + { url = "https://files.pythonhosted.org/packages/a6/94/d3055aa33d4e7e733d8fa309d9adf147b4b06a82c1346366fc15a2b1d5fa/coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3", size = 234440 }, + { url = "https://files.pythonhosted.org/packages/e4/6e/885bcd787d9dd674de4a7d8ec83faf729534c63d05d51d45d4fa168f7102/coverage-7.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8929543a7192c13d177b770008bc4e8119f2e1f881d563fc6b6305d2d0ebe9de", size = 235889 }, + { url = "https://files.pythonhosted.org/packages/f4/63/df50120a7744492710854860783d6819ff23e482dee15462c9a833cc428a/coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6", size = 235142 }, + { url = "https://files.pythonhosted.org/packages/3a/5d/9d0acfcded2b3e9ce1c7923ca52ccc00c78a74e112fc2aee661125b7843b/coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569", size = 233805 }, + { url = "https://files.pythonhosted.org/packages/c4/56/50abf070cb3cd9b1dd32f2c88f083aab561ecbffbcd783275cb51c17f11d/coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989", size = 234655 }, + { url = "https://files.pythonhosted.org/packages/25/ee/b4c246048b8485f85a2426ef4abab88e48c6e80c74e964bea5cd4cd4b115/coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7", size = 209296 }, + { url = "https://files.pythonhosted.org/packages/5c/1c/96cf86b70b69ea2b12924cdf7cabb8ad10e6130eab8d767a1099fbd2a44f/coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8", size = 210137 }, + { url = "https://files.pythonhosted.org/packages/19/d3/d54c5aa83268779d54c86deb39c1c4566e5d45c155369ca152765f8db413/coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255", size = 206688 }, + { url = "https://files.pythonhosted.org/packages/a5/fe/137d5dca72e4a258b1bc17bb04f2e0196898fe495843402ce826a7419fe3/coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8", size = 207120 }, + { url = "https://files.pythonhosted.org/packages/78/5b/a0a796983f3201ff5485323b225d7c8b74ce30c11f456017e23d8e8d1945/coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2", size = 235249 }, + { url = "https://files.pythonhosted.org/packages/4e/e1/76089d6a5ef9d68f018f65411fcdaaeb0141b504587b901d74e8587606ad/coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a", size = 233237 }, + { url = "https://files.pythonhosted.org/packages/9a/6f/eef79b779a540326fee9520e5542a8b428cc3bfa8b7c8f1022c1ee4fc66c/coverage-7.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:609b06f178fe8e9f89ef676532760ec0b4deea15e9969bf754b37f7c40326dbc", size = 234311 }, + { url = "https://files.pythonhosted.org/packages/75/e1/656d65fb126c29a494ef964005702b012f3498db1a30dd562958e85a4049/coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004", size = 233453 }, + { url = "https://files.pythonhosted.org/packages/68/6a/45f108f137941a4a1238c85f28fd9d048cc46b5466d6b8dda3aba1bb9d4f/coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb", size = 231958 }, + { url = "https://files.pythonhosted.org/packages/9b/e7/47b809099168b8b8c72ae311efc3e88c8d8a1162b3ba4b8da3cfcdb85743/coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36", size = 232938 }, + { url = "https://files.pythonhosted.org/packages/52/80/052222ba7058071f905435bad0ba392cc12006380731c37afaf3fe749b88/coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c", size = 209352 }, + { url = "https://files.pythonhosted.org/packages/b8/d8/1b92e0b3adcf384e98770a00ca095da1b5f7b483e6563ae4eb5e935d24a1/coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca", size = 210153 }, + { url = "https://files.pythonhosted.org/packages/a5/2b/0354ed096bca64dc8e32a7cbcae28b34cb5ad0b1fe2125d6d99583313ac0/coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df", size = 198926 }, +] + +[[package]] +name = "debugpy" +version = "1.8.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ce/b3/05c94639560cf0eaef33662ee5102d3e2a8b9e8c527c53190bf7187bacdb/debugpy-1.8.6.zip", hash = "sha256:c931a9371a86784cee25dec8d65bc2dc7a21f3f1552e3833d9ef8f919d22280a", size = 4956612 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/ce/5e093945df2da28dbd1bc14c631d71431d1aa08adc629e221c9658841f82/debugpy-1.8.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:30f467c5345d9dfdcc0afdb10e018e47f092e383447500f125b4e013236bf14b", size = 2089048 }, + { url = "https://files.pythonhosted.org/packages/d4/7a/a5fe4eaf648016a27a875403735a089ba7cc9a4cc906d37c8fdb2997b50d/debugpy-1.8.6-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d73d8c52614432f4215d0fe79a7e595d0dd162b5c15233762565be2f014803b", size = 3547450 }, + { url = "https://files.pythonhosted.org/packages/bf/fe/53d6d46e4a1cb5fb1a979695a9a26c8a04aed6d6ce4ba808a6d42341beba/debugpy-1.8.6-cp310-cp310-win32.whl", hash = "sha256:e3e182cd98eac20ee23a00653503315085b29ab44ed66269482349d307b08df9", size = 5151732 }, + { url = "https://files.pythonhosted.org/packages/ce/68/127cfc6012fbeef126eab1e168ad788ee9832b8b0d572743e5c6fa03ea83/debugpy-1.8.6-cp310-cp310-win_amd64.whl", hash = "sha256:e3a82da039cfe717b6fb1886cbbe5c4a3f15d7df4765af857f4307585121c2dd", size = 5183983 }, + { url = "https://files.pythonhosted.org/packages/9f/cc/3158aa2c96c677e324981230dfd33087ef4bfb5afb1d9cd40b7a1b35edb2/debugpy-1.8.6-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67479a94cf5fd2c2d88f9615e087fcb4fec169ec780464a3f2ba4a9a2bb79955", size = 2203403 }, + { url = "https://files.pythonhosted.org/packages/d5/9f/5691af62c556392ee45ed9b5c3fde4aaa7cb3b519cc8bea92fc27eab31fc/debugpy-1.8.6-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fb8653f6cbf1dd0a305ac1aa66ec246002145074ea57933978346ea5afdf70b", size = 3120088 }, + { url = "https://files.pythonhosted.org/packages/5e/3e/e32b36f9a391af4f8ff6b9c068ee822b5e4aa2d9cf4dc0937696e9249fa6/debugpy-1.8.6-cp311-cp311-win32.whl", hash = "sha256:cdaf0b9691879da2d13fa39b61c01887c34558d1ff6e5c30e2eb698f5384cd43", size = 5077329 }, + { url = "https://files.pythonhosted.org/packages/9d/de/ddad801b7fdbe2f97c744b44bb61169c4e0ab48a90f881c8f43b463f206b/debugpy-1.8.6-cp311-cp311-win_amd64.whl", hash = "sha256:43996632bee7435583952155c06881074b9a742a86cee74e701d87ca532fe833", size = 5101373 }, + { url = "https://files.pythonhosted.org/packages/b8/9e/882dae43f281fc4742fd9e5d2e0f5dae77f38d4f345e78bf1ed5e1f6202e/debugpy-1.8.6-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:db891b141fc6ee4b5fc6d1cc8035ec329cabc64bdd2ae672b4550c87d4ecb128", size = 2526807 }, + { url = "https://files.pythonhosted.org/packages/77/cf/6c0497f4b092cb4a408dda5ab84750032e5535f994d21eb812086d62094d/debugpy-1.8.6-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:567419081ff67da766c898ccf21e79f1adad0e321381b0dfc7a9c8f7a9347972", size = 4162582 }, + { url = "https://files.pythonhosted.org/packages/8e/66/e9c0aef0a5118aeaa6dfccb6d4f388602271cfb37c689da5e7b6168075d2/debugpy-1.8.6-cp312-cp312-win32.whl", hash = "sha256:c9834dfd701a1f6bf0f7f0b8b1573970ae99ebbeee68314116e0ccc5c78eea3c", size = 5193541 }, + { url = "https://files.pythonhosted.org/packages/c2/97/2196c4132c29f7cd8e574bb05a4b03ed35f94e3fcd1f56e72ea9f10732f4/debugpy-1.8.6-cp312-cp312-win_amd64.whl", hash = "sha256:e4ce0570aa4aca87137890d23b86faeadf184924ad892d20c54237bcaab75d8f", size = 5233374 }, + { url = "https://files.pythonhosted.org/packages/b9/db/1fcb9b0cd12cd417fecaab545df7d3c77793092adbe614c51b8352904e85/debugpy-1.8.6-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:df5dc9eb4ca050273b8e374a4cd967c43be1327eeb42bfe2f58b3cdfe7c68dcb", size = 2090260 }, + { url = "https://files.pythonhosted.org/packages/dd/07/301ab6ce54793213eef5f85f86fd0f0c7b3579d204149405ffdb7e5c67db/debugpy-1.8.6-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a85707c6a84b0c5b3db92a2df685b5230dd8fb8c108298ba4f11dba157a615a", size = 3617517 }, + { url = "https://files.pythonhosted.org/packages/73/f9/934930dd49ed8f5660fe0c39fded7a5b180eabc6db03e49d9243ce316f1a/debugpy-1.8.6-cp38-cp38-win32.whl", hash = "sha256:538c6cdcdcdad310bbefd96d7850be1cd46e703079cc9e67d42a9ca776cdc8a8", size = 5156468 }, + { url = "https://files.pythonhosted.org/packages/e0/19/fa925b305b4c1766ec20ee1c6301fc16a40fb7affba0a0d3d1d12248ef3d/debugpy-1.8.6-cp38-cp38-win_amd64.whl", hash = "sha256:22140bc02c66cda6053b6eb56dfe01bbe22a4447846581ba1dd6df2c9f97982d", size = 5189752 }, + { url = "https://files.pythonhosted.org/packages/e4/61/38fa2e907aae3a293e887b04045e4d30f931aafc462b207f4cb846e78c13/debugpy-1.8.6-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:c1cef65cffbc96e7b392d9178dbfd524ab0750da6c0023c027ddcac968fd1caa", size = 2090326 }, + { url = "https://files.pythonhosted.org/packages/39/b0/9790509ffeee155038f9707b74d031ed90a17552fe6a63e9069c9c42e0d9/debugpy-1.8.6-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1e60bd06bb3cc5c0e957df748d1fab501e01416c43a7bdc756d2a992ea1b881", size = 3544122 }, + { url = "https://files.pythonhosted.org/packages/7a/a1/d95a015eadf79997cdd2028a5fe3d1f37fbe2548b51470517d3d425960dc/debugpy-1.8.6-cp39-cp39-win32.whl", hash = "sha256:f7158252803d0752ed5398d291dee4c553bb12d14547c0e1843ab74ee9c31123", size = 5152549 }, + { url = "https://files.pythonhosted.org/packages/81/6a/32e2c9e980924f3c4b1b644a5c3d949d05fa7b445673ecf3e3244c883669/debugpy-1.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:3358aa619a073b620cd0d51d8a6176590af24abcc3fe2e479929a154bf591b51", size = 5185080 }, + { url = "https://files.pythonhosted.org/packages/05/ce/785925e87ce735cc3da7fb2bd66d8ca83173d8a0b60ce35a59a60b8d636f/debugpy-1.8.6-py2.py3-none-any.whl", hash = "sha256:b48892df4d810eff21d3ef37274f4c60d32cdcafc462ad5647239036b0f0649f", size = 5209208 }, +] + +[[package]] +name = "decorator" +version = "5.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/0c/8d907af351aa16b42caae42f9d6aa37b900c67308052d10fdce809f8d952/decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", size = 35016 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/50/83c593b07763e1161326b3b8c6686f0f4b0f24d5526546bee538c89837d6/decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186", size = 9073 }, +] + +[[package]] +name = "dnspython" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/37/7d/c871f55054e403fdfd6b8f65fd6d1c4e147ed100d3e9f9ba1fe695403939/dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc", size = 332727 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/a1/8c5287991ddb8d3e4662f71356d9656d91ab3a36618c3dd11b280df0d255/dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50", size = 307696 }, +] + +[[package]] +name = "docutils" +version = "0.19" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/330ea8d383eb2ce973df34d1239b3b21e91cd8c865d21ff82902d952f91f/docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6", size = 2056383 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/93/69/e391bd51bc08ed9141ecd899a0ddb61ab6465309f1eb470905c0c8868081/docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc", size = 570472 }, +] + +[[package]] +name = "exceptiongroup" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 }, +] + +[[package]] +name = "executing" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/e3/7d45f492c2c4a0e8e0fad57d081a7c8a0286cdd86372b070cca1ec0caa1e/executing-2.1.0.tar.gz", hash = "sha256:8ea27ddd260da8150fa5a708269c4a10e76161e2496ec3e587da9e3c0fe4b9ab", size = 977485 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b5/fd/afcd0496feca3276f509df3dbd5dae726fcc756f1a08d9e25abe1733f962/executing-2.1.0-py2.py3-none-any.whl", hash = "sha256:8d63781349375b5ebccc3142f4b30350c0cd9c79f921cde38be2be4637e98eaf", size = 25805 }, +] + +[[package]] +name = "fastjsonschema" +version = "2.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/03/3f/3ad5e7be13b4b8b55f4477141885ab2364f65d5f6ad5f7a9daffd634d066/fastjsonschema-2.20.0.tar.gz", hash = "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23", size = 373056 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/ca/086311cdfc017ec964b2436fe0c98c1f4efcb7e4c328956a22456e497655/fastjsonschema-2.20.0-py3-none-any.whl", hash = "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a", size = 23543 }, +] + +[[package]] +name = "googleapis-common-protos" +version = "1.65.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/53/3b/1599ceafa875ffb951480c8c74f4b77646a6b80e80970698f2aa93c216ce/googleapis_common_protos-1.65.0.tar.gz", hash = "sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0", size = 113657 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/08/49bfe7cf737952cc1a9c43e80cc258ed45dad7f183c5b8276fc94cb3862d/googleapis_common_protos-1.65.0-py2.py3-none-any.whl", hash = "sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63", size = 220890 }, +] + +[[package]] +name = "greenlet" +version = "3.1.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/ff/df5fede753cc10f6a5be0931204ea30c35fa2f2ea7a35b25bdaf4fe40e46/greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467", size = 186022 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/25/90/5234a78dc0ef6496a6eb97b67a42a8e96742a56f7dc808cb954a85390448/greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563", size = 271235 }, + { url = "https://files.pythonhosted.org/packages/7c/16/cd631fa0ab7d06ef06387135b7549fdcc77d8d859ed770a0d28e47b20972/greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83", size = 637168 }, + { url = "https://files.pythonhosted.org/packages/2f/b1/aed39043a6fec33c284a2c9abd63ce191f4f1a07319340ffc04d2ed3256f/greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0", size = 648826 }, + { url = "https://files.pythonhosted.org/packages/76/25/40e0112f7f3ebe54e8e8ed91b2b9f970805143efef16d043dfc15e70f44b/greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120", size = 644443 }, + { url = "https://files.pythonhosted.org/packages/fb/2f/3850b867a9af519794784a7eeed1dd5bc68ffbcc5b28cef703711025fd0a/greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc", size = 643295 }, + { url = "https://files.pythonhosted.org/packages/cf/69/79e4d63b9387b48939096e25115b8af7cd8a90397a304f92436bcb21f5b2/greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617", size = 599544 }, + { url = "https://files.pythonhosted.org/packages/46/1d/44dbcb0e6c323bd6f71b8c2f4233766a5faf4b8948873225d34a0b7efa71/greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7", size = 1125456 }, + { url = "https://files.pythonhosted.org/packages/e0/1d/a305dce121838d0278cee39d5bb268c657f10a5363ae4b726848f833f1bb/greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6", size = 1149111 }, + { url = "https://files.pythonhosted.org/packages/96/28/d62835fb33fb5652f2e98d34c44ad1a0feacc8b1d3f1aecab035f51f267d/greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80", size = 298392 }, + { url = "https://files.pythonhosted.org/packages/28/62/1c2665558618553c42922ed47a4e6d6527e2fa3516a8256c2f431c5d0441/greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70", size = 272479 }, + { url = "https://files.pythonhosted.org/packages/76/9d/421e2d5f07285b6e4e3a676b016ca781f63cfe4a0cd8eaecf3fd6f7a71ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159", size = 640404 }, + { url = "https://files.pythonhosted.org/packages/e5/de/6e05f5c59262a584e502dd3d261bbdd2c97ab5416cc9c0b91ea38932a901/greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e", size = 652813 }, + { url = "https://files.pythonhosted.org/packages/49/93/d5f93c84241acdea15a8fd329362c2c71c79e1a507c3f142a5d67ea435ae/greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1", size = 648517 }, + { url = "https://files.pythonhosted.org/packages/15/85/72f77fc02d00470c86a5c982b8daafdf65d38aefbbe441cebff3bf7037fc/greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383", size = 647831 }, + { url = "https://files.pythonhosted.org/packages/f7/4b/1c9695aa24f808e156c8f4813f685d975ca73c000c2a5056c514c64980f6/greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a", size = 602413 }, + { url = "https://files.pythonhosted.org/packages/76/70/ad6e5b31ef330f03b12559d19fda2606a522d3849cde46b24f223d6d1619/greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511", size = 1129619 }, + { url = "https://files.pythonhosted.org/packages/f4/fb/201e1b932e584066e0f0658b538e73c459b34d44b4bd4034f682423bc801/greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395", size = 1155198 }, + { url = "https://files.pythonhosted.org/packages/12/da/b9ed5e310bb8b89661b80cbcd4db5a067903bbcd7fc854923f5ebb4144f0/greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39", size = 298930 }, + { url = "https://files.pythonhosted.org/packages/7d/ec/bad1ac26764d26aa1353216fcbfa4670050f66d445448aafa227f8b16e80/greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d", size = 274260 }, + { url = "https://files.pythonhosted.org/packages/66/d4/c8c04958870f482459ab5956c2942c4ec35cac7fe245527f1039837c17a9/greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79", size = 649064 }, + { url = "https://files.pythonhosted.org/packages/51/41/467b12a8c7c1303d20abcca145db2be4e6cd50a951fa30af48b6ec607581/greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa", size = 663420 }, + { url = "https://files.pythonhosted.org/packages/27/8f/2a93cd9b1e7107d5c7b3b7816eeadcac2ebcaf6d6513df9abaf0334777f6/greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441", size = 658035 }, + { url = "https://files.pythonhosted.org/packages/57/5c/7c6f50cb12be092e1dccb2599be5a942c3416dbcfb76efcf54b3f8be4d8d/greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36", size = 660105 }, + { url = "https://files.pythonhosted.org/packages/f1/66/033e58a50fd9ec9df00a8671c74f1f3a320564c6415a4ed82a1c651654ba/greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9", size = 613077 }, + { url = "https://files.pythonhosted.org/packages/19/c5/36384a06f748044d06bdd8776e231fadf92fc896bd12cb1c9f5a1bda9578/greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0", size = 1135975 }, + { url = "https://files.pythonhosted.org/packages/38/f9/c0a0eb61bdf808d23266ecf1d63309f0e1471f284300ce6dac0ae1231881/greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942", size = 1163955 }, + { url = "https://files.pythonhosted.org/packages/43/21/a5d9df1d21514883333fc86584c07c2b49ba7c602e670b174bd73cfc9c7f/greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01", size = 299655 }, + { url = "https://files.pythonhosted.org/packages/f3/57/0db4940cd7bb461365ca8d6fd53e68254c9dbbcc2b452e69d0d41f10a85e/greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1", size = 272990 }, + { url = "https://files.pythonhosted.org/packages/1c/ec/423d113c9f74e5e402e175b157203e9102feeb7088cee844d735b28ef963/greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff", size = 649175 }, + { url = "https://files.pythonhosted.org/packages/a9/46/ddbd2db9ff209186b7b7c621d1432e2f21714adc988703dbdd0e65155c77/greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a", size = 663425 }, + { url = "https://files.pythonhosted.org/packages/bc/f9/9c82d6b2b04aa37e38e74f0c429aece5eeb02bab6e3b98e7db89b23d94c6/greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e", size = 657736 }, + { url = "https://files.pythonhosted.org/packages/d9/42/b87bc2a81e3a62c3de2b0d550bf91a86939442b7ff85abb94eec3fc0e6aa/greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4", size = 660347 }, + { url = "https://files.pythonhosted.org/packages/37/fa/71599c3fd06336cdc3eac52e6871cfebab4d9d70674a9a9e7a482c318e99/greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e", size = 615583 }, + { url = "https://files.pythonhosted.org/packages/4e/96/e9ef85de031703ee7a4483489b40cf307f93c1824a02e903106f2ea315fe/greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1", size = 1133039 }, + { url = "https://files.pythonhosted.org/packages/87/76/b2b6362accd69f2d1889db61a18c94bc743e961e3cab344c2effaa4b4a25/greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c", size = 1160716 }, + { url = "https://files.pythonhosted.org/packages/1f/1b/54336d876186920e185066d8c3024ad55f21d7cc3683c856127ddb7b13ce/greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761", size = 299490 }, + { url = "https://files.pythonhosted.org/packages/5f/17/bea55bf36990e1638a2af5ba10c1640273ef20f627962cf97107f1e5d637/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011", size = 643731 }, + { url = "https://files.pythonhosted.org/packages/78/d2/aa3d2157f9ab742a08e0fd8f77d4699f37c22adfbfeb0c610a186b5f75e0/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13", size = 649304 }, + { url = "https://files.pythonhosted.org/packages/f1/8e/d0aeffe69e53ccff5a28fa86f07ad1d2d2d6537a9506229431a2a02e2f15/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475", size = 646537 }, + { url = "https://files.pythonhosted.org/packages/05/79/e15408220bbb989469c8871062c97c6c9136770657ba779711b90870d867/greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b", size = 642506 }, + { url = "https://files.pythonhosted.org/packages/18/87/470e01a940307796f1d25f8167b551a968540fbe0551c0ebb853cb527dd6/greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822", size = 602753 }, + { url = "https://files.pythonhosted.org/packages/e2/72/576815ba674eddc3c25028238f74d7b8068902b3968cbe456771b166455e/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01", size = 1122731 }, + { url = "https://files.pythonhosted.org/packages/ac/38/08cc303ddddc4b3d7c628c3039a61a3aae36c241ed01393d00c2fd663473/greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6", size = 1142112 }, + { url = "https://files.pythonhosted.org/packages/97/83/bdf5f69fcf304065ec7cf8fc7c08248479cfed9bcca02bf0001c07e000aa/greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9", size = 271017 }, + { url = "https://files.pythonhosted.org/packages/31/4a/2d4443adcb38e1e90e50c653a26b2be39998ea78ca1a4cf414dfdeb2e98b/greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111", size = 642888 }, + { url = "https://files.pythonhosted.org/packages/5a/c9/b5d9ac1b932aa772dd1eb90a8a2b30dbd7ad5569dcb7fdac543810d206b4/greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81", size = 655451 }, + { url = "https://files.pythonhosted.org/packages/a8/18/218e21caf7caba5b2236370196eaebc00987d4a2b2d3bf63cc4d4dd5a69f/greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba", size = 651409 }, + { url = "https://files.pythonhosted.org/packages/a7/25/de419a2b22fa6e18ce3b2a5adb01d33ec7b2784530f76fa36ba43d8f0fac/greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8", size = 650661 }, + { url = "https://files.pythonhosted.org/packages/d8/88/0ce16c0afb2d71d85562a7bcd9b092fec80a7767ab5b5f7e1bbbca8200f8/greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1", size = 605959 }, + { url = "https://files.pythonhosted.org/packages/5a/10/39a417ad0afb0b7e5b150f1582cdeb9416f41f2e1df76018434dfac4a6cc/greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd", size = 1132341 }, + { url = "https://files.pythonhosted.org/packages/9f/f5/e9b151ddd2ed0508b7a47bef7857e46218dbc3fd10e564617a3865abfaac/greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7", size = 1159409 }, + { url = "https://files.pythonhosted.org/packages/86/97/2c86989ca4e0f089fbcdc9229c972a01ef53abdafd5ae89e0f3dcdcd4adb/greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef", size = 281126 }, + { url = "https://files.pythonhosted.org/packages/d3/50/7b7a3e10ed82c760c1fd8d3167a7c95508e9fdfc0b0604f05ed1a9a9efdc/greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d", size = 298285 }, + { url = "https://files.pythonhosted.org/packages/8c/82/8051e82af6d6b5150aacb6789a657a8afd48f0a44d8e91cb72aaaf28553a/greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3", size = 270027 }, + { url = "https://files.pythonhosted.org/packages/f9/74/f66de2785880293780eebd18a2958aeea7cbe7814af1ccef634f4701f846/greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42", size = 634822 }, + { url = "https://files.pythonhosted.org/packages/68/23/acd9ca6bc412b02b8aa755e47b16aafbe642dde0ad2f929f836e57a7949c/greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f", size = 646866 }, + { url = "https://files.pythonhosted.org/packages/a9/ab/562beaf8a53dc9f6b2459f200e7bc226bb07e51862a66351d8b7817e3efd/greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437", size = 641985 }, + { url = "https://files.pythonhosted.org/packages/03/d3/1006543621f16689f6dc75f6bcf06e3c23e044c26fe391c16c253623313e/greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145", size = 641268 }, + { url = "https://files.pythonhosted.org/packages/2f/c1/ad71ce1b5f61f900593377b3f77b39408bce5dc96754790311b49869e146/greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c", size = 597376 }, + { url = "https://files.pythonhosted.org/packages/f7/ff/183226685b478544d61d74804445589e069d00deb8ddef042699733950c7/greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e", size = 1123359 }, + { url = "https://files.pythonhosted.org/packages/c0/8b/9b3b85a89c22f55f315908b94cd75ab5fed5973f7393bbef000ca8b2c5c1/greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e", size = 1147458 }, + { url = "https://files.pythonhosted.org/packages/b8/1c/248fadcecd1790b0ba793ff81fa2375c9ad6442f4c748bf2cc2e6563346a/greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c", size = 281131 }, + { url = "https://files.pythonhosted.org/packages/ae/02/e7d0aef2354a38709b764df50b2b83608f0621493e47f47694eb80922822/greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22", size = 298306 }, +] + +[[package]] +name = "grpclib" +version = "0.4.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "h2" }, + { name = "multidict" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/79/b9/55936e462a5925190d7427e880b3033601d1effd13809b483d13a926061a/grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3", size = 61254 } + +[[package]] +name = "h2" +version = "4.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "hpack" }, + { name = "hyperframe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2a/32/fec683ddd10629ea4ea46d206752a95a2d8a48c22521edd70b142488efe1/h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb", size = 2145593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/e5/db6d438da759efbb488c4f3fbdab7764492ff3c3f953132efa6b9f0e9e53/h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d", size = 57488 }, +] + +[[package]] +name = "hpack" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3e/9b/fda93fb4d957db19b0f6b370e79d586b3e8528b20252c729c476a2c02954/hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095", size = 49117 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/34/e8b383f35b77c402d28563d2b8f83159319b509bc5f760b15d60b0abf165/hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c", size = 32611 }, +] + +[[package]] +name = "hyperframe" +version = "6.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5a/2a/4747bff0a17f7281abe73e955d60d80aae537a5d203f417fa1c2e7578ebb/hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914", size = 25008 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/de/85a784bcc4a3779d1753a7ec2dee5de90e18c7bcf402e71b51fcf150b129/hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15", size = 12389 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "imagesize" +version = "1.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/84/62473fb57d61e31fef6e36d64a179c8781605429fd927b5dd608c997be31/imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a", size = 1280026 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/62/85c4c919272577931d407be5ba5d71c20f0b616d31a0befe0ae45bb79abd/imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b", size = 8769 }, +] + +[[package]] +name = "importlib-metadata" +version = "8.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cd/12/33e59336dca5be0c398a7482335911a33aa0e20776128f038019f1a95f1b/importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7", size = 55304 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/d9/a1e041c5e7caa9a05c925f4bdbdfb7f006d1f74996af53467bc394c97be7/importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b", size = 26514 }, +] + +[[package]] +name = "importlib-resources" +version = "6.4.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/be/f3e8c6081b684f176b761e6a2fef02a0be939740ed6f54109a2951d806f3/importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065", size = 43372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/6a/4604f9ae2fa62ef47b9de2fa5ad599589d28c9fd1d335f32759813dfa91e/importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717", size = 36115 }, +] + +[[package]] +name = "iniconfig" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/4b/cbd8e699e64a6f16ca3a8220661b5f83792b3017d0f79807cb8708d33913/iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", size = 4646 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/a6/62565a6e1cf69e10f5727360368e451d4b7f58beeac6173dc9db836a5b46/iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374", size = 5892 }, +] + +[[package]] +name = "ipykernel" +version = "6.29.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "comm" }, + { name = "debugpy" }, + { name = "ipython" }, + { name = "jupyter-client" }, + { name = "jupyter-core" }, + { name = "matplotlib-inline" }, + { name = "nest-asyncio" }, + { name = "packaging" }, + { name = "psutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/5c/67594cb0c7055dc50814b21731c22a601101ea3b1b50a9a1b090e11f5d0f/ipykernel-6.29.5.tar.gz", hash = "sha256:f093a22c4a40f8828f8e330a9c297cb93dcab13bd9678ded6de8e5cf81c56215", size = 163367 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/5c/368ae6c01c7628438358e6d337c19b05425727fbb221d2a3c4303c372f42/ipykernel-6.29.5-py3-none-any.whl", hash = "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5", size = 117173 }, +] + +[[package]] +name = "ipython" +version = "8.12.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "appnope", marker = "sys_platform == 'darwin'" }, + { name = "backcall" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "decorator" }, + { name = "jedi" }, + { name = "matplotlib-inline" }, + { name = "pexpect", marker = "sys_platform != 'win32'" }, + { name = "pickleshare" }, + { name = "prompt-toolkit" }, + { name = "pygments" }, + { name = "stack-data" }, + { name = "traitlets" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/6a/44ef299b1762f5a73841e87fae8a73a8cc8aee538d6dc8c77a5afe1fd2ce/ipython-8.12.3.tar.gz", hash = "sha256:3910c4b54543c2ad73d06579aa771041b7d5707b033bd488669b4cf544e3b363", size = 5470171 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/97/8fe103906cd81bc42d3b0175b5534a9f67dccae47d6451131cf8d0d70bb2/ipython-8.12.3-py3-none-any.whl", hash = "sha256:b0340d46a933d27c657b211a329d0be23793c36595acf9e6ef4164bc01a1804c", size = 798307 }, +] + +[[package]] +name = "jedi" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/99/99b493cec4bf43176b678de30f81ed003fd6a647a301b9c927280c600f0a/jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd", size = 1227821 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/9f/bc63f0f0737ad7a60800bfd472a4836661adae21f9c2535f3957b1e54ceb/jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0", size = 1569361 }, +] + +[[package]] +name = "jinja2" +version = "3.1.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ed/55/39036716d19cab0747a5020fc7e907f362fbf48c984b14e62127f7e68e5d/jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369", size = 240245 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/31/80/3a54838c3fb461f6fec263ebf3a3a41771bd05190238de3486aae8540c36/jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d", size = 133271 }, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "importlib-resources", marker = "python_full_version < '3.9'" }, + { name = "jsonschema-specifications" }, + { name = "pkgutil-resolve-name", marker = "python_full_version < '3.9'" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2023.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-resources", marker = "python_full_version < '3.9'" }, + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f8/b9/cc0cc592e7c195fb8a650c1d5990b10175cf13b4c97465c72ec841de9e4b/jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", size = 13983 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ee/07/44bd408781594c4d0a027666ef27fab1e441b109dc3b76b4f836f8fd04fe/jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c", size = 18482 }, +] + +[[package]] +name = "jupyter-cache" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "click" }, + { name = "importlib-metadata" }, + { name = "nbclient" }, + { name = "nbformat" }, + { name = "pyyaml" }, + { name = "sqlalchemy" }, + { name = "tabulate" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/64/08dcc1f6fc54a263525edd23b5d2754793470c1c41a8dd82d52406f8d876/jupyter-cache-0.6.1.tar.gz", hash = "sha256:26f83901143edf4af2f3ff5a91e2d2ad298e46e2cee03c8071d37a23a63ccbfc", size = 31953 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/da/8e/918b115bb3b4b821e2d43315e1a08b909219723191623ffbae9072fd226a/jupyter_cache-0.6.1-py3-none-any.whl", hash = "sha256:2fce7d4975805c77f75bdfc1bc2e82bc538b8e5b1af27f2f5e06d55b9f996a82", size = 33886 }, +] + +[[package]] +name = "jupyter-client" +version = "8.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jupyter-core" }, + { name = "python-dateutil" }, + { name = "pyzmq" }, + { name = "tornado" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/22/bf9f12fdaeae18019a468b68952a60fe6dbab5d67cd2a103cac7659b41ca/jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419", size = 342019 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/85/b0394e0b6fcccd2c1eeefc230978a6f8cb0c5df1e4cd3e7625735a0d7d1e/jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f", size = 106105 }, +] + +[[package]] +name = "jupyter-core" +version = "5.7.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "platformdirs" }, + { name = "pywin32", marker = "platform_python_implementation != 'PyPy' and sys_platform == 'win32'" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/11/b56381fa6c3f4cc5d2cf54a7dbf98ad9aa0b339ef7a601d6053538b079a7/jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9", size = 87629 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/fb/108ecd1fe961941959ad0ee4e12ee7b8b1477247f30b1fdfd83ceaf017f0/jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409", size = 28965 }, +] + +[[package]] +name = "markdown-it-py" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +dependencies = [ + { name = "mdurl", marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e4/c0/59bd6d0571986f72899288a95d9d6178d0eebd70b6650f1bb3f0da90f8f7/markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1", size = 67120 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/25/2d88e8feee8e055d015343f9b86e370a1ccbec546f2865c98397aaef24af/markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30", size = 84466 }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9' and python_full_version < '3.12'", + "python_full_version == '3.12.*'", + "python_full_version >= '3.13'", +] +dependencies = [ + { name = "mdurl", marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, +] + +[[package]] +name = "markupsafe" +version = "2.1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/87/5b/aae44c6655f3801e81aa3eef09dbbf012431987ba564d7231722f68df02d/MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", size = 19384 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e4/54/ad5eb37bf9d51800010a74e4665425831a9db4e7c4e0fde4352e391e808e/MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", size = 18206 }, + { url = "https://files.pythonhosted.org/packages/6a/4a/a4d49415e600bacae038c67f9fecc1d5433b9d3c71a4de6f33537b89654c/MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", size = 14079 }, + { url = "https://files.pythonhosted.org/packages/0a/7b/85681ae3c33c385b10ac0f8dd025c30af83c78cec1c37a6aa3b55e67f5ec/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", size = 26620 }, + { url = "https://files.pythonhosted.org/packages/7c/52/2b1b570f6b8b803cef5ac28fdf78c0da318916c7d2fe9402a84d591b394c/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", size = 25818 }, + { url = "https://files.pythonhosted.org/packages/29/fe/a36ba8c7ca55621620b2d7c585313efd10729e63ef81e4e61f52330da781/MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", size = 25493 }, + { url = "https://files.pythonhosted.org/packages/60/ae/9c60231cdfda003434e8bd27282b1f4e197ad5a710c14bee8bea8a9ca4f0/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", size = 30630 }, + { url = "https://files.pythonhosted.org/packages/65/dc/1510be4d179869f5dafe071aecb3f1f41b45d37c02329dfba01ff59e5ac5/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", size = 29745 }, + { url = "https://files.pythonhosted.org/packages/30/39/8d845dd7d0b0613d86e0ef89549bfb5f61ed781f59af45fc96496e897f3a/MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", size = 30021 }, + { url = "https://files.pythonhosted.org/packages/c7/5c/356a6f62e4f3c5fbf2602b4771376af22a3b16efa74eb8716fb4e328e01e/MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", size = 16659 }, + { url = "https://files.pythonhosted.org/packages/69/48/acbf292615c65f0604a0c6fc402ce6d8c991276e16c80c46a8f758fbd30c/MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", size = 17213 }, + { url = "https://files.pythonhosted.org/packages/11/e7/291e55127bb2ae67c64d66cef01432b5933859dfb7d6949daa721b89d0b3/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", size = 18219 }, + { url = "https://files.pythonhosted.org/packages/6b/cb/aed7a284c00dfa7c0682d14df85ad4955a350a21d2e3b06d8240497359bf/MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", size = 14098 }, + { url = "https://files.pythonhosted.org/packages/1c/cf/35fe557e53709e93feb65575c93927942087e9b97213eabc3fe9d5b25a55/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", size = 29014 }, + { url = "https://files.pythonhosted.org/packages/97/18/c30da5e7a0e7f4603abfc6780574131221d9148f323752c2755d48abad30/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", size = 28220 }, + { url = "https://files.pythonhosted.org/packages/0c/40/2e73e7d532d030b1e41180807a80d564eda53babaf04d65e15c1cf897e40/MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", size = 27756 }, + { url = "https://files.pythonhosted.org/packages/18/46/5dca760547e8c59c5311b332f70605d24c99d1303dd9a6e1fc3ed0d73561/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", size = 33988 }, + { url = "https://files.pythonhosted.org/packages/6d/c5/27febe918ac36397919cd4a67d5579cbbfa8da027fa1238af6285bb368ea/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", size = 32718 }, + { url = "https://files.pythonhosted.org/packages/f8/81/56e567126a2c2bc2684d6391332e357589a96a76cb9f8e5052d85cb0ead8/MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", size = 33317 }, + { url = "https://files.pythonhosted.org/packages/00/0b/23f4b2470accb53285c613a3ab9ec19dc944eaf53592cb6d9e2af8aa24cc/MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", size = 16670 }, + { url = "https://files.pythonhosted.org/packages/b7/a2/c78a06a9ec6d04b3445a949615c4c7ed86a0b2eb68e44e7541b9d57067cc/MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", size = 17224 }, + { url = "https://files.pythonhosted.org/packages/53/bd/583bf3e4c8d6a321938c13f49d44024dbe5ed63e0a7ba127e454a66da974/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", size = 18215 }, + { url = "https://files.pythonhosted.org/packages/48/d6/e7cd795fc710292c3af3a06d80868ce4b02bfbbf370b7cee11d282815a2a/MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", size = 14069 }, + { url = "https://files.pythonhosted.org/packages/51/b5/5d8ec796e2a08fc814a2c7d2584b55f889a55cf17dd1a90f2beb70744e5c/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", size = 29452 }, + { url = "https://files.pythonhosted.org/packages/0a/0d/2454f072fae3b5a137c119abf15465d1771319dfe9e4acbb31722a0fff91/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", size = 28462 }, + { url = "https://files.pythonhosted.org/packages/2d/75/fd6cb2e68780f72d47e6671840ca517bda5ef663d30ada7616b0462ad1e3/MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", size = 27869 }, + { url = "https://files.pythonhosted.org/packages/b0/81/147c477391c2750e8fc7705829f7351cf1cd3be64406edcf900dc633feb2/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", size = 33906 }, + { url = "https://files.pythonhosted.org/packages/8b/ff/9a52b71839d7a256b563e85d11050e307121000dcebc97df120176b3ad93/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", size = 32296 }, + { url = "https://files.pythonhosted.org/packages/88/07/2dc76aa51b481eb96a4c3198894f38b480490e834479611a4053fbf08623/MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", size = 33038 }, + { url = "https://files.pythonhosted.org/packages/96/0c/620c1fb3661858c0e37eb3cbffd8c6f732a67cd97296f725789679801b31/MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", size = 16572 }, + { url = "https://files.pythonhosted.org/packages/3f/14/c3554d512d5f9100a95e737502f4a2323a1959f6d0d01e0d0997b35f7b10/MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", size = 17127 }, + { url = "https://files.pythonhosted.org/packages/f8/ff/2c942a82c35a49df5de3a630ce0a8456ac2969691b230e530ac12314364c/MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", size = 18192 }, + { url = "https://files.pythonhosted.org/packages/4f/14/6f294b9c4f969d0c801a4615e221c1e084722ea6114ab2114189c5b8cbe0/MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", size = 14072 }, + { url = "https://files.pythonhosted.org/packages/81/d4/fd74714ed30a1dedd0b82427c02fa4deec64f173831ec716da11c51a50aa/MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", size = 26928 }, + { url = "https://files.pythonhosted.org/packages/c7/bd/50319665ce81bb10e90d1cf76f9e1aa269ea6f7fa30ab4521f14d122a3df/MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", size = 26106 }, + { url = "https://files.pythonhosted.org/packages/4c/6f/f2b0f675635b05f6afd5ea03c094557bdb8622fa8e673387444fe8d8e787/MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68", size = 25781 }, + { url = "https://files.pythonhosted.org/packages/51/e0/393467cf899b34a9d3678e78961c2c8cdf49fb902a959ba54ece01273fb1/MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", size = 30518 }, + { url = "https://files.pythonhosted.org/packages/f6/02/5437e2ad33047290dafced9df741d9efc3e716b75583bbd73a9984f1b6f7/MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", size = 29669 }, + { url = "https://files.pythonhosted.org/packages/0e/7d/968284145ffd9d726183ed6237c77938c021abacde4e073020f920e060b2/MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", size = 29933 }, + { url = "https://files.pythonhosted.org/packages/bf/f3/ecb00fc8ab02b7beae8699f34db9357ae49d9f21d4d3de6f305f34fa949e/MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", size = 16656 }, + { url = "https://files.pythonhosted.org/packages/92/21/357205f03514a49b293e214ac39de01fadd0970a6e05e4bf1ddd0ffd0881/MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", size = 17206 }, + { url = "https://files.pythonhosted.org/packages/0f/31/780bb297db036ba7b7bbede5e1d7f1e14d704ad4beb3ce53fb495d22bc62/MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", size = 18193 }, + { url = "https://files.pythonhosted.org/packages/6c/77/d77701bbef72892affe060cdacb7a2ed7fd68dae3b477a8642f15ad3b132/MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", size = 14073 }, + { url = "https://files.pythonhosted.org/packages/d9/a7/1e558b4f78454c8a3a0199292d96159eb4d091f983bc35ef258314fe7269/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", size = 26486 }, + { url = "https://files.pythonhosted.org/packages/5f/5a/360da85076688755ea0cceb92472923086993e86b5613bbae9fbc14136b0/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", size = 25685 }, + { url = "https://files.pythonhosted.org/packages/6a/18/ae5a258e3401f9b8312f92b028c54d7026a97ec3ab20bfaddbdfa7d8cce8/MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", size = 25338 }, + { url = "https://files.pythonhosted.org/packages/0b/cc/48206bd61c5b9d0129f4d75243b156929b04c94c09041321456fd06a876d/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", size = 30439 }, + { url = "https://files.pythonhosted.org/packages/d1/06/a41c112ab9ffdeeb5f77bc3e331fdadf97fa65e52e44ba31880f4e7f983c/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", size = 29531 }, + { url = "https://files.pythonhosted.org/packages/02/8c/ab9a463301a50dab04d5472e998acbd4080597abc048166ded5c7aa768c8/MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", size = 29823 }, + { url = "https://files.pythonhosted.org/packages/bc/29/9bc18da763496b055d8e98ce476c8e718dcfd78157e17f555ce6dd7d0895/MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", size = 16658 }, + { url = "https://files.pythonhosted.org/packages/f6/f8/4da07de16f10551ca1f640c92b5f316f9394088b183c6a57183df6de5ae4/MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", size = 17211 }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.1.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/a36a337438a14116b16480db471ad061c36c3694df7c2084a0da7ba538b7/matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90", size = 8159 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/8e/9ad090d3553c280a8060fbf6e24dc1c0c29704ee7d1c372f0c174aa59285/matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca", size = 9899 }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.3.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +dependencies = [ + { name = "markdown-it-py", version = "2.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/e7/cc2720da8a32724b36d04c6dba5644154cdf883a1482b3bbb81959a642ed/mdit-py-plugins-0.3.5.tar.gz", hash = "sha256:eee0adc7195e5827e17e02d2a258a2ba159944a0748f59c5099a4a27f78fcf6a", size = 39871 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/4c/a9b222f045f98775034d243198212cbea36d3524c3ee1e8ab8c0346d6953/mdit_py_plugins-0.3.5-py3-none-any.whl", hash = "sha256:ca9a0714ea59a24b2b044a1831f48d817dd0c817e84339f20e7889f392d77c4e", size = 52087 }, +] + +[[package]] +name = "mdit-py-plugins" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9' and python_full_version < '3.12'", + "python_full_version == '3.12.*'", + "python_full_version >= '3.13'", +] +dependencies = [ + { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, +] + +[[package]] +name = "multidict" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d6/be/504b89a5e9ca731cd47487e91c469064f8ae5af93b7259758dcfc2b9c848/multidict-6.1.0.tar.gz", hash = "sha256:22ae2ebf9b0c69d206c003e2f6a914ea33f0a932d4aa16f236afc049d9958f4a", size = 64002 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/29/68/259dee7fd14cf56a17c554125e534f6274c2860159692a414d0b402b9a6d/multidict-6.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3380252550e372e8511d49481bd836264c009adb826b23fefcc5dd3c69692f60", size = 48628 }, + { url = "https://files.pythonhosted.org/packages/50/79/53ba256069fe5386a4a9e80d4e12857ced9de295baf3e20c68cdda746e04/multidict-6.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:99f826cbf970077383d7de805c0681799491cb939c25450b9b5b3ced03ca99f1", size = 29327 }, + { url = "https://files.pythonhosted.org/packages/ff/10/71f1379b05b196dae749b5ac062e87273e3f11634f447ebac12a571d90ae/multidict-6.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a114d03b938376557927ab23f1e950827c3b893ccb94b62fd95d430fd0e5cf53", size = 29689 }, + { url = "https://files.pythonhosted.org/packages/71/45/70bac4f87438ded36ad4793793c0095de6572d433d98575a5752629ef549/multidict-6.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1c416351ee6271b2f49b56ad7f308072f6f44b37118d69c2cad94f3fa8a40d5", size = 126639 }, + { url = "https://files.pythonhosted.org/packages/80/cf/17f35b3b9509b4959303c05379c4bfb0d7dd05c3306039fc79cf035bbac0/multidict-6.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6b5d83030255983181005e6cfbac1617ce9746b219bc2aad52201ad121226581", size = 134315 }, + { url = "https://files.pythonhosted.org/packages/ef/1f/652d70ab5effb33c031510a3503d4d6efc5ec93153562f1ee0acdc895a57/multidict-6.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3e97b5e938051226dc025ec80980c285b053ffb1e25a3db2a3aa3bc046bf7f56", size = 129471 }, + { url = "https://files.pythonhosted.org/packages/a6/64/2dd6c4c681688c0165dea3975a6a4eab4944ea30f35000f8b8af1df3148c/multidict-6.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d618649d4e70ac6efcbba75be98b26ef5078faad23592f9b51ca492953012429", size = 124585 }, + { url = "https://files.pythonhosted.org/packages/87/56/e6ee5459894c7e554b57ba88f7257dc3c3d2d379cb15baaa1e265b8c6165/multidict-6.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10524ebd769727ac77ef2278390fb0068d83f3acb7773792a5080f2b0abf7748", size = 116957 }, + { url = "https://files.pythonhosted.org/packages/36/9e/616ce5e8d375c24b84f14fc263c7ef1d8d5e8ef529dbc0f1df8ce71bb5b8/multidict-6.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ff3827aef427c89a25cc96ded1759271a93603aba9fb977a6d264648ebf989db", size = 128609 }, + { url = "https://files.pythonhosted.org/packages/8c/4f/4783e48a38495d000f2124020dc96bacc806a4340345211b1ab6175a6cb4/multidict-6.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:06809f4f0f7ab7ea2cabf9caca7d79c22c0758b58a71f9d32943ae13c7ace056", size = 123016 }, + { url = "https://files.pythonhosted.org/packages/3e/b3/4950551ab8fc39862ba5e9907dc821f896aa829b4524b4deefd3e12945ab/multidict-6.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f179dee3b863ab1c59580ff60f9d99f632f34ccb38bf67a33ec6b3ecadd0fd76", size = 133542 }, + { url = "https://files.pythonhosted.org/packages/96/4d/f0ce6ac9914168a2a71df117935bb1f1781916acdecbb43285e225b484b8/multidict-6.1.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:aaed8b0562be4a0876ee3b6946f6869b7bcdb571a5d1496683505944e268b160", size = 130163 }, + { url = "https://files.pythonhosted.org/packages/be/72/17c9f67e7542a49dd252c5ae50248607dfb780bcc03035907dafefb067e3/multidict-6.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3c8b88a2ccf5493b6c8da9076fb151ba106960a2df90c2633f342f120751a9e7", size = 126832 }, + { url = "https://files.pythonhosted.org/packages/71/9f/72d719e248cbd755c8736c6d14780533a1606ffb3fbb0fbd77da9f0372da/multidict-6.1.0-cp310-cp310-win32.whl", hash = "sha256:4a9cb68166a34117d6646c0023c7b759bf197bee5ad4272f420a0141d7eb03a0", size = 26402 }, + { url = "https://files.pythonhosted.org/packages/04/5a/d88cd5d00a184e1ddffc82aa2e6e915164a6d2641ed3606e766b5d2f275a/multidict-6.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:20b9b5fbe0b88d0bdef2012ef7dee867f874b72528cf1d08f1d59b0e3850129d", size = 28800 }, + { url = "https://files.pythonhosted.org/packages/93/13/df3505a46d0cd08428e4c8169a196131d1b0c4b515c3649829258843dde6/multidict-6.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3efe2c2cb5763f2f1b275ad2bf7a287d3f7ebbef35648a9726e3b69284a4f3d6", size = 48570 }, + { url = "https://files.pythonhosted.org/packages/f0/e1/a215908bfae1343cdb72f805366592bdd60487b4232d039c437fe8f5013d/multidict-6.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c7053d3b0353a8b9de430a4f4b4268ac9a4fb3481af37dfe49825bf45ca24156", size = 29316 }, + { url = "https://files.pythonhosted.org/packages/70/0f/6dc70ddf5d442702ed74f298d69977f904960b82368532c88e854b79f72b/multidict-6.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27e5fc84ccef8dfaabb09d82b7d179c7cf1a3fbc8a966f8274fcb4ab2eb4cadb", size = 29640 }, + { url = "https://files.pythonhosted.org/packages/d8/6d/9c87b73a13d1cdea30b321ef4b3824449866bd7f7127eceed066ccb9b9ff/multidict-6.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e2b90b43e696f25c62656389d32236e049568b39320e2735d51f08fd362761b", size = 131067 }, + { url = "https://files.pythonhosted.org/packages/cc/1e/1b34154fef373371fd6c65125b3d42ff5f56c7ccc6bfff91b9b3c60ae9e0/multidict-6.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d83a047959d38a7ff552ff94be767b7fd79b831ad1cd9920662db05fec24fe72", size = 138507 }, + { url = "https://files.pythonhosted.org/packages/fb/e0/0bc6b2bac6e461822b5f575eae85da6aae76d0e2a79b6665d6206b8e2e48/multidict-6.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d1a9dd711d0877a1ece3d2e4fea11a8e75741ca21954c919406b44e7cf971304", size = 133905 }, + { url = "https://files.pythonhosted.org/packages/ba/af/73d13b918071ff9b2205fcf773d316e0f8fefb4ec65354bbcf0b10908cc6/multidict-6.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec2abea24d98246b94913b76a125e855eb5c434f7c46546046372fe60f666351", size = 129004 }, + { url = "https://files.pythonhosted.org/packages/74/21/23960627b00ed39643302d81bcda44c9444ebcdc04ee5bedd0757513f259/multidict-6.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4867cafcbc6585e4b678876c489b9273b13e9fff9f6d6d66add5e15d11d926cb", size = 121308 }, + { url = "https://files.pythonhosted.org/packages/8b/5c/cf282263ffce4a596ed0bb2aa1a1dddfe1996d6a62d08842a8d4b33dca13/multidict-6.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5b48204e8d955c47c55b72779802b219a39acc3ee3d0116d5080c388970b76e3", size = 132608 }, + { url = "https://files.pythonhosted.org/packages/d7/3e/97e778c041c72063f42b290888daff008d3ab1427f5b09b714f5a8eff294/multidict-6.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:d8fff389528cad1618fb4b26b95550327495462cd745d879a8c7c2115248e399", size = 127029 }, + { url = "https://files.pythonhosted.org/packages/47/ac/3efb7bfe2f3aefcf8d103e9a7162572f01936155ab2f7ebcc7c255a23212/multidict-6.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a7a9541cd308eed5e30318430a9c74d2132e9a8cb46b901326272d780bf2d423", size = 137594 }, + { url = "https://files.pythonhosted.org/packages/42/9b/6c6e9e8dc4f915fc90a9b7798c44a30773dea2995fdcb619870e705afe2b/multidict-6.1.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:da1758c76f50c39a2efd5e9859ce7d776317eb1dd34317c8152ac9251fc574a3", size = 134556 }, + { url = "https://files.pythonhosted.org/packages/1d/10/8e881743b26aaf718379a14ac58572a240e8293a1c9d68e1418fb11c0f90/multidict-6.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c943a53e9186688b45b323602298ab727d8865d8c9ee0b17f8d62d14b56f0753", size = 130993 }, + { url = "https://files.pythonhosted.org/packages/45/84/3eb91b4b557442802d058a7579e864b329968c8d0ea57d907e7023c677f2/multidict-6.1.0-cp311-cp311-win32.whl", hash = "sha256:90f8717cb649eea3504091e640a1b8568faad18bd4b9fcd692853a04475a4b80", size = 26405 }, + { url = "https://files.pythonhosted.org/packages/9f/0b/ad879847ecbf6d27e90a6eabb7eff6b62c129eefe617ea45eae7c1f0aead/multidict-6.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:82176036e65644a6cc5bd619f65f6f19781e8ec2e5330f51aa9ada7504cc1926", size = 28795 }, + { url = "https://files.pythonhosted.org/packages/fd/16/92057c74ba3b96d5e211b553895cd6dc7cc4d1e43d9ab8fafc727681ef71/multidict-6.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b04772ed465fa3cc947db808fa306d79b43e896beb677a56fb2347ca1a49c1fa", size = 48713 }, + { url = "https://files.pythonhosted.org/packages/94/3d/37d1b8893ae79716179540b89fc6a0ee56b4a65fcc0d63535c6f5d96f217/multidict-6.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6180c0ae073bddeb5a97a38c03f30c233e0a4d39cd86166251617d1bbd0af436", size = 29516 }, + { url = "https://files.pythonhosted.org/packages/a2/12/adb6b3200c363062f805275b4c1e656be2b3681aada66c80129932ff0bae/multidict-6.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:071120490b47aa997cca00666923a83f02c7fbb44f71cf7f136df753f7fa8761", size = 29557 }, + { url = "https://files.pythonhosted.org/packages/47/e9/604bb05e6e5bce1e6a5cf80a474e0f072e80d8ac105f1b994a53e0b28c42/multidict-6.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50b3a2710631848991d0bf7de077502e8994c804bb805aeb2925a981de58ec2e", size = 130170 }, + { url = "https://files.pythonhosted.org/packages/7e/13/9efa50801785eccbf7086b3c83b71a4fb501a4d43549c2f2f80b8787d69f/multidict-6.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b58c621844d55e71c1b7f7c498ce5aa6985d743a1a59034c57a905b3f153c1ef", size = 134836 }, + { url = "https://files.pythonhosted.org/packages/bf/0f/93808b765192780d117814a6dfcc2e75de6dcc610009ad408b8814dca3ba/multidict-6.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55b6d90641869892caa9ca42ff913f7ff1c5ece06474fbd32fb2cf6834726c95", size = 133475 }, + { url = "https://files.pythonhosted.org/packages/d3/c8/529101d7176fe7dfe1d99604e48d69c5dfdcadb4f06561f465c8ef12b4df/multidict-6.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b820514bfc0b98a30e3d85462084779900347e4d49267f747ff54060cc33925", size = 131049 }, + { url = "https://files.pythonhosted.org/packages/ca/0c/fc85b439014d5a58063e19c3a158a889deec399d47b5269a0f3b6a2e28bc/multidict-6.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:10a9b09aba0c5b48c53761b7c720aaaf7cf236d5fe394cd399c7ba662d5f9966", size = 120370 }, + { url = "https://files.pythonhosted.org/packages/db/46/d4416eb20176492d2258fbd47b4abe729ff3b6e9c829ea4236f93c865089/multidict-6.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e16bf3e5fc9f44632affb159d30a437bfe286ce9e02754759be5536b169b305", size = 125178 }, + { url = "https://files.pythonhosted.org/packages/5b/46/73697ad7ec521df7de5531a32780bbfd908ded0643cbe457f981a701457c/multidict-6.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:76f364861c3bfc98cbbcbd402d83454ed9e01a5224bb3a28bf70002a230f73e2", size = 119567 }, + { url = "https://files.pythonhosted.org/packages/cd/ed/51f060e2cb0e7635329fa6ff930aa5cffa17f4c7f5c6c3ddc3500708e2f2/multidict-6.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:820c661588bd01a0aa62a1283f20d2be4281b086f80dad9e955e690c75fb54a2", size = 129822 }, + { url = "https://files.pythonhosted.org/packages/df/9e/ee7d1954b1331da3eddea0c4e08d9142da5f14b1321c7301f5014f49d492/multidict-6.1.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:0e5f362e895bc5b9e67fe6e4ded2492d8124bdf817827f33c5b46c2fe3ffaca6", size = 128656 }, + { url = "https://files.pythonhosted.org/packages/77/00/8538f11e3356b5d95fa4b024aa566cde7a38aa7a5f08f4912b32a037c5dc/multidict-6.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ec660d19bbc671e3a6443325f07263be452c453ac9e512f5eb935e7d4ac28b3", size = 125360 }, + { url = "https://files.pythonhosted.org/packages/be/05/5d334c1f2462d43fec2363cd00b1c44c93a78c3925d952e9a71caf662e96/multidict-6.1.0-cp312-cp312-win32.whl", hash = "sha256:58130ecf8f7b8112cdb841486404f1282b9c86ccb30d3519faf301b2e5659133", size = 26382 }, + { url = "https://files.pythonhosted.org/packages/a3/bf/f332a13486b1ed0496d624bcc7e8357bb8053823e8cd4b9a18edc1d97e73/multidict-6.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:188215fc0aafb8e03341995e7c4797860181562380f81ed0a87ff455b70bf1f1", size = 28529 }, + { url = "https://files.pythonhosted.org/packages/22/67/1c7c0f39fe069aa4e5d794f323be24bf4d33d62d2a348acdb7991f8f30db/multidict-6.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:d569388c381b24671589335a3be6e1d45546c2988c2ebe30fdcada8457a31008", size = 48771 }, + { url = "https://files.pythonhosted.org/packages/3c/25/c186ee7b212bdf0df2519eacfb1981a017bda34392c67542c274651daf23/multidict-6.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:052e10d2d37810b99cc170b785945421141bf7bb7d2f8799d431e7db229c385f", size = 29533 }, + { url = "https://files.pythonhosted.org/packages/67/5e/04575fd837e0958e324ca035b339cea174554f6f641d3fb2b4f2e7ff44a2/multidict-6.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f90c822a402cb865e396a504f9fc8173ef34212a342d92e362ca498cad308e28", size = 29595 }, + { url = "https://files.pythonhosted.org/packages/d3/b2/e56388f86663810c07cfe4a3c3d87227f3811eeb2d08450b9e5d19d78876/multidict-6.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b225d95519a5bf73860323e633a664b0d85ad3d5bede6d30d95b35d4dfe8805b", size = 130094 }, + { url = "https://files.pythonhosted.org/packages/6c/ee/30ae9b4186a644d284543d55d491fbd4239b015d36b23fea43b4c94f7052/multidict-6.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:23bfd518810af7de1116313ebd9092cb9aa629beb12f6ed631ad53356ed6b86c", size = 134876 }, + { url = "https://files.pythonhosted.org/packages/84/c7/70461c13ba8ce3c779503c70ec9d0345ae84de04521c1f45a04d5f48943d/multidict-6.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c09fcfdccdd0b57867577b719c69e347a436b86cd83747f179dbf0cc0d4c1f3", size = 133500 }, + { url = "https://files.pythonhosted.org/packages/4a/9f/002af221253f10f99959561123fae676148dd730e2daa2cd053846a58507/multidict-6.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf6bea52ec97e95560af5ae576bdac3aa3aae0b6758c6efa115236d9e07dae44", size = 131099 }, + { url = "https://files.pythonhosted.org/packages/82/42/d1c7a7301d52af79d88548a97e297f9d99c961ad76bbe6f67442bb77f097/multidict-6.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57feec87371dbb3520da6192213c7d6fc892d5589a93db548331954de8248fd2", size = 120403 }, + { url = "https://files.pythonhosted.org/packages/68/f3/471985c2c7ac707547553e8f37cff5158030d36bdec4414cb825fbaa5327/multidict-6.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0c3f390dc53279cbc8ba976e5f8035eab997829066756d811616b652b00a23a3", size = 125348 }, + { url = "https://files.pythonhosted.org/packages/67/2c/e6df05c77e0e433c214ec1d21ddd203d9a4770a1f2866a8ca40a545869a0/multidict-6.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:59bfeae4b25ec05b34f1956eaa1cb38032282cd4dfabc5056d0a1ec4d696d3aa", size = 119673 }, + { url = "https://files.pythonhosted.org/packages/c5/cd/bc8608fff06239c9fb333f9db7743a1b2eafe98c2666c9a196e867a3a0a4/multidict-6.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:b2f59caeaf7632cc633b5cf6fc449372b83bbdf0da4ae04d5be36118e46cc0aa", size = 129927 }, + { url = "https://files.pythonhosted.org/packages/44/8e/281b69b7bc84fc963a44dc6e0bbcc7150e517b91df368a27834299a526ac/multidict-6.1.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:37bb93b2178e02b7b618893990941900fd25b6b9ac0fa49931a40aecdf083fe4", size = 128711 }, + { url = "https://files.pythonhosted.org/packages/12/a4/63e7cd38ed29dd9f1881d5119f272c898ca92536cdb53ffe0843197f6c85/multidict-6.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4e9f48f58c2c523d5a06faea47866cd35b32655c46b443f163d08c6d0ddb17d6", size = 125519 }, + { url = "https://files.pythonhosted.org/packages/38/e0/4f5855037a72cd8a7a2f60a3952d9aa45feedb37ae7831642102604e8a37/multidict-6.1.0-cp313-cp313-win32.whl", hash = "sha256:3a37ffb35399029b45c6cc33640a92bef403c9fd388acce75cdc88f58bd19a81", size = 26426 }, + { url = "https://files.pythonhosted.org/packages/7e/a5/17ee3a4db1e310b7405f5d25834460073a8ccd86198ce044dfaf69eac073/multidict-6.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e9aa71e15d9d9beaad2c6b9319edcdc0a49a43ef5c0a4c8265ca9ee7d6c67774", size = 28531 }, + { url = "https://files.pythonhosted.org/packages/3e/6a/af41f3aaf5f00fd86cc7d470a2f5b25299b0c84691163b8757f4a1a205f2/multidict-6.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:db7457bac39421addd0c8449933ac32d8042aae84a14911a757ae6ca3eef1392", size = 48597 }, + { url = "https://files.pythonhosted.org/packages/d9/d6/3d4082760ed11b05734f8bf32a0615b99e7d9d2b3730ad698a4d7377c00a/multidict-6.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d094ddec350a2fb899fec68d8353c78233debde9b7d8b4beeafa70825f1c281a", size = 29338 }, + { url = "https://files.pythonhosted.org/packages/9d/7f/5d1ce7f47d44393d429922910afbe88fcd29ee3069babbb47507a4c3a7ea/multidict-6.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5845c1fd4866bb5dd3125d89b90e57ed3138241540897de748cdf19de8a2fca2", size = 29562 }, + { url = "https://files.pythonhosted.org/packages/ce/ec/c425257671af9308a9b626e2e21f7f43841616e4551de94eb3c92aca75b2/multidict-6.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9079dfc6a70abe341f521f78405b8949f96db48da98aeb43f9907f342f627cdc", size = 130980 }, + { url = "https://files.pythonhosted.org/packages/d8/d7/d4220ad2633a89b314593e9b85b5bc9287a7c563c7f9108a4a68d9da5374/multidict-6.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3914f5aaa0f36d5d60e8ece6a308ee1c9784cd75ec8151062614657a114c4478", size = 136694 }, + { url = "https://files.pythonhosted.org/packages/a1/2a/13e554db5830c8d40185a2e22aa8325516a5de9634c3fb2caf3886a829b3/multidict-6.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c08be4f460903e5a9d0f76818db3250f12e9c344e79314d1d570fc69d7f4eae4", size = 131616 }, + { url = "https://files.pythonhosted.org/packages/2e/a9/83692e37d8152f104333132105b67100aabfb2e96a87f6bed67f566035a7/multidict-6.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d093be959277cb7dee84b801eb1af388b6ad3ca6a6b6bf1ed7585895789d027d", size = 129664 }, + { url = "https://files.pythonhosted.org/packages/cc/1c/1718cd518fb9da7e8890d9d1611c1af0ea5e60f68ff415d026e38401ed36/multidict-6.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3702ea6872c5a2a4eeefa6ffd36b042e9773f05b1f37ae3ef7264b1163c2dcf6", size = 121855 }, + { url = "https://files.pythonhosted.org/packages/2b/92/f6ed67514b0e3894198f0eb42dcde22f0851ea35f4561a1e4acf36c7b1be/multidict-6.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2090f6a85cafc5b2db085124d752757c9d251548cedabe9bd31afe6363e0aff2", size = 127928 }, + { url = "https://files.pythonhosted.org/packages/f7/30/c66954115a4dc4dc3c84e02c8ae11bb35a43d79ef93122c3c3a40c4d459b/multidict-6.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:f67f217af4b1ff66c68a87318012de788dd95fcfeb24cc889011f4e1c7454dfd", size = 122793 }, + { url = "https://files.pythonhosted.org/packages/62/c9/d386d01b43871e8e1631eb7b3695f6af071b7ae1ab716caf371100f0eb24/multidict-6.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:189f652a87e876098bbc67b4da1049afb5f5dfbaa310dd67c594b01c10388db6", size = 132762 }, + { url = "https://files.pythonhosted.org/packages/69/ff/f70cb0a2f7a358acf48e32139ce3a150ff18c961ee9c714cc8c0dc7e3584/multidict-6.1.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6bb5992037f7a9eff7991ebe4273ea7f51f1c1c511e6a2ce511d0e7bdb754492", size = 127872 }, + { url = "https://files.pythonhosted.org/packages/89/5b/abea7db3ba4cd07752a9b560f9275a11787cd13f86849b5d99c1ceea921d/multidict-6.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:ac10f4c2b9e770c4e393876e35a7046879d195cd123b4f116d299d442b335bcd", size = 126161 }, + { url = "https://files.pythonhosted.org/packages/22/03/acc77a4667cca4462ee974fc39990803e58fa573d5a923d6e82b7ef6da7e/multidict-6.1.0-cp38-cp38-win32.whl", hash = "sha256:e27bbb6d14416713a8bd7aaa1313c0fc8d44ee48d74497a0ff4c3a1b6ccb5167", size = 26338 }, + { url = "https://files.pythonhosted.org/packages/90/bf/3d0c1cc9c8163abc24625fae89c0ade1ede9bccb6eceb79edf8cff3cca46/multidict-6.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:22f3105d4fb15c8f57ff3959a58fcab6ce36814486500cd7485651230ad4d4ef", size = 28736 }, + { url = "https://files.pythonhosted.org/packages/e7/c9/9e153a6572b38ac5ff4434113af38acf8d5e9957897cdb1f513b3d6614ed/multidict-6.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:4e18b656c5e844539d506a0a06432274d7bd52a7487e6828c63a63d69185626c", size = 48550 }, + { url = "https://files.pythonhosted.org/packages/76/f5/79565ddb629eba6c7f704f09a09df085c8dc04643b12506f10f718cee37a/multidict-6.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a185f876e69897a6f3325c3f19f26a297fa058c5e456bfcff8015e9a27e83ae1", size = 29298 }, + { url = "https://files.pythonhosted.org/packages/60/1b/9851878b704bc98e641a3e0bce49382ae9e05743dac6d97748feb5b7baba/multidict-6.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ab7c4ceb38d91570a650dba194e1ca87c2b543488fe9309b4212694174fd539c", size = 29641 }, + { url = "https://files.pythonhosted.org/packages/89/87/d451d45aab9e422cb0fb2f7720c31a4c1d3012c740483c37f642eba568fb/multidict-6.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e617fb6b0b6953fffd762669610c1c4ffd05632c138d61ac7e14ad187870669c", size = 126202 }, + { url = "https://files.pythonhosted.org/packages/fa/b4/27cbe9f3e2e469359887653f2e45470272eef7295139916cc21107c6b48c/multidict-6.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:16e5f4bf4e603eb1fdd5d8180f1a25f30056f22e55ce51fb3d6ad4ab29f7d96f", size = 133925 }, + { url = "https://files.pythonhosted.org/packages/4d/a3/afc841899face8adfd004235ce759a37619f6ec99eafd959650c5ce4df57/multidict-6.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4c035da3f544b1882bac24115f3e2e8760f10a0107614fc9839fd232200b875", size = 129039 }, + { url = "https://files.pythonhosted.org/packages/5e/41/0d0fb18c1ad574f807196f5f3d99164edf9de3e169a58c6dc2d6ed5742b9/multidict-6.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:957cf8e4b6e123a9eea554fa7ebc85674674b713551de587eb318a2df3e00255", size = 124072 }, + { url = "https://files.pythonhosted.org/packages/00/22/defd7a2e71a44e6e5b9a5428f972e5b572e7fe28e404dfa6519bbf057c93/multidict-6.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:483a6aea59cb89904e1ceabd2b47368b5600fb7de78a6e4a2c2987b2d256cf30", size = 116532 }, + { url = "https://files.pythonhosted.org/packages/91/25/f7545102def0b1d456ab6449388eed2dfd822debba1d65af60194904a23a/multidict-6.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:87701f25a2352e5bf7454caa64757642734da9f6b11384c1f9d1a8e699758057", size = 128173 }, + { url = "https://files.pythonhosted.org/packages/45/79/3dbe8d35fc99f5ea610813a72ab55f426cb9cf482f860fa8496e5409be11/multidict-6.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:682b987361e5fd7a139ed565e30d81fd81e9629acc7d925a205366877d8c8657", size = 122654 }, + { url = "https://files.pythonhosted.org/packages/97/cb/209e735eeab96e1b160825b5d0b36c56d3862abff828fc43999bb957dcad/multidict-6.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce2186a7df133a9c895dea3331ddc5ddad42cdd0d1ea2f0a51e5d161e4762f28", size = 133197 }, + { url = "https://files.pythonhosted.org/packages/e4/3a/a13808a7ada62808afccea67837a79d00ad6581440015ef00f726d064c2d/multidict-6.1.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9f636b730f7e8cb19feb87094949ba54ee5357440b9658b2a32a5ce4bce53972", size = 129754 }, + { url = "https://files.pythonhosted.org/packages/77/dd/8540e139eafb240079242da8f8ffdf9d3f4b4ad1aac5a786cd4050923783/multidict-6.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:73eae06aa53af2ea5270cc066dcaf02cc60d2994bbb2c4ef5764949257d10f43", size = 126402 }, + { url = "https://files.pythonhosted.org/packages/86/99/e82e1a275d8b1ea16d3a251474262258dbbe41c05cce0c01bceda1fc8ea5/multidict-6.1.0-cp39-cp39-win32.whl", hash = "sha256:1ca0083e80e791cffc6efce7660ad24af66c8d4079d2a750b29001b53ff59ada", size = 26421 }, + { url = "https://files.pythonhosted.org/packages/86/1c/9fa630272355af7e4446a2c7550c259f11ee422ab2d30ff90a0a71cf3d9e/multidict-6.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:aa466da5b15ccea564bdab9c89175c762bc12825f4659c11227f515cee76fa4a", size = 28791 }, + { url = "https://files.pythonhosted.org/packages/99/b7/b9e70fde2c0f0c9af4cc5277782a89b66d35948ea3369ec9f598358c3ac5/multidict-6.1.0-py3-none-any.whl", hash = "sha256:48e171e52d1c4d33888e529b999e5900356b9ae588c2f09a52dcefb158b27506", size = 10051 }, +] + +[[package]] +name = "mypy-protobuf" +version = "3.6.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "protobuf" }, + { name = "types-protobuf" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/4d/6f/282d64d66bf48ce60e38a6560753f784e0f88ab245ac2fb5e93f701a36cd/mypy-protobuf-3.6.0.tar.gz", hash = "sha256:02f242eb3409f66889f2b1a3aa58356ec4d909cdd0f93115622e9e70366eca3c", size = 24445 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/73/d6b999782ae22f16971cc05378b3b33f6a89ede3b9619e8366aa23484bca/mypy_protobuf-3.6.0-py3-none-any.whl", hash = "sha256:56176e4d569070e7350ea620262478b49b7efceba4103d468448f1d21492fd6c", size = 16434 }, +] + +[[package]] +name = "myst-nb" +version = "0.17.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.9'" }, + { name = "ipykernel", marker = "python_full_version < '3.9'" }, + { name = "ipython", marker = "python_full_version < '3.9'" }, + { name = "jupyter-cache", marker = "python_full_version < '3.9'" }, + { name = "myst-parser", version = "0.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "nbclient", marker = "python_full_version < '3.9'" }, + { name = "nbformat", marker = "python_full_version < '3.9'" }, + { name = "pyyaml", marker = "python_full_version < '3.9'" }, + { name = "sphinx", version = "5.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "typing-extensions", marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/15/e6/6ec454f930f1d542f1c1d9562d939acc41220408ff996b7c5b3b957fba1d/myst-nb-0.17.2.tar.gz", hash = "sha256:0f61386515fab07c73646adca97fff2f69f41e90d313a260217c5bbe419d858b", size = 74184 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/84/38b6468146945cf0466a149763d62bd0264cb221eaa74c979498ea215f22/myst_nb-0.17.2-py3-none-any.whl", hash = "sha256:132ca4d0f5c308fdd4b6fdaba077712e28e119ccdafd04d6e41b51aac5483494", size = 78636 }, +] + +[[package]] +name = "myst-nb" +version = "1.1.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9' and python_full_version < '3.12'", + "python_full_version == '3.12.*'", + "python_full_version >= '3.13'", +] +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version >= '3.9'" }, + { name = "ipykernel", marker = "python_full_version >= '3.9'" }, + { name = "ipython", marker = "python_full_version >= '3.9'" }, + { name = "jupyter-cache", marker = "python_full_version >= '3.9'" }, + { name = "myst-parser", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "nbclient", marker = "python_full_version >= '3.9'" }, + { name = "nbformat", marker = "python_full_version >= '3.9'" }, + { name = "pyyaml", marker = "python_full_version >= '3.9'" }, + { name = "sphinx", version = "7.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/04/e3/01c093f6a46be2edc0fd370cbf6d227495ea19452939b2810b36657c63d4/myst_nb-1.1.2.tar.gz", hash = "sha256:961b4005657029ca89892a4c75edbf0856c54ceaf6172368b46bf7676c1f7700", size = 78036 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/45/cf78b2f09c46b36f486b75c34a8b48580e53b543bd9a467b3c7eb9054b70/myst_nb-1.1.2-py3-none-any.whl", hash = "sha256:9b7034e5d62640cb6daf03f9ca16ef45d0462fced27944c77aa3f98c7cdcd566", size = 80281 }, +] + +[[package]] +name = "myst-parser" +version = "0.18.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +dependencies = [ + { name = "docutils", marker = "python_full_version < '3.9'" }, + { name = "jinja2", marker = "python_full_version < '3.9'" }, + { name = "markdown-it-py", version = "2.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "mdit-py-plugins", version = "0.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "pyyaml", marker = "python_full_version < '3.9'" }, + { name = "sphinx", version = "5.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "typing-extensions", marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/68/13/91438d3b835a022fcacd858a7106d4813cfccf98b1fd9a6196cfa2c859df/myst-parser-0.18.1.tar.gz", hash = "sha256:79317f4bb2c13053dd6e64f9da1ba1da6cd9c40c8a430c447a7b146a594c246d", size = 64147 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/fd/594c936c65e707deda5670e8fff5ca2c948a12e922813eab5d316694e9ca/myst_parser-0.18.1-py3-none-any.whl", hash = "sha256:61b275b85d9f58aa327f370913ae1bec26ebad372cc99f3ab85c8ec3ee8d9fb8", size = 58157 }, +] + +[[package]] +name = "myst-parser" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9' and python_full_version < '3.12'", + "python_full_version == '3.12.*'", + "python_full_version >= '3.13'", +] +dependencies = [ + { name = "docutils", marker = "python_full_version >= '3.9'" }, + { name = "jinja2", marker = "python_full_version >= '3.9'" }, + { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "mdit-py-plugins", version = "0.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "pyyaml", marker = "python_full_version >= '3.9'" }, + { name = "sphinx", version = "7.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/49/64/e2f13dac02f599980798c01156393b781aec983b52a6e4057ee58f07c43a/myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87", size = 92392 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e2/de/21aa8394f16add8f7427f0a1326ccd2b3a2a8a3245c9252bc5ac034c6155/myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1", size = 83163 }, +] + +[[package]] +name = "nbclient" +version = "0.6.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jupyter-client" }, + { name = "nbformat" }, + { name = "nest-asyncio" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/01/21/917a25fbc2b37ed37135be97efb4c98526008505451ffa841adcd7d11ed5/nbclient-0.6.8.tar.gz", hash = "sha256:268fde3457cafe1539e32eb1c6d796bbedb90b9e92bacd3e43d83413734bb0e8", size = 78917 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e6/1d/de31c4139603b3f43147dca571de7d1928bd23d5c674865342bd457ec419/nbclient-0.6.8-py3-none-any.whl", hash = "sha256:7cce8b415888539180535953f80ea2385cdbb444944cdeb73ffac1556fdbc228", size = 71848 }, +] + +[[package]] +name = "nbformat" +version = "5.10.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "fastjsonschema" }, + { name = "jsonschema" }, + { name = "jupyter-core" }, + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/fd/91545e604bc3dad7dca9ed03284086039b294c6b3d75c0d2fa45f9e9caf3/nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a", size = 142749 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/82/0340caa499416c78e5d8f5f05947ae4bc3cba53c9f038ab6e9ed964e22f1/nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b", size = 78454 }, +] + +[[package]] +name = "nbmake" +version = "1.5.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ipykernel" }, + { name = "nbclient" }, + { name = "nbformat" }, + { name = "pygments" }, + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/db/25/46a5818e699dc72e708754a8b6cfe4383d714d6d2578a795e9393bd5a0a8/nbmake-1.5.4.tar.gz", hash = "sha256:56417fe80d50069671122955532df6e26369a23f68b9c6e2191ae9cfef19abb2", size = 11306 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/b6/4c8649228a418b0a4ea0577fcc921d9facef9f2b1d8669f2808f2cdf1429/nbmake-1.5.4-py3-none-any.whl", hash = "sha256:8e440a61a7d4ab303064aa86b8d2c088177c89960e2b4a0f91a768dc9f68382b", size = 12860 }, +] + +[[package]] +name = "nest-asyncio" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/83/f8/51569ac65d696c8ecbee95938f89d4abf00f47d58d48f6fbabfe8f0baefe/nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe", size = 7418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195 }, +] + +[[package]] +name = "nodeenv" +version = "1.9.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/16/fc88b08840de0e0a72a2f9d8c6bae36be573e475a6326ae854bcc549fc45/nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", size = 47437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314 }, +] + +[[package]] +name = "numpy" +version = "1.24.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +sdist = { url = "https://files.pythonhosted.org/packages/a4/9b/027bec52c633f6556dba6b722d9a0befb40498b9ceddd29cbe67a45a127c/numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463", size = 10911229 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/80/6cdfb3e275d95155a34659163b83c09e3a3ff9f1456880bec6cc63d71083/numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64", size = 19789140 }, + { url = "https://files.pythonhosted.org/packages/64/5f/3f01d753e2175cfade1013eea08db99ba1ee4bdb147ebcf3623b75d12aa7/numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1", size = 13854297 }, + { url = "https://files.pythonhosted.org/packages/5a/b3/2f9c21d799fa07053ffa151faccdceeb69beec5a010576b8991f614021f7/numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4", size = 13995611 }, + { url = "https://files.pythonhosted.org/packages/10/be/ae5bf4737cb79ba437879915791f6f26d92583c738d7d960ad94e5c36adf/numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6", size = 17282357 }, + { url = "https://files.pythonhosted.org/packages/c0/64/908c1087be6285f40e4b3e79454552a701664a079321cff519d8c7051d06/numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc", size = 12429222 }, + { url = "https://files.pythonhosted.org/packages/22/55/3d5a7c1142e0d9329ad27cece17933b0e2ab4e54ddc5c1861fbfeb3f7693/numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e", size = 14841514 }, + { url = "https://files.pythonhosted.org/packages/a9/cc/5ed2280a27e5dab12994c884f1f4d8c3bd4d885d02ae9e52a9d213a6a5e2/numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810", size = 19775508 }, + { url = "https://files.pythonhosted.org/packages/c0/bc/77635c657a3668cf652806210b8662e1aff84b818a55ba88257abf6637a8/numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254", size = 13840033 }, + { url = "https://files.pythonhosted.org/packages/a7/4c/96cdaa34f54c05e97c1c50f39f98d608f96f0677a6589e64e53104e22904/numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7", size = 13991951 }, + { url = "https://files.pythonhosted.org/packages/22/97/dfb1a31bb46686f09e68ea6ac5c63fdee0d22d7b23b8f3f7ea07712869ef/numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5", size = 17278923 }, + { url = "https://files.pythonhosted.org/packages/35/e2/76a11e54139654a324d107da1d98f99e7aa2a7ef97cfd7c631fba7dbde71/numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d", size = 12422446 }, + { url = "https://files.pythonhosted.org/packages/d8/ec/ebef2f7d7c28503f958f0f8b992e7ce606fb74f9e891199329d5f5f87404/numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694", size = 14834466 }, + { url = "https://files.pythonhosted.org/packages/11/10/943cfb579f1a02909ff96464c69893b1d25be3731b5d3652c2e0cf1281ea/numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61", size = 19780722 }, + { url = "https://files.pythonhosted.org/packages/a7/ae/f53b7b265fdc701e663fbb322a8e9d4b14d9cb7b2385f45ddfabfc4327e4/numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f", size = 13843102 }, + { url = "https://files.pythonhosted.org/packages/25/6f/2586a50ad72e8dbb1d8381f837008a0321a3516dfd7cb57fc8cf7e4bb06b/numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e", size = 14039616 }, + { url = "https://files.pythonhosted.org/packages/98/5d/5738903efe0ecb73e51eb44feafba32bdba2081263d40c5043568ff60faf/numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc", size = 17316263 }, + { url = "https://files.pythonhosted.org/packages/d1/57/8d328f0b91c733aa9aa7ee540dbc49b58796c862b4fbcb1146c701e888da/numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2", size = 12455660 }, + { url = "https://files.pythonhosted.org/packages/69/65/0d47953afa0ad569d12de5f65d964321c208492064c38fe3b0b9744f8d44/numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706", size = 14868112 }, + { url = "https://files.pythonhosted.org/packages/9a/cd/d5b0402b801c8a8b56b04c1e85c6165efab298d2f0ab741c2406516ede3a/numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400", size = 19816549 }, + { url = "https://files.pythonhosted.org/packages/14/27/638aaa446f39113a3ed38b37a66243e21b38110d021bfcb940c383e120f2/numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f", size = 13879950 }, + { url = "https://files.pythonhosted.org/packages/8f/27/91894916e50627476cff1a4e4363ab6179d01077d71b9afed41d9e1f18bf/numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9", size = 14030228 }, + { url = "https://files.pythonhosted.org/packages/7a/7c/d7b2a0417af6428440c0ad7cb9799073e507b1a465f827d058b826236964/numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d", size = 17311170 }, + { url = "https://files.pythonhosted.org/packages/18/9d/e02ace5d7dfccee796c37b995c63322674daf88ae2f4a4724c5dd0afcc91/numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835", size = 12454918 }, + { url = "https://files.pythonhosted.org/packages/63/38/6cc19d6b8bfa1d1a459daf2b3fe325453153ca7019976274b6f33d8b5663/numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8", size = 14867441 }, + { url = "https://files.pythonhosted.org/packages/a4/fd/8dff40e25e937c94257455c237b9b6bf5a30d42dd1cc11555533be099492/numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef", size = 19156590 }, + { url = "https://files.pythonhosted.org/packages/42/e7/4bf953c6e05df90c6d351af69966384fed8e988d0e8c54dad7103b59f3ba/numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a", size = 16705744 }, + { url = "https://files.pythonhosted.org/packages/fc/dd/9106005eb477d022b60b3817ed5937a43dad8fd1f20b0610ea8a32fcb407/numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2", size = 14734290 }, +] + +[[package]] +name = "numpy" +version = "1.26.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9' and python_full_version < '3.12'", + "python_full_version == '3.12.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/65/6e/09db70a523a96d25e115e71cc56a6f9031e7b8cd166c1ac8438307c14058/numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010", size = 15786129 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a7/94/ace0fdea5241a27d13543ee117cbc65868e82213fb31a8eb7fe9ff23f313/numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0", size = 20631468 }, + { url = "https://files.pythonhosted.org/packages/20/f7/b24208eba89f9d1b58c1668bc6c8c4fd472b20c45573cb767f59d49fb0f6/numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a", size = 13966411 }, + { url = "https://files.pythonhosted.org/packages/fc/a5/4beee6488160798683eed5bdb7eead455892c3b4e1f78d79d8d3f3b084ac/numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4", size = 14219016 }, + { url = "https://files.pythonhosted.org/packages/4b/d7/ecf66c1cd12dc28b4040b15ab4d17b773b87fa9d29ca16125de01adb36cd/numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f", size = 18240889 }, + { url = "https://files.pythonhosted.org/packages/24/03/6f229fe3187546435c4f6f89f6d26c129d4f5bed40552899fcf1f0bf9e50/numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a", size = 13876746 }, + { url = "https://files.pythonhosted.org/packages/39/fe/39ada9b094f01f5a35486577c848fe274e374bbf8d8f472e1423a0bbd26d/numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2", size = 18078620 }, + { url = "https://files.pythonhosted.org/packages/d5/ef/6ad11d51197aad206a9ad2286dc1aac6a378059e06e8cf22cd08ed4f20dc/numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07", size = 5972659 }, + { url = "https://files.pythonhosted.org/packages/19/77/538f202862b9183f54108557bfda67e17603fc560c384559e769321c9d92/numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5", size = 15808905 }, + { url = "https://files.pythonhosted.org/packages/11/57/baae43d14fe163fa0e4c47f307b6b2511ab8d7d30177c491960504252053/numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71", size = 20630554 }, + { url = "https://files.pythonhosted.org/packages/1a/2e/151484f49fd03944c4a3ad9c418ed193cfd02724e138ac8a9505d056c582/numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef", size = 13997127 }, + { url = "https://files.pythonhosted.org/packages/79/ae/7e5b85136806f9dadf4878bf73cf223fe5c2636818ba3ab1c585d0403164/numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e", size = 14222994 }, + { url = "https://files.pythonhosted.org/packages/3a/d0/edc009c27b406c4f9cbc79274d6e46d634d139075492ad055e3d68445925/numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5", size = 18252005 }, + { url = "https://files.pythonhosted.org/packages/09/bf/2b1aaf8f525f2923ff6cfcf134ae5e750e279ac65ebf386c75a0cf6da06a/numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a", size = 13885297 }, + { url = "https://files.pythonhosted.org/packages/df/a0/4e0f14d847cfc2a633a1c8621d00724f3206cfeddeb66d35698c4e2cf3d2/numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a", size = 18093567 }, + { url = "https://files.pythonhosted.org/packages/d2/b7/a734c733286e10a7f1a8ad1ae8c90f2d33bf604a96548e0a4a3a6739b468/numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20", size = 5968812 }, + { url = "https://files.pythonhosted.org/packages/3f/6b/5610004206cf7f8e7ad91c5a85a8c71b2f2f8051a0c0c4d5916b76d6cbb2/numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2", size = 15811913 }, + { url = "https://files.pythonhosted.org/packages/95/12/8f2020a8e8b8383ac0177dc9570aad031a3beb12e38847f7129bacd96228/numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218", size = 20335901 }, + { url = "https://files.pythonhosted.org/packages/75/5b/ca6c8bd14007e5ca171c7c03102d17b4f4e0ceb53957e8c44343a9546dcc/numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b", size = 13685868 }, + { url = "https://files.pythonhosted.org/packages/79/f8/97f10e6755e2a7d027ca783f63044d5b1bc1ae7acb12afe6a9b4286eac17/numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b", size = 13925109 }, + { url = "https://files.pythonhosted.org/packages/0f/50/de23fde84e45f5c4fda2488c759b69990fd4512387a8632860f3ac9cd225/numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed", size = 17950613 }, + { url = "https://files.pythonhosted.org/packages/4c/0c/9c603826b6465e82591e05ca230dfc13376da512b25ccd0894709b054ed0/numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a", size = 13572172 }, + { url = "https://files.pythonhosted.org/packages/76/8c/2ba3902e1a0fc1c74962ea9bb33a534bb05984ad7ff9515bf8d07527cadd/numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0", size = 17786643 }, + { url = "https://files.pythonhosted.org/packages/28/4a/46d9e65106879492374999e76eb85f87b15328e06bd1550668f79f7b18c6/numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110", size = 5677803 }, + { url = "https://files.pythonhosted.org/packages/16/2e/86f24451c2d530c88daf997cb8d6ac622c1d40d19f5a031ed68a4b73a374/numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818", size = 15517754 }, + { url = "https://files.pythonhosted.org/packages/7d/24/ce71dc08f06534269f66e73c04f5709ee024a1afe92a7b6e1d73f158e1f8/numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c", size = 20636301 }, + { url = "https://files.pythonhosted.org/packages/ae/8c/ab03a7c25741f9ebc92684a20125fbc9fc1b8e1e700beb9197d750fdff88/numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be", size = 13971216 }, + { url = "https://files.pythonhosted.org/packages/6d/64/c3bcdf822269421d85fe0d64ba972003f9bb4aa9a419da64b86856c9961f/numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764", size = 14226281 }, + { url = "https://files.pythonhosted.org/packages/54/30/c2a907b9443cf42b90c17ad10c1e8fa801975f01cb9764f3f8eb8aea638b/numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3", size = 18249516 }, + { url = "https://files.pythonhosted.org/packages/43/12/01a563fc44c07095996d0129b8899daf89e4742146f7044cdbdb3101c57f/numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd", size = 13882132 }, + { url = "https://files.pythonhosted.org/packages/16/ee/9df80b06680aaa23fc6c31211387e0db349e0e36d6a63ba3bd78c5acdf11/numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c", size = 18084181 }, + { url = "https://files.pythonhosted.org/packages/28/7d/4b92e2fe20b214ffca36107f1a3e75ef4c488430e64de2d9af5db3a4637d/numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6", size = 5976360 }, + { url = "https://files.pythonhosted.org/packages/b5/42/054082bd8220bbf6f297f982f0a8f5479fcbc55c8b511d928df07b965869/numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea", size = 15814633 }, + { url = "https://files.pythonhosted.org/packages/3f/72/3df6c1c06fc83d9cfe381cccb4be2532bbd38bf93fbc9fad087b6687f1c0/numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30", size = 20455961 }, + { url = "https://files.pythonhosted.org/packages/8e/02/570545bac308b58ffb21adda0f4e220ba716fb658a63c151daecc3293350/numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c", size = 18061071 }, + { url = "https://files.pythonhosted.org/packages/f4/5f/fafd8c51235f60d49f7a88e2275e13971e90555b67da52dd6416caec32fe/numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0", size = 15709730 }, +] + +[[package]] +name = "numpy" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.13'", +] +sdist = { url = "https://files.pythonhosted.org/packages/47/1b/1d565e0f6e156e1522ab564176b8b29d71e13d8caf003a08768df3d5cec5/numpy-2.2.0.tar.gz", hash = "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0", size = 20225497 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/81/3882353e097204fe4d7a5fe026b694b0104b78f930c969faadeed1538e00/numpy-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa", size = 21212476 }, + { url = "https://files.pythonhosted.org/packages/2c/64/5577dc71240272749e07fcacb47c0f29e31ba4fbd1613fefbd1aa88efc29/numpy-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219", size = 14351441 }, + { url = "https://files.pythonhosted.org/packages/c9/43/850c040481c19c1c2289203a606df1a202eeb3aa81440624bac891024f83/numpy-2.2.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e", size = 5390304 }, + { url = "https://files.pythonhosted.org/packages/73/96/a4c8a86300dbafc7e4f44d8986f8b64950b7f4640a2dc5c91e036afe28c6/numpy-2.2.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9", size = 6925476 }, + { url = "https://files.pythonhosted.org/packages/0c/0a/22129c3107c4fb237f97876df4399a5c3a83f3d95f86e0353ae6fbbd202f/numpy-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3", size = 14329997 }, + { url = "https://files.pythonhosted.org/packages/4c/49/c2adeccc8a47bcd9335ec000dfcb4de34a7c34aeaa23af57cd504017e8c3/numpy-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83", size = 16378908 }, + { url = "https://files.pythonhosted.org/packages/8d/85/b65f4596748cc5468c0a978a16b3be45f6bcec78339b0fe7bce71d121d89/numpy-2.2.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a", size = 15540949 }, + { url = "https://files.pythonhosted.org/packages/ff/b3/3b18321c94a6a6a1d972baf1b39a6de50e65c991002c014ffbcce7e09be8/numpy-2.2.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31", size = 18167677 }, + { url = "https://files.pythonhosted.org/packages/41/f0/fa2a76e893a05764e4474f6011575c4e4ccf32af9c95bfcc8ef4b8a99f69/numpy-2.2.0-cp310-cp310-win32.whl", hash = "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661", size = 6570288 }, + { url = "https://files.pythonhosted.org/packages/97/4e/0b7debcd013214db224997b0d3e39bb7b3656d37d06dfc31bb57d42d143b/numpy-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4", size = 12912730 }, + { url = "https://files.pythonhosted.org/packages/80/1b/736023977a96e787c4e7653a1ac2d31d4f6ab6b4048f83c8359f7c0af2e3/numpy-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6", size = 21216607 }, + { url = "https://files.pythonhosted.org/packages/85/4f/5f0be4c5c93525e663573bab9e29bd88a71f85de3a0d01413ee05bce0c2f/numpy-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90", size = 14387756 }, + { url = "https://files.pythonhosted.org/packages/36/78/c38af7833c4f29999cdacdf12452b43b660cd25a1990ea9a7edf1fb01f17/numpy-2.2.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608", size = 5388483 }, + { url = "https://files.pythonhosted.org/packages/e9/b5/306ac6ee3f8f0c51abd3664ee8a9b8e264cbf179a860674827151ecc0a9c/numpy-2.2.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da", size = 6929721 }, + { url = "https://files.pythonhosted.org/packages/ea/15/e33a7d86d8ce91de82c34ce94a87f2b8df891e603675e83ec7039325ff10/numpy-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74", size = 14334667 }, + { url = "https://files.pythonhosted.org/packages/52/33/10825f580f42a353f744abc450dcd2a4b1e6f1931abb0ccbd1d63bd3993c/numpy-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e", size = 16390204 }, + { url = "https://files.pythonhosted.org/packages/b4/24/36cce77559572bdc6c8bcdd2f3e0db03c7079d14b9a1cd342476d7f451e8/numpy-2.2.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b", size = 15556123 }, + { url = "https://files.pythonhosted.org/packages/05/51/2d706d14adee8f5c70c5de3831673d4d57051fc9ac6f3f6bff8811d2f9bd/numpy-2.2.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d", size = 18179898 }, + { url = "https://files.pythonhosted.org/packages/8a/e7/ea8b7652564113f218e75b296e3545a256d88b233021f792fd08591e8f33/numpy-2.2.0-cp311-cp311-win32.whl", hash = "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410", size = 6568146 }, + { url = "https://files.pythonhosted.org/packages/d0/06/3d1ff6ed377cb0340baf90487a35f15f9dc1db8e0a07de2bf2c54a8e490f/numpy-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73", size = 12916677 }, + { url = "https://files.pythonhosted.org/packages/7f/bc/a20dc4e1d051149052762e7647455311865d11c603170c476d1e910a353e/numpy-2.2.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3", size = 20909153 }, + { url = "https://files.pythonhosted.org/packages/60/3d/ac4fb63f36db94f4c7db05b45e3ecb3f88f778ca71850664460c78cfde41/numpy-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e", size = 14095021 }, + { url = "https://files.pythonhosted.org/packages/41/6d/a654d519d24e4fcc7a83d4a51209cda086f26cf30722b3d8ffc1aa9b775e/numpy-2.2.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67", size = 5125491 }, + { url = "https://files.pythonhosted.org/packages/e6/22/fab7e1510a62e5092f4e6507a279020052b89f11d9cfe52af7f52c243b04/numpy-2.2.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e", size = 6658534 }, + { url = "https://files.pythonhosted.org/packages/fc/29/a3d938ddc5a534cd53df7ab79d20a68db8c67578de1df0ae0118230f5f54/numpy-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038", size = 14046306 }, + { url = "https://files.pythonhosted.org/packages/90/24/d0bbb56abdd8934f30384632e3c2ca1ebfeb5d17e150c6e366ba291de36b/numpy-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03", size = 16095819 }, + { url = "https://files.pythonhosted.org/packages/99/9c/58a673faa9e8a0e77248e782f7a17410cf7259b326265646fd50ed49c4e1/numpy-2.2.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a", size = 15243215 }, + { url = "https://files.pythonhosted.org/packages/9c/61/f311693f78cbf635cfb69ce9e1e857ff83937a27d93c96ac5932fd33e330/numpy-2.2.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef", size = 17860175 }, + { url = "https://files.pythonhosted.org/packages/11/3e/491c34262cb1fc9dd13a00beb80d755ee0517b17db20e54cac7aa524533e/numpy-2.2.0-cp312-cp312-win32.whl", hash = "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1", size = 6273281 }, + { url = "https://files.pythonhosted.org/packages/89/ea/00537f599eb230771157bc509f6ea5b2dddf05d4b09f9d2f1d7096a18781/numpy-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3", size = 12613227 }, + { url = "https://files.pythonhosted.org/packages/bd/4c/0d1eef206545c994289e7a9de21b642880a11e0ed47a2b0c407c688c4f69/numpy-2.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367", size = 20895707 }, + { url = "https://files.pythonhosted.org/packages/16/cb/88f6c1e6df83002c421d5f854ccf134aa088aa997af786a5dac3f32ec99b/numpy-2.2.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae", size = 14110592 }, + { url = "https://files.pythonhosted.org/packages/b4/54/817e6894168a43f33dca74199ba0dd0f1acd99aa6323ed6d323d63d640a2/numpy-2.2.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69", size = 5110858 }, + { url = "https://files.pythonhosted.org/packages/c7/99/00d8a1a8eb70425bba7880257ed73fed08d3e8d05da4202fb6b9a81d5ee4/numpy-2.2.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13", size = 6645143 }, + { url = "https://files.pythonhosted.org/packages/34/86/5b9c2b7c56e7a9d9297a0a4be0b8433f498eba52a8f5892d9132b0f64627/numpy-2.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671", size = 14042812 }, + { url = "https://files.pythonhosted.org/packages/df/54/13535f74391dbe5f479ceed96f1403267be302c840040700d4fd66688089/numpy-2.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571", size = 16093419 }, + { url = "https://files.pythonhosted.org/packages/dd/37/dfb2056842ac61315f225aa56f455da369f5223e4c5a38b91d20da1b628b/numpy-2.2.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d", size = 15238969 }, + { url = "https://files.pythonhosted.org/packages/5a/3d/d20d24ee313992f0b7e7b9d9eef642d9b545d39d5b91c4a2cc8c98776328/numpy-2.2.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742", size = 17855705 }, + { url = "https://files.pythonhosted.org/packages/5b/40/944c9ee264f875a2db6f79380944fd2b5bb9d712bb4a134d11f45ad5b693/numpy-2.2.0-cp313-cp313-win32.whl", hash = "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e", size = 6270078 }, + { url = "https://files.pythonhosted.org/packages/30/04/e1ee6f8b22034302d4c5c24e15782bdedf76d90b90f3874ed0b48525def0/numpy-2.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2", size = 12605791 }, + { url = "https://files.pythonhosted.org/packages/ef/fb/51d458625cd6134d60ac15180ae50995d7d21b0f2f92a6286ae7b0792d19/numpy-2.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95", size = 20920160 }, + { url = "https://files.pythonhosted.org/packages/b4/34/162ae0c5d2536ea4be98c813b5161c980f0443cd5765fde16ddfe3450140/numpy-2.2.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c", size = 14119064 }, + { url = "https://files.pythonhosted.org/packages/17/6c/4195dd0e1c41c55f466d516e17e9e28510f32af76d23061ea3da67438e3c/numpy-2.2.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca", size = 5152778 }, + { url = "https://files.pythonhosted.org/packages/2f/47/ea804ae525832c8d05ed85b560dfd242d34e4bb0962bc269ccaa720fb934/numpy-2.2.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d", size = 6667605 }, + { url = "https://files.pythonhosted.org/packages/76/99/34d20e50b3d894bb16b5374bfbee399ab8ff3a33bf1e1f0b8acfe7bbd70d/numpy-2.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529", size = 14013275 }, + { url = "https://files.pythonhosted.org/packages/69/8f/a1df7bd02d434ab82539517d1b98028985700cfc4300bc5496fb140ca648/numpy-2.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3", size = 16074900 }, + { url = "https://files.pythonhosted.org/packages/04/94/b419e7a76bf21a00fcb03c613583f10e389fdc8dfe420412ff5710c8ad3d/numpy-2.2.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab", size = 15219122 }, + { url = "https://files.pythonhosted.org/packages/65/d9/dddf398b2b6c5d750892a207a469c2854a8db0f033edaf72103af8cf05aa/numpy-2.2.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72", size = 17851668 }, + { url = "https://files.pythonhosted.org/packages/d4/dc/09a4e5819a9782a213c0eb4eecacdc1cd75ad8dac99279b04cfccb7eeb0a/numpy-2.2.0-cp313-cp313t-win32.whl", hash = "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066", size = 6325288 }, + { url = "https://files.pythonhosted.org/packages/ce/e1/e0d06ec34036c92b43aef206efe99a5f5f04e12c776eab82a36e00c40afc/numpy-2.2.0-cp313-cp313t-win_amd64.whl", hash = "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881", size = 12692303 }, + { url = "https://files.pythonhosted.org/packages/f3/18/6d4e1274f221073058b621f4df8050958b7564b24b4fa25be9f1b7639274/numpy-2.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773", size = 21043901 }, + { url = "https://files.pythonhosted.org/packages/19/3e/2b20599e7ead7ae1b89a77bb34f88c5ec12e43fbb320576ed646388d2cb7/numpy-2.2.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e", size = 6789122 }, + { url = "https://files.pythonhosted.org/packages/c9/5a/378954132c192fafa6c3d5c160092a427c7562e5bda0cc6ad9cc37008a7a/numpy-2.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7", size = 16194018 }, + { url = "https://files.pythonhosted.org/packages/67/17/209bda34fc83f3436834392f44643e66dcf3c77465f232102e7f1c7d8eae/numpy-2.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221", size = 12819486 }, +] + +[[package]] +name = "packaging" +version = "24.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/51/65/50db4dda066951078f0a96cf12f4b9ada6e4b811516bf0262c0f4f7064d4/packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", size = 148788 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/aa/cc0199a5f0ad350994d660967a8efb233fe0416e4639146c089643407ce6/packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124", size = 53985 }, +] + +[[package]] +name = "parso" +version = "0.8.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/94/68e2e17afaa9169cf6412ab0f28623903be73d1b32e208d9e8e541bb086d/parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d", size = 400609 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/ac/dac4a63f978e4dcb3c6d3a78c4d8e0192a113d288502a1216950c41b1027/parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", size = 103650 }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772 }, +] + +[[package]] +name = "pickleshare" +version = "0.7.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", size = 6161 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/41/220f49aaea88bc6fa6cba8d05ecf24676326156c23b991e80b3f2fc24c77/pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56", size = 6877 }, +] + +[[package]] +name = "pillow" +version = "10.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/74/ad3d526f3bf7b6d3f408b73fde271ec69dfac8b81341a318ce825f2b3812/pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", size = 46555059 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/69/a31cccd538ca0b5272be2a38347f8839b97a14be104ea08b0db92f749c74/pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", size = 3509271 }, + { url = "https://files.pythonhosted.org/packages/9a/9e/4143b907be8ea0bce215f2ae4f7480027473f8b61fcedfda9d851082a5d2/pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", size = 3375658 }, + { url = "https://files.pythonhosted.org/packages/8a/25/1fc45761955f9359b1169aa75e241551e74ac01a09f487adaaf4c3472d11/pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", size = 4332075 }, + { url = "https://files.pythonhosted.org/packages/5e/dd/425b95d0151e1d6c951f45051112394f130df3da67363b6bc75dc4c27aba/pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", size = 4444808 }, + { url = "https://files.pythonhosted.org/packages/b1/84/9a15cc5726cbbfe7f9f90bfb11f5d028586595907cd093815ca6644932e3/pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", size = 4356290 }, + { url = "https://files.pythonhosted.org/packages/b5/5b/6651c288b08df3b8c1e2f8c1152201e0b25d240e22ddade0f1e242fc9fa0/pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", size = 4525163 }, + { url = "https://files.pythonhosted.org/packages/07/8b/34854bf11a83c248505c8cb0fcf8d3d0b459a2246c8809b967963b6b12ae/pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", size = 4463100 }, + { url = "https://files.pythonhosted.org/packages/78/63/0632aee4e82476d9cbe5200c0cdf9ba41ee04ed77887432845264d81116d/pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", size = 4592880 }, + { url = "https://files.pythonhosted.org/packages/df/56/b8663d7520671b4398b9d97e1ed9f583d4afcbefbda3c6188325e8c297bd/pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", size = 2235218 }, + { url = "https://files.pythonhosted.org/packages/f4/72/0203e94a91ddb4a9d5238434ae6c1ca10e610e8487036132ea9bf806ca2a/pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", size = 2554487 }, + { url = "https://files.pythonhosted.org/packages/bd/52/7e7e93d7a6e4290543f17dc6f7d3af4bd0b3dd9926e2e8a35ac2282bc5f4/pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1", size = 2243219 }, + { url = "https://files.pythonhosted.org/packages/a7/62/c9449f9c3043c37f73e7487ec4ef0c03eb9c9afc91a92b977a67b3c0bbc5/pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", size = 3509265 }, + { url = "https://files.pythonhosted.org/packages/f4/5f/491dafc7bbf5a3cc1845dc0430872e8096eb9e2b6f8161509d124594ec2d/pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", size = 3375655 }, + { url = "https://files.pythonhosted.org/packages/73/d5/c4011a76f4207a3c151134cd22a1415741e42fa5ddecec7c0182887deb3d/pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", size = 4340304 }, + { url = "https://files.pythonhosted.org/packages/ac/10/c67e20445a707f7a610699bba4fe050583b688d8cd2d202572b257f46600/pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", size = 4452804 }, + { url = "https://files.pythonhosted.org/packages/a9/83/6523837906d1da2b269dee787e31df3b0acb12e3d08f024965a3e7f64665/pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", size = 4365126 }, + { url = "https://files.pythonhosted.org/packages/ba/e5/8c68ff608a4203085158cff5cc2a3c534ec384536d9438c405ed6370d080/pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", size = 4533541 }, + { url = "https://files.pythonhosted.org/packages/f4/7c/01b8dbdca5bc6785573f4cee96e2358b0918b7b2c7b60d8b6f3abf87a070/pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", size = 4471616 }, + { url = "https://files.pythonhosted.org/packages/c8/57/2899b82394a35a0fbfd352e290945440e3b3785655a03365c0ca8279f351/pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", size = 4600802 }, + { url = "https://files.pythonhosted.org/packages/4d/d7/a44f193d4c26e58ee5d2d9db3d4854b2cfb5b5e08d360a5e03fe987c0086/pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", size = 2235213 }, + { url = "https://files.pythonhosted.org/packages/c1/d0/5866318eec2b801cdb8c82abf190c8343d8a1cd8bf5a0c17444a6f268291/pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", size = 2554498 }, + { url = "https://files.pythonhosted.org/packages/d4/c8/310ac16ac2b97e902d9eb438688de0d961660a87703ad1561fd3dfbd2aa0/pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", size = 2243219 }, + { url = "https://files.pythonhosted.org/packages/05/cb/0353013dc30c02a8be34eb91d25e4e4cf594b59e5a55ea1128fde1e5f8ea/pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", size = 3509350 }, + { url = "https://files.pythonhosted.org/packages/e7/cf/5c558a0f247e0bf9cec92bff9b46ae6474dd736f6d906315e60e4075f737/pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", size = 3374980 }, + { url = "https://files.pythonhosted.org/packages/84/48/6e394b86369a4eb68b8a1382c78dc092245af517385c086c5094e3b34428/pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", size = 4343799 }, + { url = "https://files.pythonhosted.org/packages/3b/f3/a8c6c11fa84b59b9df0cd5694492da8c039a24cd159f0f6918690105c3be/pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", size = 4459973 }, + { url = "https://files.pythonhosted.org/packages/7d/1b/c14b4197b80150fb64453585247e6fb2e1d93761fa0fa9cf63b102fde822/pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", size = 4370054 }, + { url = "https://files.pythonhosted.org/packages/55/77/40daddf677897a923d5d33329acd52a2144d54a9644f2a5422c028c6bf2d/pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", size = 4539484 }, + { url = "https://files.pythonhosted.org/packages/40/54/90de3e4256b1207300fb2b1d7168dd912a2fb4b2401e439ba23c2b2cabde/pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", size = 4477375 }, + { url = "https://files.pythonhosted.org/packages/13/24/1bfba52f44193860918ff7c93d03d95e3f8748ca1de3ceaf11157a14cf16/pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", size = 4608773 }, + { url = "https://files.pythonhosted.org/packages/55/04/5e6de6e6120451ec0c24516c41dbaf80cce1b6451f96561235ef2429da2e/pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", size = 2235690 }, + { url = "https://files.pythonhosted.org/packages/74/0a/d4ce3c44bca8635bd29a2eab5aa181b654a734a29b263ca8efe013beea98/pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", size = 2554951 }, + { url = "https://files.pythonhosted.org/packages/b5/ca/184349ee40f2e92439be9b3502ae6cfc43ac4b50bc4fc6b3de7957563894/pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", size = 2243427 }, + { url = "https://files.pythonhosted.org/packages/c3/00/706cebe7c2c12a6318aabe5d354836f54adff7156fd9e1bd6c89f4ba0e98/pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", size = 3525685 }, + { url = "https://files.pythonhosted.org/packages/cf/76/f658cbfa49405e5ecbfb9ba42d07074ad9792031267e782d409fd8fe7c69/pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", size = 3374883 }, + { url = "https://files.pythonhosted.org/packages/46/2b/99c28c4379a85e65378211971c0b430d9c7234b1ec4d59b2668f6299e011/pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", size = 4339837 }, + { url = "https://files.pythonhosted.org/packages/f1/74/b1ec314f624c0c43711fdf0d8076f82d9d802afd58f1d62c2a86878e8615/pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", size = 4455562 }, + { url = "https://files.pythonhosted.org/packages/4a/2a/4b04157cb7b9c74372fa867096a1607e6fedad93a44deeff553ccd307868/pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", size = 4366761 }, + { url = "https://files.pythonhosted.org/packages/ac/7b/8f1d815c1a6a268fe90481232c98dd0e5fa8c75e341a75f060037bd5ceae/pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", size = 4536767 }, + { url = "https://files.pythonhosted.org/packages/e5/77/05fa64d1f45d12c22c314e7b97398ffb28ef2813a485465017b7978b3ce7/pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", size = 4477989 }, + { url = "https://files.pythonhosted.org/packages/12/63/b0397cfc2caae05c3fb2f4ed1b4fc4fc878f0243510a7a6034ca59726494/pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", size = 4610255 }, + { url = "https://files.pythonhosted.org/packages/7b/f9/cfaa5082ca9bc4a6de66ffe1c12c2d90bf09c309a5f52b27759a596900e7/pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", size = 2235603 }, + { url = "https://files.pythonhosted.org/packages/01/6a/30ff0eef6e0c0e71e55ded56a38d4859bf9d3634a94a88743897b5f96936/pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", size = 2554972 }, + { url = "https://files.pythonhosted.org/packages/48/2c/2e0a52890f269435eee38b21c8218e102c621fe8d8df8b9dd06fabf879ba/pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", size = 2243375 }, + { url = "https://files.pythonhosted.org/packages/56/70/f40009702a477ce87d8d9faaa4de51d6562b3445d7a314accd06e4ffb01d/pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736", size = 3509213 }, + { url = "https://files.pythonhosted.org/packages/10/43/105823d233c5e5d31cea13428f4474ded9d961652307800979a59d6a4276/pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b", size = 3375883 }, + { url = "https://files.pythonhosted.org/packages/3c/ad/7850c10bac468a20c918f6a5dbba9ecd106ea1cdc5db3c35e33a60570408/pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2", size = 4330810 }, + { url = "https://files.pythonhosted.org/packages/84/4c/69bbed9e436ac22f9ed193a2b64f64d68fcfbc9f4106249dc7ed4889907b/pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680", size = 4444341 }, + { url = "https://files.pythonhosted.org/packages/8f/4f/c183c63828a3f37bf09644ce94cbf72d4929b033b109160a5379c2885932/pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b", size = 4356005 }, + { url = "https://files.pythonhosted.org/packages/fb/ad/435fe29865f98a8fbdc64add8875a6e4f8c97749a93577a8919ec6f32c64/pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd", size = 4525201 }, + { url = "https://files.pythonhosted.org/packages/80/74/be8bf8acdfd70e91f905a12ae13cfb2e17c0f1da745c40141e26d0971ff5/pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84", size = 4460635 }, + { url = "https://files.pythonhosted.org/packages/e4/90/763616e66dc9ad59c9b7fb58f863755e7934ef122e52349f62c7742b82d3/pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0", size = 4590283 }, + { url = "https://files.pythonhosted.org/packages/69/66/03002cb5b2c27bb519cba63b9f9aa3709c6f7a5d3b285406c01f03fb77e5/pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e", size = 2235185 }, + { url = "https://files.pythonhosted.org/packages/f2/75/3cb820b2812405fc7feb3d0deb701ef0c3de93dc02597115e00704591bc9/pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab", size = 2554594 }, + { url = "https://files.pythonhosted.org/packages/31/85/955fa5400fa8039921f630372cfe5056eed6e1b8e0430ee4507d7de48832/pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d", size = 3509283 }, + { url = "https://files.pythonhosted.org/packages/23/9c/343827267eb28d41cd82b4180d33b10d868af9077abcec0af9793aa77d2d/pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b", size = 3375691 }, + { url = "https://files.pythonhosted.org/packages/60/a3/7ebbeabcd341eab722896d1a5b59a3df98c4b4d26cf4b0385f8aa94296f7/pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd", size = 4328295 }, + { url = "https://files.pythonhosted.org/packages/32/3f/c02268d0c6fb6b3958bdda673c17b315c821d97df29ae6969f20fb49388a/pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126", size = 4440810 }, + { url = "https://files.pythonhosted.org/packages/67/5d/1c93c8cc35f2fdd3d6cc7e4ad72d203902859a2867de6ad957d9b708eb8d/pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b", size = 4352283 }, + { url = "https://files.pythonhosted.org/packages/bc/a8/8655557c9c7202b8abbd001f61ff36711cefaf750debcaa1c24d154ef602/pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c", size = 4521800 }, + { url = "https://files.pythonhosted.org/packages/58/78/6f95797af64d137124f68af1bdaa13b5332da282b86031f6fa70cf368261/pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1", size = 4459177 }, + { url = "https://files.pythonhosted.org/packages/8a/6d/2b3ce34f1c4266d79a78c9a51d1289a33c3c02833fe294ef0dcbb9cba4ed/pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df", size = 4589079 }, + { url = "https://files.pythonhosted.org/packages/e3/e0/456258c74da1ff5bf8ef1eab06a95ca994d8b9ed44c01d45c3f8cbd1db7e/pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef", size = 2235247 }, + { url = "https://files.pythonhosted.org/packages/37/f8/bef952bdb32aa53741f58bf21798642209e994edc3f6598f337f23d5400a/pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5", size = 2554479 }, + { url = "https://files.pythonhosted.org/packages/bb/8e/805201619cad6651eef5fc1fdef913804baf00053461522fabbc5588ea12/pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e", size = 2243226 }, + { url = "https://files.pythonhosted.org/packages/38/30/095d4f55f3a053392f75e2eae45eba3228452783bab3d9a920b951ac495c/pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", size = 3493889 }, + { url = "https://files.pythonhosted.org/packages/f3/e8/4ff79788803a5fcd5dc35efdc9386af153569853767bff74540725b45863/pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", size = 3346160 }, + { url = "https://files.pythonhosted.org/packages/d7/ac/4184edd511b14f760c73f5bb8a5d6fd85c591c8aff7c2229677a355c4179/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", size = 3435020 }, + { url = "https://files.pythonhosted.org/packages/da/21/1749cd09160149c0a246a81d646e05f35041619ce76f6493d6a96e8d1103/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", size = 3490539 }, + { url = "https://files.pythonhosted.org/packages/b6/f5/f71fe1888b96083b3f6dfa0709101f61fc9e972c0c8d04e9d93ccef2a045/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", size = 3476125 }, + { url = "https://files.pythonhosted.org/packages/96/b9/c0362c54290a31866c3526848583a2f45a535aa9d725fd31e25d318c805f/pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", size = 3579373 }, + { url = "https://files.pythonhosted.org/packages/52/3b/ce7a01026a7cf46e5452afa86f97a5e88ca97f562cafa76570178ab56d8d/pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", size = 2554661 }, + { url = "https://files.pythonhosted.org/packages/e1/1f/5a9fcd6ced51633c22481417e11b1b47d723f64fb536dfd67c015eb7f0ab/pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b", size = 3493850 }, + { url = "https://files.pythonhosted.org/packages/cb/e6/3ea4755ed5320cb62aa6be2f6de47b058c6550f752dd050e86f694c59798/pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908", size = 3346118 }, + { url = "https://files.pythonhosted.org/packages/0a/22/492f9f61e4648422b6ca39268ec8139277a5b34648d28f400faac14e0f48/pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b", size = 3434958 }, + { url = "https://files.pythonhosted.org/packages/f9/19/559a48ad4045704bb0547965b9a9345f5cd461347d977a56d178db28819e/pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8", size = 3490340 }, + { url = "https://files.pythonhosted.org/packages/d9/de/cebaca6fb79905b3a1aa0281d238769df3fb2ede34fd7c0caa286575915a/pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a", size = 3476048 }, + { url = "https://files.pythonhosted.org/packages/71/f0/86d5b2f04693b0116a01d75302b0a307800a90d6c351a8aa4f8ae76cd499/pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27", size = 3579366 }, + { url = "https://files.pythonhosted.org/packages/37/ae/2dbfc38cc4fd14aceea14bc440d5151b21f64c4c3ba3f6f4191610b7ee5d/pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3", size = 2554652 }, +] + +[[package]] +name = "pkgutil-resolve-name" +version = "1.3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/f2/f2891a9dc37398696ddd945012b90ef8d0a034f0012e3f83c3f7a70b0f79/pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174", size = 5054 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c9/5c/3d4882ba113fd55bdba9326c1e4c62a15e674a2501de4869e6bd6301f87e/pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e", size = 4734 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "pluggy" +version = "1.5.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.48" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2d/4f/feb5e137aff82f7c7f3248267b97451da3644f6cdc218edfe549fb354127/prompt_toolkit-3.0.48.tar.gz", hash = "sha256:d6623ab0477a80df74e646bdbc93621143f5caf104206aa29294d53de1a03d90", size = 424684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/6a/fd08d94654f7e67c52ca30523a178b3f8ccc4237fce4be90d39c938a831a/prompt_toolkit-3.0.48-py3-none-any.whl", hash = "sha256:f49a827f90062e411f1ce1f854f2aedb3c23353244f8108b89283587397ac10e", size = 386595 }, +] + +[[package]] +name = "protobuf" +version = "5.29.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/73/4e6295c1420a9d20c9c351db3a36109b4c9aa601916cb7c6871e3196a1ca/protobuf-5.29.2.tar.gz", hash = "sha256:b2cc8e8bb7c9326996f0e160137b0861f1a82162502658df2951209d0cb0309e", size = 424901 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/42/6db5387124708d619ffb990a846fb123bee546f52868039f8fa964c5bc54/protobuf-5.29.2-cp310-abi3-win32.whl", hash = "sha256:c12ba8249f5624300cf51c3d0bfe5be71a60c63e4dcf51ffe9a68771d958c851", size = 422697 }, + { url = "https://files.pythonhosted.org/packages/6c/38/2fcc968b377b531882d6ab2ac99b10ca6d00108394f6ff57c2395fb7baff/protobuf-5.29.2-cp310-abi3-win_amd64.whl", hash = "sha256:842de6d9241134a973aab719ab42b008a18a90f9f07f06ba480df268f86432f9", size = 434495 }, + { url = "https://files.pythonhosted.org/packages/cb/26/41debe0f6615fcb7e97672057524687ed86fcd85e3da3f031c30af8f0c51/protobuf-5.29.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a0c53d78383c851bfa97eb42e3703aefdc96d2036a41482ffd55dc5f529466eb", size = 417812 }, + { url = "https://files.pythonhosted.org/packages/e4/20/38fc33b60dcfb380507b99494aebe8c34b68b8ac7d32808c4cebda3f6f6b/protobuf-5.29.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:494229ecd8c9009dd71eda5fd57528395d1eacdf307dbece6c12ad0dd09e912e", size = 319562 }, + { url = "https://files.pythonhosted.org/packages/90/4d/c3d61e698e0e41d926dbff6aa4e57428ab1a6fc3b5e1deaa6c9ec0fd45cf/protobuf-5.29.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:b6b0d416bbbb9d4fbf9d0561dbfc4e324fd522f61f7af0fe0f282ab67b22477e", size = 319662 }, + { url = "https://files.pythonhosted.org/packages/4f/a3/cd7db453d67ed4513d33e95aa1a3b0cd19c5b432c7746be1a6e5ce2744f1/protobuf-5.29.2-cp38-cp38-win32.whl", hash = "sha256:e621a98c0201a7c8afe89d9646859859be97cb22b8bf1d8eacfd90d5bda2eb19", size = 422582 }, + { url = "https://files.pythonhosted.org/packages/27/73/e589b13e083f79fc49eceefb7294ae39293cfc1d149b7e7a0b07da6a6b60/protobuf-5.29.2-cp38-cp38-win_amd64.whl", hash = "sha256:13d6d617a2a9e0e82a88113d7191a1baa1e42c2cc6f5f1398d3b054c8e7e714a", size = 434506 }, + { url = "https://files.pythonhosted.org/packages/5e/d0/76d086c744c8252b35c2bc9c49c3be7c815b806191e58ad82c6d228c07a8/protobuf-5.29.2-cp39-cp39-win32.whl", hash = "sha256:36000f97ea1e76e8398a3f02936aac2a5d2b111aae9920ec1b769fc4a222c4d9", size = 422665 }, + { url = "https://files.pythonhosted.org/packages/84/08/be8223de1967ae8a100aaa1f7076f65c42ed1ff5ed413ff5dd718cff9fa8/protobuf-5.29.2-cp39-cp39-win_amd64.whl", hash = "sha256:2d2e674c58a06311c8e99e74be43e7f3a8d1e2b2fdf845eaa347fbd866f23355", size = 434584 }, + { url = "https://files.pythonhosted.org/packages/f3/fd/c7924b4c2a1c61b8f4b64edd7a31ffacf63432135a2606f03a2f0d75a750/protobuf-5.29.2-py3-none-any.whl", hash = "sha256:fde4554c0e578a5a0bcc9a276339594848d1e89f9ea47b4427c80e5d72f90181", size = 172539 }, +] + +[[package]] +name = "psutil" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/c7/8c6872f7372eb6a6b2e4708b88419fb46b857f7a2e1892966b851cc79fc9/psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2", size = 508067 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0b/37/f8da2fbd29690b3557cca414c1949f92162981920699cd62095a984983bf/psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0", size = 250961 }, + { url = "https://files.pythonhosted.org/packages/35/56/72f86175e81c656a01c4401cd3b1c923f891b31fbcebe98985894176d7c9/psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0", size = 287478 }, + { url = "https://files.pythonhosted.org/packages/19/74/f59e7e0d392bc1070e9a70e2f9190d652487ac115bb16e2eff6b22ad1d24/psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd", size = 290455 }, + { url = "https://files.pythonhosted.org/packages/cd/5f/60038e277ff0a9cc8f0c9ea3d0c5eb6ee1d2470ea3f9389d776432888e47/psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132", size = 292046 }, + { url = "https://files.pythonhosted.org/packages/8b/20/2ff69ad9c35c3df1858ac4e094f20bd2374d33c8643cf41da8fd7cdcb78b/psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d", size = 253560 }, + { url = "https://files.pythonhosted.org/packages/73/44/561092313ae925f3acfaace6f9ddc4f6a9c748704317bad9c8c8f8a36a79/psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3", size = 257399 }, + { url = "https://files.pythonhosted.org/packages/7c/06/63872a64c312a24fb9b4af123ee7007a306617da63ff13bcc1432386ead7/psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0", size = 251988 }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993 }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842 }, +] + +[[package]] +name = "pycparser" +version = "2.22" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, +] + +[[package]] +name = "pygments" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/62/8336eff65bcbc8e4cb5d05b55faf041285951b6e80f33e2bff2024788f31/pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", size = 4891905 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/3f/01c8b82017c199075f8f788d0d906b9ffbbc5a47dc9918a945e13d5a2bda/pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a", size = 1205513 }, +] + +[[package]] +name = "pymongo" +version = "4.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "dnspython" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/35/b62a3139f908c68b69aac6a6a3f8cc146869de0a7929b994600e2c587c77/pymongo-4.10.1.tar.gz", hash = "sha256:a9de02be53b6bb98efe0b9eda84ffa1ec027fcb23a2de62c4f941d9a2f2f3330", size = 1903902 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ca/f56b1dd84541de658d246f86828be27e32285f2151fab97efbce1db3ed57/pymongo-4.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e699aa68c4a7dea2ab5a27067f7d3e08555f8d2c0dc6a0c8c60cfd9ff2e6a4b1", size = 835459 }, + { url = "https://files.pythonhosted.org/packages/97/01/fe4ee34b33c6863be6a09d1e805ceb1122d9cd5d4a5d1664e360b91adf7e/pymongo-4.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:70645abc714f06b4ad6b72d5bf73792eaad14e3a2cfe29c62a9c81ada69d9e4b", size = 835716 }, + { url = "https://files.pythonhosted.org/packages/46/ff/9eb21c1d5861729ae1c91669b02f5bfbd23221ba9809fb97fade761f3f3b/pymongo-4.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae2fd94c9fe048c94838badcc6e992d033cb9473eb31e5710b3707cba5e8aee2", size = 1407173 }, + { url = "https://files.pythonhosted.org/packages/e5/d9/8cf042449d6804e00e38d3bb138b0e9acb8a8e0c9dd9dd989ffffd481c3b/pymongo-4.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ded27a4a5374dae03a92e084a60cdbcecd595306555bda553b833baf3fc4868", size = 1456455 }, + { url = "https://files.pythonhosted.org/packages/37/9a/da0d121f98c1413853e1172e2095fe77c1629c83a1db107d45a37ca935c2/pymongo-4.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ecc2455e3974a6c429687b395a0bc59636f2d6aedf5785098cf4e1f180f1c71", size = 1433360 }, + { url = "https://files.pythonhosted.org/packages/7d/6d/50480f0452e2fb59256d9d641d192366c0079920c36851b818ebeff0cec9/pymongo-4.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a920fee41f7d0259f5f72c1f1eb331bc26ffbdc952846f9bd8c3b119013bb52c", size = 1410758 }, + { url = "https://files.pythonhosted.org/packages/cd/8f/b83b9910c54f63bfff34305074e79cd08cf5e12dda22d1a2b4ad009b32b3/pymongo-4.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0a15665b2d6cf364f4cd114d62452ce01d71abfbd9c564ba8c74dcd7bbd6822", size = 1380257 }, + { url = "https://files.pythonhosted.org/packages/ed/e3/8f381b576e5f912cf0fe34218c6b0ef23d7afdef13fed592900fb52f0ed4/pymongo-4.10.1-cp310-cp310-win32.whl", hash = "sha256:29e1c323c28a4584b7095378ff046815e39ff82cdb8dc4cc6dfe3acf6f9ad1f8", size = 812324 }, + { url = "https://files.pythonhosted.org/packages/ab/14/1cae5359e2c4677856527a2965c999c23f596cced4b7828d880cb8fc0f54/pymongo-4.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:88dc4aa45f8744ccfb45164aedb9a4179c93567bbd98a33109d7dc400b00eb08", size = 826774 }, + { url = "https://files.pythonhosted.org/packages/e4/a3/d6403ec53fa2fe922b4a5c86388ea5fada01dd51d803e17bb2a7c9cda839/pymongo-4.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:57ee6becae534e6d47848c97f6a6dff69e3cce7c70648d6049bd586764febe59", size = 889238 }, + { url = "https://files.pythonhosted.org/packages/29/a2/9643450424bcf241e80bb713497ec2e3273c183d548b4eca357f75d71885/pymongo-4.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6f437a612f4d4f7aca1812311b1e84477145e950fdafe3285b687ab8c52541f3", size = 889504 }, + { url = "https://files.pythonhosted.org/packages/ec/40/4759984f34415509e9111be8ee863034611affdc1e0b41016c9d53b2f1b3/pymongo-4.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a970fd3117ab40a4001c3dad333bbf3c43687d90f35287a6237149b5ccae61d", size = 1649069 }, + { url = "https://files.pythonhosted.org/packages/56/0f/b6e917478a3ada81e768475516cd544982cc42cbb7d3be325182768139e1/pymongo-4.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c4d0e7cd08ef9f8fbf2d15ba281ed55604368a32752e476250724c3ce36c72e", size = 1714927 }, + { url = "https://files.pythonhosted.org/packages/56/c5/4237d94dfa19ebdf9a92b1071e2139c91f48908c5782e592c571c33b67ab/pymongo-4.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca6f700cff6833de4872a4e738f43123db34400173558b558ae079b5535857a4", size = 1683454 }, + { url = "https://files.pythonhosted.org/packages/9a/16/dbffca9d4ad66f2a325c280f1177912fa23235987f7b9033e283da889b7a/pymongo-4.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cec237c305fcbeef75c0bcbe9d223d1e22a6e3ba1b53b2f0b79d3d29c742b45b", size = 1653840 }, + { url = "https://files.pythonhosted.org/packages/2b/4d/21df934ef5cf8f0e587bac922a129e13d4c0346c54e9bf2371b90dd31112/pymongo-4.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3337804ea0394a06e916add4e5fac1c89902f1b6f33936074a12505cab4ff05", size = 1613233 }, + { url = "https://files.pythonhosted.org/packages/24/07/dd9c3db30e754680606295d5574521956898005db0629411a89163cc6eee/pymongo-4.10.1-cp311-cp311-win32.whl", hash = "sha256:778ac646ce6ac1e469664062dfe9ae1f5c9961f7790682809f5ec3b8fda29d65", size = 857331 }, + { url = "https://files.pythonhosted.org/packages/02/68/b71c4106d03eef2482eade440c6f5737c2a4a42f6155726009f80ea38d06/pymongo-4.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:9df4ab5594fdd208dcba81be815fa8a8a5d8dedaf3b346cbf8b61c7296246a7a", size = 876473 }, + { url = "https://files.pythonhosted.org/packages/10/d1/60ad99fe3f64d45e6c71ac0e3078e88d9b64112b1bae571fc3707344d6d1/pymongo-4.10.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fbedc4617faa0edf423621bb0b3b8707836687161210d470e69a4184be9ca011", size = 943356 }, + { url = "https://files.pythonhosted.org/packages/ca/9b/21d4c6b4ee9c1fa9691c68dc2a52565e0acb644b9e95148569b4736a4ebd/pymongo-4.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7bd26b2aec8ceeb95a5d948d5cc0f62b0eb6d66f3f4230705c1e3d3d2c04ec76", size = 943142 }, + { url = "https://files.pythonhosted.org/packages/07/af/691b7454e219a8eb2d1641aecedd607e3a94b93650c2011ad8a8fd74ef9f/pymongo-4.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb104c3c2a78d9d85571c8ac90ec4f95bca9b297c6eee5ada71fabf1129e1674", size = 1909129 }, + { url = "https://files.pythonhosted.org/packages/0c/74/fd75d5ad4181d6e71ce0fca32404fb71b5046ac84d9a1a2f0862262dd032/pymongo-4.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4924355245a9c79f77b5cda2db36e0f75ece5faf9f84d16014c0a297f6d66786", size = 1987763 }, + { url = "https://files.pythonhosted.org/packages/8a/56/6d3d0ef63c6d8cb98c7c653a3a2e617675f77a95f3853851d17a7664876a/pymongo-4.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:11280809e5dacaef4971113f0b4ff4696ee94cfdb720019ff4fa4f9635138252", size = 1950821 }, + { url = "https://files.pythonhosted.org/packages/70/ed/1603fa0c0e51444752c3fa91f16c3a97e6d92eb9fe5e553dae4f18df16f6/pymongo-4.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5d55f2a82e5eb23795f724991cac2bffbb1c0f219c0ba3bf73a835f97f1bb2e", size = 1912247 }, + { url = "https://files.pythonhosted.org/packages/c1/66/e98b2308971d45667cb8179d4d66deca47336c90663a7e0527589f1038b7/pymongo-4.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e974ab16a60be71a8dfad4e5afccf8dd05d41c758060f5d5bda9a758605d9a5d", size = 1862230 }, + { url = "https://files.pythonhosted.org/packages/6c/80/ba9b7ed212a5f8cf8ad7037ed5bbebc1c587fc09242108f153776e4a338b/pymongo-4.10.1-cp312-cp312-win32.whl", hash = "sha256:544890085d9641f271d4f7a47684450ed4a7344d6b72d5968bfae32203b1bb7c", size = 903045 }, + { url = "https://files.pythonhosted.org/packages/76/8b/5afce891d78159912c43726fab32641e3f9718f14be40f978c148ea8db48/pymongo-4.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:dcc07b1277e8b4bf4d7382ca133850e323b7ab048b8353af496d050671c7ac52", size = 926686 }, + { url = "https://files.pythonhosted.org/packages/83/76/df0fd0622a85b652ad0f91ec8a0ebfd0cb86af6caec8999a22a1f7481203/pymongo-4.10.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:90bc6912948dfc8c363f4ead54d54a02a15a7fee6cfafb36dc450fc8962d2cb7", size = 996981 }, + { url = "https://files.pythonhosted.org/packages/4c/39/fa50531de8d1d8af8c253caeed20c18ccbf1de5d970119c4a42c89f2bd09/pymongo-4.10.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:594dd721b81f301f33e843453638e02d92f63c198358e5a0fa8b8d0b1218dabc", size = 996769 }, + { url = "https://files.pythonhosted.org/packages/bf/50/6936612c1b2e32d95c30e860552d3bc9e55cfa79a4f73b73225fa05a028c/pymongo-4.10.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0783e0c8e95397c84e9cf8ab092ab1e5dd7c769aec0ef3a5838ae7173b98dea0", size = 2169159 }, + { url = "https://files.pythonhosted.org/packages/78/8c/45cb23096e66c7b1da62bb8d9c7ac2280e7c1071e13841e7fb71bd44fd9f/pymongo-4.10.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6fb6a72e88df46d1c1040fd32cd2d2c5e58722e5d3e31060a0393f04ad3283de", size = 2260569 }, + { url = "https://files.pythonhosted.org/packages/29/b6/e5ec697087e527a6a15c5f8daa5bcbd641edb8813487345aaf963d3537dc/pymongo-4.10.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e3a593333e20c87415420a4fb76c00b7aae49b6361d2e2205b6fece0563bf40", size = 2218142 }, + { url = "https://files.pythonhosted.org/packages/ad/8a/c0b45bee0f0c57732c5c36da5122c1796efd5a62d585fbc504e2f1401244/pymongo-4.10.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72e2ace7456167c71cfeca7dcb47bd5dceda7db2231265b80fc625c5e8073186", size = 2170623 }, + { url = "https://files.pythonhosted.org/packages/3b/26/6c0a5360a571df24c9bfbd51b1dae279f4f0c511bdbc0906f6df6d1543fa/pymongo-4.10.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ad05eb9c97e4f589ed9e74a00fcaac0d443ccd14f38d1258eb4c39a35dd722b", size = 2111112 }, + { url = "https://files.pythonhosted.org/packages/38/bc/5b91b728e1cf505d931f04e24cbac71ae519523785570ed046cdc31e6efc/pymongo-4.10.1-cp313-cp313-win32.whl", hash = "sha256:ee4c86d8e6872a61f7888fc96577b0ea165eb3bdb0d841962b444fa36001e2bb", size = 948727 }, + { url = "https://files.pythonhosted.org/packages/0d/2a/7c24a6144eaa06d18ed52822ea2b0f119fd9267cd1abbb75dae4d89a3803/pymongo-4.10.1-cp313-cp313-win_amd64.whl", hash = "sha256:45ee87a4e12337353242bc758accc7fb47a2f2d9ecc0382a61e64c8f01e86708", size = 976873 }, + { url = "https://files.pythonhosted.org/packages/31/22/0580e055aa609ae61c5c054afc746968be771d75d42b6a43f3979fa1c20e/pymongo-4.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:442ca247f53ad24870a01e80a71cd81b3f2318655fd9d66748ee2bd1b1569d9e", size = 727824 }, + { url = "https://files.pythonhosted.org/packages/82/9f/62599a11b2371ea176ddc4444fe9cef7bb1b8d8c2ba3ec2fce03a63f6b37/pymongo-4.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23e1d62df5592518204943b507be7b457fb8a4ad95a349440406fd42db5d0923", size = 728136 }, + { url = "https://files.pythonhosted.org/packages/7f/87/ea553a6b3e2607d409ffcb8958153e84370f1cd54e1f2191b16dcc28b408/pymongo-4.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6131bc6568b26e7495a9f3ef2b1700566b76bbecd919f4472bfe90038a61f425", size = 928973 }, + { url = "https://files.pythonhosted.org/packages/6e/be/f7d3df1415af12200b63b3cfebd2d156ea3215f3ce054bf3f73d81e0be73/pymongo-4.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdeba88c540c9ed0338c0b2062d9f81af42b18d6646b3e6dda05cf6edd46ada9", size = 945186 }, + { url = "https://files.pythonhosted.org/packages/f6/0c/8b3981432b8d635763aef504475c1f1ec51447889fd13ac30676cecf1229/pymongo-4.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15a624d752dd3c89d10deb0ef6431559b6d074703cab90a70bb849ece02adc6b", size = 938573 }, + { url = "https://files.pythonhosted.org/packages/5f/54/7da021c70dbac3e993aee5544398f6c1ec58d0aaaf6f91de555e4b6a6e29/pymongo-4.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba164e73fdade9b4614a2497321c5b7512ddf749ed508950bdecc28d8d76a2d9", size = 930164 }, + { url = "https://files.pythonhosted.org/packages/bb/f1/848132a5db3f5fba1928018d53d990f4ba81db9126129f2e5a99d9da8d55/pymongo-4.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9235fa319993405ae5505bf1333366388add2e06848db7b3deee8f990b69808e", size = 919676 }, + { url = "https://files.pythonhosted.org/packages/ad/31/9b5a831f7bd96e932c0b6e234a154896fe0373c19ba32054e959ae258b57/pymongo-4.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e4a65567bd17d19f03157c7ec992c6530eafd8191a4e5ede25566792c4fe3fa2", size = 920828 }, + { url = "https://files.pythonhosted.org/packages/65/5b/d134139e96fa085ebc7545a7bfb335606e7ee3ae403c170a3fe64ba7b854/pymongo-4.10.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f1945d48fb9b8a87d515da07f37e5b2c35b364a435f534c122e92747881f4a7c", size = 928386 }, + { url = "https://files.pythonhosted.org/packages/9e/35/b932c0db60508971701ef42c47ab6db3e60497f0821da4f340ecff9eebf0/pymongo-4.10.1-cp38-cp38-win32.whl", hash = "sha256:345f8d340802ebce509f49d5833cc913da40c82f2e0daf9f60149cacc9ca680f", size = 722400 }, + { url = "https://files.pythonhosted.org/packages/38/52/a58196a45232e9046c8b13f4fa3d89cca0e462b5c6d28969759226f781ee/pymongo-4.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:3a70d5efdc0387ac8cd50f9a5f379648ecfc322d14ec9e1ba8ec957e5d08c372", size = 727386 }, + { url = "https://files.pythonhosted.org/packages/39/69/a13a61fd9a19e659cb43cb98240a75dab2f8e7b60bee568c309a123f8edc/pymongo-4.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15b1492cc5c7cd260229590be7218261e81684b8da6d6de2660cf743445500ce", size = 781650 }, + { url = "https://files.pythonhosted.org/packages/14/13/a5a3da69ee146bac81baacc7b27995849bfbb89eaabfb3d19824c7056fb4/pymongo-4.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:95207503c41b97e7ecc7e596d84a61f441b4935f11aa8332828a754e7ada8c82", size = 781929 }, + { url = "https://files.pythonhosted.org/packages/b0/65/118bc20fc66451ae4b8ceafbd9aab1168bb13cf4e5699b81807bb911a443/pymongo-4.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb99f003c720c6d83be02c8f1a7787c22384a8ca9a4181e406174db47a048619", size = 1167200 }, + { url = "https://files.pythonhosted.org/packages/d1/4a/19c3d45659835f814efef8b250456923899cc7542c52f8beb1a6a164106f/pymongo-4.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f2bc1ee4b1ca2c4e7e6b7a5e892126335ec8d9215bcd3ac2fe075870fefc3358", size = 1200021 }, + { url = "https://files.pythonhosted.org/packages/1c/03/69cee6f23463251a8f6de6670fb665e2531cc0df500d61150e57d1aedf70/pymongo-4.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:93a0833c10a967effcd823b4e7445ec491f0bf6da5de0ca33629c0528f42b748", size = 1185518 }, + { url = "https://files.pythonhosted.org/packages/00/58/01d8b3b374e45931581364752bf7a4693e9c7422704f81e6e91ffa42c38d/pymongo-4.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f56707497323150bd2ed5d63067f4ffce940d0549d4ea2dfae180deec7f9363", size = 1169799 }, + { url = "https://files.pythonhosted.org/packages/be/6f/9aa0ba2f4cfa4c30c962bd1f5a38b2eb95b23e3965918d211da82ebaacf6/pymongo-4.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:409ab7d6c4223e5c85881697f365239dd3ed1b58f28e4124b846d9d488c86880", size = 1149344 }, + { url = "https://files.pythonhosted.org/packages/fc/af/40f0112605f75f5ae5da554b2f779a83c40ecf2298c57e120e6d7bfcff65/pymongo-4.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dac78a650dc0637d610905fd06b5fa6419ae9028cf4d04d6a2657bc18a66bbce", size = 1134277 }, + { url = "https://files.pythonhosted.org/packages/a1/92/9a4afaf02d4d633c3211b7535e6bfbb2527524ca8ac8039b8d7eb3c765eb/pymongo-4.10.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1ec3fa88b541e0481aff3c35194c9fac96e4d57ec5d1c122376000eb28c01431", size = 1167588 }, + { url = "https://files.pythonhosted.org/packages/29/de/3c23e7739b0d0dafa4d802e9773011cf54e305c7cb788a52d50c3ef585d2/pymongo-4.10.1-cp39-cp39-win32.whl", hash = "sha256:e0e961923a7b8a1c801c43552dcb8153e45afa41749d9efbd3a6d33f45489f7a", size = 767325 }, + { url = "https://files.pythonhosted.org/packages/36/d4/49198168f296838c49d732743ae073f9ca9e8f65f15f5209637456892968/pymongo-4.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:dabe8bf1ad644e6b93f3acf90ff18536d94538ca4d27e583c6db49889e98e48f", size = 777078 }, +] + +[[package]] +name = "pyright" +version = "1.1.382.post1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "nodeenv" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/d3/fbf59f51cb74fc62a0c2b791aaa6242c8004db551b30c726783ba4432f05/pyright-1.1.382.post1.tar.gz", hash = "sha256:66a5d4e83be9452853d73e9dd9e95ba0ac3061845270e4e331d0070a597d3445", size = 21965 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/e2/7661f69927bf052e36d9c162c5087a02c346d0269be238cea2f5ae1e80f2/pyright-1.1.382.post1-py3-none-any.whl", hash = "sha256:21a4749dd1740e209f88d3a601e9f40748670d39481ea32b9d77edf7f3f1fb2e", size = 18669 }, +] + +[[package]] +name = "pytest" +version = "8.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "iniconfig" }, + { name = "packaging" }, + { name = "pluggy" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8b/6c/62bbd536103af674e227c41a8f3dcd022d591f6eed5facb5a0f31ee33bbc/pytest-8.3.3.tar.gz", hash = "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", size = 1442487 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/77/7440a06a8ead44c7757a64362dd22df5760f9b12dc5f11b6188cd2fc27a0/pytest-8.3.3-py3-none-any.whl", hash = "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2", size = 342341 }, +] + +[[package]] +name = "pytest-asyncio" +version = "0.24.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/6d/c6cf50ce320cf8611df7a1254d86233b3df7cc07f9b5f5cbcb82e08aa534/pytest_asyncio-0.24.0.tar.gz", hash = "sha256:d081d828e576d85f875399194281e92bf8a68d60d72d1a2faf2feddb6c46b276", size = 49855 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/96/31/6607dab48616902f76885dfcf62c08d929796fc3b2d2318faf9fd54dbed9/pytest_asyncio-0.24.0-py3-none-any.whl", hash = "sha256:a811296ed596b69bf0b6f3dc40f83bcaf341b155a269052d82efa2b25ac7037b", size = 18024 }, +] + +[[package]] +name = "pytest-mock" +version = "3.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pytest" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/90/a955c3ab35ccd41ad4de556596fa86685bf4fc5ffcc62d22d856cfd4e29a/pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0", size = 32814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/3b/b26f90f74e2986a82df6e7ac7e319b8ea7ccece1caec9f8ab6104dc70603/pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f", size = 9863 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pytz" +version = "2024.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/31/3c70bf7603cc2dca0f19bdc53b4537a797747a58875b552c8c413d963a3f/pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", size = 319692 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/c3/005fcca25ce078d2cc29fd559379817424e94885510568bc1bc53d7d5846/pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725", size = 508002 }, +] + +[[package]] +name = "pywin32" +version = "306" +source = { registry = "https://pypi.org/simple" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/dc/28c668097edfaf4eac4617ef7adf081b9cf50d254672fcf399a70f5efc41/pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d", size = 8506422 }, + { url = "https://files.pythonhosted.org/packages/d3/d6/891894edec688e72c2e308b3243fad98b4066e1839fd2fe78f04129a9d31/pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8", size = 9226392 }, + { url = "https://files.pythonhosted.org/packages/8b/1e/fc18ad83ca553e01b97aa8393ff10e33c1fb57801db05488b83282ee9913/pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407", size = 8507689 }, + { url = "https://files.pythonhosted.org/packages/7e/9e/ad6b1ae2a5ad1066dc509350e0fbf74d8d50251a51e420a2a8feaa0cecbd/pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e", size = 9227547 }, + { url = "https://files.pythonhosted.org/packages/91/20/f744bff1da8f43388498503634378dbbefbe493e65675f2cc52f7185c2c2/pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a", size = 10388324 }, + { url = "https://files.pythonhosted.org/packages/14/91/17e016d5923e178346aabda3dfec6629d1a26efe587d19667542105cf0a6/pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b", size = 8507705 }, + { url = "https://files.pythonhosted.org/packages/83/1c/25b79fc3ec99b19b0a0730cc47356f7e2959863bf9f3cd314332bddb4f68/pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e", size = 9227429 }, + { url = "https://files.pythonhosted.org/packages/1c/43/e3444dc9a12f8365d9603c2145d16bf0a2f8180f343cf87be47f5579e547/pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040", size = 10388145 }, + { url = "https://files.pythonhosted.org/packages/0e/57/c3ec32b498f24a2392404d1f0fd29f47a3f7339d7d579df7a0560cff337c/pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a", size = 8632118 }, + { url = "https://files.pythonhosted.org/packages/fa/80/a6b22e031590cc5f4fcbd5bf4bcf63a9dabce9d59065f53add99a8caaec5/pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0", size = 9373699 }, + { url = "https://files.pythonhosted.org/packages/7e/7f/419c4fcadcaa374a0ae41cbdf6c3a81452892dd6c523aea629d17e49146e/pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802", size = 8573451 }, + { url = "https://files.pythonhosted.org/packages/1c/f7/24d8ed4fd9c43b90354df7764f81f0dd5e623f9a50f1538f90fe085d6dff/pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4", size = 9312883 }, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9b/95/a3fac87cb7158e231b5a6012e438c647e1a87f09f8e0d123acec8ab8bf71/PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", size = 184199 }, + { url = "https://files.pythonhosted.org/packages/c7/7a/68bd47624dab8fd4afbfd3c48e3b79efe09098ae941de5b58abcbadff5cb/PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", size = 171758 }, + { url = "https://files.pythonhosted.org/packages/49/ee/14c54df452143b9ee9f0f29074d7ca5516a36edb0b4cc40c3f280131656f/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", size = 718463 }, + { url = "https://files.pythonhosted.org/packages/4d/61/de363a97476e766574650d742205be468921a7b532aa2499fcd886b62530/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", size = 719280 }, + { url = "https://files.pythonhosted.org/packages/6b/4e/1523cb902fd98355e2e9ea5e5eb237cbc5f3ad5f3075fa65087aa0ecb669/PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", size = 751239 }, + { url = "https://files.pythonhosted.org/packages/b7/33/5504b3a9a4464893c32f118a9cc045190a91637b119a9c881da1cf6b7a72/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", size = 695802 }, + { url = "https://files.pythonhosted.org/packages/5c/20/8347dcabd41ef3a3cdc4f7b7a2aff3d06598c8779faa189cdbf878b626a4/PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", size = 720527 }, + { url = "https://files.pythonhosted.org/packages/be/aa/5afe99233fb360d0ff37377145a949ae258aaab831bde4792b32650a4378/PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", size = 144052 }, + { url = "https://files.pythonhosted.org/packages/b5/84/0fa4b06f6d6c958d207620fc60005e241ecedceee58931bb20138e1e5776/PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", size = 161774 }, + { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, + { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, + { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, + { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, + { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, + { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, + { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, + { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, + { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, + { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, + { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, + { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, + { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, + { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, + { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, + { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, + { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, + { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, + { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, + { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, + { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, + { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, + { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, + { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, + { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, + { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, + { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, + { url = "https://files.pythonhosted.org/packages/74/d9/323a59d506f12f498c2097488d80d16f4cf965cee1791eab58b56b19f47a/PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", size = 183218 }, + { url = "https://files.pythonhosted.org/packages/74/cc/20c34d00f04d785f2028737e2e2a8254e1425102e730fee1d6396f832577/PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", size = 728067 }, + { url = "https://files.pythonhosted.org/packages/20/52/551c69ca1501d21c0de51ddafa8c23a0191ef296ff098e98358f69080577/PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", size = 757812 }, + { url = "https://files.pythonhosted.org/packages/fd/7f/2c3697bba5d4aa5cc2afe81826d73dfae5f049458e44732c7a0938baa673/PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", size = 746531 }, + { url = "https://files.pythonhosted.org/packages/8c/ab/6226d3df99900e580091bb44258fde77a8433511a86883bd4681ea19a858/PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", size = 800820 }, + { url = "https://files.pythonhosted.org/packages/a0/99/a9eb0f3e710c06c5d922026f6736e920d431812ace24aae38228d0d64b04/PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", size = 145514 }, + { url = "https://files.pythonhosted.org/packages/75/8a/ee831ad5fafa4431099aa4e078d4c8efd43cd5e48fbc774641d233b683a9/PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", size = 162702 }, + { url = "https://files.pythonhosted.org/packages/65/d8/b7a1db13636d7fb7d4ff431593c510c8b8fca920ade06ca8ef20015493c5/PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", size = 184777 }, + { url = "https://files.pythonhosted.org/packages/0a/02/6ec546cd45143fdf9840b2c6be8d875116a64076218b61d68e12548e5839/PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", size = 172318 }, + { url = "https://files.pythonhosted.org/packages/0e/9a/8cc68be846c972bda34f6c2a93abb644fb2476f4dcc924d52175786932c9/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", size = 720891 }, + { url = "https://files.pythonhosted.org/packages/e9/6c/6e1b7f40181bc4805e2e07f4abc10a88ce4648e7e95ff1abe4ae4014a9b2/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", size = 722614 }, + { url = "https://files.pythonhosted.org/packages/3d/32/e7bd8535d22ea2874cef6a81021ba019474ace0d13a4819c2a4bce79bd6a/PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", size = 737360 }, + { url = "https://files.pythonhosted.org/packages/d7/12/7322c1e30b9be969670b672573d45479edef72c9a0deac3bb2868f5d7469/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", size = 699006 }, + { url = "https://files.pythonhosted.org/packages/82/72/04fcad41ca56491995076630c3ec1e834be241664c0c09a64c9a2589b507/PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", size = 723577 }, + { url = "https://files.pythonhosted.org/packages/ed/5e/46168b1f2757f1fcd442bc3029cd8767d88a98c9c05770d8b420948743bb/PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", size = 144593 }, + { url = "https://files.pythonhosted.org/packages/19/87/5124b1c1f2412bb95c59ec481eaf936cd32f0fe2a7b16b97b81c4c017a6a/PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", size = 162312 }, +] + +[[package]] +name = "pyzmq" +version = "26.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "implementation_name == 'pypy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fd/05/bed626b9f7bb2322cdbbf7b4bd8f54b1b617b0d2ab2d3547d6e39428a48e/pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f", size = 271975 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/a8/9837c39aba390eb7d01924ace49d761c8dbe7bc2d6082346d00c8332e431/pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629", size = 1340058 }, + { url = "https://files.pythonhosted.org/packages/a2/1f/a006f2e8e4f7d41d464272012695da17fb95f33b54342612a6890da96ff6/pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b", size = 1008818 }, + { url = "https://files.pythonhosted.org/packages/b6/09/b51b6683fde5ca04593a57bbe81788b6b43114d8f8ee4e80afc991e14760/pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764", size = 673199 }, + { url = "https://files.pythonhosted.org/packages/c9/78/486f3e2e824f3a645238332bf5a4c4b4477c3063033a27c1e4052358dee2/pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c", size = 911762 }, + { url = "https://files.pythonhosted.org/packages/5e/3b/2eb1667c9b866f53e76ee8b0c301b0469745a23bd5a87b7ee3d5dd9eb6e5/pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a", size = 868773 }, + { url = "https://files.pythonhosted.org/packages/16/29/ca99b4598a9dc7e468b5417eda91f372b595be1e3eec9b7cbe8e5d3584e8/pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88", size = 868834 }, + { url = "https://files.pythonhosted.org/packages/ad/e5/9efaeb1d2f4f8c50da04144f639b042bc52869d3a206d6bf672ab3522163/pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f", size = 1202861 }, + { url = "https://files.pythonhosted.org/packages/c3/62/c721b5608a8ac0a69bb83cbb7d07a56f3ff00b3991a138e44198a16f94c7/pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282", size = 1515304 }, + { url = "https://files.pythonhosted.org/packages/87/84/e8bd321aa99b72f48d4606fc5a0a920154125bd0a4608c67eab742dab087/pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea", size = 1414712 }, + { url = "https://files.pythonhosted.org/packages/cd/cd/420e3fd1ac6977b008b72e7ad2dae6350cc84d4c5027fc390b024e61738f/pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2", size = 578113 }, + { url = "https://files.pythonhosted.org/packages/5c/57/73930d56ed45ae0cb4946f383f985c855c9b3d4063f26416998f07523c0e/pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971", size = 641631 }, + { url = "https://files.pythonhosted.org/packages/61/d2/ae6ac5c397f1ccad59031c64beaafce7a0d6182e0452cc48f1c9c87d2dd0/pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa", size = 543528 }, + { url = "https://files.pythonhosted.org/packages/12/20/de7442172f77f7c96299a0ac70e7d4fb78cd51eca67aa2cf552b66c14196/pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218", size = 1340639 }, + { url = "https://files.pythonhosted.org/packages/98/4d/5000468bd64c7910190ed0a6c76a1ca59a68189ec1f007c451dc181a22f4/pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4", size = 1008710 }, + { url = "https://files.pythonhosted.org/packages/e1/bf/c67fd638c2f9fbbab8090a3ee779370b97c82b84cc12d0c498b285d7b2c0/pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef", size = 673129 }, + { url = "https://files.pythonhosted.org/packages/86/94/99085a3f492aa538161cbf27246e8886ff850e113e0c294a5b8245f13b52/pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317", size = 910107 }, + { url = "https://files.pythonhosted.org/packages/31/1d/346809e8a9b999646d03f21096428453465b1bca5cd5c64ecd048d9ecb01/pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf", size = 867960 }, + { url = "https://files.pythonhosted.org/packages/ab/68/6fb6ae5551846ad5beca295b7bca32bf0a7ce19f135cb30e55fa2314e6b6/pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e", size = 869204 }, + { url = "https://files.pythonhosted.org/packages/0f/f9/18417771dee223ccf0f48e29adf8b4e25ba6d0e8285e33bcbce078070bc3/pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37", size = 1203351 }, + { url = "https://files.pythonhosted.org/packages/e0/46/f13e67fe0d4f8a2315782cbad50493de6203ea0d744610faf4d5f5b16e90/pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3", size = 1514204 }, + { url = "https://files.pythonhosted.org/packages/50/11/ddcf7343b7b7a226e0fc7b68cbf5a5bb56291fac07f5c3023bb4c319ebb4/pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6", size = 1414339 }, + { url = "https://files.pythonhosted.org/packages/01/14/1c18d7d5b7be2708f513f37c61bfadfa62161c10624f8733f1c8451b3509/pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4", size = 576928 }, + { url = "https://files.pythonhosted.org/packages/3b/1b/0a540edd75a41df14ec416a9a500b9fec66e554aac920d4c58fbd5756776/pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5", size = 642317 }, + { url = "https://files.pythonhosted.org/packages/98/77/1cbfec0358078a4c5add529d8a70892db1be900980cdb5dd0898b3d6ab9d/pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003", size = 543834 }, + { url = "https://files.pythonhosted.org/packages/28/2f/78a766c8913ad62b28581777ac4ede50c6d9f249d39c2963e279524a1bbe/pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9", size = 1343105 }, + { url = "https://files.pythonhosted.org/packages/b7/9c/4b1e2d3d4065be715e007fe063ec7885978fad285f87eae1436e6c3201f4/pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52", size = 1008365 }, + { url = "https://files.pythonhosted.org/packages/4f/ef/5a23ec689ff36d7625b38d121ef15abfc3631a9aecb417baf7a4245e4124/pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08", size = 665923 }, + { url = "https://files.pythonhosted.org/packages/ae/61/d436461a47437d63c6302c90724cf0981883ec57ceb6073873f32172d676/pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5", size = 903400 }, + { url = "https://files.pythonhosted.org/packages/47/42/fc6d35ecefe1739a819afaf6f8e686f7f02a4dd241c78972d316f403474c/pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae", size = 860034 }, + { url = "https://files.pythonhosted.org/packages/07/3b/44ea6266a6761e9eefaa37d98fabefa112328808ac41aa87b4bbb668af30/pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711", size = 860579 }, + { url = "https://files.pythonhosted.org/packages/38/6f/4df2014ab553a6052b0e551b37da55166991510f9e1002c89cab7ce3b3f2/pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6", size = 1196246 }, + { url = "https://files.pythonhosted.org/packages/38/9d/ee240fc0c9fe9817f0c9127a43238a3e28048795483c403cc10720ddef22/pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3", size = 1507441 }, + { url = "https://files.pythonhosted.org/packages/85/4f/01711edaa58d535eac4a26c294c617c9a01f09857c0ce191fd574d06f359/pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b", size = 1406498 }, + { url = "https://files.pythonhosted.org/packages/07/18/907134c85c7152f679ed744e73e645b365f3ad571f38bdb62e36f347699a/pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7", size = 575533 }, + { url = "https://files.pythonhosted.org/packages/ce/2c/a6f4a20202a4d3c582ad93f95ee78d79bbdc26803495aec2912b17dbbb6c/pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a", size = 637768 }, + { url = "https://files.pythonhosted.org/packages/5f/0e/eb16ff731632d30554bf5af4dbba3ffcd04518219d82028aea4ae1b02ca5/pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b", size = 540675 }, + { url = "https://files.pythonhosted.org/packages/04/a7/0f7e2f6c126fe6e62dbae0bc93b1bd3f1099cf7fea47a5468defebe3f39d/pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726", size = 1006564 }, + { url = "https://files.pythonhosted.org/packages/31/b6/a187165c852c5d49f826a690857684333a6a4a065af0a6015572d2284f6a/pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3", size = 1340447 }, + { url = "https://files.pythonhosted.org/packages/68/ba/f4280c58ff71f321602a6e24fd19879b7e79793fb8ab14027027c0fb58ef/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50", size = 665485 }, + { url = "https://files.pythonhosted.org/packages/77/b5/c987a5c53c7d8704216f29fc3d810b32f156bcea488a940e330e1bcbb88d/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb", size = 903484 }, + { url = "https://files.pythonhosted.org/packages/29/c9/07da157d2db18c72a7eccef8e684cefc155b712a88e3d479d930aa9eceba/pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187", size = 859981 }, + { url = "https://files.pythonhosted.org/packages/43/09/e12501bd0b8394b7d02c41efd35c537a1988da67fc9c745cae9c6c776d31/pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b", size = 860334 }, + { url = "https://files.pythonhosted.org/packages/eb/ff/f5ec1d455f8f7385cc0a8b2acd8c807d7fade875c14c44b85c1bddabae21/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18", size = 1196179 }, + { url = "https://files.pythonhosted.org/packages/ec/8a/bb2ac43295b1950fe436a81fc5b298be0b96ac76fb029b514d3ed58f7b27/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115", size = 1507668 }, + { url = "https://files.pythonhosted.org/packages/a9/49/dbc284ebcfd2dca23f6349227ff1616a7ee2c4a35fe0a5d6c3deff2b4fed/pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e", size = 1406539 }, + { url = "https://files.pythonhosted.org/packages/00/68/093cdce3fe31e30a341d8e52a1ad86392e13c57970d722c1f62a1d1a54b6/pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5", size = 575567 }, + { url = "https://files.pythonhosted.org/packages/92/ae/6cc4657148143412b5819b05e362ae7dd09fb9fe76e2a539dcff3d0386bc/pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad", size = 637551 }, + { url = "https://files.pythonhosted.org/packages/6c/67/fbff102e201688f97c8092e4c3445d1c1068c2f27bbd45a578df97ed5f94/pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797", size = 540378 }, + { url = "https://files.pythonhosted.org/packages/3f/fe/2d998380b6e0122c6c4bdf9b6caf490831e5f5e2d08a203b5adff060c226/pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a", size = 1007378 }, + { url = "https://files.pythonhosted.org/packages/4a/f4/30d6e7157f12b3a0390bde94d6a8567cdb88846ed068a6e17238a4ccf600/pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc", size = 1329532 }, + { url = "https://files.pythonhosted.org/packages/82/86/3fe917870e15ee1c3ad48229a2a64458e36036e64b4afa9659045d82bfa8/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5", size = 653242 }, + { url = "https://files.pythonhosted.org/packages/50/2d/242e7e6ef6c8c19e6cb52d095834508cd581ffb925699fd3c640cdc758f1/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672", size = 888404 }, + { url = "https://files.pythonhosted.org/packages/ac/11/7270566e1f31e4ea73c81ec821a4b1688fd551009a3d2bab11ec66cb1e8f/pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797", size = 845858 }, + { url = "https://files.pythonhosted.org/packages/91/d5/72b38fbc69867795c8711bdd735312f9fef1e3d9204e2f63ab57085434b9/pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386", size = 847375 }, + { url = "https://files.pythonhosted.org/packages/dd/9a/10ed3c7f72b4c24e719c59359fbadd1a27556a28b36cdf1cd9e4fb7845d5/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306", size = 1183489 }, + { url = "https://files.pythonhosted.org/packages/72/2d/8660892543fabf1fe41861efa222455811adac9f3c0818d6c3170a1153e3/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6", size = 1492932 }, + { url = "https://files.pythonhosted.org/packages/7b/d6/32fd69744afb53995619bc5effa2a405ae0d343cd3e747d0fbc43fe894ee/pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0", size = 1392485 }, + { url = "https://files.pythonhosted.org/packages/64/e7/d5d59205d446c299001d27bfc18702c5353512c5485b11ec7cf6df9552d7/pyzmq-26.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f", size = 1340492 }, + { url = "https://files.pythonhosted.org/packages/59/bb/aa6616a83694ab43cfb3bdb868d194a5ee2fa24b49e6ec7ec4400691ac3b/pyzmq-26.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2", size = 1008257 }, + { url = "https://files.pythonhosted.org/packages/a6/b6/e578e6c08970df0daa08b7c54e82b606211f9a7e61317ef2db79cc334389/pyzmq-26.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6", size = 907602 }, + { url = "https://files.pythonhosted.org/packages/ab/3a/a26b98aebeb7924b24e9973a2f5bf8974201bb5a3f6ed06ddc3bac19372d/pyzmq-26.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289", size = 862291 }, + { url = "https://files.pythonhosted.org/packages/c1/b5/7eedb8d63af13c2858beb9c1f58e90e7e00929176b57f45e3592fccd56dc/pyzmq-26.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732", size = 673879 }, + { url = "https://files.pythonhosted.org/packages/af/22/38734f47543e61b4eb97eee476f0f7ae544988533215eea22fc65e1ca1d7/pyzmq-26.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780", size = 1207011 }, + { url = "https://files.pythonhosted.org/packages/59/a4/104cc979ae88ed948ef829db5fb49bca4a771891125fa4166bba1598b2ec/pyzmq-26.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640", size = 1516183 }, + { url = "https://files.pythonhosted.org/packages/52/8f/73a8e08897f8ed21fe44fc73b5faf3ea4cacb97bfd219a63ee5f3ea203a8/pyzmq-26.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd", size = 1417481 }, + { url = "https://files.pythonhosted.org/packages/67/cf/f418670a83fb3a91e2d6d26f271a828a58e0265199944a76e4ef274f9ba7/pyzmq-26.2.0-cp38-cp38-win32.whl", hash = "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988", size = 577930 }, + { url = "https://files.pythonhosted.org/packages/f0/51/1f2b47c8d8fb85c07f088e21df6364b8b5e8298e75bb23ea0e65340ebd82/pyzmq-26.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f", size = 642503 }, + { url = "https://files.pythonhosted.org/packages/ac/9e/ad5fbbe1bcc7a9d1e8c5f4f7de48f2c1dc481e151ef80cc1ce9a7fe67b55/pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2", size = 1341256 }, + { url = "https://files.pythonhosted.org/packages/4c/d9/d7a8022108c214803a82b0b69d4885cee00933d21928f1f09dca371cf4bf/pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c", size = 1009385 }, + { url = "https://files.pythonhosted.org/packages/ed/69/0529b59ac667ea8bfe8796ac71796b688fbb42ff78e06525dabfed3bc7ae/pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98", size = 908009 }, + { url = "https://files.pythonhosted.org/packages/6e/bd/3ff3e1172f12f55769793a3a334e956ec2886805ebfb2f64756b6b5c6a1a/pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9", size = 862078 }, + { url = "https://files.pythonhosted.org/packages/c3/ec/ab13585c3a1f48e2874253844c47b194d56eb25c94718691349c646f336f/pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db", size = 673756 }, + { url = "https://files.pythonhosted.org/packages/1e/be/febcd4b04dd50ee6d514dfbc33a3d5d9cb38ec9516e02bbfc929baa0f141/pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073", size = 1203684 }, + { url = "https://files.pythonhosted.org/packages/16/28/304150e71afd2df3b82f52f66c0d8ab9ac6fe1f1ffdf92bad4c8cc91d557/pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc", size = 1515864 }, + { url = "https://files.pythonhosted.org/packages/18/89/8d48d8cd505c12a1f5edee597cc32ffcedc65fd8d2603aebaaedc38a7041/pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940", size = 1415383 }, + { url = "https://files.pythonhosted.org/packages/d4/7e/43a60c3b179f7da0cbc2b649bd2702fd6a39bff5f72aa38d6e1aeb00256d/pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44", size = 578540 }, + { url = "https://files.pythonhosted.org/packages/3a/55/8841dcd28f783ad06674c8fe8d7d72794b548d0bff8829aaafeb72e8b44d/pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec", size = 642147 }, + { url = "https://files.pythonhosted.org/packages/b4/78/b3c31ccfcfcdd6ea50b6abc8f46a2a7aadb9c3d40531d1b908d834aaa12e/pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb", size = 543903 }, + { url = "https://files.pythonhosted.org/packages/53/fb/36b2b2548286e9444e52fcd198760af99fd89102b5be50f0660fcfe902df/pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072", size = 906955 }, + { url = "https://files.pythonhosted.org/packages/77/8f/6ce54f8979a01656e894946db6299e2273fcee21c8e5fa57c6295ef11f57/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1", size = 565701 }, + { url = "https://files.pythonhosted.org/packages/ee/1c/bf8cd66730a866b16db8483286078892b7f6536f8c389fb46e4beba0a970/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d", size = 794312 }, + { url = "https://files.pythonhosted.org/packages/71/43/91fa4ff25bbfdc914ab6bafa0f03241d69370ef31a761d16bb859f346582/pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca", size = 752775 }, + { url = "https://files.pythonhosted.org/packages/ec/d2/3b2ab40f455a256cb6672186bea95cd97b459ce4594050132d71e76f0d6f/pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c", size = 550762 }, + { url = "https://files.pythonhosted.org/packages/38/a7/1c80b0c8013befad391b92ba8a8e597de8884605ad5ad8ab943c888eb3ca/pyzmq-26.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20", size = 906946 }, + { url = "https://files.pythonhosted.org/packages/9c/ac/34a7ee2e7edb07c7222752096650313424eb05f18401ed0a964e996088fb/pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919", size = 802021 }, + { url = "https://files.pythonhosted.org/packages/cd/70/c65ddccfb88b469b6044f9664c81f0b7f649711e0dc172cba8b2a968ad99/pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5", size = 756818 }, + { url = "https://files.pythonhosted.org/packages/07/7a/fc77f6d57f592207403eab2deca4c6f1ffa9c78b0f03b59e69069a12a1a1/pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc", size = 565698 }, + { url = "https://files.pythonhosted.org/packages/dc/13/e8494ba2d161fb471955fadbef7f48076bd29b19a4dd3c5d61d22e500505/pyzmq-26.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277", size = 550757 }, + { url = "https://files.pythonhosted.org/packages/6c/78/3096d72581365dfb0081ac9512a3b53672fa69854aa174d78636510c4db8/pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3", size = 906945 }, + { url = "https://files.pythonhosted.org/packages/da/f2/8054574d77c269c31d055d4daf3d8407adf61ea384a50c8d14b158551d09/pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a", size = 565698 }, + { url = "https://files.pythonhosted.org/packages/77/21/c3ad93236d1d60eea10b67528f55e7db115a9d32e2bf163fcf601f85e9cc/pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6", size = 794307 }, + { url = "https://files.pythonhosted.org/packages/6a/49/e95b491724500fcb760178ce8db39b923429e328e57bcf9162e32c2c187c/pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a", size = 752769 }, + { url = "https://files.pythonhosted.org/packages/9b/a9/50c9c06762b30792f71aaad8d1886748d39c4bffedc1171fbc6ad2b92d67/pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4", size = 751338 }, + { url = "https://files.pythonhosted.org/packages/ca/63/27e6142b4f67a442ee480986ca5b88edb01462dd2319843057683a5148bd/pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f", size = 550757 }, +] + +[[package]] +name = "referencing" +version = "0.35.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/99/5b/73ca1f8e72fff6fa52119dbd185f73a907b1989428917b24cff660129b6d/referencing-0.35.1.tar.gz", hash = "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", size = 62991 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/59/2056f61236782a2c86b33906c025d4f4a0b17be0161b63b70fd9e8775d36/referencing-0.35.1-py3-none-any.whl", hash = "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de", size = 26684 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "rpds-py" +version = "0.20.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/55/64/b693f262791b818880d17268f3f8181ef799b0d187f6f731b1772e05a29a/rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121", size = 25814 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/2d/a7e60483b72b91909e18f29a5c5ae847bac4e2ae95b77bb77e1f41819a58/rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2", size = 318432 }, + { url = "https://files.pythonhosted.org/packages/b5/b4/f15b0c55a6d880ce74170e7e28c3ed6c5acdbbd118df50b91d1dabf86008/rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f", size = 311333 }, + { url = "https://files.pythonhosted.org/packages/36/10/3f4e490fe6eb069c07c22357d0b4804cd94cb9f8d01345ef9b1d93482b9d/rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150", size = 366697 }, + { url = "https://files.pythonhosted.org/packages/f5/c8/cd6ab31b4424c7fab3b17e153b6ea7d1bb0d7cabea5c1ef683cc8adb8bc2/rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e", size = 368386 }, + { url = "https://files.pythonhosted.org/packages/60/5e/642a44fda6dda90b5237af7a0ef1d088159c30a504852b94b0396eb62125/rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2", size = 395374 }, + { url = "https://files.pythonhosted.org/packages/7c/b5/ff18c093c9e72630f6d6242e5ccb0728ef8265ba0a154b5972f89d23790a/rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3", size = 433189 }, + { url = "https://files.pythonhosted.org/packages/4a/6d/1166a157b227f2333f8e8ae320b6b7ea2a6a38fbe7a3563ad76dffc8608d/rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf", size = 354849 }, + { url = "https://files.pythonhosted.org/packages/70/a4/70ea49863ea09ae4c2971f2eef58e80b757e3c0f2f618c5815bb751f7847/rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140", size = 373233 }, + { url = "https://files.pythonhosted.org/packages/3b/d3/822a28152a1e7e2ba0dc5d06cf8736f4cd64b191bb6ec47fb51d1c3c5ccf/rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f", size = 541852 }, + { url = "https://files.pythonhosted.org/packages/c6/a5/6ef91e4425dc8b3445ff77d292fc4c5e37046462434a0423c4e0a596a8bd/rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce", size = 547630 }, + { url = "https://files.pythonhosted.org/packages/72/f8/d5625ee05c4e5c478954a16d9359069c66fe8ac8cd5ddf28f80d3b321837/rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94", size = 525766 }, + { url = "https://files.pythonhosted.org/packages/94/3c/1ff1ed6ae323b3e16fdfcdae0f0a67f373a6c3d991229dc32b499edeffb7/rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee", size = 199174 }, + { url = "https://files.pythonhosted.org/packages/ec/ba/5762c0aee2403dfea14ed74b0f8a2415cfdbb21cf745d600d9a8ac952c5b/rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399", size = 213543 }, + { url = "https://files.pythonhosted.org/packages/ab/2a/191374c52d7be0b056cc2a04d718d2244c152f915d4a8d2db2aacc526189/rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489", size = 318369 }, + { url = "https://files.pythonhosted.org/packages/0e/6a/2c9fdcc6d235ac0d61ec4fd9981184689c3e682abd05e3caa49bccb9c298/rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318", size = 311303 }, + { url = "https://files.pythonhosted.org/packages/d2/b2/725487d29633f64ef8f9cbf4729111a0b61702c8f8e94db1653930f52cce/rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db", size = 366424 }, + { url = "https://files.pythonhosted.org/packages/7a/8c/668195ab9226d01b7cf7cd9e59c1c0be1df05d602df7ec0cf46f857dcf59/rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5", size = 368359 }, + { url = "https://files.pythonhosted.org/packages/52/28/356f6a39c1adeb02cf3e5dd526f5e8e54e17899bef045397abcfbf50dffa/rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5", size = 394886 }, + { url = "https://files.pythonhosted.org/packages/a2/65/640fb1a89080a8fb6f4bebd3dafb65a2edba82e2e44c33e6eb0f3e7956f1/rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6", size = 432416 }, + { url = "https://files.pythonhosted.org/packages/a7/e8/85835077b782555d6b3416874b702ea6ebd7db1f145283c9252968670dd5/rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209", size = 354819 }, + { url = "https://files.pythonhosted.org/packages/4f/87/1ac631e923d65cbf36fbcfc6eaa702a169496de1311e54be142f178e53ee/rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3", size = 373282 }, + { url = "https://files.pythonhosted.org/packages/e4/ce/cb316f7970189e217b998191c7cf0da2ede3d5437932c86a7210dc1e9994/rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272", size = 541540 }, + { url = "https://files.pythonhosted.org/packages/90/d7/4112d7655ec8aff168ecc91d4ceb51c557336edde7e6ccf6463691a2f253/rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad", size = 547640 }, + { url = "https://files.pythonhosted.org/packages/ab/44/4f61d64dfed98cc71623f3a7fcb612df636a208b4b2c6611eaa985e130a9/rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58", size = 525555 }, + { url = "https://files.pythonhosted.org/packages/35/f2/a862d81eacb21f340d584cd1c749c289979f9a60e9229f78bffc0418a199/rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0", size = 199338 }, + { url = "https://files.pythonhosted.org/packages/cc/ec/77d0674f9af4872919f3738018558dd9d37ad3f7ad792d062eadd4af7cba/rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c", size = 213585 }, + { url = "https://files.pythonhosted.org/packages/89/b7/f9682c5cc37fcc035f4a0fc33c1fe92ec9cbfdee0cdfd071cf948f53e0df/rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6", size = 321468 }, + { url = "https://files.pythonhosted.org/packages/b8/ad/fc82be4eaceb8d444cb6fc1956ce972b3a0795104279de05e0e4131d0a47/rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b", size = 313062 }, + { url = "https://files.pythonhosted.org/packages/0e/1c/6039e80b13a08569a304dc13476dc986352dca4598e909384db043b4e2bb/rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739", size = 370168 }, + { url = "https://files.pythonhosted.org/packages/dc/c9/5b9aa35acfb58946b4b785bc8e700ac313669e02fb100f3efa6176a83e81/rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c", size = 371376 }, + { url = "https://files.pythonhosted.org/packages/7b/dd/0e0dbeb70d8a5357d2814764d467ded98d81d90d3570de4fb05ec7224f6b/rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee", size = 397200 }, + { url = "https://files.pythonhosted.org/packages/e4/da/a47d931eb688ccfd77a7389e45935c79c41e8098d984d87335004baccb1d/rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96", size = 426824 }, + { url = "https://files.pythonhosted.org/packages/0f/f7/a59a673594e6c2ff2dbc44b00fd4ecdec2fc399bb6a7bd82d612699a0121/rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4", size = 357967 }, + { url = "https://files.pythonhosted.org/packages/5f/61/3ba1905396b2cb7088f9503a460b87da33452da54d478cb9241f6ad16d00/rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef", size = 378905 }, + { url = "https://files.pythonhosted.org/packages/08/31/6d0df9356b4edb0a3a077f1ef714e25ad21f9f5382fc490c2383691885ea/rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821", size = 546348 }, + { url = "https://files.pythonhosted.org/packages/ae/15/d33c021de5cb793101df9961c3c746dfc476953dbbf5db337d8010dffd4e/rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940", size = 553152 }, + { url = "https://files.pythonhosted.org/packages/70/2d/5536d28c507a4679179ab15aa0049440e4d3dd6752050fa0843ed11e9354/rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174", size = 528807 }, + { url = "https://files.pythonhosted.org/packages/e3/62/7ebe6ec0d3dd6130921f8cffb7e34afb7f71b3819aa0446a24c5e81245ec/rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139", size = 200993 }, + { url = "https://files.pythonhosted.org/packages/ec/2f/b938864d66b86a6e4acadefdc56de75ef56f7cafdfd568a6464605457bd5/rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585", size = 214458 }, + { url = "https://files.pythonhosted.org/packages/99/32/43b919a0a423c270a838ac2726b1c7168b946f2563fd99a51aaa9692d00f/rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29", size = 321465 }, + { url = "https://files.pythonhosted.org/packages/58/a9/c4d899cb28e9e47b0ff12462e8f827381f243176036f17bef9c1604667f2/rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91", size = 312900 }, + { url = "https://files.pythonhosted.org/packages/8f/90/9e51670575b5dfaa8c823369ef7d943087bfb73d4f124a99ad6ef19a2b26/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24", size = 370973 }, + { url = "https://files.pythonhosted.org/packages/fc/c1/523f2a03f853fc0d4c1acbef161747e9ab7df0a8abf6236106e333540921/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7", size = 370890 }, + { url = "https://files.pythonhosted.org/packages/51/ca/2458a771f16b0931de4d384decbe43016710bc948036c8f4562d6e063437/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9", size = 397174 }, + { url = "https://files.pythonhosted.org/packages/00/7d/6e06807f6305ea2408b364efb0eef83a6e21b5e7b5267ad6b473b9a7e416/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8", size = 426449 }, + { url = "https://files.pythonhosted.org/packages/8c/d1/6c9e65260a819a1714510a7d69ac1d68aa23ee9ce8a2d9da12187263c8fc/rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879", size = 357698 }, + { url = "https://files.pythonhosted.org/packages/5d/fb/ecea8b5286d2f03eec922be7173a03ed17278944f7c124348f535116db15/rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f", size = 378530 }, + { url = "https://files.pythonhosted.org/packages/e3/e3/ac72f858957f52a109c588589b73bd2fad4a0fc82387fb55fb34aeb0f9cd/rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c", size = 545753 }, + { url = "https://files.pythonhosted.org/packages/b2/a4/a27683b519d5fc98e4390a3b130117d80fd475c67aeda8aac83c0e8e326a/rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2", size = 552443 }, + { url = "https://files.pythonhosted.org/packages/a1/ed/c074d248409b4432b1ccb2056974175fa0af2d1bc1f9c21121f80a358fa3/rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57", size = 528380 }, + { url = "https://files.pythonhosted.org/packages/d5/bd/04caf938895d2d78201e89c0c8a94dfd9990c34a19ff52fb01d0912343e3/rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a", size = 200540 }, + { url = "https://files.pythonhosted.org/packages/95/cc/109eb8b9863680411ae703664abacaa035820c7755acc9686d5dd02cdd2e/rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2", size = 214111 }, + { url = "https://files.pythonhosted.org/packages/37/cf/0081318cde7d7e89f802b4939ec8d079d7b59b0ee3fc168435bde31e861c/rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24", size = 319397 }, + { url = "https://files.pythonhosted.org/packages/09/4e/ea988bb4fe0f39613dd2b868fc698c19fd970e33dfe4f1bd90658f94fed3/rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29", size = 311674 }, + { url = "https://files.pythonhosted.org/packages/9e/38/d4a1f901068dfcb51183266a91bcef614f616d4d820a4dd288ccaff83cbb/rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965", size = 367607 }, + { url = "https://files.pythonhosted.org/packages/7a/e3/dc75f3f118f3dc29be962f68729b2d203be9ad52ad34b1ae907c60271302/rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1", size = 367870 }, + { url = "https://files.pythonhosted.org/packages/8d/23/2b6acbc76fddb7f89ef2382f136a7a4cf10e078e6e149508a59d7448e2e1/rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752", size = 395245 }, + { url = "https://files.pythonhosted.org/packages/53/21/9d405f690986f0215d655c2980de10f63c073e66c56bd5f4d039214d1624/rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c", size = 431699 }, + { url = "https://files.pythonhosted.org/packages/88/9d/07fedb6afebe0fe6f1c2981223ffa82c3ff3cc09ffeab8c9859b4852d7e3/rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751", size = 355123 }, + { url = "https://files.pythonhosted.org/packages/16/80/857ed7ca6dbb33805f2c8298868d029f9cf0a06f182d7058c8484b47941b/rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8", size = 373349 }, + { url = "https://files.pythonhosted.org/packages/97/69/ae242d3c59f04ca3f56d9dbd768e7cabfc093cfb9e030dfc8fbd7fadbc4d/rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e", size = 542737 }, + { url = "https://files.pythonhosted.org/packages/9f/c1/06d6c461c41e73c8187471595ce1c9a67c280d533fbd705889e6a0e9da2f/rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253", size = 547562 }, + { url = "https://files.pythonhosted.org/packages/1c/0b/918acbb2aa360822f18c6bc8672ee3c231d357f55d5e7f980d8207360742/rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a", size = 525769 }, + { url = "https://files.pythonhosted.org/packages/0e/7f/446eb1f1ed22ca855e3966e1b97e10f68f3a40578d9596a4b83323456cef/rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5", size = 199321 }, + { url = "https://files.pythonhosted.org/packages/3d/c7/ae73dfcf417fa1bb087341b670083afc3228d6a496d0d2221afd5b20d95f/rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232", size = 213048 }, + { url = "https://files.pythonhosted.org/packages/a1/55/228f6d9a8c6940c8d5e49db5e0434ffcbad669c33509ac39cb0af061b0fa/rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22", size = 319496 }, + { url = "https://files.pythonhosted.org/packages/68/61/074236253586feb550954f8b4359d38eefb45bafcbbb7d2e74062a82f386/rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789", size = 311837 }, + { url = "https://files.pythonhosted.org/packages/03/67/ed6c2fe076bf78296934d4356145fedf3c7c2f8d490e099bcf6f31794dc0/rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5", size = 367819 }, + { url = "https://files.pythonhosted.org/packages/30/25/4a9e7b89b6760ac032f375cb236e4f8e518ad1fad685c40b6a9752056d6f/rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2", size = 368322 }, + { url = "https://files.pythonhosted.org/packages/67/17/0255bb0e564517b53343ea672ebec9fb7ad40e9083ca09a4080fbc986bb9/rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c", size = 395552 }, + { url = "https://files.pythonhosted.org/packages/af/6e/77c65ccb0d7cdc39ec2be19b918a4d4fe9e2d2a1c5cab36745b36f2c1e59/rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de", size = 433735 }, + { url = "https://files.pythonhosted.org/packages/04/d8/e73d56b1908a6c0e3e5982365eb293170cd458cc25a19363f69c76e00fd2/rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda", size = 355542 }, + { url = "https://files.pythonhosted.org/packages/47/df/e72c79053b0c882b818bfd8f0ed1f1ace550bc9cdba27165cb73dddb9394/rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580", size = 373644 }, + { url = "https://files.pythonhosted.org/packages/7f/00/3e16cb08c0cc6a233f0f61e4d009e3098cbe280ec975d14f28935bd15316/rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b", size = 543139 }, + { url = "https://files.pythonhosted.org/packages/41/71/799c6b6f6031ed535f22fcf6802601cc7f981842bd28007bb7bb4bd10b2f/rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420", size = 548007 }, + { url = "https://files.pythonhosted.org/packages/53/58/ad03eb6718e814fa045198c72d45d2ae60180eb48338f22c9fa34bd89964/rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b", size = 526102 }, + { url = "https://files.pythonhosted.org/packages/78/99/a52e5b460f2311fc8ee75ff769e8d67e76208947180eacb4f153af2d9967/rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7", size = 199391 }, + { url = "https://files.pythonhosted.org/packages/0c/7d/fd42a27fe392a69faf4a5e635870fc425edcb998485ee73afbc734ecef16/rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364", size = 213205 }, + { url = "https://files.pythonhosted.org/packages/06/39/bf1f664c347c946ef56cecaa896e3693d91acc741afa78ebb3fdb7aba08b/rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045", size = 319444 }, + { url = "https://files.pythonhosted.org/packages/c1/71/876135d3cb90d62468540b84e8e83ff4dc92052ab309bfdea7ea0b9221ad/rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc", size = 311699 }, + { url = "https://files.pythonhosted.org/packages/f7/da/8ccaeba6a3dda7467aebaf893de9eafd56275e2c90773c83bf15fb0b8374/rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02", size = 367825 }, + { url = "https://files.pythonhosted.org/packages/04/b6/02a54c47c178d180395b3c9a8bfb3b93906e08f9acf7b4a1067d27c3fae0/rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92", size = 369046 }, + { url = "https://files.pythonhosted.org/packages/a7/64/df4966743aa4def8727dc13d06527c8b13eb7412c1429def2d4701bee520/rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d", size = 395896 }, + { url = "https://files.pythonhosted.org/packages/6f/d9/7ff03ff3642c600f27ff94512bb158a8d815fea5ed4162c75a7e850d6003/rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855", size = 432427 }, + { url = "https://files.pythonhosted.org/packages/b8/c6/e1b886f7277b3454e55e85332e165091c19114eecb5377b88d892fd36ccf/rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511", size = 355403 }, + { url = "https://files.pythonhosted.org/packages/e2/62/e26bd5b944e547c7bfd0b6ca7e306bfa430f8bd298ab72a1217976a7ca8d/rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51", size = 374491 }, + { url = "https://files.pythonhosted.org/packages/c3/92/93c5a530898d3a5d1ce087455071ba714b77806ed9ffee4070d0c7a53b7e/rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075", size = 543622 }, + { url = "https://files.pythonhosted.org/packages/01/9e/d68fba289625b5d3c9d1925825d7da716fbf812bda2133ac409021d5db13/rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60", size = 548558 }, + { url = "https://files.pythonhosted.org/packages/bf/d6/4b2fad4898154365f0f2bd72ffd190349274a4c1d6a6f94f02a83bb2b8f1/rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344", size = 525753 }, + { url = "https://files.pythonhosted.org/packages/d2/ea/6f121d1802f3adae1981aea4209ea66f9d3c7f2f6d6b85ef4f13a61d17ef/rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989", size = 213529 }, + { url = "https://files.pythonhosted.org/packages/0a/6f/7ab47005469f0d73dad89d29b733e3555d454a45146c30f5628242e56d33/rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e", size = 320800 }, + { url = "https://files.pythonhosted.org/packages/cc/a1/bef9e0ef30f89c7516559ca7acc40e8ae70397535a0b1a4535a4a01d9ed0/rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c", size = 312001 }, + { url = "https://files.pythonhosted.org/packages/31/44/9093c5dca95ee463c3669651e710af182eb6f9cd83626b15a2ebde2247b1/rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03", size = 369279 }, + { url = "https://files.pythonhosted.org/packages/6f/ac/0c36e067681fa3fe4c60a9422b011ec0ccc80c1e124f5210951f7982e887/rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921", size = 369716 }, + { url = "https://files.pythonhosted.org/packages/6b/78/8896e08625d46ea5bfdd526ee688b91eeafecbc3cf7223612c82ed77905b/rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab", size = 396708 }, + { url = "https://files.pythonhosted.org/packages/24/5f/d865ae460e47e46fd2b489f2aceed34439bd8f18a1ff414c299142e0e22a/rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5", size = 433356 }, + { url = "https://files.pythonhosted.org/packages/bd/8b/04031937ffa565021f934a9acf44bb6b1b60ea19fa9e58950b32357e85a1/rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f", size = 356157 }, + { url = "https://files.pythonhosted.org/packages/3a/64/1f0471b1e688704a716e07340b85f4145109359951feb08676a1f3b8cec4/rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1", size = 374826 }, + { url = "https://files.pythonhosted.org/packages/73/4e/082c0c5eba463e29dff1c6b49557f6ad0d6faae4b46832fa9c1e5b69b7ba/rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074", size = 544549 }, + { url = "https://files.pythonhosted.org/packages/cd/ee/f4af0a62d1ba912c4a3a7f5ec04350946ddd59017f3f3d1c227b20ddf558/rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08", size = 549245 }, + { url = "https://files.pythonhosted.org/packages/59/42/34601dc773be86a85a9ca47f68301a69fdb019aaae0c1426813f265f5ac0/rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec", size = 526722 }, + { url = "https://files.pythonhosted.org/packages/ff/4f/280745d5180c9d78df6b53b6e8b65336f8b6adeb958a8fd19c749fded637/rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8", size = 214379 }, +] + +[[package]] +name = "ruff" +version = "0.6.8" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/74/f9/4ce3e765a72ab8fe0f80f48508ea38b4196daab3da14d803c21349b2d367/ruff-0.6.8.tar.gz", hash = "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18", size = 3084543 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/db/07/42ee57e8b76ca585297a663a552b4f6d6a99372ca47fdc2276ef72cc0f2f/ruff-0.6.8-py3-none-linux_armv6l.whl", hash = "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2", size = 10404327 }, + { url = "https://files.pythonhosted.org/packages/eb/51/d42571ff8156d65086acb72d39aa64cb24181db53b497d0ed6293f43f07a/ruff-0.6.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c", size = 10018797 }, + { url = "https://files.pythonhosted.org/packages/c1/d7/fa5514a60b03976af972b67fe345deb0335dc96b9f9a9fa4df9890472427/ruff-0.6.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5", size = 9691303 }, + { url = "https://files.pythonhosted.org/packages/d6/c4/d812a74976927e51d0782a47539069657ac78535779bfa4d061c4fc8d89d/ruff-0.6.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f", size = 10719452 }, + { url = "https://files.pythonhosted.org/packages/ec/b6/aa700c4ae6db9b3ee660e23f3c7db596e2b16a3034b797704fba33ddbc96/ruff-0.6.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb", size = 10161353 }, + { url = "https://files.pythonhosted.org/packages/ea/39/0b10075ffcd52ff3a581b9b69eac53579deb230aad300ce8f9d0b58e77bc/ruff-0.6.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f", size = 10980630 }, + { url = "https://files.pythonhosted.org/packages/c1/af/9eb9efc98334f62652e2f9318f137b2667187851911fac3b395365a83708/ruff-0.6.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0", size = 11768996 }, + { url = "https://files.pythonhosted.org/packages/e0/59/8b1369cf7878358952b1c0a1559b4d6b5c824c003d09b0db26d26c9d094f/ruff-0.6.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87", size = 11317469 }, + { url = "https://files.pythonhosted.org/packages/b9/6d/e252e9b11bbca4114c386ee41ad559d0dac13246201d77ea1223c6fea17f/ruff-0.6.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098", size = 12467185 }, + { url = "https://files.pythonhosted.org/packages/48/44/7caa223af7d4ea0f0b2bd34acca65a7694a58317714675a2478815ab3f45/ruff-0.6.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0", size = 10887766 }, + { url = "https://files.pythonhosted.org/packages/81/ed/394aff3a785f171869158b9d5be61eec9ffb823c3ad5d2bdf2e5f13cb029/ruff-0.6.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750", size = 10711609 }, + { url = "https://files.pythonhosted.org/packages/47/31/f31d04c842e54699eab7e3b864538fea26e6c94b71806cd10aa49f13e1c1/ruff-0.6.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce", size = 10237621 }, + { url = "https://files.pythonhosted.org/packages/20/95/a764e84acf11d425f2f23b8b78b4fd715e9c20be4aac157c6414ca859a67/ruff-0.6.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa", size = 10558329 }, + { url = "https://files.pythonhosted.org/packages/2a/76/d4e38846ac9f6dd62dce858a54583911361b5339dcf8f84419241efac93a/ruff-0.6.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44", size = 10954102 }, + { url = "https://files.pythonhosted.org/packages/e7/36/f18c678da6c69f8d022480f3e8ddce6e4a52e07602c1d212056fbd234f8f/ruff-0.6.8-py3-none-win32.whl", hash = "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a", size = 8511090 }, + { url = "https://files.pythonhosted.org/packages/4c/c4/0ca7d8ffa358b109db7d7d045a1a076fd8e5d9cbeae022242d3c060931da/ruff-0.6.8-py3-none-win_amd64.whl", hash = "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263", size = 9350079 }, + { url = "https://files.pythonhosted.org/packages/d9/bd/a8b0c64945a92eaeeb8d0283f27a726a776a1c9d12734d990c5fc7a1278c/ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc", size = 8669595 }, +] + +[[package]] +name = "six" +version = "1.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/71/39/171f1c67cd00715f190ba0b100d606d440a28c93c7714febeca8b79af85e/six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", size = 34041 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/5a/e7c31adbe875f2abbb91bd84cf2dc52d792b5a01506781dbcf25c91daf11/six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254", size = 11053 }, +] + +[[package]] +name = "snowballstemmer" +version = "2.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/7b/af302bebf22c749c56c9c3e8ae13190b5b5db37a33d9068652e8f73b7089/snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", size = 86699 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ed/dc/c02e01294f7265e63a7315fe086dd1df7dacb9f840a804da846b96d01b96/snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a", size = 93002 }, +] + +[[package]] +name = "sphinx" +version = "5.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +dependencies = [ + { name = "alabaster", marker = "python_full_version < '3.9'" }, + { name = "babel", marker = "python_full_version < '3.9'" }, + { name = "colorama", marker = "python_full_version < '3.9' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version < '3.9'" }, + { name = "imagesize", marker = "python_full_version < '3.9'" }, + { name = "importlib-metadata", marker = "python_full_version < '3.9'" }, + { name = "jinja2", marker = "python_full_version < '3.9'" }, + { name = "packaging", marker = "python_full_version < '3.9'" }, + { name = "pygments", marker = "python_full_version < '3.9'" }, + { name = "requests", marker = "python_full_version < '3.9'" }, + { name = "snowballstemmer", marker = "python_full_version < '3.9'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.9'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.9'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.9'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.9'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.9'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/af/b2/02a43597980903483fe5eb081ee8e0ba2bb62ea43a70499484343795f3bf/Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5", size = 6811365 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/67/a7/01dd6fd9653c056258d65032aa09a615b5d7b07dd840845a9f41a8860fbc/sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d", size = 3183160 }, +] + +[[package]] +name = "sphinx" +version = "7.1.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9' and python_full_version < '3.12'", + "python_full_version == '3.12.*'", + "python_full_version >= '3.13'", +] +dependencies = [ + { name = "alabaster", marker = "python_full_version >= '3.9'" }, + { name = "babel", marker = "python_full_version >= '3.9'" }, + { name = "colorama", marker = "python_full_version >= '3.9' and sys_platform == 'win32'" }, + { name = "docutils", marker = "python_full_version >= '3.9'" }, + { name = "imagesize", marker = "python_full_version >= '3.9'" }, + { name = "importlib-metadata", marker = "python_full_version == '3.9.*'" }, + { name = "jinja2", marker = "python_full_version >= '3.9'" }, + { name = "packaging", marker = "python_full_version >= '3.9'" }, + { name = "pygments", marker = "python_full_version >= '3.9'" }, + { name = "requests", marker = "python_full_version >= '3.9'" }, + { name = "snowballstemmer", marker = "python_full_version >= '3.9'" }, + { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.9'" }, + { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.9'" }, + { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.9'" }, + { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.9'" }, + { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.9'" }, + { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/dc/01/688bdf9282241dca09fe6e3a1110eda399fa9b10d0672db609e37c2e7a39/sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f", size = 6828258 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/17/325cf6a257d84751a48ae90752b3d8fe0be8f9535b6253add61c49d0d9bc/sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe", size = 3169543 }, +] + +[[package]] +name = "sphinx-autoapi" +version = "2.1.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.9'", +] +dependencies = [ + { name = "anyascii", marker = "python_full_version < '3.9'" }, + { name = "astroid", marker = "python_full_version < '3.9'" }, + { name = "jinja2", marker = "python_full_version < '3.9'" }, + { name = "pyyaml", marker = "python_full_version < '3.9'" }, + { name = "sphinx", version = "5.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/6a/e9448b7bd3f892e05e538779f0c39e764457807bf08b252c19542f6d0833/sphinx-autoapi-2.1.1.tar.gz", hash = "sha256:fbadb96e79020d6b0ec45d888517bf816d6b587a2d340fbe1ec31135e300a6c8", size = 42160540 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/33/90ed4963df94d3780fe0fd61ce834ae17dcdc507060a3fb1610a68309f2f/sphinx_autoapi-2.1.1-py2.py3-none-any.whl", hash = "sha256:d8da890477bd18e3327cafdead9d5a44a7d798476c6fa32492100e288250a5a3", size = 54197 }, +] + +[[package]] +name = "sphinx-autoapi" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9' and python_full_version < '3.12'", + "python_full_version == '3.12.*'", + "python_full_version >= '3.13'", +] +dependencies = [ + { name = "astroid", marker = "python_full_version >= '3.9'" }, + { name = "jinja2", marker = "python_full_version >= '3.9'" }, + { name = "pyyaml", marker = "python_full_version >= '3.9'" }, + { name = "sphinx", version = "7.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "stdlib-list", marker = "python_full_version == '3.9.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e1/00/aeca40dec5288678df6dbe69c9fcc3fd5d53d1f94a462f4e6bb821d389a8/sphinx_autoapi-3.3.2.tar.gz", hash = "sha256:ebf8b44b2ebab5c28f0263ec6c2f8acdd156e9b2d539a58eca39d2f368445173", size = 66053 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/f4/73094ddb805607c5661e1b80f528e5235710e52b37a890423ad2ba5a7cee/sphinx_autoapi-3.3.2-py2.py3-none-any.whl", hash = "sha256:08afa656f7fcd45fe7dd64bf9f44698ddb8ca7c2d5cd0614c7455912ed580324", size = 34778 }, +] + +[[package]] +name = "sphinx-rtd-theme" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "sphinx", version = "5.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "sphinx", version = "7.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "sphinxcontrib-jquery" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fe/33/2a35a9cdbfda9086bda11457bcc872173ab3565b16b6d7f6b3efaa6dc3d6/sphinx_rtd_theme-2.0.0.tar.gz", hash = "sha256:bd5d7b80622406762073a04ef8fadc5f9151261563d47027de09910ce03afe6b", size = 2785005 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ea/46/00fda84467815c29951a9c91e3ae7503c409ddad04373e7cfc78daad4300/sphinx_rtd_theme-2.0.0-py2.py3-none-any.whl", hash = "sha256:ec93d0856dc280cf3aee9a4c9807c60e027c7f7b461b77aeffed682e68f0e586", size = 2824721 }, +] + +[[package]] +name = "sphinxcontrib-applehelp" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/32/df/45e827f4d7e7fcc84e853bcef1d836effd762d63ccb86f43ede4e98b478c/sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e", size = 24766 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/06/c1/5e2cafbd03105ce50d8500f9b4e8a6e8d02e22d0475b574c3b3e9451a15f/sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228", size = 120601 }, +] + +[[package]] +name = "sphinxcontrib-devhelp" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/98/33/dc28393f16385f722c893cb55539c641c9aaec8d1bc1c15b69ce0ac2dbb3/sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4", size = 17398 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/09/5de5ed43a521387f18bdf5f5af31d099605c992fd25372b2b9b825ce48ee/sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e", size = 84690 }, +] + +[[package]] +name = "sphinxcontrib-htmlhelp" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b3/47/64cff68ea3aa450c373301e5bebfbb9fce0a3e70aca245fcadd4af06cd75/sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff", size = 27967 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/ee/a1f5e39046cbb5f8bc8fba87d1ddf1c6643fbc9194e58d26e606de4b9074/sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903", size = 99833 }, +] + +[[package]] +name = "sphinxcontrib-jquery" +version = "4.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "sphinx", version = "5.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "sphinx", version = "7.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/de/f3/aa67467e051df70a6330fe7770894b3e4f09436dea6881ae0b4f3d87cad8/sphinxcontrib-jquery-4.1.tar.gz", hash = "sha256:1620739f04e36a2c779f1a131a2dfd49b2fd07351bf1968ced074365933abc7a", size = 122331 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/85/749bd22d1a68db7291c89e2ebca53f4306c3f205853cf31e9de279034c3c/sphinxcontrib_jquery-4.1-py2.py3-none-any.whl", hash = "sha256:f936030d7d0147dd026a4f2b5a57343d233f1fc7b363f68b3d4f1cb0993878ae", size = 121104 }, +] + +[[package]] +name = "sphinxcontrib-jsmath" +version = "1.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071 }, +] + +[[package]] +name = "sphinxcontrib-qthelp" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b1/8e/c4846e59f38a5f2b4a0e3b27af38f2fcf904d4bfd82095bf92de0b114ebd/sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72", size = 21658 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2b/14/05f9206cf4e9cfca1afb5fd224c7cd434dcc3a433d6d9e4e0264d29c6cdb/sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6", size = 90609 }, +] + +[[package]] +name = "sphinxcontrib-serializinghtml" +version = "1.1.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b5/72/835d6fadb9e5d02304cf39b18f93d227cd93abd3c41ebf58e6853eeb1455/sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952", size = 21019 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/77/5464ec50dd0f1c1037e3c93249b040c8fc8078fdda97530eeb02424b6eea/sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd", size = 94021 }, +] + +[[package]] +name = "sqlalchemy" +version = "2.0.35" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "greenlet", marker = "(python_full_version < '3.13' and platform_machine == 'AMD64') or (python_full_version < '3.13' and platform_machine == 'WIN32') or (python_full_version < '3.13' and platform_machine == 'aarch64') or (python_full_version < '3.13' and platform_machine == 'amd64') or (python_full_version < '3.13' and platform_machine == 'ppc64le') or (python_full_version < '3.13' and platform_machine == 'win32') or (python_full_version < '3.13' and platform_machine == 'x86_64')" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/48/4f190a83525f5cefefa44f6adc9e6386c4de5218d686c27eda92eb1f5424/sqlalchemy-2.0.35.tar.gz", hash = "sha256:e11d7ea4d24f0a262bccf9a7cd6284c976c5369dac21db237cff59586045ab9f", size = 9562798 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1a/61/19395d0ae78c94f6f80c8adf39a142f3fe56cfb2235d8f2317d6dae1bf0e/SQLAlchemy-2.0.35-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:67219632be22f14750f0d1c70e62f204ba69d28f62fd6432ba05ab295853de9b", size = 2090086 }, + { url = "https://files.pythonhosted.org/packages/e6/82/06b5fcbe5d49043e40cf4e01e3b33c471c8d9292d478420b08538cae8928/SQLAlchemy-2.0.35-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4668bd8faf7e5b71c0319407b608f278f279668f358857dbfd10ef1954ac9f90", size = 2081278 }, + { url = "https://files.pythonhosted.org/packages/68/d1/7fb7ee46949a5fb34005795b1fc06a8fef67587a66da731c14e545f7eb5b/SQLAlchemy-2.0.35-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb8bea573863762bbf45d1e13f87c2d2fd32cee2dbd50d050f83f87429c9e1ea", size = 3063763 }, + { url = "https://files.pythonhosted.org/packages/7e/ff/a1eacd78b31e52a5073e9924fb4722ecc2a72f093ca8181ed81fc61aed2e/SQLAlchemy-2.0.35-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f552023710d4b93d8fb29a91fadf97de89c5926c6bd758897875435f2a939f33", size = 3072032 }, + { url = "https://files.pythonhosted.org/packages/21/ae/ddfecf149a6d16af87408bca7bd108eef7ef23d376cc8464317efb3cea3f/SQLAlchemy-2.0.35-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:016b2e665f778f13d3c438651dd4de244214b527a275e0acf1d44c05bc6026a9", size = 3028092 }, + { url = "https://files.pythonhosted.org/packages/cc/51/3e84d42121662a160bacd311cfacb29c1e6a229d59dd8edb09caa8ab283b/SQLAlchemy-2.0.35-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7befc148de64b6060937231cbff8d01ccf0bfd75aa26383ffdf8d82b12ec04ff", size = 3053543 }, + { url = "https://files.pythonhosted.org/packages/3e/7a/039c78105958da3fc361887f0a82c974cb6fa5bba965c1689ec778be1c01/SQLAlchemy-2.0.35-cp310-cp310-win32.whl", hash = "sha256:22b83aed390e3099584b839b93f80a0f4a95ee7f48270c97c90acd40ee646f0b", size = 2062372 }, + { url = "https://files.pythonhosted.org/packages/a2/50/f31e927d32f9729f69d150ffe47e7cf51e3e0bb2148fc400b3e93a92ca4c/SQLAlchemy-2.0.35-cp310-cp310-win_amd64.whl", hash = "sha256:a29762cd3d116585278ffb2e5b8cc311fb095ea278b96feef28d0b423154858e", size = 2086485 }, + { url = "https://files.pythonhosted.org/packages/c3/46/9215a35bf98c3a2528e987791e6180eb51624d2c7d5cb8e2d96a6450b657/SQLAlchemy-2.0.35-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e21f66748ab725ade40fa7af8ec8b5019c68ab00b929f6643e1b1af461eddb60", size = 2091274 }, + { url = "https://files.pythonhosted.org/packages/1e/69/919673c5101a0c633658d58b11b454b251ca82300941fba801201434755d/SQLAlchemy-2.0.35-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8a6219108a15fc6d24de499d0d515c7235c617b2540d97116b663dade1a54d62", size = 2081672 }, + { url = "https://files.pythonhosted.org/packages/67/ea/a6b0597cbda12796be2302153369dbbe90573fdab3bc4885f8efac499247/SQLAlchemy-2.0.35-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:042622a5306c23b972192283f4e22372da3b8ddf5f7aac1cc5d9c9b222ab3ff6", size = 3200083 }, + { url = "https://files.pythonhosted.org/packages/8c/d6/97bdc8d714fb21762f2092511f380f18cdb2d985d516071fa925bb433a90/SQLAlchemy-2.0.35-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:627dee0c280eea91aed87b20a1f849e9ae2fe719d52cbf847c0e0ea34464b3f7", size = 3200080 }, + { url = "https://files.pythonhosted.org/packages/87/d2/8c2adaf2ade4f6f1b725acd0b0be9210bb6a2df41024729a8eec6a86fe5a/SQLAlchemy-2.0.35-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4fdcd72a789c1c31ed242fd8c1bcd9ea186a98ee8e5408a50e610edfef980d71", size = 3137108 }, + { url = "https://files.pythonhosted.org/packages/7e/ae/ea05d0bfa8f2b25ae34591895147152854fc950f491c4ce362ae06035db8/SQLAlchemy-2.0.35-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:89b64cd8898a3a6f642db4eb7b26d1b28a497d4022eccd7717ca066823e9fb01", size = 3157437 }, + { url = "https://files.pythonhosted.org/packages/fe/5d/8ad6df01398388a766163d27960b3365f1bbd8bb7b05b5cad321a8b69b25/SQLAlchemy-2.0.35-cp311-cp311-win32.whl", hash = "sha256:6a93c5a0dfe8d34951e8a6f499a9479ffb9258123551fa007fc708ae2ac2bc5e", size = 2061935 }, + { url = "https://files.pythonhosted.org/packages/ff/68/8557efc0c32c8e2c147cb6512237448b8ed594a57cd015fda67f8e56bb3f/SQLAlchemy-2.0.35-cp311-cp311-win_amd64.whl", hash = "sha256:c68fe3fcde03920c46697585620135b4ecfdfc1ed23e75cc2c2ae9f8502c10b8", size = 2087281 }, + { url = "https://files.pythonhosted.org/packages/2f/2b/fff87e6db0da31212c98bbc445f83fb608ea92b96bda3f3f10e373bac76c/SQLAlchemy-2.0.35-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:eb60b026d8ad0c97917cb81d3662d0b39b8ff1335e3fabb24984c6acd0c900a2", size = 2089790 }, + { url = "https://files.pythonhosted.org/packages/68/92/4bb761bd82764d5827bf6b6095168c40fb5dbbd23670203aef2f96ba6bc6/SQLAlchemy-2.0.35-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6921ee01caf375363be5e9ae70d08ce7ca9d7e0e8983183080211a062d299468", size = 2080266 }, + { url = "https://files.pythonhosted.org/packages/22/46/068a65db6dc253c6f25a7598d99e0a1d60b14f661f9d09ef6c73c718fa4e/SQLAlchemy-2.0.35-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8cdf1a0dbe5ced887a9b127da4ffd7354e9c1a3b9bb330dce84df6b70ccb3a8d", size = 3229760 }, + { url = "https://files.pythonhosted.org/packages/6e/36/59830dafe40dda592304debd4cd86e583f63472f3a62c9e2695a5795e786/SQLAlchemy-2.0.35-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93a71c8601e823236ac0e5d087e4f397874a421017b3318fd92c0b14acf2b6db", size = 3240649 }, + { url = "https://files.pythonhosted.org/packages/00/50/844c50c6996f9c7f000c959dd1a7436a6c94e449ee113046a1d19e470089/SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e04b622bb8a88f10e439084486f2f6349bf4d50605ac3e445869c7ea5cf0fa8c", size = 3176138 }, + { url = "https://files.pythonhosted.org/packages/df/d2/336b18cac68eecb67de474fc15c85f13be4e615c6f5bae87ea38c6734ce0/SQLAlchemy-2.0.35-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1b56961e2d31389aaadf4906d453859f35302b4eb818d34a26fab72596076bb8", size = 3202753 }, + { url = "https://files.pythonhosted.org/packages/f0/f3/ee1e62fabdc10910b5ef720ae08e59bc785f26652876af3a50b89b97b412/SQLAlchemy-2.0.35-cp312-cp312-win32.whl", hash = "sha256:0f9f3f9a3763b9c4deb8c5d09c4cc52ffe49f9876af41cc1b2ad0138878453cf", size = 2060113 }, + { url = "https://files.pythonhosted.org/packages/60/63/a3cef44a52979169d884f3583d0640e64b3c28122c096474a1d7cfcaf1f3/SQLAlchemy-2.0.35-cp312-cp312-win_amd64.whl", hash = "sha256:25b0f63e7fcc2a6290cb5f7f5b4fc4047843504983a28856ce9b35d8f7de03cc", size = 2085839 }, + { url = "https://files.pythonhosted.org/packages/da/bb/38c09ba518abc7da92d6bf638143cfe6298c12e5df0f1b37538e4ea46feb/SQLAlchemy-2.0.35-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4c31943b61ed8fdd63dfd12ccc919f2bf95eefca133767db6fbbd15da62078ec", size = 2095015 }, + { url = "https://files.pythonhosted.org/packages/c2/73/7d133479bc602dfb993bafe109727cf8684b3119e9bdee8c8510ebface91/SQLAlchemy-2.0.35-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a62dd5d7cc8626a3634208df458c5fe4f21200d96a74d122c83bc2015b333bc1", size = 2085697 }, + { url = "https://files.pythonhosted.org/packages/d9/54/bef04ac2ad5becdcc9f9de6f80d7bf0f679a5d288b1de95507b9823644d6/SQLAlchemy-2.0.35-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0630774b0977804fba4b6bbea6852ab56c14965a2b0c7fc7282c5f7d90a1ae72", size = 3088293 }, + { url = "https://files.pythonhosted.org/packages/19/7f/95df57d4763477c23ef2c72ca2fdb0ac3dbf4cc473052b3728cd586185ea/SQLAlchemy-2.0.35-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d625eddf7efeba2abfd9c014a22c0f6b3796e0ffb48f5d5ab106568ef01ff5a", size = 3095166 }, + { url = "https://files.pythonhosted.org/packages/97/38/ec497616369b9d1dabd90f80a9a7da2f5e07b8a1ea934a283cbd19a9f3eb/SQLAlchemy-2.0.35-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ada603db10bb865bbe591939de854faf2c60f43c9b763e90f653224138f910d9", size = 3042288 }, + { url = "https://files.pythonhosted.org/packages/30/99/9d8513d54b9455ebd10d6e1089e0742578e64c144f5dbbf1f8b74bb0f851/SQLAlchemy-2.0.35-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c41411e192f8d3ea39ea70e0fae48762cd11a2244e03751a98bd3c0ca9a4e936", size = 3065416 }, + { url = "https://files.pythonhosted.org/packages/b3/bb/1e31ed9b69080d5d962497189628c3f695e3186949640635db5aa3190ab2/SQLAlchemy-2.0.35-cp38-cp38-win32.whl", hash = "sha256:d299797d75cd747e7797b1b41817111406b8b10a4f88b6e8fe5b5e59598b43b0", size = 2064976 }, + { url = "https://files.pythonhosted.org/packages/a2/0c/9e6f99e0e7ae3e01a159b91f8adef6a32d893a8b21196d9d2d1591280ae5/SQLAlchemy-2.0.35-cp38-cp38-win_amd64.whl", hash = "sha256:0375a141e1c0878103eb3d719eb6d5aa444b490c96f3fedab8471c7f6ffe70ee", size = 2089743 }, + { url = "https://files.pythonhosted.org/packages/d1/4c/3a538c077057a449441b3e10a62ff1608eaa6e0910e681b2664ce0bcf961/SQLAlchemy-2.0.35-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ccae5de2a0140d8be6838c331604f91d6fafd0735dbdcee1ac78fc8fbaba76b4", size = 2092966 }, + { url = "https://files.pythonhosted.org/packages/4e/f0/ac290c05f9118cff70e48abd598bcb0dfb725a7ad0aeebaaa0d781d75367/SQLAlchemy-2.0.35-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2a275a806f73e849e1c309ac11108ea1a14cd7058577aba962cd7190e27c9e3c", size = 2084203 }, + { url = "https://files.pythonhosted.org/packages/8f/a7/6ddbcefb0ada3dbc9bc9260de32f9cd85b1eb5ea35c12344472a028a606e/SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:732e026240cdd1c1b2e3ac515c7a23820430ed94292ce33806a95869c46bd139", size = 3078233 }, + { url = "https://files.pythonhosted.org/packages/08/7b/6fae14cf33ebc54423f0e9c7de793dd2debe3ffd44b399a4905adb4b1225/SQLAlchemy-2.0.35-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:890da8cd1941fa3dab28c5bac3b9da8502e7e366f895b3b8e500896f12f94d11", size = 3086017 }, + { url = "https://files.pythonhosted.org/packages/d7/0c/fa68271e608bf4a86c131044966a9f63bc7c6f44e93535be70c7562ff19e/SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c0d8326269dbf944b9201911b0d9f3dc524d64779a07518199a58384c3d37a44", size = 3045236 }, + { url = "https://files.pythonhosted.org/packages/f1/29/65d6441a2875c9e960703fb730c651f0a62505a16e968e6f99f5dd5bb925/SQLAlchemy-2.0.35-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b76d63495b0508ab9fc23f8152bac63205d2a704cd009a2b0722f4c8e0cba8e0", size = 3071708 }, + { url = "https://files.pythonhosted.org/packages/13/cb/c354d16eb4b9af27a8ef16ef476118497f61f99bf21cf177ec85d9a23d3f/SQLAlchemy-2.0.35-cp39-cp39-win32.whl", hash = "sha256:69683e02e8a9de37f17985905a5eca18ad651bf592314b4d3d799029797d0eb3", size = 2064708 }, + { url = "https://files.pythonhosted.org/packages/56/0a/3025867277836c325a0f69d3e43cfd35e72f877f472d4c4791d32c264d13/SQLAlchemy-2.0.35-cp39-cp39-win_amd64.whl", hash = "sha256:aee110e4ef3c528f3abbc3c2018c121e708938adeeff9006428dd7c8555e9b3f", size = 2088933 }, + { url = "https://files.pythonhosted.org/packages/0e/c6/33c706449cdd92b1b6d756b247761e27d32230fd6b2de5f44c4c3e5632b2/SQLAlchemy-2.0.35-py3-none-any.whl", hash = "sha256:2ab3f0336c0387662ce6221ad30ab3a5e6499aab01b9790879b6578fd9b8faa1", size = 1881276 }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521 }, +] + +[[package]] +name = "stdlib-list" +version = "0.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/39/bb/1cdbc326a5ab0026602e0489cbf02357e78140253c4b57cd866d380eb355/stdlib_list-0.10.0.tar.gz", hash = "sha256:6519c50d645513ed287657bfe856d527f277331540691ddeaf77b25459964a14", size = 59447 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/d9/9085375f0d23a4896b307bf14dcc61b49ec8cc67cb33e06cf95bf3af3966/stdlib_list-0.10.0-py3-none-any.whl", hash = "sha256:b3a911bc441d03e0332dd1a9e7d0870ba3bb0a542a74d7524f54fb431256e214", size = 79814 }, +] + +[[package]] +name = "tabulate" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/fe/802052aecb21e3797b8f7902564ab6ea0d60ff8ca23952079064155d1ae1/tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", size = 81090 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/44/4a5f08c96eb108af5cb50b41f76142f0afa346dfa99d5296fe7202a11854/tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f", size = 35252 }, +] + +[[package]] +name = "tomli" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f", size = 15164 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/75/10a9ebee3fd790d20926a90a2547f0bf78f371b2f13aa822c759680ca7b9/tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", size = 12757 }, +] + +[[package]] +name = "tornado" +version = "6.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/66/398ac7167f1c7835406888a386f6d0d26ee5dbf197d8a571300be57662d3/tornado-6.4.1.tar.gz", hash = "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9", size = 500623 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/d9/c33be3c1a7564f7d42d87a8d186371a75fd142097076767a5c27da941fef/tornado-6.4.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8", size = 435924 }, + { url = "https://files.pythonhosted.org/packages/2e/0f/721e113a2fac2f1d7d124b3279a1da4c77622e104084f56119875019ffab/tornado-6.4.1-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14", size = 433883 }, + { url = "https://files.pythonhosted.org/packages/13/cf/786b8f1e6fe1c7c675e79657448178ad65e41c1c9765ef82e7f6f765c4c5/tornado-6.4.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4", size = 437224 }, + { url = "https://files.pythonhosted.org/packages/e4/8e/a6ce4b8d5935558828b0f30f3afcb2d980566718837b3365d98e34f6067e/tornado-6.4.1-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842", size = 436597 }, + { url = "https://files.pythonhosted.org/packages/22/d4/54f9d12668b58336bd30defe0307e6c61589a3e687b05c366f804b7faaf0/tornado-6.4.1-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3", size = 436797 }, + { url = "https://files.pythonhosted.org/packages/cf/3f/2c792e7afa7dd8b24fad7a2ed3c2f24a5ec5110c7b43a64cb6095cc106b8/tornado-6.4.1-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f", size = 437516 }, + { url = "https://files.pythonhosted.org/packages/71/63/c8fc62745e669ac9009044b889fc531b6f88ac0f5f183cac79eaa950bb23/tornado-6.4.1-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4", size = 436958 }, + { url = "https://files.pythonhosted.org/packages/94/d4/f8ac1f5bd22c15fad3b527e025ce219bd526acdbd903f52053df2baecc8b/tornado-6.4.1-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698", size = 436882 }, + { url = "https://files.pythonhosted.org/packages/4b/3e/a8124c21cc0bbf144d7903d2a0cadab15cadaf683fa39a0f92bc567f0d4d/tornado-6.4.1-cp38-abi3-win32.whl", hash = "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d", size = 438092 }, + { url = "https://files.pythonhosted.org/packages/d9/2f/3f2f05e84a7aff787a96d5fb06821323feb370fe0baed4db6ea7b1088f32/tornado-6.4.1-cp38-abi3-win_amd64.whl", hash = "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7", size = 438532 }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359 }, +] + +[[package]] +name = "types-pillow" +version = "10.2.0.20240822" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/18/4a/4495264dddaa600d65d68bcedb64dcccf9d9da61adff51f7d2ffd8e4c9ce/types-Pillow-10.2.0.20240822.tar.gz", hash = "sha256:559fb52a2ef991c326e4a0d20accb3bb63a7ba8d40eb493e0ecb0310ba52f0d3", size = 35389 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/66/23/e81a5354859831fcf54d488d33b80ba6133ea84f874a9c0ec40a4881e133/types_Pillow-10.2.0.20240822-py3-none-any.whl", hash = "sha256:d9dab025aba07aeb12fd50a6799d4eac52a9603488eca09d7662543983f16c5d", size = 54354 }, +] + +[[package]] +name = "types-protobuf" +version = "5.28.0.20240924" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/90/c3/217fe2c6a4b8ed75c5ecbd27ae8dedd7bc8e8728ac4b29d16005d3a3aba2/types-protobuf-5.28.0.20240924.tar.gz", hash = "sha256:d181af8a256e5a91ce8d5adb53496e880efd9144c7d54483e3653332b60296f0", size = 54324 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/61/2b/98bfe67a73b15964513b471ce10b610ab0df28825900e0e7517b2bf23952/types_protobuf-5.28.0.20240924-py3-none-any.whl", hash = "sha256:5cecf612ccdefb7dc95f7a51fb502902f20fc2e6681cd500184aaa1b3931d6a7", size = 68761 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "urllib3" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ed/63/22ba4ebfe7430b76388e7cd448d5478814d3032121827c12a2cc287e2260/urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9", size = 300677 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/d9/5f4c13cecde62396b0d3fe530a50ccea91e7dfc1ccf0e09c228841bb5ba8/urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", size = 126338 }, +] + +[[package]] +name = "viam-sdk" +source = { editable = "." } +dependencies = [ + { name = "googleapis-common-protos" }, + { name = "grpclib" }, + { name = "protobuf" }, + { name = "pymongo" }, + { name = "typing-extensions" }, +] + +[package.optional-dependencies] +mlmodel = [ + { name = "numpy", version = "1.24.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.13'" }, + { name = "numpy", version = "2.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, +] + +[package.dev-dependencies] +dev = [ + { name = "coverage" }, + { name = "mypy-protobuf" }, + { name = "myst-nb", version = "0.17.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "myst-nb", version = "1.1.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "nbmake" }, + { name = "numpy", version = "1.24.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "numpy", version = "1.26.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9' and python_full_version < '3.13'" }, + { name = "numpy", version = "2.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.13'" }, + { name = "pillow" }, + { name = "pyright" }, + { name = "pytest" }, + { name = "pytest-asyncio" }, + { name = "pytest-mock" }, + { name = "ruff" }, + { name = "sphinx-autoapi", version = "2.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.9'" }, + { name = "sphinx-autoapi", version = "3.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.9'" }, + { name = "sphinx-rtd-theme" }, + { name = "types-pillow" }, +] + +[package.metadata] +requires-dist = [ + { name = "googleapis-common-protos", specifier = ">=1.65.0" }, + { name = "grpclib", specifier = ">=0.4.7" }, + { name = "numpy", marker = "extra == 'mlmodel'" }, + { name = "protobuf", specifier = "==5.29.2" }, + { name = "pymongo", specifier = ">=4.10.1" }, + { name = "typing-extensions", specifier = ">=4.12.2" }, +] +provides-extras = ["mlmodel"] + +[package.metadata.requires-dev] +dev = [ + { name = "coverage", specifier = ">=7.6.1" }, + { name = "mypy-protobuf", specifier = ">=3.6.0" }, + { name = "myst-nb", marker = "python_full_version < '3.9'", specifier = "<1.0.0" }, + { name = "myst-nb", marker = "python_full_version >= '3.9'", specifier = ">=1.0.0" }, + { name = "nbmake", specifier = ">=1.5.4" }, + { name = "numpy", marker = "python_full_version < '3.9'", specifier = "<1.25.0" }, + { name = "numpy", marker = "python_full_version >= '3.9'", specifier = ">=1.26.2" }, + { name = "pillow", specifier = ">=10.4.0" }, + { name = "pyright", specifier = ">=1.1.382.post1" }, + { name = "pytest", specifier = ">=8.3.3" }, + { name = "pytest-asyncio", specifier = ">=0.24.0" }, + { name = "pytest-mock", specifier = ">=3.14.0" }, + { name = "ruff", specifier = ">=0.6.8" }, + { name = "sphinx-autoapi", marker = "python_full_version < '3.9'", specifier = "<3.0.0" }, + { name = "sphinx-autoapi", marker = "python_full_version >= '3.9'", specifier = ">=3.0.0" }, + { name = "sphinx-rtd-theme", specifier = ">=2.0.0" }, + { name = "types-pillow", specifier = ">=10.2.0.20240822" }, +] + +[[package]] +name = "wcwidth" +version = "0.2.13" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/63/53559446a878410fc5a5974feb13d31d78d752eb18aeba59c7fef1af7598/wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5", size = 101301 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/84/fd2ba7aafacbad3c4201d395674fc6348826569da3c0937e75505ead3528/wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859", size = 34166 }, +] + +[[package]] +name = "zipp" +version = "3.20.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/54/bf/5c0000c44ebc80123ecbdddba1f5dcd94a5ada602a9c225d84b5aaa55e86/zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29", size = 24199 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/8b/5ba542fa83c90e09eac972fc9baca7a88e7e7ca4b221a89251954019308b/zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", size = 9200 }, +]