diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3e38781 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: CI + +on: + push: + branches: + - main + + pull_request: + branches: + - main + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# security: restrict permissions for CI jobs. +permissions: + contents: read + +jobs: + server-ci: + name: Server CI + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Python + id: install_python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install Poetry + uses: abatilo/actions-poetry@v2 + with: + poetry-version: "1.8.3" + + - name: Setup a local virtual environment (if no poetry.toml file) + working-directory: ./python + run: | + poetry config virtualenvs.create true --local + poetry config virtualenvs.in-project true --local + + - name: Restore cached virtualenv + uses: actions/cache/restore@v4 + with: + path: ./python/.venv + key: venv-${{ runner.os }}-${{ steps.install_python.outputs.python-version }}-${{ hashFiles('./python/poetry.lock') }} + + - name: Install dependencies (used by later workflows) + working-directory: ./python + run: | + poetry install + echo "$(poetry env info --path)/bin" >> $GITHUB_PATH + echo "VIRTUAL_ENV=$(poetry env info --path)/bin" >> $GITHUB_ENV + + - name: Saved cached virtualenv + uses: actions/cache/save@v4 + with: + path: ./python/.venv + key: venv-${{ runner.os }}-${{ steps.install_python.outputs.python-version }}-${{ hashFiles('./python/poetry.lock') }} + + - name: Lint with ruff + working-directory: ./python + run: ruff check --output-format=github + + - name: Typecheck with pyright + working-directory: ./python + run: pyright arflow + + - name: Test with pytest + working-directory: ./python + timeout-minutes: 5 # pytest sometimes hangs for (yet) unknown reasons + # TODO: Add coverage tracking once we have a stable test suite + run: | + pytest diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 4f9c582..0000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,17 +0,0 @@ -on: - push: - branches: - - main - -jobs: - contrib-readme-job: - runs-on: ubuntu-latest - name: A job to automate contrib in readme - permissions: - contents: write - pull-requests: write - steps: - - name: Contribute List - uses: akhilmhdh/contributors-readme-action@v2.3.10 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 0000000..b6933d2 --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,65 @@ +name: Pre-release + +on: + push: + tags: + - "*" + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +permissions: + contents: write + +jobs: + release: + name: Release package + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Python + id: install_python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install Poetry + uses: abatilo/actions-poetry@v2 + with: + poetry-version: "1.8.3" + + - name: Setup a local virtual environment (if no poetry.toml file) + working-directory: ./python + run: | + poetry config virtualenvs.create true --local + poetry config virtualenvs.in-project true --local + + - name: Restore cached virtualenv + uses: actions/cache/restore@v4 + with: + path: ./python/.venv + key: venv-${{ runner.os }}-${{ steps.install_python.outputs.python-version }}-${{ hashFiles('./python/poetry.lock') }} + + - name: Install dependencies (skipped if cache hit, fallback to install) + working-directory: ./python + run: | + poetry install + echo "$(poetry env info --path)/bin" >> $GITHUB_PATH + echo "VIRTUAL_ENV=$(poetry env info --path)/bin" >> $GITHUB_ENV + + - name: Saved cached virtualenv + uses: actions/cache/save@v4 + with: + path: ./python/.venv + key: venv-${{ runner.os }}-${{ steps.install_python.outputs.python-version }}-${{ hashFiles('./python/poetry.lock') }} + + - name: Configure Test PyPI with Poetry + working-directory: ./python + run: | + poetry config repositories.testpypi https://test.pypi.org/legacy/ + poetry config pypi-token.testpypi ${{ secrets.TEST_PYPI_API_TOKEN }} + + - name: Build and publish the package + working-directory: ./python + run: poetry publish --build -r testpypi diff --git a/.github/workflows/website.yml b/.github/workflows/publish-docs.yml similarity index 73% rename from .github/workflows/website.yml rename to .github/workflows/publish-docs.yml index b0276b3..46c68b4 100644 --- a/.github/workflows/website.yml +++ b/.github/workflows/publish-docs.yml @@ -1,30 +1,17 @@ -# Simple workflow for deploying static content to GitHub Pages -name: Deploy static content to Pages +name: Publish docs on: - # Runs on pushes targeting the default branch - push: - branches: - - main - paths: - - 'python/**' - - '.github/workflows/website.yml' - - 'website/**' - - 'unity/**' - - # Alternative: only build for tags. - # tags: - # - '*' - # Allows you to run this workflow manually from the Actions tab workflow_dispatch: + workflow_call: -# security: restrict permissions for CI jobs. permissions: - contents: read + pages: write + id-token: write jobs: build-client-docs-as-artifact: + name: Build client docs runs-on: windows-latest steps: - uses: actions/checkout@v4 @@ -47,20 +34,22 @@ jobs: path: ./unity/Documentation/clientHTMLOutput build-server-docs: + name: Build server docs needs: build-client-docs-as-artifact runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install Python + id: install_python uses: actions/setup-python@v5 with: - python-version: '3.10.12' + python-version: "3.10" - name: Install Poetry uses: abatilo/actions-poetry@v2 with: - poetry-version: '1.8.3' + poetry-version: "1.8.3" - name: Setup a local virtual environment (if no poetry.toml file) working-directory: ./python @@ -68,20 +57,29 @@ jobs: poetry config virtualenvs.create true --local poetry config virtualenvs.in-project true --local - - name: Define a cache for the virtual environment based on the dependencies lock file - uses: actions/cache@v3 + - name: Restore cached virtualenv + uses: actions/cache/restore@v4 with: path: ./python/.venv - key: venv-${{ hashFiles('./python/poetry.lock') }} - + key: venv-${{ runner.os }}-${{ steps.install_python.outputs.python-version }}-${{ hashFiles('./python/poetry.lock') }} + - name: Install docs dependencies working-directory: ./python - run: poetry install --with docs + run: | + poetry install --with docs + echo "$(poetry env info --path)/bin" >> $GITHUB_PATH + echo "VIRTUAL_ENV=$(poetry env info --path)/bin" >> $GITHUB_ENV + + # - name: Saved cached virtualenv + # uses: actions/cache/save@v4 + # with: + # path: ./python/.venv + # key: venv-${{ runner.os }}-${{ steps.install_python.outputs.python-version }}-${{ hashFiles('./python/poetry.lock') }} - name: Build the documentation working-directory: ./python - run: poetry run python tools/make_docs_cli.py - + run: python tools/make_docs_cli.py + - name: Move docs to website directory run: | mkdir -p ./website/docs/server/ @@ -92,7 +90,7 @@ jobs: with: name: client-docs path: ./website/docs/client - + # # cleanup client docs artifacts # - name: Delete client docs artifact # run: | @@ -107,7 +105,8 @@ jobs: path: ./website # Single deploy job since we're just deploying - deploy: + deploy-website: + name: Deploy website needs: build-server-docs runs-on: ubuntu-latest permissions: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..70c6074 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,76 @@ +name: Release + +on: + release: + types: + - published + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +permissions: + contents: write + +jobs: + release: + name: Release package + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Python + id: install_python + uses: actions/setup-python@v5 + with: + python-version: "3.10" + + - name: Install Poetry + uses: abatilo/actions-poetry@v2 + with: + poetry-version: "1.8.3" + + - name: Setup a local virtual environment (if no poetry.toml file) + working-directory: ./python + run: | + poetry config virtualenvs.create true --local + poetry config virtualenvs.in-project true --local + + - name: Restore cached virtualenv + uses: actions/cache/restore@v4 + with: + path: ./python/.venv + key: venv-${{ runner.os }}-${{ steps.install_python.outputs.python-version }}-${{ hashFiles('./python/poetry.lock') }} + + - name: Install dependencies (skipped if cache hit, fallback to install) + working-directory: ./python + run: | + poetry install + echo "$(poetry env info --path)/bin" >> $GITHUB_PATH + echo "VIRTUAL_ENV=$(poetry env info --path)/bin" >> $GITHUB_ENV + + - name: Saved cached virtualenv + uses: actions/cache/save@v4 + with: + path: ./python/.venv + key: venv-${{ runner.os }}-${{ steps.install_python.outputs.python-version }}-${{ hashFiles('./python/poetry.lock') }} + + - name: Use PyPI API token + working-directory: ./python + run: poetry config pypi-token.pypi ${{ secrets.PYPI_API_TOKEN }} + + - name: Build and publish the package + working-directory: ./python + run: poetry publish --build + + - name: Upload Python package to GitHub + uses: actions/upload-artifact@v4 + with: + path: ./python/dist/ + if-no-files-found: error + + publish-docs: + name: Publish documentation + permissions: + pages: write + id-token: write + uses: ./.github/workflows/publish-docs.yml diff --git a/.github/workflows/update-contributors.yml b/.github/workflows/update-contributors.yml new file mode 100644 index 0000000..e6ce4be --- /dev/null +++ b/.github/workflows/update-contributors.yml @@ -0,0 +1,19 @@ +name: Update contributors + +on: + push: + branches: + - main + +jobs: + contrib-readme-job: + runs-on: ubuntu-latest + name: A job to automate contrib in readme + permissions: + contents: write + pull-requests: write + steps: + - name: Contribute List + uses: akhilmhdh/contributors-readme-action@v2.3.10 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 705a983..d3b689d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ -# Created by https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,python,unity -# Edit at https://www.toptal.com/developers/gitignore?templates=macos,visualstudiocode,python,unity +# Created by https://www.toptal.com/developers/gitignore/api/macos,windows,visualstudiocode +# Edit at https://www.toptal.com/developers/gitignore?templates=macos,windows,visualstudiocode #additional files -.grpc_tools +.grpc_tools/** +act.exe +my.secrets ### macOS ### # General @@ -13,7 +15,6 @@ # Icon must end with two \r Icon - # Thumbnails ._* @@ -37,252 +38,6 @@ Temporary Items # iCloud generated files *.icloud -### Python ### -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -### Python Patch ### -# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration -poetry.toml - -# ruff -.ruff_cache/ - -# LSP config files -pyrightconfig.json - -### Unity ### -# This .gitignore file should be placed at the root of your Unity project directory -# -# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore -/[Ll]ibrary/ -/[Tt]emp/ -/[Oo]bj/ -/[Bb]uild/ -/[Bb]uilds/ -/[Ll]ogs/ -/[Uu]ser[Ss]ettings/ - -# MemoryCaptures can get excessive in size. -# They also could contain extremely sensitive data -/[Mm]emoryCaptures/ - -# Recordings can get excessive in size -/[Rr]ecordings/ - -# Uncomment this line if you wish to ignore the asset store tools plugin -# /[Aa]ssets/AssetStoreTools* - -# Autogenerated Jetbrains Rider plugin -/[Aa]ssets/Plugins/Editor/JetBrains* - -# Visual Studio cache directory -.vs/ - -# Gradle cache directory -.gradle/ - -# Autogenerated VS/MD/Consulo solution and project files -ExportedObj/ -.consulo/ -*.csproj -*.unityproj -# add .sln files to build documentation -*.sln -*.suo -*.tmp -*.user -*.userprefs -*.pidb -*.booproj -*.svd -*.pdb -*.mdb -*.opendb -*.VC.db - -# Unity3D generated meta files -*.pidb.meta -*.pdb.meta -*.mdb.meta - -# Unity3D generated file on crash reports -sysinfo.txt - -# Builds -*.apk -*.aab -*.unitypackage -*.app - -# Crashlytics generated file -crashlytics-build.properties - -# Packed Addressables -/[Aa]ssets/[Aa]ddressable[Aa]ssets[Dd]ata/*/*.bin* - -# Temporary auto-generated Android Assets -/[Aa]ssets/[Ss]treamingAssets/aa.meta -/[Aa]ssets/[Ss]treamingAssets/aa/* - ### VisualStudioCode ### .vscode/* !.vscode/settings.json @@ -302,6 +57,49 @@ crashlytics-build.properties .history .ionide -# End of https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,python,unity +### Windows ### +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db -website/docs/* +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.toptal.com/developers/gitignore/api/macos,windows,visualstudiocode + +### ARFlow ### +# Docs are generated in CI and combined with existing static files +website/docs/** + +# Unit test / coverage reports sometimes get "leaked" out of the `python` directory +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 379aa6d..9987e53 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,11 +1,11 @@ { - "recommendations": [ - "ms-python.python", - "ms-python.vscode-pylance", - "redhat.vscode-yaml", - "charliermarsh.ruff", - "ms-toolsai.jupyter", - "streetsidesoftware.code-spell-checker", - "visualstudiotoolsforunity.vstuc", - ] + "recommendations": [ + "ms-python.python", + "ms-python.vscode-pylance", + "redhat.vscode-yaml", + "charliermarsh.ruff", + "ms-toolsai.jupyter", + "streetsidesoftware.code-spell-checker", + "visualstudiotoolsforunity.vstuc" + ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ce4ab1..f2f21db 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,8 +12,11 @@ }, "editor.defaultFormatter": "charliermarsh.ruff" }, - "python.analysis.typeCheckingMode": "off", // TODO: Enable strict type checking + "python.analysis.typeCheckingMode": "strict", "python.analysis.autoImportCompletions": true, + "python.testing.pytestArgs": ["python"], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true, "[csharp]": { "editor.formatOnSave": true, "editor.maxTokenizationLineLength": 2500, @@ -26,7 +29,7 @@ "**/CVS": true, "**/.DS_Store": true, "**/Thumbs.db": true, - "**/__pycache__": true, + "**/__pycache__": true }, "cSpell.words": [ "arange", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ade5131..86e28f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ ARFlow uses [`poetry`](https://python-poetry.org) for dependency management. Ins Clone the forked repository: -```bash +```shell git clone https://github.com/{your-account}/ARFlow.git cd ARFlow/python poetry install @@ -22,31 +22,74 @@ ARFlow uses [`ruff`](https://docs.astral.sh/ruff/) for linting and formatting. W These tools should run automatically in your editor. If you want to run them manually, you can also use the following commands: -```bash +```shell poetry run ruff check # check for linting errors poetry run ruff check --fix # check for linting errors and fix them poetry run ruff format # format the code - -poetry run pyright # type check ``` All of these quality checks are run automatically before every commit using [`pre-commit`](https://pre-commit.com). To install the pre-commit hooks, run: -```bash +```shell poetry run pre-commit install ``` +To manually invoke the pre-commit checks, run: + +```shell +poetry run pre-commit run --all-files +``` + +### Type Completeness + +Library authors are encouraged to prioritize bringing their public API to 100% type coverage. Although this is very hard in ARFlow's case due to our dependency on `gRPC`, we should still strive to achieve this goal. To check for type completeness, run: + +```shell +poetry run pyright --ignoreexternal --verifytypes arflow +``` + +To read more about formalizing libraries' public APIs, please refer to this excellent [blog post](https://dagster.io/blog/adding-python-types#-step-3-formalize-public-api) by Dagster. + ### Testing ARFlow uses [`pytest`](https://pytest.org). Make sure you are in the `python` directory and then run tests with: -```bash +```shell poetry run pytest ``` -### +### Logging + +- Log key events for debugging and tracking. +- Avoid logging sensitive information (e.g., user data). +- Initialize a logger in each module using `logger = logging.getLogger(__name__)`. This enables granular logging and gives users control over logs from specific parts of the library. +- Use appropriate log levels: + +| Level | Usage | +| ----------- | ---------------------------- | +| `debug()` | Detailed internal state info | +| `info()` | General operational events | +| `warning()` | Unexpected events, non-fatal | +| `error()` | Errors, exceptions | + +Example: + +```python +logger = logging.getLogger(__name__) +logger.debug("Processing request: %s", request_id) +``` + +### Continuous Integration + +ARFlow uses GitHub Actions for continuous integration. The CI pipeline runs the following checks: + +```shell +poetry run ruff check # linting +poetry run pyright arflow # type checking +poetry run pytest # testing +``` ## Packages & Tools @@ -60,7 +103,7 @@ ARFlow uses `poetry` to manage dependencies and run commands. Commands can be fo A language-neutral, platform-neutral, extensible mechanism for serializing structured data. -ARFlow uses `protobuf` to define the communication protocol between the server and the client. The protocol is defined in [`service.proto`](../protos/arflow/service.proto) and can be compiled using [`compile.sh`](../protos/scripts/compile.sh). +ARFlow uses `protobuf` to define the communication protocol between the server and the client. The protocol is defined in [`service.proto`](./protos/arflow/_grpc/service.proto) and can be compiled using [`compile.sh`](./protos/compile.sh). ### [`pickle`](https://docs.python.org/3/library/pickle.html) @@ -78,20 +121,60 @@ ARFlow uses the Rerun Python SDK to visualize the data collected by the ARFlow s ## Documentation -ARFlow uses [`pdoc`](https://pdoc.dev). You can refer to their documentation for more information on how to generate documentation. If you create a new submodule, make sure to add it to the `__all__` list defined in the `_init__.py` file of the `arflow` package. +ARFlow uses [`pdoc`](https://pdoc.dev). You can refer to their documentation for more information on how to generate documentation. To preview the documentation locally, run: -```bash -poetry run pdoc arflow # or replace with module_name that you want to preview +```shell +poetry run pdoc arflow examples # or replace with module_name that you want to preview ``` +## gRPC Best Practices + +The ARFlow server and client communicates through gRPC. Here are some best practices to keep in mind when working with gRPC: + +### Input validation + +All fields in `proto3` are optional, so you’ll need to validate that they’re all set. If you leave one unset, then it’ll default to zero for numeric types or to an empty string for strings. + +### Error handling + +gRPC is built on top of HTTP/2, the status code is like the standard HTTP status code. This allows clients to take different actions based on the code they receive. Proper error handling also allows middleware, like monitoring systems, to log how many requests have errors. + +ARFlow uses the `grpc_interceptor` library to handle exceptions. This library provides a way to raise exceptions in your service handlers, and have them automatically converted to gRPC status codes. Check out an example usage [here](https://github.com/d5h-foss/grpc-interceptor/tree/master?tab=readme-ov-file#server-interceptor). + +`grpc_interceptor` also provides a testing framework to run a gRPC service with interceptors. You can check out the example usage [here](./python/tests/test_interceptor.py). + +### Protobuf versioning + +To achieve **backward compatibility**, you should never remove a field from a message. Instead, mark it as deprecated and add a new field with the new name. This way, clients that use the old field will still work. + +### Protobuf linting + +We use `buf` to lint our protobuf files. You can install it by following the instructions [here](https://buf.build/docs/installation). + +### Type checking Protobuf-generated code + +We use `pyright` and `grpc-stubs` to type check our Protobuf-generated code. + +### Graceful shutdown + +When the server is shutting down, it should wait for all in-flight requests to complete before shutting down. This is to prevent data loss or corruption. We have done this in the ARFlow server. + +### Securing channels + +gRPC supports TLS encryption out of the box. We have not implemented this in the ARFlow server yet. If you are interested in working on this, please let us know. + ## Common Issues ### VSCode Force Changes Locale VSCode may force changes the locale to `en_US.UTF-8` for git commit hooks. To fix this, run: -```bash +```shell sudo locale-gen en_US.UTF-8 ``` + +### Running Rerun on WSL2 + +Please refer to their documentation [documentation](https://rerun.io/docs/getting-started/troubleshooting#wsl2). diff --git a/README.md b/README.md index 3344db8..4a61f5e 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This project aims to provide a tool to democratize and accelerate AR research an [Paper](https://doi.org/10.1145/3638550.3643617) | [BibTeX](#how-to-cite-arflow) | [Project Page](https://cake.wpi.edu/ARFlow/) | [Video](https://youtu.be/mml8YrCgfTk) -## Getting Started +## Quick Start ### Device Preparation @@ -15,39 +15,10 @@ Make sure you have the developer mode enabled on your device. ### Server Setup -Next, create your own ARFlow server instance to start playing with the device collected AR data. Here we show the steps to build a simple server: +Next, start up your own ARFlow server instance: -```bash -# Create a python environment using your favorite tool, then -pip install arflow -``` - -Create a Python file `simple_arflow_server.py` and paste the following code: - -```python -"""A simple example of extending the ARFlow server.""" - -import arflow - - -class CustomService(arflow.ARFlowService): - def on_frame_received(self, frame: arflow.DataFrameRequest): - """Called when a frame is received.""" - print("Frame received!") - - -def main(): - arflow.create_server(CustomService, port=8500, path_to_save="./") - - -if __name__ == "__main__": - main() -``` - -Run it! - -``` -python simple_arflow_server.py +```shell +arflow serve # This will start the server on port 8500 ``` ### Client Setup @@ -55,35 +26,34 @@ python simple_arflow_server.py Next, go to the [releases](https://github.com/cake-lab/ARFlow/releases) page and find the prebuilt items for Android and iOS. For Android, directly install the prebuilt apk on your device. For iOS, compile the generated Xcode project to deploy the ARFlow client app to your iOS device. Note that you will need to configure the developer credentials in the Xcode project. -After lunching the ARFlow client app, follow the onscreen instruction to input the server address and port (8500 for the previous example) information, then tap **connect** and **start**. +After launching the ARFlow client app, follow the onscreen instruction to input the server address and port (8500 for the previous example) information, then tap **connect** and **start**. Watch our demo video: [![Demo video](https://img.youtube.com/vi/mml8YrCgfTk/maxresdefault.jpg)](https://youtu.be/mml8YrCgfTk) - ## Contribution Please read the [CONTRIBUTING](./CONTRIBUTING.md) guideline first, and refer to the individual [server](./python/README.md) and [client](./unity/README.md) installation guides. -### Contributors +### Contributors
- - YiqinZhao + + legoeruro
- Yiqin Zhao + Khang Luu
- - legoeruro + + YiqinZhao
- Khang Luu + Yiqin Zhao
diff --git a/protos/arflow/service.proto b/protos/arflow_grpc/service.proto similarity index 60% rename from protos/arflow/service.proto rename to protos/arflow_grpc/service.proto index 2d93753..e6bbbd7 100644 --- a/protos/arflow/service.proto +++ b/protos/arflow_grpc/service.proto @@ -2,16 +2,27 @@ syntax = "proto3"; option csharp_namespace = "ARFlow"; -// The ARFlowService service definition. -service ARFlowService { - // Registers a device with the given specifications. - rpc register(RegisterRequest) returns (RegisterResponse); +package arflow.v1; - // Sends a data frame from a device. - rpc data_frame(DataFrameRequest) returns (DataFrameResponse); +// The ARFlow service definition. +service ARFlowService { + // Registers a client with the given specifications. + // + // The client is registered with the server and is assigned a unique identifier. + // The client can then send data frames to the server using the assigned identifier. + rpc RegisterClient(RegisterClientRequest) returns (RegisterClientResponse); + + // Accepts a data frame from a client, returning an acknowledgment. + // + // Errors: + // - NOT_FOUND: If the client configuration is not found. + // - INVALID_ARGUMENT: If the color data type is not recognized or the depth data type + // is not recognized or if the request's data cannot be decoded (e.g., corrupted or invalid data). + rpc ProcessFrame(ProcessFrameRequest) returns (ProcessFrameResponse); } -message RegisterRequest { +message RegisterClientRequest { + // TODO: Add documentation for each field, units of measurement, etc. string device_name = 1; message CameraIntrinsics { @@ -78,11 +89,11 @@ message RegisterRequest { Meshing meshing = 10; } -message RegisterResponse { +message RegisterClientResponse { string uid = 1; } -message DataFrameRequest { +message ProcessFrameRequest { string uid = 1; bytes color = 2; bytes depth = 3; @@ -98,12 +109,13 @@ message DataFrameRequest { float x = 1; float y = 2; } - message Planes { + message Plane { Vector3 center = 1; Vector3 normal = 2; Vector2 size = 3; + repeated Vector2 boundary_points = 4; } - repeated Planes plane_detection = 5; + repeated Plane plane_detection = 5; message Quaternion { float x = 1; @@ -111,18 +123,24 @@ message DataFrameRequest { float z = 3; float w = 4; } - message gyroscope_data { + message GyroscopeData { Quaternion attitude = 1; Vector3 rotation_rate = 2; Vector3 gravity = 3; Vector3 acceleration = 4; } - gyroscope_data gyroscope = 6; + GyroscopeData gyroscope = 6; - bytes audio = 8; - bytes meshing = 9; + repeated float audio_data = 8; + + // Represent a mesh + message Mesh { + bytes data = 1; + } + // Multiple meshes can be sent in a single frame + repeated Mesh meshes = 9; } -message DataFrameResponse { +message ProcessFrameResponse { string message = 1; } diff --git a/protos/scripts/compile.sh b/protos/scripts/compile.sh deleted file mode 100755 index ebe2b64..0000000 --- a/protos/scripts/compile.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -python -m grpc_tools.protoc -Iprotos --python_out=python --pyi_out=python --grpc_python_out=python protos/arflow/*.proto -protoc --csharp_out=unity/Assets/Scripts/ARFlow --grpc_csharp_out=unity/Assets/Scripts/ARFlow protos/arflow/*.proto diff --git a/python/.gitignore b/python/.gitignore index 5bc5b96..62f429b 100644 --- a/python/.gitignore +++ b/python/.gitignore @@ -1,38 +1,5 @@ -# Created by https://www.toptal.com/developers/gitignore/api/macos,windows,visualstudiocode,python -# Edit at https://www.toptal.com/developers/gitignore?templates=macos,windows,visualstudiocode,python - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### macOS Patch ### -# iCloud generated files -*.icloud +# Created by https://www.toptal.com/developers/gitignore/api/python +# Edit at https://www.toptal.com/developers/gitignore?templates=python ### Python ### # Byte-compiled / optimized / DLL files @@ -43,6 +10,8 @@ __pycache__/ # C extensions *.so +.test-draco + # Distribution / packaging .Python build/ @@ -122,7 +91,7 @@ ipython_config.py # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. @@ -136,6 +105,8 @@ ipython_config.py # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +# NOTICE FOR ARFLOW DEVELOPERS: poetry.lock should be kept in version control. See +# https://python-poetry.org/docs/basic-usage/#as-a-library-developer #poetry.lock # pdm @@ -206,57 +177,4 @@ poetry.toml # LSP config files pyrightconfig.json -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -# Built Visual Studio Code Extensions -*.vsix - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history -.ionide - -### Windows ### -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -# End of https://www.toptal.com/developers/gitignore/api/macos,windows,visualstudiocode,python - -test_data/* -docs/* - -# Ignore all gRPC-generated files -# arflow/service_pb2_grpc.py -# arflow/service_pb2.py -# arflow/service_pb2.pyi +# End of https://www.toptal.com/developers/gitignore/api/python diff --git a/python/.pre-commit-config.yaml b/python/.pre-commit-config.yaml index 29fb08d..a5fd91e 100644 --- a/python/.pre-commit-config.yaml +++ b/python/.pre-commit-config.yaml @@ -1,25 +1,19 @@ repos: -- repo: https://github.com/pre-commit/pre-commit-hooks + - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 hooks: - - id: check-added-large-files - - id: check-toml - - id: check-yaml + - id: check-added-large-files + - id: check-json + - id: check-toml + - id: check-yaml args: - - --unsafe - - id: end-of-file-fixer - - id: trailing-whitespace -- repo: https://github.com/astral-sh/ruff-pre-commit - # Ruff version. - rev: v0.6.4 - hooks: - # Run the linter. - - id: ruff - args: [ --fix ] - # Run the formatter. - - id: ruff-format -# TODO: Add pyright. Current issue is pre-commit not picking up the virtual environment. -# - repo: https://github.com/RobertCraigie/pyright-python -# rev: v1.1.379 -# hooks: -# - id: pyright + - --unsafe + - repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.6.4 + hooks: + # Run the linter. + - id: ruff + args: [--fix] + # Run the formatter. + - id: ruff-format diff --git a/python/README.md b/python/README.md index ee24374..27d1222 100644 --- a/python/README.md +++ b/python/README.md @@ -1,8 +1,12 @@ # The ARFlow Python Server -[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) +[![image](https://img.shields.io/pypi/v/arflow.svg)](https://pypi.python.org/pypi/arflow) +[![image](https://img.shields.io/pypi/l/arflow.svg)](https://github.com/cake-lab/ARFlow/blob/main/LICENSE) +[![image](https://img.shields.io/pypi/pyversions/arflow.svg)](https://pypi.python.org/pypi/arflow) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![Checked with pyright](https://microsoft.github.io/pyright/img/pyright_badge.svg)](https://microsoft.github.io/pyright/) +[![CI status](https://github.com/cake-lab/ARFlow/actions/workflows/ci.yml/badge.svg)](https://github.com/cake-lab/ARFlow/actions) +[![Release status](https://github.com/cake-lab/ARFlow/actions/workflows/release.yml/badge.svg)](https://github.com/cake-lab/ARFlow/actions) The ARFlow Python server collects streaming data from your ARFlow clients. The server is designed to be easily extensible and can be integrated with your own research prototype. Data is streamed to the `rerun` logger and saved to a `pickle` file at the end of a session, which can be visualized later using the ARFlow Player. @@ -12,75 +16,28 @@ The ARFlow Python server collects streaming data from your ARFlow clients. The s The ARFlow server can be simply installed via `pip`: -```bash +```shell +# Create a python environment using your favorite tool, then pip install arflow ``` -## Examples - -Next, you may integrate ARFlow with your own research prototype via the Python API: - - - -```python -"""A simple example of extending the ARFlow server.""" - -import arflow - - -class CustomService(arflow.ARFlowService): - def on_frame_received(self, frame: arflow.DataFrameRequest): - """Called when a frame is received.""" - print("Frame received!") - - -def main(): - arflow.create_server(CustomService, port=8500, path_to_save="./") - - -if __name__ == "__main__": - main() - -``` - -Save the above code to a file, e.g., `simple_server.py`, and run it: - -```bash -python3 simple_server.py -``` - -Once you have your server running, you can start your ARFlow clients and connect them to the server. The server will start collecting data from the clients and save it to a `pickle` file at the end of the session. - -You can visualize the data using the ARFlow Player: +## Server CLI -```python -"""A simple example of replaying saved ARFlow data.""" +Here are some example usages of the ARFlow server CLI: -import arflow -from .simple_server import CustomService +```shell +arflow serve # ARFlow port 8500, no save file +arflow serve -p 1234 -s ./ # ARFlow port 1234, save to current working directory -def main(): - """Run the example.""" - player = arflow.ARFlowPlayer( - CustomService, frame_data_path="FRAME_DATA_PATH.pkl" - ) - player.run() - - -if __name__ == "__main__": - main() +arflow replay ./FRAME_DATA_PATH.pkl # replay ARFlow data file +arflow -h # show help ``` -Save the above code to a file, e.g., `simple_replay.py`, replace `FRAME_DATA_PATH` with the path to your saved `pickle` file, and run it: - - -```bash -python3 simple_replay.py -``` +## Examples -For more examples, check out the [examples](https://github.com/cake-lab/ARFlow/tree/main/python/examples) directory. +Check out the [examples](https://github.com/cake-lab/ARFlow/tree/main/python/examples). We recommend starting with the [`simple`](examples/simple/README.md) example. ## Contributing diff --git a/python/arflow/__init__.py b/python/arflow/__init__.py index f930367..6ac0414 100644 --- a/python/arflow/__init__.py +++ b/python/arflow/__init__.py @@ -1,15 +1,19 @@ -""" -.. include:: ../README.md -""" +""".. include:: ../README.md""" # noqa: D415 -from arflow.core import * # noqa -from arflow.replay import * # noqa -from arflow.serve import * # noqa -from arflow.service_pb2 import * # noqa +# Imported symbols are private by default. By aliasing them here, we make it clear that they are part of the public API. +from arflow._core import ARFlowServicer as ARFlowServicer +from arflow._core import run_server as run_server +from arflow._replay import ARFlowPlayer as ARFlowPlayer +from arflow._types import DecodedDataFrame as DecodedDataFrame +from arflow_grpc.service_pb2 import RegisterClientRequest as RegisterClientRequest + +__docformat__ = "google" # Should match Ruff docstring format in ../pyproject.toml # https://pdoc.dev/docs/pdoc.html#exclude-submodules-from-being-documented -__all__ = [ # noqa - "core", # noqa - "replay", # noqa - "serve", # noqa +__all__ = [ + "run_server", + "ARFlowServicer", + "ARFlowPlayer", + "DecodedDataFrame", + "RegisterClientRequest", ] diff --git a/python/arflow/_cli.py b/python/arflow/_cli.py new file mode 100644 index 0000000..a857209 --- /dev/null +++ b/python/arflow/_cli.py @@ -0,0 +1,116 @@ +"""ARFlow command line interface.""" + +import argparse +import logging +from pathlib import Path +from typing import Any, Sequence + +from arflow._core import ARFlowServicer, run_server +from arflow._replay import ARFlowPlayer + + +def _validate_dir_path(path_as_str: str | None) -> str | None: + """Check if the path is a valid directory.""" + if path_as_str is None: + return None + path = Path(path_as_str) + if not path.is_dir(): + raise argparse.ArgumentTypeError(f"{path_as_str} is not a valid path.") + return path_as_str + + +def _validate_file_path(path_as_str: str) -> str: + """Check if the path is a valid file.""" + path = Path(path_as_str) + if not path.is_file(): + raise argparse.ArgumentTypeError(f"{path_as_str} is not a valid file.") + return path_as_str + + +def serve(args: Any): + """Run the ARFlow server.""" + run_server( + ARFlowServicer, + port=args.port, + path_to_save=Path(args.save_path) if args.save_path else None, + ) + + +def replay(args: Any): + """Replay an ARFlow data file.""" + player = ARFlowPlayer(ARFlowServicer, Path(args.file_path)) + player.run() + + +def parse_args( + argv: Sequence[str] | None = None, +) -> tuple[argparse.ArgumentParser, argparse.Namespace]: + parser = argparse.ArgumentParser(description="ARFlow CLI") + subparsers = parser.add_subparsers() + + group = parser.add_mutually_exclusive_group() + group.add_argument( + "-d", + "--debug", + help="Print debug information.", + action="store_const", + dest="loglevel", + const=logging.DEBUG, + default=logging.WARNING, + ) + group.add_argument( + "-v", + "--verbose", + help="Print verbose information.", + action="store_const", + dest="loglevel", + const=logging.INFO, + ) + + # Serve subcommand + serve_parser = subparsers.add_parser("serve", help="Run a simple ARFlow server") + serve_parser.add_argument( + "-p", + "--port", + type=int, + default=8500, + help="Port to run the server on.", + ) + serve_parser.add_argument( + "-s", + "--save_path", + type=_validate_dir_path, + default=None, + help="Path to the directory to save the requests history. If not provided, the requests history will not be saved.", + ) + serve_parser.set_defaults(func=serve) + + # Replay subcommand + replay_parser = subparsers.add_parser("replay", help="Replay an ARFlow data file") + replay_parser.add_argument( + "file_path", + type=_validate_file_path, + help="Path to the ARFlow data file.", + ) + replay_parser.set_defaults(func=replay) + + parsed_args = parser.parse_args(argv) + + logging.basicConfig( + level=parsed_args.loglevel, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)", + ) + + return parser, parsed_args + + +def main(argv: Sequence[str] | None = None): # pragma: no cover + parser, args = parse_args(argv) + if hasattr(args, "func"): + args.func(args) + else: + parser.print_help() + + +if __name__ == "__main__": # pragma: no cover + main() diff --git a/python/arflow/_core.py b/python/arflow/_core.py new file mode 100644 index 0000000..d573582 --- /dev/null +++ b/python/arflow/_core.py @@ -0,0 +1,442 @@ +"""Data exchanging service.""" + +import logging +import pickle +import time +import uuid +from concurrent import futures +from pathlib import Path +from signal import SIGINT, SIGTERM, signal +from typing import Any, List, Type, cast + +import DracoPy +import grpc +import numpy as np +import rerun as rr +from grpc_interceptor.exceptions import InvalidArgument, NotFound + +from arflow._decoding import ( + convert_2d_to_3d_boundary_points, + decode_depth_image, + decode_intrinsic, + decode_point_cloud, + decode_rgb_image, + decode_transform, +) +from arflow._error_interceptor import ErrorInterceptor +from arflow._types import ( + ARFlowRequest, + Audio, + ClientConfigurations, + ColorDataType, + ColorRGB, + DecodedDataFrame, + DepthDataType, + DepthImg, + EnrichedARFlowRequest, + GyroscopeInfo, + HashableClientIdentifier, + Intrinsic, + Mesh, + PlaneBoundaryPoints3D, + PlaneInfo, + PointCloudCLR, + PointCloudPCD, + RequestsHistory, + Transform, +) +from arflow_grpc import service_pb2_grpc +from arflow_grpc.service_pb2 import ( + ProcessFrameRequest, + ProcessFrameResponse, + RegisterClientRequest, + RegisterClientResponse, +) + +logger = logging.getLogger(__name__) + + +class ARFlowServicer(service_pb2_grpc.ARFlowServiceServicer): + """Provides methods that implement the functionality of the ARFlow gRPC server.""" + + def __init__(self) -> None: + """Initialize the ARFlowServicer.""" + self._start_time = time.time_ns() + self._requests_history: RequestsHistory = [] + self._client_configurations: ClientConfigurations = {} + self.recorder = rr + """A recorder object for logging data.""" + super().__init__() + + def _save_request(self, request: ARFlowRequest): + timestamp = (time.time_ns() - self._start_time) / 1e9 + enriched_request = EnrichedARFlowRequest( + timestamp=timestamp, + data=request, + ) + self._requests_history.append(enriched_request) + + def RegisterClient( + self, + request: RegisterClientRequest, + context: grpc.ServicerContext | None = None, + init_uid: str | None = None, + ) -> RegisterClientResponse: + """Register a client. + + @private + """ + self._save_request(request) + + if init_uid is None: + init_uid = str(uuid.uuid4()) + + self._client_configurations[HashableClientIdentifier(init_uid)] = request + + self.recorder.init(f"{request.device_name} - ARFlow", spawn=True) + logger.debug( + "Registered a client with UUID: %s, Request: %s", init_uid, request + ) + + # Call the for user extension code. + self.on_register(request) + + return RegisterClientResponse(uid=init_uid) + + def ProcessFrame( + self, + request: ProcessFrameRequest, + context: grpc.ServicerContext | None = None, + ) -> ProcessFrameResponse: + """Process an incoming frame. + + @private + + Raises: + grpc_interceptor.exceptions.NotFound: If the client configuration is not found. + grpc_interceptor.exceptions.InvalidArgument: If the color data type is not recognized + or the depth data type is not recognized or if the request's data cannot be decoded (e.g., corrupted or invalid data). + """ + self._save_request(request) + + try: + client_config = self._client_configurations[ + HashableClientIdentifier(request.uid) + ] + except KeyError: + raise NotFound("Client configuration not found") + + color_rgb: ColorRGB | None = None + depth_img: DepthImg | None = None + transform: Transform | None = None + k: Intrinsic | None = None + point_cloud_pcd: PointCloudPCD | None = None + point_cloud_clr: PointCloudCLR | None = None + audio_data: Audio | None = None + + if client_config.camera_color.enabled: + try: + color_rgb = np.flipud( + decode_rgb_image( + client_config.camera_intrinsics.resolution_y, + client_config.camera_intrinsics.resolution_x, + client_config.camera_color.resize_factor_y, + client_config.camera_color.resize_factor_x, + cast(ColorDataType, client_config.camera_color.data_type), + request.color, + ) + ) + except ValueError as e: + raise InvalidArgument(str(e)) + + self.recorder.log("rgb", rr.Image(color_rgb)) + + if client_config.camera_depth.enabled: + try: + depth_img = np.flipud( + decode_depth_image( + client_config.camera_depth.resolution_y, + client_config.camera_depth.resolution_x, + cast(DepthDataType, client_config.camera_depth.data_type), + request.depth, + ) + ) + except ValueError as e: + raise InvalidArgument(str(e)) + self.recorder.log("depth", rr.DepthImage(depth_img, meter=1.0)) + + if client_config.camera_transform.enabled: + self.recorder.log("world/origin", rr.ViewCoordinates.RIGHT_HAND_Y_DOWN) + # self.logger.log( + # "world/xyz", + # rr.Arrows3D( + # vectors=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], + # colors=[[255, 0, 0], [0, 255, 0], [0, 0, 255]], + # ), + # ) + + try: + transform = decode_transform(request.transform) + except ValueError as e: + raise InvalidArgument(str(e)) + self.recorder.log( + "world/camera", + self.recorder.Transform3D( + mat3x3=transform[:3, :3], translation=transform[:3, 3] + ), + ) + + # Won't thow any potential exceptions for now. + k = decode_intrinsic( + client_config.camera_color.resize_factor_y, + client_config.camera_color.resize_factor_x, + client_config.camera_intrinsics.focal_length_y, + client_config.camera_intrinsics.focal_length_x, + client_config.camera_intrinsics.principal_point_y, + client_config.camera_intrinsics.principal_point_x, + ) + + self.recorder.log("world/camera", rr.Pinhole(image_from_camera=k)) + if color_rgb is not None: + self.recorder.log("world/camera", rr.Image(np.flipud(color_rgb))) + + if client_config.camera_point_cloud.enabled: + if ( + k is not None + and color_rgb is not None + and depth_img is not None + and transform is not None + ): + # Won't thow any potential exceptions for now. + point_cloud_pcd, point_cloud_clr = decode_point_cloud( + client_config.camera_intrinsics.resolution_y, + client_config.camera_intrinsics.resolution_x, + client_config.camera_color.resize_factor_y, + client_config.camera_color.resize_factor_x, + k, + color_rgb, + depth_img, + transform, + ) + self.recorder.log( + "world/point_cloud", + rr.Points3D(point_cloud_pcd, colors=point_cloud_clr), + ) + + if client_config.camera_plane_detection.enabled: + strips: List[PlaneBoundaryPoints3D] = [] + for plane in request.plane_detection: + boundary_points_2d: List[List[float]] = list( + map(lambda pt: [pt.x, pt.y], plane.boundary_points) + ) + + plane = PlaneInfo( + center=np.array([plane.center.x, plane.center.y, plane.center.z]), + normal=np.array([plane.normal.x, plane.normal.y, plane.normal.z]), + size=np.array([plane.size.x, plane.size.y]), + boundary_points=np.array(boundary_points_2d), + ) + + try: + boundary_3d = convert_2d_to_3d_boundary_points( + plane.boundary_points, plane.normal, plane.center + ) + except ValueError as e: + raise InvalidArgument(str(e)) + + # Close the boundary by adding the first point to the end. + boundary_3d = np.vstack([boundary_3d, boundary_3d[0]]) + strips.append(boundary_3d) + self.recorder.log( + f"world/detected-planes", + rr.LineStrips3D( + strips=strips, + colors=[[255, 0, 0]], + radii=rr.Radius.ui_points(5.0), + ), + ) + + if client_config.gyroscope.enabled: + gyro_data_proto = request.gyroscope + gyro_data = GyroscopeInfo( + attitude=np.array( + [ + gyro_data_proto.attitude.x, + gyro_data_proto.attitude.y, + gyro_data_proto.attitude.z, + gyro_data_proto.attitude.w, + ] + ), + rotation_rate=np.array( + [ + gyro_data_proto.rotation_rate.x, + gyro_data_proto.rotation_rate.y, + gyro_data_proto.rotation_rate.z, + ] + ), + gravity=np.array( + [ + gyro_data_proto.gravity.x, + gyro_data_proto.gravity.y, + gyro_data_proto.gravity.z, + ] + ), + acceleration=np.array( + [ + gyro_data_proto.acceleration.x, + gyro_data_proto.acceleration.y, + gyro_data_proto.acceleration.z, + ] + ), + ) + attitude = rr.Quaternion( + xyzw=gyro_data.attitude, + ) + rotation_rate = rr.datatypes.Vec3D(gyro_data.rotation_rate) + gravity = rr.datatypes.Vec3D(gyro_data.gravity) + acceleration = rr.datatypes.Vec3D(gyro_data.acceleration) + # Attitute is displayed as a box, and the other acceleration variables are displayed as arrows. + rr.log( + "rotations/gyroscope/attitude", + rr.Boxes3D(half_sizes=[0.5, 0.5, 0.5], quaternions=[attitude]), + ) + rr.log( + "rotations/gyroscope/rotation_rate", + rr.Arrows3D(vectors=[rotation_rate], colors=[[0, 255, 0]]), + ) + rr.log( + "rotations/gyroscope/gravity", + rr.Arrows3D(vectors=[gravity], colors=[[0, 0, 255]]), + ) + rr.log( + "rotations/gyroscope/acceleration", + rr.Arrows3D(vectors=[acceleration], colors=[[255, 255, 0]]), + ) + + if client_config.audio.enabled: + audio_data = np.array(request.audio_data) + for i in audio_data: + self.recorder.log("world/audio", rr.Scalar(i)) + + if client_config.meshing.enabled: + logger.debug("Number of meshes: %s", len(request.meshes)) + # Binary arrays can be empty if no mesh is sent. This could be due to non-supporting devices. We can log this in the future. + binary_arrays = request.meshes + for index, mesh_data in enumerate(binary_arrays): + # We are ignoring type because DracoPy is written with Cython, and Pyright cannot infer types from a native module. + dracoMesh = DracoPy.decode(mesh_data.data) # pyright: ignore [reportUnknownMemberType, reportUnknownVariableType] + + mesh = Mesh( + faces=dracoMesh.faces, # pyright: ignore [reportUnknownMemberType, reportUnknownArgumentType] + points=dracoMesh.points, # pyright: ignore [reportUnknownMemberType, reportUnknownArgumentType] + normals=dracoMesh.normals, # pyright: ignore [reportUnknownMemberType, reportUnknownArgumentType] + tex_coord=dracoMesh.tex_coord, # pyright: ignore [reportUnknownMemberType, reportUnknownArgumentType] + colors=dracoMesh.colors, # pyright: ignore [reportUnknownMemberType, reportUnknownArgumentType] + ) + + rr.log( + f"world/mesh/mesh-{index}", + rr.Mesh3D( + vertex_positions=mesh.points, + triangle_indices=mesh.faces, + vertex_normals=mesh.normals, + vertex_colors=mesh.colors, + vertex_texcoords=mesh.tex_coord, + ), + ) + + # Call the for user extension code. + self.on_frame_received( + DecodedDataFrame( + color_rgb=color_rgb, + depth_img=depth_img, + transform=transform, + intrinsic=k, + point_cloud_pcd=point_cloud_pcd, + point_cloud_clr=point_cloud_clr, + ) + ) + + return ProcessFrameResponse(message="OK") + + def on_register(self, request: RegisterClientRequest) -> None: + """Called when a new device is registered. Override this method to process the data.""" + pass + + def on_frame_received(self, decoded_data_frame: DecodedDataFrame) -> None: + """Called when a frame is received. Override this method to process the data.""" + pass # pragma: no cover + + def on_program_exit(self, path_to_save: Path) -> None: + """Save the data and exit. + + @private + """ + logger.debug("Saving the data...") + # Ensure the directory exists. + path_to_save.mkdir(parents=True, exist_ok=True) + save_path = ( + path_to_save + / f"frames_{time.strftime('%Y_%m_%d_%H_%M_%S', time.gmtime())}.pkl" + ) + with save_path.open("wb") as f: + pickle.dump(self._requests_history, f) + + logger.info("Data saved to %s", save_path) + + +# TODO: Integration tests once more infrastructure work has been done (e.g., Docker). Remove pragma once implemented. +def run_server( # pragma: no cover + service: Type[ARFlowServicer], port: int = 8500, path_to_save: Path | None = None +) -> None: + """Run gRPC server. + + Args: + service: The service class to use. Custom servers should subclass `arflow.ARFlowServicer`. + port: The port to listen on. + path_to_save: The path to save data to. + """ + servicer = service() + interceptors = [ErrorInterceptor()] # pyright: ignore [reportUnknownVariableType] + server = grpc.server( # pyright: ignore [reportUnknownMemberType] + futures.ThreadPoolExecutor(max_workers=10), + interceptors=interceptors, # pyright: ignore [reportArgumentType] + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + service_pb2_grpc.add_ARFlowServiceServicer_to_server(servicer, server) # pyright: ignore [reportUnknownMemberType] + server.add_insecure_port("[::]:%s" % port) + server.start() + logger.info("Server started, listening on %s", port) + + def handle_shutdown(*_: Any) -> None: + """Shutdown gracefully. + + This function handles graceful shutdown of the server. It is triggered by termination signals, + which are typically sent by Kubernetes or other orchestration tools to stop the service. + + - When running locally, pressing will raise a `KeyboardInterrupt`, which can be caught to call this function. + - In a Kubernetes environment, a SIGTERM signal is sent to the service, followed by a SIGKILL if the service does not stop within 30 seconds. + + Steps: + 1. Catch the SIGTERM signal. + 2. Call `server.stop(30)` to refuse new requests and wait up to 30 seconds for ongoing requests to complete. + 3. Wait on the `threading.Event` object returned by `server.stop(30)` to ensure Python does not exit prematurely. + 4. Optionally, perform cleanup procedures and save any necessary data before shutting down completely. + """ + logger.debug("Shutting down gracefully") + all_rpcs_done_event = server.stop(30) + all_rpcs_done_event.wait(30) + + if path_to_save is not None: + servicer.on_program_exit(path_to_save) + + # TODO: Discuss hook for user-defined cleanup procedures. + + logger.debug("Server shut down gracefully") + + signal(SIGTERM, handle_shutdown) + signal(SIGINT, handle_shutdown) + server.wait_for_termination() diff --git a/python/arflow/_decoding.py b/python/arflow/_decoding.py new file mode 100644 index 0000000..2ac33ec --- /dev/null +++ b/python/arflow/_decoding.py @@ -0,0 +1,227 @@ +import numpy as np + +from arflow._types import ( + ColorDataType, + ColorRGB, + DepthDataType, + DepthImg, + Intrinsic, + PlaneBoundaryPoints2D, + PlaneBoundaryPoints3D, + PlaneCenter, + PlaneNormal, + PointCloudCLR, + PointCloudPCD, + Transform, +) + + +def decode_rgb_image( + resolution_y: int, + resolution_x: int, + resize_factor_y: float, + resize_factor_x: float, + data_type: ColorDataType, + buffer: bytes, +) -> ColorRGB: + """Decode the color image from the buffer. + + Raises: + ValueError: If the data type is not recognized + """ + # Calculate the size of the image. + color_img_w = int(resolution_x * resize_factor_x) + color_img_h = int(resolution_y * resize_factor_y) + p = color_img_w * color_img_h + color_img = np.frombuffer(buffer, dtype=np.uint8) + + # Decode RGB bytes into RGB. + if data_type == "RGB24": + color_rgb = color_img.reshape((color_img_h, color_img_w, 3)) + + # Decode YCbCr bytes into RGB. + elif data_type == "YCbCr420": + y = color_img[:p].reshape((color_img_h, color_img_w)) + cbcr = color_img[p:].reshape((color_img_h // 2, color_img_w // 2, 2)) + cb, cr = cbcr[:, :, 0], cbcr[:, :, 1] + + # Very important! Convert to float32 first! + cb = np.repeat(cb, 2, axis=0).repeat(2, axis=1).astype(np.float32) - 128 + cr = np.repeat(cr, 2, axis=0).repeat(2, axis=1).astype(np.float32) - 128 + + r = np.clip(y + 1.403 * cr, 0, 255) + g = np.clip(y - 0.344 * cb - 0.714 * cr, 0, 255) + b = np.clip(y + 1.772 * cb, 0, 255) + + color_rgb = np.stack([r, g, b], axis=-1) + + else: + raise ValueError(f"Unknown data type: {data_type}") + + return color_rgb.astype(np.uint8) + + +def decode_depth_image( + resolution_y: int, + resolution_x: int, + data_type: DepthDataType, + buffer: bytes, +) -> DepthImg: + """Decode the depth image from the buffer. + + Args: + data_type: `f32` for iOS, `u16` for Android. + + Raises: + ValueError: If the data type is not recognized. + """ + # The `Any` means that the array can have any shape. We cannot + # determine the shape of the array from the buffer. + if data_type == "f32": + dtype = np.float32 + elif data_type == "u16": + dtype = np.uint16 + else: + raise ValueError(f"Unknown data type: {data_type}") + + depth_img = np.frombuffer(buffer, dtype=dtype).reshape( + ( + resolution_y, + resolution_x, + ) + ) + + # If it's a 16-bit unsigned integer, convert to float32 and scale to meters. + if dtype == np.uint16: + depth_img = (depth_img.astype(np.float32) / 1000.0).astype(np.float32) + + return depth_img.astype(np.float32) + + +def decode_transform(buffer: bytes) -> Transform: + y_down_to_y_up = np.array( + [ + [1.0, -0.0, 0.0, 0], + [0.0, -1.0, 0.0, 0], + [0.0, 0.0, 1.0, 0], + [0.0, 0.0, 0, 1.0], + ], + dtype=np.float32, + ) + + t = np.frombuffer(buffer, dtype=np.float32) + transform = np.eye(4, dtype=np.float32) + transform[:3, :] = t.reshape((3, 4)) + transform[:3, 3] = 0 + transform = y_down_to_y_up @ transform + + return transform.astype(np.float32) + + +def decode_intrinsic( + resize_factor_y: float, + resize_factor_x: float, + focal_length_y: float, + focal_length_x: float, + principal_point_y: float, + principal_point_x: float, +) -> Intrinsic: + sx = resize_factor_x + sy = resize_factor_y + + fx, fy = ( + focal_length_x * sx, + focal_length_y * sy, + ) + cx, cy = ( + principal_point_x * sx, + principal_point_y * sy, + ) + + k = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]], dtype=np.float32) + + return k + + +def decode_point_cloud( + resolution_y: int, + resolution_x: int, + resize_factor_y: float, + resize_factor_x: float, + k: Intrinsic, + color_rgb: ColorRGB, + depth_img: DepthImg, + transform: Transform, +) -> tuple[PointCloudPCD, PointCloudCLR]: + # Flip image is needed for point cloud generation. + color_rgb = np.flipud(color_rgb) + depth_img = np.flipud(depth_img) + + color_img_w = int(resolution_x * resize_factor_x) + color_img_h = int(resolution_y * resize_factor_y) + + u, v = np.meshgrid(np.arange(color_img_w), np.arange(color_img_h)) + + fx: np.float32 = k[0, 0] + fy: np.float32 = k[1, 1] + cx: np.float32 = k[0, 2] + cy: np.float32 = k[1, 2] + + z = depth_img.copy() + x = ((u - cx) * z) / fx + y = ((v - cy) * z) / fy + pre_pcd = np.stack([x, y, z], axis=-1).reshape(-1, 3) + pcd = np.matmul(transform[:3, :3], pre_pcd.T).T + transform[:3, 3] + clr = color_rgb.reshape(-1, 3) + + return pcd.astype(np.float32), clr + + +def convert_2d_to_3d_boundary_points( + boundary_points_2d: PlaneBoundaryPoints2D, normal: PlaneNormal, center: PlaneCenter +) -> PlaneBoundaryPoints3D: + # Check boundary points validity + if boundary_points_2d.shape[1] != 2: + raise ValueError("Boundary points should be in 2D") + if boundary_points_2d.shape[0] < 3: + raise ValueError("At least 3 boundary points are required") + + # Check normal validity + if len(normal.shape) != 1: + raise ValueError("There should only be 1 normal") + if normal.shape[0] != 3: + raise ValueError("Normal should be in 3D") + if np.linalg.norm(normal) == 0: + raise ValueError("Normal should be non-zero") + + # Check center validity + if len(center.shape) != 1: + raise ValueError("There should only be 1 center") + if center.shape[0] != 3: + raise ValueError("Center should be in 3D") + + # Ensure the normal is normalized + normal = normal / np.linalg.norm(normal) + + # Generate two orthogonal vectors (u and v) that lie on the plane + # Find a vector that is not parallel to the normal + arbitrary_vector = ( + np.array([1, 0, 0]) + if not np.allclose(normal, [1, 0, 0]) + else np.array([0, 1, 0]) + ) + + # Create u vector, which is perpendicular to the normal + u = np.cross(normal, arbitrary_vector) + u = u / np.linalg.norm(u) + + # Create v vector, which is perpendicular to both the normal and u + v = np.cross(normal, u) + + # Convert the 2D points into 3D + # Each 2D point can be written as a linear combination of u and v, plus the center + boundary_points_3d = np.array( + [center + point_2d[0] * u + point_2d[1] * v for point_2d in boundary_points_2d] + ) + + return np.array(boundary_points_3d, dtype=np.float32) diff --git a/python/arflow/_error_interceptor.py b/python/arflow/_error_interceptor.py new file mode 100644 index 0000000..0708fa9 --- /dev/null +++ b/python/arflow/_error_interceptor.py @@ -0,0 +1,22 @@ +import logging +from typing import Any, NoReturn + +import grpc +from grpc_interceptor import ExceptionToStatusInterceptor + +logger = logging.getLogger(__name__) + + +class ErrorInterceptor(ExceptionToStatusInterceptor): + def handle_exception( + self, + ex: Exception, + request_or_iterator: Any, + context: grpc.ServicerContext, + method_name: str, + ) -> NoReturn: + self.log_error(ex) + super().handle_exception(ex, request_or_iterator, context, method_name) + + def log_error(self, e: Exception) -> None: + logger.exception(e) diff --git a/python/arflow/_replay.py b/python/arflow/_replay.py new file mode 100644 index 0000000..f99c88e --- /dev/null +++ b/python/arflow/_replay.py @@ -0,0 +1,97 @@ +"""A library for replaying ARFlow data.""" + +import logging +import pickle +import threading +import time +from pathlib import Path +from typing import Type + +from arflow._core import ARFlowServicer +from arflow._types import EnrichedARFlowRequest, RequestsHistory +from arflow_grpc.service_pb2 import ProcessFrameRequest, RegisterClientRequest + +logger = logging.getLogger(__name__) + + +class ARFlowPlayer(threading.Thread): + """A class for replaying ARFlow data.""" + + def __init__(self, service: Type[ARFlowServicer], frame_data_path: Path) -> None: + """Initialize the ARFlowPlayer.""" + super().__init__() + self._service = service() + self._requests_history: RequestsHistory = [] + + with frame_data_path.open("rb") as f: + raw_data: RequestsHistory = pickle.load(f) + + if not raw_data: + raise ValueError("No data to replay.") + if not isinstance(raw_data[0].data, RegisterClientRequest): + raise ValueError("The first request should be a RegisterClientRequest.") + if not isinstance(raw_data[1].data, ProcessFrameRequest): + raise ValueError("The second request should be a ProcessFrameRequest.") + + start_delta = 0 + for i, data in enumerate(raw_data): + if i == 0: + start_delta = data.timestamp - 3 + self._requests_history.append( + EnrichedARFlowRequest( + timestamp=data.timestamp - start_delta, + data=data.data, + ) + ) + else: + self._requests_history.append( + EnrichedARFlowRequest( + timestamp=data.timestamp - start_delta, + data=data.data, + ) + ) + + sent_dataframe = self._requests_history[1].data + if not isinstance(sent_dataframe, ProcessFrameRequest): + raise ValueError("The second request should be a ProcessFrameRequest.") + else: + self._uid = sent_dataframe.uid + + self._period = 0.001 # Simulate a 1ms loop. + self._n_frame = 0 + + self._i = 0 + self._t0 = time.time() + self.start() + + def _sleep(self): + self._i += 1 + delta = self._t0 + self._period * self._i - time.time() + if delta > 0: + time.sleep(delta) + + def run(self) -> None: + """Run the replay.""" + while True: + current_time = time.time() - self._t0 + + t = self._requests_history[self._n_frame].timestamp + + if t - current_time < 0.001: + data = self._requests_history[self._n_frame].data + if self._n_frame == 0 and isinstance(data, RegisterClientRequest): + self._service.RegisterClient(data, None, init_uid=self._uid) + elif isinstance(data, ProcessFrameRequest): + self._service.ProcessFrame(data, None) + else: + raise ValueError("Unknown request data type.") + + self._n_frame += 1 + + if self._n_frame > len(self._requests_history) - 1: + break + + self._sleep() + + logger.debug("Reply finished.") + exit() diff --git a/python/arflow/_types.py b/python/arflow/_types.py new file mode 100644 index 0000000..42bacc5 --- /dev/null +++ b/python/arflow/_types.py @@ -0,0 +1,122 @@ +"""Type definitions for ARFlow.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Dict, List, Literal, NewType + +import numpy as np +import numpy.typing as npt + +from arflow_grpc.service_pb2 import ProcessFrameRequest, RegisterClientRequest + +ARFlowRequest = ProcessFrameRequest | RegisterClientRequest + + +@dataclass +class EnrichedARFlowRequest: + """An enriched ARFlow request.""" + + timestamp: float + """The timestamp of the request.""" + data: ARFlowRequest + """The ARFlow request data.""" + + +ColorDataType = Literal["RGB24", "YCbCr420"] +DepthDataType = Literal["f32", "u16"] +"""The depth data type. `f32` for iOS, `u16` for Android.""" + +ColorRGB = npt.NDArray[np.uint8] +DepthImg = npt.NDArray[np.float32] +Transform = npt.NDArray[np.float32] +Intrinsic = npt.NDArray[np.float32] +PointCloudPCD = npt.NDArray[np.float32] +PointCloudCLR = npt.NDArray[np.uint8] + +Attitude = npt.NDArray[np.float32] +RotationRate = npt.NDArray[np.float32] +Gravity = npt.NDArray[np.float32] +Acceleration = npt.NDArray[np.float32] + +Audio = npt.NDArray[np.float32] + +MeshFaces = npt.NDArray[np.uint32] +MeshPoints = npt.NDArray[np.float32] +MeshNormals = npt.NDArray[np.float32] +MeshTexCoord = npt.NDArray[np.float32] +MeshColors = npt.NDArray[np.float32] + +PlaneCenter = npt.NDArray[np.float32] +PlaneNormal = npt.NDArray[np.float32] +PlaneSize = npt.NDArray[np.float32] +PlaneBoundaryPoints2D = npt.NDArray[np.float32] +PlaneBoundaryPoints3D = npt.NDArray[np.float32] + + +@dataclass +class PlaneInfo: + """Information about a plane.""" + + center: PlaneCenter + """The center of the plane. In world space (3D).""" + normal: PlaneNormal + """The normal of the plane. In world space (3D).""" + size: PlaneSize + """Width and Height of the plane. In meters (2D)""" + boundary_points: PlaneBoundaryPoints2D + """The boundary points of the plane. In plane space (2D).""" + + +@dataclass +class GyroscopeInfo: + """Information about a gyroscope.""" + + attitude: Attitude + """The attitude of the gyroscope.""" + rotation_rate: RotationRate + """The rotation rate of the gyroscope.""" + gravity: Gravity + """The gravity of the gyroscope.""" + acceleration: Acceleration + """The acceleration of the gyroscope.""" + + +@dataclass +class Mesh: + """A mesh object. Draco's meshes have additional methods and properties, but with limited documentation and usage on them, I will not include them here.""" + + faces: MeshFaces + """The mesh faces. Each face is an array of [3] indices.""" + points: MeshPoints + """The mesh points. Each point is an array of [3] coordinates.""" + normals: MeshNormals | None = None + """The mesh normals. Each normal is an array of [3] coordinates. If the mesh does not have normals, this field is `None`.""" + tex_coord: MeshTexCoord | None = None + """The mesh texture coordinates. Each texture coordinate is an array of [2] coordinates. If the mesh does not have texture coordinates, this field is `None`.""" + colors: MeshColors | None = None + """The mesh colors. If the mesh does not have colors, this field is `None`.""" + + +@dataclass +class DecodedDataFrame: + """A decoded data frame.""" + + color_rgb: ColorRGB | None = None + """The color image in RGB format.""" + depth_img: DepthImg | None = None + """The depth image.""" + transform: Transform | None = None + """The transformation matrix of the camera.""" + intrinsic: Intrinsic | None = None + """The intrinsic matrix of the camera.""" + point_cloud_pcd: PointCloudPCD | None = None + """The point cloud in PCD format.""" + point_cloud_clr: PointCloudCLR | None = None + """The point cloud colors in RGB format.""" + + +RequestsHistory = List[EnrichedARFlowRequest] +HashableClientIdentifier = NewType("HashableClientIdentifier", str) +"""This should match a hashable field in the `RegisterClientRequest` message.""" +ClientConfigurations = Dict[HashableClientIdentifier, RegisterClientRequest] diff --git a/python/arflow/core.py b/python/arflow/core.py deleted file mode 100644 index 21425d3..0000000 --- a/python/arflow/core.py +++ /dev/null @@ -1,331 +0,0 @@ -"""Data exchanging service.""" - -import os -import pickle -import time -import uuid -from time import gmtime, strftime -from typing import Dict, List - -import numpy as np -import rerun as rr - -from arflow import service_pb2, service_pb2_grpc - -sessions: Dict[str, service_pb2.RegisterRequest] = {} -"""@private""" - - -class ARFlowService(service_pb2_grpc.ARFlowService): - """ARFlow gRPC service.""" - - _start_time = time.time_ns() - _frame_data: List[Dict[str, float | bytes]] = [] - - def __init__(self, use_visualizer: bool = True) -> None: - self.recorder = rr - self.use_visualizer = use_visualizer - super().__init__() - - def _save_frame_data( - self, request: service_pb2.DataFrameRequest | service_pb2.RegisterRequest - ): - """@private""" - time_stamp = (time.time_ns() - self._start_time) / 1e9 - self._frame_data.append( - {"time_stamp": time_stamp, "data": request.SerializeToString()} - ) - - def register( - self, request: service_pb2.RegisterRequest, context, uid: str | None = None - ) -> service_pb2.RegisterResponse: - """Register a client.""" - - self._save_frame_data(request) - - # Start processing. - if uid is None: - uid = str(uuid.uuid4()) - - sessions[uid] = request - - self.recorder.init( - f"{request.device_name} - ARFlow", - spawn=self.use_visualizer, - default_enabled=self.use_visualizer, - ) - print("Registered a client with UUID: %s" % uid, request) - - # Call the for user extension code. - self.on_register(request) - - return service_pb2.RegisterResponse(uid=uid) - - def data_frame( - self, - request: service_pb2.DataFrameRequest, - context, - ) -> service_pb2.DataFrameResponse: - """Process an incoming frame.""" - - self._save_frame_data(request) - - # Start processing. - decoded_data = {} - session_configs = sessions[request.uid] - - if session_configs.camera_color.enabled: - color_rgb = ARFlowService.decode_rgb_image(session_configs, request.color) - decoded_data["image/color_rgb"] = color_rgb - color_rgb = np.flipud(color_rgb) - self.recorder.log("rgb", rr.Image(color_rgb)) - - if session_configs.camera_depth.enabled: - depth_img = ARFlowService.decode_depth_image(session_configs, request.depth) - decoded_data["image/depth_img"] = depth_img - depth_img = np.flipud(depth_img) - self.recorder.log("depth", rr.DepthImage(depth_img, meter=1.0)) - - if session_configs.camera_transform.enabled: - self.recorder.log("world/origin", rr.ViewCoordinates.RIGHT_HAND_Y_DOWN) - # self.logger.log( - # "world/xyz", - # rr.Arrows3D( - # vectors=[[1, 0, 0], [0, 1, 0], [0, 0, 1]], - # colors=[[255, 0, 0], [0, 255, 0], [0, 0, 255]], - # ), - # ) - - transform = ARFlowService.decode_transform(request.transform) - decoded_data["transform"] = transform - self.recorder.log( - "world/camera", - self.recorder.Transform3D( - mat3x3=transform[:3, :3], translation=transform[:3, 3] - ), - ) - - k = ARFlowService.decode_intrinsic(session_configs) - self.recorder.log("world/camera", rr.Pinhole(image_from_camera=k)) - self.recorder.log("world/camera", rr.Image(np.flipud(color_rgb))) - - if session_configs.camera_point_cloud.enabled: - pcd, clr = ARFlowService.decode_point_cloud( - session_configs, k, color_rgb, depth_img, transform - ) - decoded_data["point_cloud_pcd"] = pcd - decoded_data["point_cloud_clr"] = clr - self.recorder.log("world/point_cloud", rr.Points3D(pcd, colors=clr)) - - if session_configs.camera_plane_detection.enabled: - pass - - if session_configs.gyroscope.enabled: - gyro_data = request.gyroscope - - attitude = rr.Quaternion( - xyzw=[ - gyro_data.attitude.x, - gyro_data.attitude.y, - gyro_data.attitude.z, - gyro_data.attitude.w, - ] - ) - rotation_rate = rr.datatypes.Vec3D( - [ - gyro_data.rotation_rate.x, - gyro_data.rotation_rate.y, - gyro_data.rotation_rate.z, - ] - ) - gravity = rr.datatypes.Vec3D( - [gyro_data.gravity.x, gyro_data.gravity.y, gyro_data.gravity.z] - ) - acceleration = rr.datatypes.Vec3D( - [ - gyro_data.acceleration.x, - gyro_data.acceleration.y, - gyro_data.acceleration.z, - ] - ) - - # Attitute is displayed as a box, and the other acceleration variables are displayed as arrows. - rr.log( - "rotations/gyroscope/attitude", - rr.Boxes3D(half_sizes=[0.5, 0.5, 0.5], quaternions=[attitude]), - ) - rr.log( - "rotations/gyroscope/rotation_rate", - rr.Arrows3D(vectors=[rotation_rate], colors=[[0, 255, 0]]), - ) - rr.log( - "rotations/gyroscope/gravity", - rr.Arrows3D(vectors=[gravity], colors=[[0, 0, 255]]), - ) - rr.log( - "rotations/gyroscope/acceleration", - rr.Arrows3D(vectors=[acceleration], colors=[[255, 255, 0]]), - ) - - # Call the for user extension code. - self.on_frame_received(decoded_data) - - return service_pb2.DataFrameResponse(message="OK") - - def on_register(self, request: service_pb2.RegisterRequest): - """Called when a new device is registered. Override this method to process the data.""" - pass - - def on_frame_received(self, frame_data: service_pb2.DataFrameRequest): - """Called when a frame is received. Override this method to process the data.""" - pass - - def on_program_exit(self, path_to_save: str | None): - """Save the data and exit.""" - if path_to_save is None: - return - print("Saving the data...") - f_name = strftime("%Y_%m_%d_%H_%M_%S", gmtime()) - save_path = os.path.join(path_to_save, f"frames_{f_name}.pkl") - with open(save_path, "wb") as f: - pickle.dump(self._frame_data, f) - - print(f"Data saved to {save_path}") - - @staticmethod - def decode_rgb_image( - session_configs: service_pb2.RegisterRequest, buffer: bytes - ) -> np.ndarray: - # Calculate the size of the image. - color_img_w = int( - session_configs.camera_intrinsics.resolution_x - * session_configs.camera_color.resize_factor_x - ) - color_img_h = int( - session_configs.camera_intrinsics.resolution_y - * session_configs.camera_color.resize_factor_y - ) - p = color_img_w * color_img_h - color_img = np.frombuffer(buffer, dtype=np.uint8) - - # Decode RGB bytes into RGB. - if session_configs.camera_color.data_type == "RGB24": - color_rgb = color_img.reshape((color_img_h, color_img_w, 3)) - color_rgb = color_rgb.astype(np.uint8) - - # Decode YCbCr bytes into RGB. - elif session_configs.camera_color.data_type == "YCbCr420": - y = color_img[:p].reshape((color_img_h, color_img_w)) - cbcr = color_img[p:].reshape((color_img_h // 2, color_img_w // 2, 2)) - cb, cr = cbcr[:, :, 0], cbcr[:, :, 1] - - # Very important! Convert to float32 first! - cb = np.repeat(cb, 2, axis=0).repeat(2, axis=1).astype(np.float32) - 128 - cr = np.repeat(cr, 2, axis=0).repeat(2, axis=1).astype(np.float32) - 128 - - r = np.clip(y + 1.403 * cr, 0, 255) - g = np.clip(y - 0.344 * cb - 0.714 * cr, 0, 255) - b = np.clip(y + 1.772 * cb, 0, 255) - - color_rgb = np.stack([r, g, b], axis=-1) - color_rgb = color_rgb.astype(np.uint8) - - return color_rgb - - @staticmethod - def decode_depth_image( - session_configs: service_pb2.RegisterRequest, buffer: bytes - ) -> np.ndarray: - if session_configs.camera_depth.data_type == "f32": - dtype = np.float32 - elif session_configs.camera_depth.data_type == "u16": - dtype = np.uint16 - else: - raise ValueError( - f"Unknown depth data type: {session_configs.camera_depth.data_type}" - ) - - depth_img = np.frombuffer(buffer, dtype=dtype) - depth_img = depth_img.reshape( - ( - session_configs.camera_depth.resolution_y, - session_configs.camera_depth.resolution_x, - ) - ) - - # 16-bit unsigned integer, describing the depth (distance to an object) in millimeters. - if dtype == np.uint16: - depth_img = depth_img.astype(np.float32) / 1000.0 - - return depth_img - - @staticmethod - def decode_transform(buffer: bytes): - y_down_to_y_up = np.array( - [ - [1.0, -0.0, 0.0, 0], - [0.0, -1.0, 0.0, 0], - [0.0, 0.0, 1.0, 0], - [0.0, 0.0, 0, 1.0], - ], - dtype=np.float32, - ) - - t = np.frombuffer(buffer, dtype=np.float32) - transform = np.eye(4) - transform[:3, :] = t.reshape((3, 4)) - transform[:3, 3] = 0 - transform = y_down_to_y_up @ transform - - return transform - - @staticmethod - def decode_intrinsic(session_configs: service_pb2.RegisterRequest): - sx = session_configs.camera_color.resize_factor_x - sy = session_configs.camera_color.resize_factor_y - - fx, fy = ( - session_configs.camera_intrinsics.focal_length_x * sx, - session_configs.camera_intrinsics.focal_length_y * sy, - ) - cx, cy = ( - session_configs.camera_intrinsics.principal_point_x * sx, - session_configs.camera_intrinsics.principal_point_y * sy, - ) - - k = np.array([[fx, 0, cx], [0, fy, cy], [0, 0, 1]]) - - return k - - @staticmethod - def decode_point_cloud( - session_configs: service_pb2.RegisterRequest, - k: np.ndarray, - color_rgb: np.ndarray, - depth_img: np.ndarray, - transform: np.ndarray, - ) -> np.ndarray: - # Flip image is needed for point cloud generation. - color_rgb = np.flipud(color_rgb) - depth_img = np.flipud(depth_img) - - color_img_w = int( - session_configs.camera_intrinsics.resolution_x - * session_configs.camera_color.resize_factor_x - ) - color_img_h = int( - session_configs.camera_intrinsics.resolution_y - * session_configs.camera_color.resize_factor_y - ) - u, v = np.meshgrid(np.arange(color_img_w), np.arange(color_img_h)) - fx, fy = k[0, 0], k[1, 1] - cx, cy = k[0, 2], k[1, 2] - - z = depth_img.copy() - x = ((u - cx) * z) / fx - y = ((v - cy) * z) / fy - pcd = np.stack([x, y, z], axis=-1).reshape(-1, 3) - pcd = np.matmul(transform[:3, :3], pcd.T).T + transform[:3, 3] - clr = color_rgb.reshape(-1, 3) - - return pcd, clr diff --git a/python/arflow/py.typed b/python/arflow/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/python/arflow/replay.py b/python/arflow/replay.py deleted file mode 100644 index 894f3c5..0000000 --- a/python/arflow/replay.py +++ /dev/null @@ -1,80 +0,0 @@ -"""A library for replaying ARFlow data.""" - -import pickle -import threading -import time -from typing import List - -from arflow.core import ARFlowService -from arflow.service_pb2 import DataFrameRequest, RegisterRequest - - -class ARFlowPlayer(threading.Thread): - """A class for replaying ARFlow data.""" - - service: ARFlowService - frame_data: List - n_frame: int - - def __init__(self, service: ARFlowService, frame_data_path: str) -> None: - super().__init__() - self.service = service() - with open(frame_data_path, "rb") as f: - raw_data = pickle.load(f) - - self.frame_data = [] - start_delta = 0 - for i, data in enumerate(raw_data): - if i == 0: - start_delta = data["time_stamp"] - 3 - self.frame_data.append( - { - "time_stamp": data["time_stamp"] - start_delta, - "data": RegisterRequest.FromString(data["data"]), - } - ) - else: - self.frame_data.append( - { - "time_stamp": data["time_stamp"] - start_delta, - "data": DataFrameRequest.FromString(data["data"]), - } - ) - - self.uid = self.frame_data[1]["data"].uid - - self.period = 0.001 # Simulate a 1ms loop. - self.n_frame = 0 - - self.i = 0 - self.t0 = time.time() - self.start() - - def sleep(self): - self.i += 1 - delta = self.t0 + self.period * self.i - time.time() - if delta > 0: - time.sleep(delta) - - def run(self): - while True: - current_time = time.time() - self.t0 - - t = self.frame_data[self.n_frame]["time_stamp"] - - if t - current_time < 0.001: - data = self.frame_data[self.n_frame]["data"] - if self.n_frame == 0: - self.service.register(data, None, uid=self.uid) - else: - self.service.data_frame(data, None) - - self.n_frame += 1 - - if self.n_frame > len(self.frame_data) - 1: - break - - self.sleep() - - print("Reply finished.") - exit() diff --git a/python/arflow/serve.py b/python/arflow/serve.py deleted file mode 100644 index 2b2f468..0000000 --- a/python/arflow/serve.py +++ /dev/null @@ -1,46 +0,0 @@ -"""Simple server for ARFlow service.""" - -import sys -from concurrent import futures - -import grpc - -from arflow import service_pb2_grpc -from arflow.core import ARFlowService - - -def create_server( - service: ARFlowService, port: int = 8500, path_to_save: str | None = None -): - """Run gRPC server.""" - try: - service = service() - server = grpc.server( - futures.ThreadPoolExecutor(max_workers=10), - options=[ - ("grpc.max_send_message_length", -1), - ("grpc.max_receive_message_length", -1), - ], - ) - service_pb2_grpc.add_ARFlowServiceServicer_to_server(service, server) - server.add_insecure_port("[::]:%s" % port) - server.start() - - print(f"ARFlow server started on port {port}") - server.wait_for_termination() - except KeyboardInterrupt: - if path_to_save is not None: - service.on_program_exit(path_to_save) - sys.exit(0) - - # except Exception as e: - # print(e) - - -def serve(): - """Run a simple ARFlow server.""" - create_server(ARFlowService) - - -if __name__ == "__main__": - serve() diff --git a/python/arflow/service_pb2.py b/python/arflow/service_pb2.py deleted file mode 100644 index fc3e571..0000000 --- a/python/arflow/service_pb2.py +++ /dev/null @@ -1,73 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE -# source: arflow/service.proto -# Protobuf Python Version: 5.27.2 -"""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, - 27, - 2, - '', - 'arflow/service.proto' -) -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61rflow/service.proto\"\x83\t\n\x0fRegisterRequest\x12\x13\n\x0b\x64\x65vice_name\x18\x01 \x01(\t\x12<\n\x11\x63\x61mera_intrinsics\x18\x02 \x01(\x0b\x32!.RegisterRequest.CameraIntrinsics\x12\x32\n\x0c\x63\x61mera_color\x18\x03 \x01(\x0b\x32\x1c.RegisterRequest.CameraColor\x12\x32\n\x0c\x63\x61mera_depth\x18\x04 \x01(\x0b\x32\x1c.RegisterRequest.CameraDepth\x12:\n\x10\x63\x61mera_transform\x18\x05 \x01(\x0b\x32 .RegisterRequest.CameraTransform\x12=\n\x12\x63\x61mera_point_cloud\x18\x06 \x01(\x0b\x32!.RegisterRequest.CameraPointCloud\x12\x45\n\x16\x63\x61mera_plane_detection\x18\x07 \x01(\x0b\x32%.RegisterRequest.CameraPlaneDetection\x12-\n\tgyroscope\x18\x08 \x01(\x0b\x32\x1a.RegisterRequest.Gyroscope\x12%\n\x05\x61udio\x18\t \x01(\x0b\x32\x16.RegisterRequest.Audio\x12)\n\x07meshing\x18\n \x01(\x0b\x32\x18.RegisterRequest.Meshing\x1a\xa4\x01\n\x10\x43\x61meraIntrinsics\x12\x16\n\x0e\x66ocal_length_x\x18\x01 \x01(\x02\x12\x16\n\x0e\x66ocal_length_y\x18\x02 \x01(\x02\x12\x19\n\x11principal_point_x\x18\x03 \x01(\x02\x12\x19\n\x11principal_point_y\x18\x04 \x01(\x02\x12\x14\n\x0cresolution_x\x18\x05 \x01(\x05\x12\x14\n\x0cresolution_y\x18\x06 \x01(\x05\x1a\x63\n\x0b\x43\x61meraColor\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x11\n\tdata_type\x18\x02 \x01(\t\x12\x17\n\x0fresize_factor_x\x18\x03 \x01(\x02\x12\x17\n\x0fresize_factor_y\x18\x04 \x01(\x02\x1a\x81\x01\n\x0b\x43\x61meraDepth\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x11\n\tdata_type\x18\x02 \x01(\t\x12\"\n\x1a\x63onfidence_filtering_level\x18\x03 \x01(\x05\x12\x14\n\x0cresolution_x\x18\x04 \x01(\x05\x12\x14\n\x0cresolution_y\x18\x05 \x01(\x05\x1a\"\n\x0f\x43\x61meraTransform\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x1a\x41\n\x10\x43\x61meraPointCloud\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x1c\n\x14\x64\x65pth_upscale_factor\x18\x02 \x01(\x02\x1a\'\n\x14\x43\x61meraPlaneDetection\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x1a\x1c\n\tGyroscope\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x1a\x18\n\x05\x41udio\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x1a\x1a\n\x07Meshing\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\"\x1f\n\x10RegisterResponse\x12\x0b\n\x03uid\x18\x01 \x01(\t\"\xbb\x05\n\x10\x44\x61taFrameRequest\x12\x0b\n\x03uid\x18\x01 \x01(\t\x12\r\n\x05\x63olor\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65pth\x18\x03 \x01(\x0c\x12\x11\n\ttransform\x18\x04 \x01(\x0c\x12\x31\n\x0fplane_detection\x18\x05 \x03(\x0b\x32\x18.DataFrameRequest.Planes\x12\x33\n\tgyroscope\x18\x06 \x01(\x0b\x32 .DataFrameRequest.gyroscope_data\x12\r\n\x05\x61udio\x18\x08 \x01(\x0c\x12\x0f\n\x07meshing\x18\t \x01(\x0c\x1a*\n\x07Vector3\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\x1a\x1f\n\x07Vector2\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x1a\x87\x01\n\x06Planes\x12)\n\x06\x63\x65nter\x18\x01 \x01(\x0b\x32\x19.DataFrameRequest.Vector3\x12)\n\x06normal\x18\x02 \x01(\x0b\x32\x19.DataFrameRequest.Vector3\x12\'\n\x04size\x18\x03 \x01(\x0b\x32\x19.DataFrameRequest.Vector2\x1a\x38\n\nQuaternion\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\x12\t\n\x01w\x18\x04 \x01(\x02\x1a\xcf\x01\n\x0egyroscope_data\x12.\n\x08\x61ttitude\x18\x01 \x01(\x0b\x32\x1c.DataFrameRequest.Quaternion\x12\x30\n\rrotation_rate\x18\x02 \x01(\x0b\x32\x19.DataFrameRequest.Vector3\x12*\n\x07gravity\x18\x03 \x01(\x0b\x32\x19.DataFrameRequest.Vector3\x12/\n\x0c\x61\x63\x63\x65leration\x18\x04 \x01(\x0b\x32\x19.DataFrameRequest.Vector3\"$\n\x11\x44\x61taFrameResponse\x12\x0f\n\x07message\x18\x01 \x01(\t2u\n\rARFlowService\x12/\n\x08register\x12\x10.RegisterRequest\x1a\x11.RegisterResponse\x12\x33\n\ndata_frame\x12\x11.DataFrameRequest\x1a\x12.DataFrameResponseB\t\xaa\x02\x06\x41RFlowb\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'arflow.service_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'\252\002\006ARFlow' - _globals['_REGISTERREQUEST']._serialized_start=25 - _globals['_REGISTERREQUEST']._serialized_end=1180 - _globals['_REGISTERREQUEST_CAMERAINTRINSICS']._serialized_start=555 - _globals['_REGISTERREQUEST_CAMERAINTRINSICS']._serialized_end=719 - _globals['_REGISTERREQUEST_CAMERACOLOR']._serialized_start=721 - _globals['_REGISTERREQUEST_CAMERACOLOR']._serialized_end=820 - _globals['_REGISTERREQUEST_CAMERADEPTH']._serialized_start=823 - _globals['_REGISTERREQUEST_CAMERADEPTH']._serialized_end=952 - _globals['_REGISTERREQUEST_CAMERATRANSFORM']._serialized_start=954 - _globals['_REGISTERREQUEST_CAMERATRANSFORM']._serialized_end=988 - _globals['_REGISTERREQUEST_CAMERAPOINTCLOUD']._serialized_start=990 - _globals['_REGISTERREQUEST_CAMERAPOINTCLOUD']._serialized_end=1055 - _globals['_REGISTERREQUEST_CAMERAPLANEDETECTION']._serialized_start=1057 - _globals['_REGISTERREQUEST_CAMERAPLANEDETECTION']._serialized_end=1096 - _globals['_REGISTERREQUEST_GYROSCOPE']._serialized_start=1098 - _globals['_REGISTERREQUEST_GYROSCOPE']._serialized_end=1126 - _globals['_REGISTERREQUEST_AUDIO']._serialized_start=1128 - _globals['_REGISTERREQUEST_AUDIO']._serialized_end=1152 - _globals['_REGISTERREQUEST_MESHING']._serialized_start=1154 - _globals['_REGISTERREQUEST_MESHING']._serialized_end=1180 - _globals['_REGISTERRESPONSE']._serialized_start=1182 - _globals['_REGISTERRESPONSE']._serialized_end=1213 - _globals['_DATAFRAMEREQUEST']._serialized_start=1216 - _globals['_DATAFRAMEREQUEST']._serialized_end=1915 - _globals['_DATAFRAMEREQUEST_VECTOR3']._serialized_start=1434 - _globals['_DATAFRAMEREQUEST_VECTOR3']._serialized_end=1476 - _globals['_DATAFRAMEREQUEST_VECTOR2']._serialized_start=1478 - _globals['_DATAFRAMEREQUEST_VECTOR2']._serialized_end=1509 - _globals['_DATAFRAMEREQUEST_PLANES']._serialized_start=1512 - _globals['_DATAFRAMEREQUEST_PLANES']._serialized_end=1647 - _globals['_DATAFRAMEREQUEST_QUATERNION']._serialized_start=1649 - _globals['_DATAFRAMEREQUEST_QUATERNION']._serialized_end=1705 - _globals['_DATAFRAMEREQUEST_GYROSCOPE_DATA']._serialized_start=1708 - _globals['_DATAFRAMEREQUEST_GYROSCOPE_DATA']._serialized_end=1915 - _globals['_DATAFRAMERESPONSE']._serialized_start=1917 - _globals['_DATAFRAMERESPONSE']._serialized_end=1953 - _globals['_ARFLOWSERVICE']._serialized_start=1955 - _globals['_ARFLOWSERVICE']._serialized_end=2072 -# @@protoc_insertion_point(module_scope) diff --git a/python/arflow_grpc/__init__.py b/python/arflow_grpc/__init__.py new file mode 100644 index 0000000..22dc6e8 --- /dev/null +++ b/python/arflow_grpc/__init__.py @@ -0,0 +1 @@ +"""Python gRPC modules generated by `protoc`.""" diff --git a/python/arflow_grpc/py.typed b/python/arflow_grpc/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/python/arflow_grpc/service_pb2.py b/python/arflow_grpc/service_pb2.py new file mode 100644 index 0000000..732bebe --- /dev/null +++ b/python/arflow_grpc/service_pb2.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: arflow_grpc/service.proto +# Protobuf Python Version: 5.27.2 +"""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, + 27, + 2, + '', + 'arflow_grpc/service.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19\x61rflow_grpc/service.proto\x12\tarflow.v1\"\x99\n\n\x15RegisterClientRequest\x12\x13\n\x0b\x64\x65vice_name\x18\x01 \x01(\t\x12L\n\x11\x63\x61mera_intrinsics\x18\x02 \x01(\x0b\x32\x31.arflow.v1.RegisterClientRequest.CameraIntrinsics\x12\x42\n\x0c\x63\x61mera_color\x18\x03 \x01(\x0b\x32,.arflow.v1.RegisterClientRequest.CameraColor\x12\x42\n\x0c\x63\x61mera_depth\x18\x04 \x01(\x0b\x32,.arflow.v1.RegisterClientRequest.CameraDepth\x12J\n\x10\x63\x61mera_transform\x18\x05 \x01(\x0b\x32\x30.arflow.v1.RegisterClientRequest.CameraTransform\x12M\n\x12\x63\x61mera_point_cloud\x18\x06 \x01(\x0b\x32\x31.arflow.v1.RegisterClientRequest.CameraPointCloud\x12U\n\x16\x63\x61mera_plane_detection\x18\x07 \x01(\x0b\x32\x35.arflow.v1.RegisterClientRequest.CameraPlaneDetection\x12=\n\tgyroscope\x18\x08 \x01(\x0b\x32*.arflow.v1.RegisterClientRequest.Gyroscope\x12\x35\n\x05\x61udio\x18\t \x01(\x0b\x32&.arflow.v1.RegisterClientRequest.Audio\x12\x39\n\x07meshing\x18\n \x01(\x0b\x32(.arflow.v1.RegisterClientRequest.Meshing\x1a\xa4\x01\n\x10\x43\x61meraIntrinsics\x12\x16\n\x0e\x66ocal_length_x\x18\x01 \x01(\x02\x12\x16\n\x0e\x66ocal_length_y\x18\x02 \x01(\x02\x12\x19\n\x11principal_point_x\x18\x03 \x01(\x02\x12\x19\n\x11principal_point_y\x18\x04 \x01(\x02\x12\x14\n\x0cresolution_x\x18\x05 \x01(\x05\x12\x14\n\x0cresolution_y\x18\x06 \x01(\x05\x1a\x63\n\x0b\x43\x61meraColor\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x11\n\tdata_type\x18\x02 \x01(\t\x12\x17\n\x0fresize_factor_x\x18\x03 \x01(\x02\x12\x17\n\x0fresize_factor_y\x18\x04 \x01(\x02\x1a\x81\x01\n\x0b\x43\x61meraDepth\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x11\n\tdata_type\x18\x02 \x01(\t\x12\"\n\x1a\x63onfidence_filtering_level\x18\x03 \x01(\x05\x12\x14\n\x0cresolution_x\x18\x04 \x01(\x05\x12\x14\n\x0cresolution_y\x18\x05 \x01(\x05\x1a\"\n\x0f\x43\x61meraTransform\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x1a\x41\n\x10\x43\x61meraPointCloud\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x1c\n\x14\x64\x65pth_upscale_factor\x18\x02 \x01(\x02\x1a\'\n\x14\x43\x61meraPlaneDetection\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x1a\x1c\n\tGyroscope\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x1a\x18\n\x05\x41udio\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x1a\x1a\n\x07Meshing\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\"%\n\x16RegisterClientResponse\x12\x0b\n\x03uid\x18\x01 \x01(\t\"\xaf\x07\n\x13ProcessFrameRequest\x12\x0b\n\x03uid\x18\x01 \x01(\t\x12\r\n\x05\x63olor\x18\x02 \x01(\x0c\x12\r\n\x05\x64\x65pth\x18\x03 \x01(\x0c\x12\x11\n\ttransform\x18\x04 \x01(\x0c\x12=\n\x0fplane_detection\x18\x05 \x03(\x0b\x32$.arflow.v1.ProcessFrameRequest.Plane\x12?\n\tgyroscope\x18\x06 \x01(\x0b\x32,.arflow.v1.ProcessFrameRequest.GyroscopeData\x12\x12\n\naudio_data\x18\x08 \x03(\x02\x12\x33\n\x06meshes\x18\t \x03(\x0b\x32#.arflow.v1.ProcessFrameRequest.Mesh\x1a*\n\x07Vector3\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\x1a\x1f\n\x07Vector2\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x1a\xee\x01\n\x05Plane\x12\x36\n\x06\x63\x65nter\x18\x01 \x01(\x0b\x32&.arflow.v1.ProcessFrameRequest.Vector3\x12\x36\n\x06normal\x18\x02 \x01(\x0b\x32&.arflow.v1.ProcessFrameRequest.Vector3\x12\x34\n\x04size\x18\x03 \x01(\x0b\x32&.arflow.v1.ProcessFrameRequest.Vector2\x12?\n\x0f\x62oundary_points\x18\x04 \x03(\x0b\x32&.arflow.v1.ProcessFrameRequest.Vector2\x1a\x38\n\nQuaternion\x12\t\n\x01x\x18\x01 \x01(\x02\x12\t\n\x01y\x18\x02 \x01(\x02\x12\t\n\x01z\x18\x03 \x01(\x02\x12\t\n\x01w\x18\x04 \x01(\x02\x1a\x82\x02\n\rGyroscopeData\x12;\n\x08\x61ttitude\x18\x01 \x01(\x0b\x32).arflow.v1.ProcessFrameRequest.Quaternion\x12=\n\rrotation_rate\x18\x02 \x01(\x0b\x32&.arflow.v1.ProcessFrameRequest.Vector3\x12\x37\n\x07gravity\x18\x03 \x01(\x0b\x32&.arflow.v1.ProcessFrameRequest.Vector3\x12<\n\x0c\x61\x63\x63\x65leration\x18\x04 \x01(\x0b\x32&.arflow.v1.ProcessFrameRequest.Vector3\x1a\x14\n\x04Mesh\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\'\n\x14ProcessFrameResponse\x12\x0f\n\x07message\x18\x01 \x01(\t2\xb7\x01\n\rARFlowService\x12U\n\x0eRegisterClient\x12 .arflow.v1.RegisterClientRequest\x1a!.arflow.v1.RegisterClientResponse\x12O\n\x0cProcessFrame\x12\x1e.arflow.v1.ProcessFrameRequest\x1a\x1f.arflow.v1.ProcessFrameResponseB\t\xaa\x02\x06\x41RFlowb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'arflow_grpc.service_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'\252\002\006ARFlow' + _globals['_REGISTERCLIENTREQUEST']._serialized_start=41 + _globals['_REGISTERCLIENTREQUEST']._serialized_end=1346 + _globals['_REGISTERCLIENTREQUEST_CAMERAINTRINSICS']._serialized_start=721 + _globals['_REGISTERCLIENTREQUEST_CAMERAINTRINSICS']._serialized_end=885 + _globals['_REGISTERCLIENTREQUEST_CAMERACOLOR']._serialized_start=887 + _globals['_REGISTERCLIENTREQUEST_CAMERACOLOR']._serialized_end=986 + _globals['_REGISTERCLIENTREQUEST_CAMERADEPTH']._serialized_start=989 + _globals['_REGISTERCLIENTREQUEST_CAMERADEPTH']._serialized_end=1118 + _globals['_REGISTERCLIENTREQUEST_CAMERATRANSFORM']._serialized_start=1120 + _globals['_REGISTERCLIENTREQUEST_CAMERATRANSFORM']._serialized_end=1154 + _globals['_REGISTERCLIENTREQUEST_CAMERAPOINTCLOUD']._serialized_start=1156 + _globals['_REGISTERCLIENTREQUEST_CAMERAPOINTCLOUD']._serialized_end=1221 + _globals['_REGISTERCLIENTREQUEST_CAMERAPLANEDETECTION']._serialized_start=1223 + _globals['_REGISTERCLIENTREQUEST_CAMERAPLANEDETECTION']._serialized_end=1262 + _globals['_REGISTERCLIENTREQUEST_GYROSCOPE']._serialized_start=1264 + _globals['_REGISTERCLIENTREQUEST_GYROSCOPE']._serialized_end=1292 + _globals['_REGISTERCLIENTREQUEST_AUDIO']._serialized_start=1294 + _globals['_REGISTERCLIENTREQUEST_AUDIO']._serialized_end=1318 + _globals['_REGISTERCLIENTREQUEST_MESHING']._serialized_start=1320 + _globals['_REGISTERCLIENTREQUEST_MESHING']._serialized_end=1346 + _globals['_REGISTERCLIENTRESPONSE']._serialized_start=1348 + _globals['_REGISTERCLIENTRESPONSE']._serialized_end=1385 + _globals['_PROCESSFRAMEREQUEST']._serialized_start=1388 + _globals['_PROCESSFRAMEREQUEST']._serialized_end=2331 + _globals['_PROCESSFRAMEREQUEST_VECTOR3']._serialized_start=1674 + _globals['_PROCESSFRAMEREQUEST_VECTOR3']._serialized_end=1716 + _globals['_PROCESSFRAMEREQUEST_VECTOR2']._serialized_start=1718 + _globals['_PROCESSFRAMEREQUEST_VECTOR2']._serialized_end=1749 + _globals['_PROCESSFRAMEREQUEST_PLANE']._serialized_start=1752 + _globals['_PROCESSFRAMEREQUEST_PLANE']._serialized_end=1990 + _globals['_PROCESSFRAMEREQUEST_QUATERNION']._serialized_start=1992 + _globals['_PROCESSFRAMEREQUEST_QUATERNION']._serialized_end=2048 + _globals['_PROCESSFRAMEREQUEST_GYROSCOPEDATA']._serialized_start=2051 + _globals['_PROCESSFRAMEREQUEST_GYROSCOPEDATA']._serialized_end=2309 + _globals['_PROCESSFRAMEREQUEST_MESH']._serialized_start=2311 + _globals['_PROCESSFRAMEREQUEST_MESH']._serialized_end=2331 + _globals['_PROCESSFRAMERESPONSE']._serialized_start=2333 + _globals['_PROCESSFRAMERESPONSE']._serialized_end=2372 + _globals['_ARFLOWSERVICE']._serialized_start=2375 + _globals['_ARFLOWSERVICE']._serialized_end=2558 +# @@protoc_insertion_point(module_scope) diff --git a/python/arflow/service_pb2.pyi b/python/arflow_grpc/service_pb2.pyi similarity index 66% rename from python/arflow/service_pb2.pyi rename to python/arflow_grpc/service_pb2.pyi index 1d2182f..43c2453 100644 --- a/python/arflow/service_pb2.pyi +++ b/python/arflow_grpc/service_pb2.pyi @@ -5,7 +5,7 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor -class RegisterRequest(_message.Message): +class RegisterClientRequest(_message.Message): __slots__ = ("device_name", "camera_intrinsics", "camera_color", "camera_depth", "camera_transform", "camera_point_cloud", "camera_plane_detection", "gyroscope", "audio", "meshing") class CameraIntrinsics(_message.Message): __slots__ = ("focal_length_x", "focal_length_y", "principal_point_x", "principal_point_y", "resolution_x", "resolution_y") @@ -89,25 +89,25 @@ class RegisterRequest(_message.Message): AUDIO_FIELD_NUMBER: _ClassVar[int] MESHING_FIELD_NUMBER: _ClassVar[int] device_name: str - camera_intrinsics: RegisterRequest.CameraIntrinsics - camera_color: RegisterRequest.CameraColor - camera_depth: RegisterRequest.CameraDepth - camera_transform: RegisterRequest.CameraTransform - camera_point_cloud: RegisterRequest.CameraPointCloud - camera_plane_detection: RegisterRequest.CameraPlaneDetection - gyroscope: RegisterRequest.Gyroscope - audio: RegisterRequest.Audio - meshing: RegisterRequest.Meshing - def __init__(self, device_name: _Optional[str] = ..., camera_intrinsics: _Optional[_Union[RegisterRequest.CameraIntrinsics, _Mapping]] = ..., camera_color: _Optional[_Union[RegisterRequest.CameraColor, _Mapping]] = ..., camera_depth: _Optional[_Union[RegisterRequest.CameraDepth, _Mapping]] = ..., camera_transform: _Optional[_Union[RegisterRequest.CameraTransform, _Mapping]] = ..., camera_point_cloud: _Optional[_Union[RegisterRequest.CameraPointCloud, _Mapping]] = ..., camera_plane_detection: _Optional[_Union[RegisterRequest.CameraPlaneDetection, _Mapping]] = ..., gyroscope: _Optional[_Union[RegisterRequest.Gyroscope, _Mapping]] = ..., audio: _Optional[_Union[RegisterRequest.Audio, _Mapping]] = ..., meshing: _Optional[_Union[RegisterRequest.Meshing, _Mapping]] = ...) -> None: ... + camera_intrinsics: RegisterClientRequest.CameraIntrinsics + camera_color: RegisterClientRequest.CameraColor + camera_depth: RegisterClientRequest.CameraDepth + camera_transform: RegisterClientRequest.CameraTransform + camera_point_cloud: RegisterClientRequest.CameraPointCloud + camera_plane_detection: RegisterClientRequest.CameraPlaneDetection + gyroscope: RegisterClientRequest.Gyroscope + audio: RegisterClientRequest.Audio + meshing: RegisterClientRequest.Meshing + def __init__(self, device_name: _Optional[str] = ..., camera_intrinsics: _Optional[_Union[RegisterClientRequest.CameraIntrinsics, _Mapping]] = ..., camera_color: _Optional[_Union[RegisterClientRequest.CameraColor, _Mapping]] = ..., camera_depth: _Optional[_Union[RegisterClientRequest.CameraDepth, _Mapping]] = ..., camera_transform: _Optional[_Union[RegisterClientRequest.CameraTransform, _Mapping]] = ..., camera_point_cloud: _Optional[_Union[RegisterClientRequest.CameraPointCloud, _Mapping]] = ..., camera_plane_detection: _Optional[_Union[RegisterClientRequest.CameraPlaneDetection, _Mapping]] = ..., gyroscope: _Optional[_Union[RegisterClientRequest.Gyroscope, _Mapping]] = ..., audio: _Optional[_Union[RegisterClientRequest.Audio, _Mapping]] = ..., meshing: _Optional[_Union[RegisterClientRequest.Meshing, _Mapping]] = ...) -> None: ... -class RegisterResponse(_message.Message): +class RegisterClientResponse(_message.Message): __slots__ = ("uid",) UID_FIELD_NUMBER: _ClassVar[int] uid: str def __init__(self, uid: _Optional[str] = ...) -> None: ... -class DataFrameRequest(_message.Message): - __slots__ = ("uid", "color", "depth", "transform", "plane_detection", "gyroscope", "audio", "meshing") +class ProcessFrameRequest(_message.Message): + __slots__ = ("uid", "color", "depth", "transform", "plane_detection", "gyroscope", "audio_data", "meshes") class Vector3(_message.Message): __slots__ = ("x", "y", "z") X_FIELD_NUMBER: _ClassVar[int] @@ -124,15 +124,17 @@ class DataFrameRequest(_message.Message): x: float y: float def __init__(self, x: _Optional[float] = ..., y: _Optional[float] = ...) -> None: ... - class Planes(_message.Message): - __slots__ = ("center", "normal", "size") + class Plane(_message.Message): + __slots__ = ("center", "normal", "size", "boundary_points") CENTER_FIELD_NUMBER: _ClassVar[int] NORMAL_FIELD_NUMBER: _ClassVar[int] SIZE_FIELD_NUMBER: _ClassVar[int] - center: DataFrameRequest.Vector3 - normal: DataFrameRequest.Vector3 - size: DataFrameRequest.Vector2 - def __init__(self, center: _Optional[_Union[DataFrameRequest.Vector3, _Mapping]] = ..., normal: _Optional[_Union[DataFrameRequest.Vector3, _Mapping]] = ..., size: _Optional[_Union[DataFrameRequest.Vector2, _Mapping]] = ...) -> None: ... + BOUNDARY_POINTS_FIELD_NUMBER: _ClassVar[int] + center: ProcessFrameRequest.Vector3 + normal: ProcessFrameRequest.Vector3 + size: ProcessFrameRequest.Vector2 + boundary_points: _containers.RepeatedCompositeFieldContainer[ProcessFrameRequest.Vector2] + def __init__(self, center: _Optional[_Union[ProcessFrameRequest.Vector3, _Mapping]] = ..., normal: _Optional[_Union[ProcessFrameRequest.Vector3, _Mapping]] = ..., size: _Optional[_Union[ProcessFrameRequest.Vector2, _Mapping]] = ..., boundary_points: _Optional[_Iterable[_Union[ProcessFrameRequest.Vector2, _Mapping]]] = ...) -> None: ... class Quaternion(_message.Message): __slots__ = ("x", "y", "z", "w") X_FIELD_NUMBER: _ClassVar[int] @@ -144,36 +146,41 @@ class DataFrameRequest(_message.Message): z: float w: float def __init__(self, x: _Optional[float] = ..., y: _Optional[float] = ..., z: _Optional[float] = ..., w: _Optional[float] = ...) -> None: ... - class gyroscope_data(_message.Message): + class GyroscopeData(_message.Message): __slots__ = ("attitude", "rotation_rate", "gravity", "acceleration") ATTITUDE_FIELD_NUMBER: _ClassVar[int] ROTATION_RATE_FIELD_NUMBER: _ClassVar[int] GRAVITY_FIELD_NUMBER: _ClassVar[int] ACCELERATION_FIELD_NUMBER: _ClassVar[int] - attitude: DataFrameRequest.Quaternion - rotation_rate: DataFrameRequest.Vector3 - gravity: DataFrameRequest.Vector3 - acceleration: DataFrameRequest.Vector3 - def __init__(self, attitude: _Optional[_Union[DataFrameRequest.Quaternion, _Mapping]] = ..., rotation_rate: _Optional[_Union[DataFrameRequest.Vector3, _Mapping]] = ..., gravity: _Optional[_Union[DataFrameRequest.Vector3, _Mapping]] = ..., acceleration: _Optional[_Union[DataFrameRequest.Vector3, _Mapping]] = ...) -> None: ... + attitude: ProcessFrameRequest.Quaternion + rotation_rate: ProcessFrameRequest.Vector3 + gravity: ProcessFrameRequest.Vector3 + acceleration: ProcessFrameRequest.Vector3 + def __init__(self, attitude: _Optional[_Union[ProcessFrameRequest.Quaternion, _Mapping]] = ..., rotation_rate: _Optional[_Union[ProcessFrameRequest.Vector3, _Mapping]] = ..., gravity: _Optional[_Union[ProcessFrameRequest.Vector3, _Mapping]] = ..., acceleration: _Optional[_Union[ProcessFrameRequest.Vector3, _Mapping]] = ...) -> None: ... + class Mesh(_message.Message): + __slots__ = ("data",) + DATA_FIELD_NUMBER: _ClassVar[int] + data: bytes + def __init__(self, data: _Optional[bytes] = ...) -> None: ... UID_FIELD_NUMBER: _ClassVar[int] COLOR_FIELD_NUMBER: _ClassVar[int] DEPTH_FIELD_NUMBER: _ClassVar[int] TRANSFORM_FIELD_NUMBER: _ClassVar[int] PLANE_DETECTION_FIELD_NUMBER: _ClassVar[int] GYROSCOPE_FIELD_NUMBER: _ClassVar[int] - AUDIO_FIELD_NUMBER: _ClassVar[int] - MESHING_FIELD_NUMBER: _ClassVar[int] + AUDIO_DATA_FIELD_NUMBER: _ClassVar[int] + MESHES_FIELD_NUMBER: _ClassVar[int] uid: str color: bytes depth: bytes transform: bytes - plane_detection: _containers.RepeatedCompositeFieldContainer[DataFrameRequest.Planes] - gyroscope: DataFrameRequest.gyroscope_data - audio: bytes - meshing: bytes - def __init__(self, uid: _Optional[str] = ..., color: _Optional[bytes] = ..., depth: _Optional[bytes] = ..., transform: _Optional[bytes] = ..., plane_detection: _Optional[_Iterable[_Union[DataFrameRequest.Planes, _Mapping]]] = ..., gyroscope: _Optional[_Union[DataFrameRequest.gyroscope_data, _Mapping]] = ..., audio: _Optional[bytes] = ..., meshing: _Optional[bytes] = ...) -> None: ... + plane_detection: _containers.RepeatedCompositeFieldContainer[ProcessFrameRequest.Plane] + gyroscope: ProcessFrameRequest.GyroscopeData + audio_data: _containers.RepeatedScalarFieldContainer[float] + meshes: _containers.RepeatedCompositeFieldContainer[ProcessFrameRequest.Mesh] + def __init__(self, uid: _Optional[str] = ..., color: _Optional[bytes] = ..., depth: _Optional[bytes] = ..., transform: _Optional[bytes] = ..., plane_detection: _Optional[_Iterable[_Union[ProcessFrameRequest.Plane, _Mapping]]] = ..., gyroscope: _Optional[_Union[ProcessFrameRequest.GyroscopeData, _Mapping]] = ..., audio_data: _Optional[_Iterable[float]] = ..., meshes: _Optional[_Iterable[_Union[ProcessFrameRequest.Mesh, _Mapping]]] = ...) -> None: ... -class DataFrameResponse(_message.Message): +class ProcessFrameResponse(_message.Message): __slots__ = ("message",) MESSAGE_FIELD_NUMBER: _ClassVar[int] message: str diff --git a/python/arflow/service_pb2_grpc.py b/python/arflow_grpc/service_pb2_grpc.py similarity index 50% rename from python/arflow/service_pb2_grpc.py rename to python/arflow_grpc/service_pb2_grpc.py index 5c08c8f..29f1cf4 100644 --- a/python/arflow/service_pb2_grpc.py +++ b/python/arflow_grpc/service_pb2_grpc.py @@ -3,9 +3,9 @@ import grpc import warnings -from arflow import service_pb2 as arflow_dot_service__pb2 +from arflow_grpc import service_pb2 as arflow__grpc_dot_service__pb2 -GRPC_GENERATED_VERSION = '1.66.1' +GRPC_GENERATED_VERSION = '1.66.2' GRPC_VERSION = grpc.__version__ _version_not_supported = False @@ -18,7 +18,7 @@ if _version_not_supported: raise RuntimeError( f'The grpc package installed is at version {GRPC_VERSION},' - + f' but the generated code in arflow/service_pb2_grpc.py depends on' + + f' but the generated code in arflow_grpc/service_pb2_grpc.py depends on' + f' grpcio>={GRPC_GENERATED_VERSION}.' + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' @@ -26,7 +26,7 @@ class ARFlowServiceStub(object): - """The ARFlowService service definition. + """The ARFlow service definition. """ def __init__(self, channel): @@ -35,31 +35,39 @@ def __init__(self, channel): Args: channel: A grpc.Channel. """ - self.register = channel.unary_unary( - '/ARFlowService/register', - request_serializer=arflow_dot_service__pb2.RegisterRequest.SerializeToString, - response_deserializer=arflow_dot_service__pb2.RegisterResponse.FromString, + self.RegisterClient = channel.unary_unary( + '/arflow.v1.ARFlowService/RegisterClient', + request_serializer=arflow__grpc_dot_service__pb2.RegisterClientRequest.SerializeToString, + response_deserializer=arflow__grpc_dot_service__pb2.RegisterClientResponse.FromString, _registered_method=True) - self.data_frame = channel.unary_unary( - '/ARFlowService/data_frame', - request_serializer=arflow_dot_service__pb2.DataFrameRequest.SerializeToString, - response_deserializer=arflow_dot_service__pb2.DataFrameResponse.FromString, + self.ProcessFrame = channel.unary_unary( + '/arflow.v1.ARFlowService/ProcessFrame', + request_serializer=arflow__grpc_dot_service__pb2.ProcessFrameRequest.SerializeToString, + response_deserializer=arflow__grpc_dot_service__pb2.ProcessFrameResponse.FromString, _registered_method=True) class ARFlowServiceServicer(object): - """The ARFlowService service definition. + """The ARFlow service definition. """ - def register(self, request, context): - """Registers a device with the given specifications. + def RegisterClient(self, request, context): + """Registers a client with the given specifications. + + The client is registered with the server and is assigned a unique identifier. + The client can then send data frames to the server using the assigned identifier. """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') - def data_frame(self, request, context): - """Sends a data frame from a device. + def ProcessFrame(self, request, context): + """Accepts a data frame from a client, returning an acknowledgment. + + Errors: + - NOT_FOUND: If the client configuration is not found. + - INVALID_ARGUMENT: If the color data type is not recognized or the depth data type + is not recognized or if the request's data cannot be decoded (e.g., corrupted or invalid data). """ context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') @@ -68,30 +76,30 @@ def data_frame(self, request, context): def add_ARFlowServiceServicer_to_server(servicer, server): rpc_method_handlers = { - 'register': grpc.unary_unary_rpc_method_handler( - servicer.register, - request_deserializer=arflow_dot_service__pb2.RegisterRequest.FromString, - response_serializer=arflow_dot_service__pb2.RegisterResponse.SerializeToString, + 'RegisterClient': grpc.unary_unary_rpc_method_handler( + servicer.RegisterClient, + request_deserializer=arflow__grpc_dot_service__pb2.RegisterClientRequest.FromString, + response_serializer=arflow__grpc_dot_service__pb2.RegisterClientResponse.SerializeToString, ), - 'data_frame': grpc.unary_unary_rpc_method_handler( - servicer.data_frame, - request_deserializer=arflow_dot_service__pb2.DataFrameRequest.FromString, - response_serializer=arflow_dot_service__pb2.DataFrameResponse.SerializeToString, + 'ProcessFrame': grpc.unary_unary_rpc_method_handler( + servicer.ProcessFrame, + request_deserializer=arflow__grpc_dot_service__pb2.ProcessFrameRequest.FromString, + response_serializer=arflow__grpc_dot_service__pb2.ProcessFrameResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( - 'ARFlowService', rpc_method_handlers) + 'arflow.v1.ARFlowService', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) - server.add_registered_method_handlers('ARFlowService', rpc_method_handlers) + server.add_registered_method_handlers('arflow.v1.ARFlowService', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. class ARFlowService(object): - """The ARFlowService service definition. + """The ARFlow service definition. """ @staticmethod - def register(request, + def RegisterClient(request, target, options=(), channel_credentials=None, @@ -104,9 +112,9 @@ def register(request, return grpc.experimental.unary_unary( request, target, - '/ARFlowService/register', - arflow_dot_service__pb2.RegisterRequest.SerializeToString, - arflow_dot_service__pb2.RegisterResponse.FromString, + '/arflow.v1.ARFlowService/RegisterClient', + arflow__grpc_dot_service__pb2.RegisterClientRequest.SerializeToString, + arflow__grpc_dot_service__pb2.RegisterClientResponse.FromString, options, channel_credentials, insecure, @@ -118,7 +126,7 @@ def register(request, _registered_method=True) @staticmethod - def data_frame(request, + def ProcessFrame(request, target, options=(), channel_credentials=None, @@ -131,9 +139,9 @@ def data_frame(request, return grpc.experimental.unary_unary( request, target, - '/ARFlowService/data_frame', - arflow_dot_service__pb2.DataFrameRequest.SerializeToString, - arflow_dot_service__pb2.DataFrameResponse.FromString, + '/arflow.v1.ARFlowService/ProcessFrame', + arflow__grpc_dot_service__pb2.ProcessFrameRequest.SerializeToString, + arflow__grpc_dot_service__pb2.ProcessFrameResponse.FromString, options, channel_credentials, insecure, diff --git a/python/examples/README.md b/python/examples/README.md index 2dc98cc..9c52fe6 100644 --- a/python/examples/README.md +++ b/python/examples/README.md @@ -1,12 +1,12 @@ # ARFlow Server Examples -The simplest example is [`minimal`](minimal/minimal.py). You may want to start there! +The simplest example is [`simple`](simple/simple.py). You may want to start there! ## Setup If you're using `pip`, you should create and activate a virtual environment before installing any example's dependencies: -```sh +```shell python3 -m venv .venv source .venv/bin/activate ``` @@ -17,14 +17,14 @@ If you're using `poetry` instead, you can just install the dependencies directly Each example is packaged as a regular Python package, with a `pyproject.toml` file specifying its required dependencies. To run an example, it must first be installed. -For example, to install dependencies and run the toy `minimal` example (which doesn't need to download any data) run: +For example, to install dependencies and run the toy `simple` example (which doesn't need to download any data) run: -```sh +```shell # Using pip: -pip install -e python/examples/minimal +pip install -e python/examples/simple # Using poetry: -cd python/examples/minimal +cd python/examples/simple poetry install ``` @@ -33,19 +33,20 @@ poetry install Once installed, the example can be run as a regular Python module: ```shell -python3 -m minimal +python3 -m simple # or, if you're using poetry: -poetry run minimal +poetry run simple ``` Examples also declare console script, so they can also be run directly: ```shell -minimal +simple ``` ## Contributions welcome + Feel free to open a PR to add a new example! -See the [`CONTRIBUTING.md`](https://github.com/cake-lab/ARFlow/blob/main/CONTRIBUTING.md) file for details on how to contribute. +See the [`CONTRIBUTING.md`](https://github.com/cake-lab/ARFlow/blob/main/CONTRIBUTING.md) file for details on how to contribute. diff --git a/python/examples/__init__.py b/python/examples/__init__.py index a57e990..431c6bf 100644 --- a/python/examples/__init__.py +++ b/python/examples/__init__.py @@ -1,3 +1,2 @@ -""" -.. include:: ./README.md +""".. include:: ./README.md """ diff --git a/python/examples/depthanythingv2/depthanythingv2.py b/python/examples/depthanythingv2/depthanythingv2.py old mode 100755 new mode 100644 index 5e71f07..37a2209 --- a/python/examples/depthanythingv2/depthanythingv2.py +++ b/python/examples/depthanythingv2/depthanythingv2.py @@ -1,22 +1,27 @@ #!/usr/bin/env python3 -"""Demonstrates the usage of ARFlow with Depth Anything v2.""" +# type: ignore +"""Demonstrates the usage of ARFlow with Depth Anything v2. + +Note: `# type: ignore` is added to the first line to suppress typecheck errors. +In case you want to copy this code, please remove the first line if you are using typecheck. +""" from __future__ import annotations import sys from threading import Thread -from typing import Any, Dict import numpy as np import torch +import numpy.typing as npt from PIL import Image from transformers import pipeline import arflow -class DepthAnythingV2Service(arflow.ARFlowService): - def __init__(self, *args, **kwargs) -> None: +class DepthAnythingV2Service(arflow.ARFlowServicer): + def __init__(self) -> None: super().__init__() self.device = "cuda" if torch.cuda.is_available() else "cpu" self.pipe = pipeline( @@ -25,18 +30,19 @@ def __init__(self, *args, **kwargs) -> None: device=self.device, ) - def on_register(self, request: arflow.RegisterRequest): + def on_register(self, request: arflow.RegisterClientRequest): self.num_frame = 0 - def on_frame_received(self, frame_data: Dict[str, Any]): - color_rgb = frame_data["color_rgb"] + def on_frame_received(self, decoded_data_frame: arflow.DecodedDataFrame): if self.num_frame % 50 == 0: - thread = Thread(target=lambda: (self.run_depth_estimation(color_rgb.copy())) ) + thread = Thread( + target=lambda: (self.run_depth_estimation(decoded_data_frame.color_rgb.copy())) + ) thread.start() self.num_frame = self.num_frame + 1 - def run_depth_estimation(self, color_rgb: np.ndarray): + def run_depth_estimation(self, color_rgb: npt.NDArray[np.uint8]): """Run depth estimation on the given image. The pipeline returns a dictionary with two entries. The first one, called predicted_depth, is a tensor with the values being the depth expressed in meters for each pixel. The second one, depth, is a PIL image that visualizes the depth estimation result.""" @@ -47,7 +53,7 @@ def run_depth_estimation(self, color_rgb: np.ndarray): self.record_predictions(predictions) return predictions - def record_predictions(self, predictions: dict): + def record_predictions(self, predictions): self.recorder.log( "DepthAnythingV2/depth", self.recorder.Image(predictions["depth"]) ) @@ -56,7 +62,7 @@ def record_predictions(self, predictions: dict): def main() -> None: # sanity-check since all other example scripts take arguments: assert len(sys.argv) == 1, f"{sys.argv[0]} does not take any arguments" - arflow.create_server(DepthAnythingV2Service, port=8500, path_to_save=None) + arflow.run_server(DepthAnythingV2Service, port=8500, path_to_save=None) if __name__ == "__main__": diff --git a/python/examples/minimal/README.md b/python/examples/minimal/README.md deleted file mode 100644 index 4640904..0000000 --- a/python/examples/minimal/README.md +++ /dev/null @@ -1 +0,0 @@ -# TODO diff --git a/python/examples/minimal/minimal.py b/python/examples/minimal/minimal.py deleted file mode 100755 index 2ede854..0000000 --- a/python/examples/minimal/minimal.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python3 -"""Demonstrates the most barebone usage of ARFlow.""" - -from __future__ import annotations - -import sys - -import numpy as np - -import arflow - - -class MinimalService(arflow.ARFlowService): - def __init__(self): - super().__init__() - - def on_register(self, request: arflow.RegisterRequest): - positions = np.vstack( - [xyz.ravel() for xyz in np.mgrid[3 * [slice(-10, 10, 10j)]]] - ).T - colors = ( - np.vstack([rgb.ravel() for rgb in np.mgrid[3 * [slice(0, 255, 10j)]]]) - .astype(np.uint8) - .T - ) - - self.recorder.log( - "my_points", self.recorder.Points3D(positions, colors=colors, radii=0.5) - ) - pass - - def on_frame_received(self, frame_data: arflow.DataFrameRequest): - print("Received a frame") - - -def main() -> None: - # sanity-check since all other example scripts take arguments: - assert len(sys.argv) == 1, f"{sys.argv[0]} does not take any arguments" - arflow.create_server(MinimalService, port=8500, path_to_save=None) - - -if __name__ == "__main__": - main() diff --git a/python/examples/simple/README.md b/python/examples/simple/README.md new file mode 100644 index 0000000..60bda30 --- /dev/null +++ b/python/examples/simple/README.md @@ -0,0 +1,19 @@ +# ARFlow Simple Example + +Next, you may integrate ARFlow with your own research prototype via the Python API. [`simple.py`](simple.py) demonstrates how to build your own custom server by extending the default ARFlow server. + +First, let's start the server: + +```shell +server +``` + +Once you have your server running, you can start your ARFlow clients and connect them to the server. The server will start collecting data from the clients and save it to a `pickle` file at the end of the session. + +You can visualize the data using the ARFlow Player: + +```shell +arflow replay ./FRAME_DATA_PATH.pkl +``` + +Replace `FRAME_DATA_PATH` with the path to your saved `pickle` file and you will see the ARFlow data visualized in the ARFlow Player. diff --git a/python/examples/minimal/poetry.lock b/python/examples/simple/poetry.lock similarity index 64% rename from python/examples/minimal/poetry.lock rename to python/examples/simple/poetry.lock index ff0855c..147b75a 100644 --- a/python/examples/minimal/poetry.lock +++ b/python/examples/simple/poetry.lock @@ -2,7 +2,7 @@ [[package]] name = "arflow" -version = "0.2.0" +version = "0.3.0" description = "ARFlow is a data-sharing layer that enables developer-friendly data streaming, storage, and visualization for augmented reality (AR) device data." optional = false python-versions = ">=3.9,<3.13" @@ -10,6 +10,7 @@ files = [] develop = false [package.dependencies] +grpc-interceptor = "^0.15.4" grpcio = "^1.60.1" grpcio-tools = "^1.60.1" rerun-sdk = "^0.18.2" @@ -37,121 +38,156 @@ docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphi tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] +[[package]] +name = "grpc-interceptor" +version = "0.15.4" +description = "Simplifies gRPC interceptors" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "grpc-interceptor-0.15.4.tar.gz", hash = "sha256:1f45c0bcb58b6f332f37c637632247c9b02bc6af0fdceb7ba7ce8d2ebbfb0926"}, + {file = "grpc_interceptor-0.15.4-py3-none-any.whl", hash = "sha256:0035f33228693ed3767ee49d937bac424318db173fef4d2d0170b3215f254d9d"}, +] + +[package.dependencies] +grpcio = ">=1.49.1,<2.0.0" + +[package.extras] +testing = ["protobuf (>=4.21.9)"] + [[package]] name = "grpcio" -version = "1.66.1" +version = "1.66.2" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:4877ba180591acdf127afe21ec1c7ff8a5ecf0fe2600f0d3c50e8c4a1cbc6492"}, - {file = "grpcio-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3750c5a00bd644c75f4507f77a804d0189d97a107eb1481945a0cf3af3e7a5ac"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a013c5fbb12bfb5f927444b477a26f1080755a931d5d362e6a9a720ca7dbae60"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1b24c23d51a1e8790b25514157d43f0a4dce1ac12b3f0b8e9f66a5e2c4c132f"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffb8ea674d68de4cac6f57d2498fef477cef582f1fa849e9f844863af50083"}, - {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:307b1d538140f19ccbd3aed7a93d8f71103c5d525f3c96f8616111614b14bf2a"}, - {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c17ebcec157cfb8dd445890a03e20caf6209a5bd4ac5b040ae9dbc59eef091d"}, - {file = "grpcio-1.66.1-cp310-cp310-win32.whl", hash = "sha256:ef82d361ed5849d34cf09105d00b94b6728d289d6b9235513cb2fcc79f7c432c"}, - {file = "grpcio-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:292a846b92cdcd40ecca46e694997dd6b9be6c4c01a94a0dfb3fcb75d20da858"}, - {file = "grpcio-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:c30aeceeaff11cd5ddbc348f37c58bcb96da8d5aa93fed78ab329de5f37a0d7a"}, - {file = "grpcio-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a1e224ce6f740dbb6b24c58f885422deebd7eb724aff0671a847f8951857c26"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a66fe4dc35d2330c185cfbb42959f57ad36f257e0cc4557d11d9f0a3f14311df"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ba04659e4fce609de2658fe4dbf7d6ed21987a94460f5f92df7579fd5d0e22"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4573608e23f7e091acfbe3e84ac2045680b69751d8d67685ffa193a4429fedb1"}, - {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7e06aa1f764ec8265b19d8f00140b8c4b6ca179a6dc67aa9413867c47e1fb04e"}, - {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3885f037eb11f1cacc41f207b705f38a44b69478086f40608959bf5ad85826dd"}, - {file = "grpcio-1.66.1-cp311-cp311-win32.whl", hash = "sha256:97ae7edd3f3f91480e48ede5d3e7d431ad6005bfdbd65c1b56913799ec79e791"}, - {file = "grpcio-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:cfd349de4158d797db2bd82d2020554a121674e98fbe6b15328456b3bf2495bb"}, - {file = "grpcio-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:a92c4f58c01c77205df6ff999faa008540475c39b835277fb8883b11cada127a"}, - {file = "grpcio-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fdb14bad0835914f325349ed34a51940bc2ad965142eb3090081593c6e347be9"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f03a5884c56256e08fd9e262e11b5cfacf1af96e2ce78dc095d2c41ccae2c80d"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ca2559692d8e7e245d456877a85ee41525f3ed425aa97eb7a70fc9a79df91a0"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca1be089fb4446490dd1135828bd42a7c7f8421e74fa581611f7afdf7ab761"}, - {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d639c939ad7c440c7b2819a28d559179a4508783f7e5b991166f8d7a34b52815"}, - {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b9feb4e5ec8dc2d15709f4d5fc367794d69277f5d680baf1910fc9915c633524"}, - {file = "grpcio-1.66.1-cp312-cp312-win32.whl", hash = "sha256:7101db1bd4cd9b880294dec41a93fcdce465bdbb602cd8dc5bd2d6362b618759"}, - {file = "grpcio-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:b0aa03d240b5539648d996cc60438f128c7f46050989e35b25f5c18286c86734"}, - {file = "grpcio-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:ecfe735e7a59e5a98208447293ff8580e9db1e890e232b8b292dc8bd15afc0d2"}, - {file = "grpcio-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4825a3aa5648010842e1c9d35a082187746aa0cdbf1b7a2a930595a94fb10fce"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f517fd7259fe823ef3bd21e508b653d5492e706e9f0ef82c16ce3347a8a5620c"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1fe60d0772831d96d263b53d83fb9a3d050a94b0e94b6d004a5ad111faa5b5b"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a049daa428f928f21090403e5d18ea02670e3d5d172581670be006100db9ef"}, - {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f914386e52cbdeb5d2a7ce3bf1fdfacbe9d818dd81b6099a05b741aaf3848bb"}, - {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bff2096bdba686019fb32d2dde45b95981f0d1490e054400f70fc9a8af34b49d"}, - {file = "grpcio-1.66.1-cp38-cp38-win32.whl", hash = "sha256:aa8ba945c96e73de29d25331b26f3e416e0c0f621e984a3ebdb2d0d0b596a3b3"}, - {file = "grpcio-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:161d5c535c2bdf61b95080e7f0f017a1dfcb812bf54093e71e5562b16225b4ce"}, - {file = "grpcio-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:d0cd7050397b3609ea51727b1811e663ffda8bda39c6a5bb69525ef12414b503"}, - {file = "grpcio-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e6c9b42ded5d02b6b1fea3a25f036a2236eeb75d0579bfd43c0018c88bf0a3e"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c9f80f9fad93a8cf71c7f161778ba47fd730d13a343a46258065c4deb4b550c0"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dd67ed9da78e5121efc5c510f0122a972216808d6de70953a740560c572eb44"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48b0d92d45ce3be2084b92fb5bae2f64c208fea8ceed7fccf6a7b524d3c4942e"}, - {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d813316d1a752be6f5c4360c49f55b06d4fe212d7df03253dfdae90c8a402bb"}, - {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c9bebc6627873ec27a70fc800f6083a13c70b23a5564788754b9ee52c5aef6c"}, - {file = "grpcio-1.66.1-cp39-cp39-win32.whl", hash = "sha256:30a1c2cf9390c894c90bbc70147f2372130ad189cffef161f0432d0157973f45"}, - {file = "grpcio-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:17663598aadbedc3cacd7bbde432f541c8e07d2496564e22b214b22c7523dac8"}, - {file = "grpcio-1.66.1.tar.gz", hash = "sha256:35334f9c9745add3e357e3372756fd32d925bd52c41da97f4dfdafbde0bf0ee2"}, + {file = "grpcio-1.66.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:fe96281713168a3270878255983d2cb1a97e034325c8c2c25169a69289d3ecfa"}, + {file = "grpcio-1.66.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:73fc8f8b9b5c4a03e802b3cd0c18b2b06b410d3c1dcbef989fdeb943bd44aff7"}, + {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:03b0b307ba26fae695e067b94cbb014e27390f8bc5ac7a3a39b7723fed085604"}, + {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d69ce1f324dc2d71e40c9261d3fdbe7d4c9d60f332069ff9b2a4d8a257c7b2b"}, + {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05bc2ceadc2529ab0b227b1310d249d95d9001cd106aa4d31e8871ad3c428d73"}, + {file = "grpcio-1.66.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ac475e8da31484efa25abb774674d837b343afb78bb3bcdef10f81a93e3d6bf"}, + {file = "grpcio-1.66.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0be4e0490c28da5377283861bed2941d1d20ec017ca397a5df4394d1c31a9b50"}, + {file = "grpcio-1.66.2-cp310-cp310-win32.whl", hash = "sha256:4e504572433f4e72b12394977679161d495c4c9581ba34a88d843eaf0f2fbd39"}, + {file = "grpcio-1.66.2-cp310-cp310-win_amd64.whl", hash = "sha256:2018b053aa15782db2541ca01a7edb56a0bf18c77efed975392583725974b249"}, + {file = "grpcio-1.66.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:2335c58560a9e92ac58ff2bc5649952f9b37d0735608242973c7a8b94a6437d8"}, + {file = "grpcio-1.66.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45a3d462826f4868b442a6b8fdbe8b87b45eb4f5b5308168c156b21eca43f61c"}, + {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a9539f01cb04950fd4b5ab458e64a15f84c2acc273670072abe49a3f29bbad54"}, + {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce89f5876662f146d4c1f695dda29d4433a5d01c8681fbd2539afff535da14d4"}, + {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25a14af966438cddf498b2e338f88d1c9706f3493b1d73b93f695c99c5f0e2a"}, + {file = "grpcio-1.66.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6001e575b8bbd89eee11960bb640b6da6ae110cf08113a075f1e2051cc596cae"}, + {file = "grpcio-1.66.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4ea1d062c9230278793820146c95d038dc0f468cbdd172eec3363e42ff1c7d01"}, + {file = "grpcio-1.66.2-cp311-cp311-win32.whl", hash = "sha256:38b68498ff579a3b1ee8f93a05eb48dc2595795f2f62716e797dc24774c1aaa8"}, + {file = "grpcio-1.66.2-cp311-cp311-win_amd64.whl", hash = "sha256:6851de821249340bdb100df5eacfecfc4e6075fa85c6df7ee0eb213170ec8e5d"}, + {file = "grpcio-1.66.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:802d84fd3d50614170649853d121baaaa305de7b65b3e01759247e768d691ddf"}, + {file = "grpcio-1.66.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80fd702ba7e432994df208f27514280b4b5c6843e12a48759c9255679ad38db8"}, + {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:12fda97ffae55e6526825daf25ad0fa37483685952b5d0f910d6405c87e3adb6"}, + {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:950da58d7d80abd0ea68757769c9db0a95b31163e53e5bb60438d263f4bed7b7"}, + {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e636ce23273683b00410f1971d209bf3689238cf5538d960adc3cdfe80dd0dbd"}, + {file = "grpcio-1.66.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a917d26e0fe980b0ac7bfcc1a3c4ad6a9a4612c911d33efb55ed7833c749b0ee"}, + {file = "grpcio-1.66.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49f0ca7ae850f59f828a723a9064cadbed90f1ece179d375966546499b8a2c9c"}, + {file = "grpcio-1.66.2-cp312-cp312-win32.whl", hash = "sha256:31fd163105464797a72d901a06472860845ac157389e10f12631025b3e4d0453"}, + {file = "grpcio-1.66.2-cp312-cp312-win_amd64.whl", hash = "sha256:ff1f7882e56c40b0d33c4922c15dfa30612f05fb785074a012f7cda74d1c3679"}, + {file = "grpcio-1.66.2-cp313-cp313-linux_armv7l.whl", hash = "sha256:3b00efc473b20d8bf83e0e1ae661b98951ca56111feb9b9611df8efc4fe5d55d"}, + {file = "grpcio-1.66.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1caa38fb22a8578ab8393da99d4b8641e3a80abc8fd52646f1ecc92bcb8dee34"}, + {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c408f5ef75cfffa113cacd8b0c0e3611cbfd47701ca3cdc090594109b9fcbaed"}, + {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c806852deaedee9ce8280fe98955c9103f62912a5b2d5ee7e3eaa284a6d8d8e7"}, + {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f145cc21836c332c67baa6fc81099d1d27e266401565bf481948010d6ea32d46"}, + {file = "grpcio-1.66.2-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:73e3b425c1e155730273f73e419de3074aa5c5e936771ee0e4af0814631fb30a"}, + {file = "grpcio-1.66.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9c509a4f78114cbc5f0740eb3d7a74985fd2eff022971bc9bc31f8bc93e66a3b"}, + {file = "grpcio-1.66.2-cp313-cp313-win32.whl", hash = "sha256:20657d6b8cfed7db5e11b62ff7dfe2e12064ea78e93f1434d61888834bc86d75"}, + {file = "grpcio-1.66.2-cp313-cp313-win_amd64.whl", hash = "sha256:fb70487c95786e345af5e854ffec8cb8cc781bcc5df7930c4fbb7feaa72e1cdf"}, + {file = "grpcio-1.66.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:a18e20d8321c6400185b4263e27982488cb5cdd62da69147087a76a24ef4e7e3"}, + {file = "grpcio-1.66.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:02697eb4a5cbe5a9639f57323b4c37bcb3ab2d48cec5da3dc2f13334d72790dd"}, + {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:99a641995a6bc4287a6315989ee591ff58507aa1cbe4c2e70d88411c4dcc0839"}, + {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ed71e81782966ffead60268bbda31ea3f725ebf8aa73634d5dda44f2cf3fb9c"}, + {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbd27c24a4cc5e195a7f56cfd9312e366d5d61b86e36d46bbe538457ea6eb8dd"}, + {file = "grpcio-1.66.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d9a9724a156c8ec6a379869b23ba3323b7ea3600851c91489b871e375f710bc8"}, + {file = "grpcio-1.66.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d8d4732cc5052e92cea2f78b233c2e2a52998ac40cd651f40e398893ad0d06ec"}, + {file = "grpcio-1.66.2-cp38-cp38-win32.whl", hash = "sha256:7b2c86457145ce14c38e5bf6bdc19ef88e66c5fee2c3d83285c5aef026ba93b3"}, + {file = "grpcio-1.66.2-cp38-cp38-win_amd64.whl", hash = "sha256:e88264caad6d8d00e7913996030bac8ad5f26b7411495848cc218bd3a9040b6c"}, + {file = "grpcio-1.66.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:c400ba5675b67025c8a9f48aa846f12a39cf0c44df5cd060e23fda5b30e9359d"}, + {file = "grpcio-1.66.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:66a0cd8ba6512b401d7ed46bb03f4ee455839957f28b8d61e7708056a806ba6a"}, + {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:06de8ec0bd71be123eec15b0e0d457474931c2c407869b6c349bd9bed4adbac3"}, + {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb57870449dfcfac428afbb5a877829fcb0d6db9d9baa1148705739e9083880e"}, + {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b672abf90a964bfde2d0ecbce30f2329a47498ba75ce6f4da35a2f4532b7acbc"}, + {file = "grpcio-1.66.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ad2efdbe90c73b0434cbe64ed372e12414ad03c06262279b104a029d1889d13e"}, + {file = "grpcio-1.66.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c3a99c519f4638e700e9e3f83952e27e2ea10873eecd7935823dab0c1c9250e"}, + {file = "grpcio-1.66.2-cp39-cp39-win32.whl", hash = "sha256:78fa51ebc2d9242c0fc5db0feecc57a9943303b46664ad89921f5079e2e4ada7"}, + {file = "grpcio-1.66.2-cp39-cp39-win_amd64.whl", hash = "sha256:728bdf36a186e7f51da73be7f8d09457a03061be848718d0edf000e709418987"}, + {file = "grpcio-1.66.2.tar.gz", hash = "sha256:563588c587b75c34b928bc428548e5b00ea38c46972181a4d8b75ba7e3f24231"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.66.1)"] +protobuf = ["grpcio-tools (>=1.66.2)"] [[package]] name = "grpcio-tools" -version = "1.66.1" +version = "1.66.2" description = "Protobuf code generator for gRPC" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio_tools-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:e0c71405399ef59782600b1f0bdebc69ba12d7c9527cd268162a86273971d294"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:df1a174a6f9d3b4c380f005f33352d2e95464f33f021fb08084735a2eb6e23b1"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7d789bfe53fce9e87aa80c3694a366258ce4c41b706258e9228ed4994832b780"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95c44a265ff01fd05166edae9350bc2e7d1d9a95e8f53b8cd04d2ae0a588c583"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b962a8767c3c0f9afe92e0dd6bb0b2305d35195a1053f84d4d31f585b87557ed"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d8616773126ec3cdf747b06a12e957b43ac15c34e4728def91fa67249a7c689a"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0067e79b6001560ac6acc78cca11fd3504fa27f8af46e3cdbac2f4998505e597"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-win32.whl", hash = "sha256:fa4f95a79a34afc3b5464895d091cd1911227fc3ab0441b9a37cd1817cf7db86"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:3acce426f5e643de63019311171f4d31131da8149de518716a95c29a2c12dd38"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:9a07e24feb7472419cf70ebbb38dd4299aea696f91f191b62a99b3ee9ff03f89"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:097a069e7c640043921ecaf3e88d7af78ccd40c25dbddc91db2a4a2adbd0393d"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:016fa273dc696c9d8045091ac50e000bce766183a6b150801f51c2946e33dbe3"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ec9f4f964f8e8ed5e9cc13deb678c83d5597074c256805373220627833bc5ad"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3198815814cdd12bdb69b7580d7770a4ad4c8b2093e0bd6b987bc817618e3eec"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:796620fc41d3fbb566d9614ef22bc55df67fac1f1e19c1e0fb6ec48bc9b6a44b"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:222d8dc218560698e1abf652fb47e4015994ec7a265ef46e012fd9c9e77a4d6b"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-win32.whl", hash = "sha256:56e17a11f34df252b4c6fb8aa8cd7b44d162dba9f3333be87ddf7c8bf496622a"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:edd52d667f2aa3c73233be0a821596937f24536647c12d96bfc54aa4cb04747d"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:869b6960d5daffda0dac1a474b44144f0dace0d4336394e499c4f400c5e2f8d9"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:68d9390bf9ba863ac147fc722d6548caa587235e887cac1bc2438212e89d1de7"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:b8660401beca7e3af28722439e07b0bcdca80b4a68f5a5a1138ae7b7780a6abf"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb67b9aa9cd69468bceb933e8e0f89fd13695746c018c4d2e6b3b84e73f3ad97"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5daceb9716e31edc0e1ba0f93303785211438c43502edddad7a919fc4cb3d664"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0a86398a4cd0665bc7f09fa90b89bac592c959d2c895bf3cf5d47a98c0f2d24c"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1b4acb53338072ab3023e418a5c7059cb15686abd1607516fa1453406dd5f69d"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-win32.whl", hash = "sha256:88e04b7546101bc79c868c941777efd5088063a9e4f03b4d7263dde796fbabf7"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:5b4fc56abeafae74140f5da29af1093e88ce64811d77f1a81c3146e9e996fb6a"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:d4dd2ff982c1aa328ef47ce34f07af82f1f13599912fb1618ebc5fe1e14dddb8"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:066648543f786cb74b1fef5652359952455dbba37e832642026fd9fd8a219b5f"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d19d47744c30e6bafa76b3113740e71f382d75ebb2918c1efd62ebe6ba7e20f9"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739c53571130b359b738ac7d6d0a1f772e15779b66df7e6764bee4071cd38689"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2226ff8d3ecba83b7622946df19d6e8e15cb52f761b8d9e2f807b228db5f1b1e"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2f4b1498cb8b422fbae32a491c9154e8d47650caf5852fbe6b3b34253e824343"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:93d2d9e14e81affdc63d67c42eb16a8da1b6fecc16442a703ca60eb0e7591691"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-win32.whl", hash = "sha256:d761dfd97a10e4aae73628b5120c64e56f0cded88651d0003d2d80e678c3e7c9"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:e1c2ac0955f5fb87b8444316e475242d194c3f3cd0b7b6e54b889a7b6f05156f"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:5f1f04578b72c281e39274348a61d240c48d5321ba8d7a8838e194099ecbc322"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:da9b0c08dbbf07535ee1b75a22d0acc5675a808a3a3df9f9b21e0e73ddfbb3a9"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:e302b4e1fa856d74ff65c65888b3a37153287ce6ad5bad80b2fdf95130accec2"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fc3f62494f238774755ff90f0e66a93ac7972ea1eb7180c45acf4fd53b25cca"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23cad65ff22459aa387f543d293f54834c9aac8f76fb7416a7046556df75b567"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3d17a27c567a5e4d18f487368215cb51b43e2499059fd6113b92f7ae1fee48be"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4df167e67b083f96bc277032a526f6186e98662aaa49baea1dfb8ecfe26ce117"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-win32.whl", hash = "sha256:f94d5193b2f2a9595795b83e7978b2bee1c0399da66f2f24d179c388f81fb99c"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:66f527a1e3f063065e29cf6f3e55892434d13a5a51e3b22402e09da9521e98a3"}, - {file = "grpcio_tools-1.66.1.tar.gz", hash = "sha256:5055ffe840ea8f505c30378be02afb4dbecb33480e554debe10b63d6b2f641c3"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:40b7ad804ff78490408177cfe87427d5a67224f82a2bdfabe9d8d6ac6239733b"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:a886fa2ff9e897b35489557d1c61cbc0e4efc42c4dc0d120a9516f294fefb107"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:1d5e22b2c7f5b453462c85aa66f99961d5c7b275d1c60b84fe847c06c73c9400"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a425b2600ad4fcf887107ee975a9b7c20478c2959c58b12af7f36577d7a7f7b3"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef979af76b0cd3f5235d3ec30e86a4f0acc0eab179e796ddbb481aa351a1e6ca"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:99638043e1a78b8617f31b676f1ecf248d75a45b318776af3acc48a85c8e10a2"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a465850c7e5c4ab588c7b7275d47781e9c0ee397a8faf4977262592f95e1831"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-win32.whl", hash = "sha256:48997b704d2fcf59d922228c7a79fcd35d52ca8b2202e5cfe193962643b8354f"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-win_amd64.whl", hash = "sha256:ab4eda584ba2e647e9bb5098f5e4e8d370a333761bf33924e9a7c14f069c8b08"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:007750b4db62018e441f8401fa567aa11174ae0173826cbbe54982fdf2383067"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18554bc91640b2f1ce18aa5c6bebd51500ca0b43b5df4e700e6f76522e2b0e94"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:3fe2fc2e4a16d745cae01e1348b401378e58ced920ff759a6b4b85a7ad507896"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0933420362621d8792fea9350f0c82c514da5f93888d1476c37d9e3722d260b0"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3aef5abd34bea8ea98448cd58a938992238c4717df93d12f84fa5f56efb11d0"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7afd9eb9be413a731cff7ad638081795a7ed0fec4b23af5cec2099fbd9d742f9"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fd1fa95188ae7d5460a8c4a2abcb2777fdf9c3b80d592a2e8434c52a6eb48e8d"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-win32.whl", hash = "sha256:80c233215cf0f08353b7aac4e86cdedf4d545ed368a7491ccc9996e5a317dce4"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-win_amd64.whl", hash = "sha256:2a9a376b300aa2b4da8e6c4f6f746e824d3f24eefeac2753ffffe2b9f37d156d"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:d8ca76fc40a7d35ddf1229afd04408e2ff94caf4385068c8b147e064e951e0ba"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6cc3da6994d575c425c74ce33e34b86a975ea7e78bb9c3525e8439a3af3c508f"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:89e437ced43275e7427cc82a837f5cd43ebe18a1080b0e50a47627895b44b0e6"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d95f030e708266d7fd6d3e5d56e30a9bbbe230604856b1fe93edd892e4389aab"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b3cf9ae67f8bb431ab3ff60db75c3586dc5aa993be4b15bd7cad651362563cd"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b4896a0853fc402273e908c0a0710d25242f1ae907efb9d22ba6d82d4ba00ad8"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d31aad10f90fccb0073bc03b4d1b67690ef4f0cd9af96e82944b9cc655d12b6f"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-win32.whl", hash = "sha256:d8f976f35683e49467d0bf2b90c170ac5443cd162d48d8d868801fd0d87a5fa8"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-win_amd64.whl", hash = "sha256:b2c19e5a888a6ee48ba699581a90c04806b2a93f574f37449c359ec17a793669"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-linux_armv7l.whl", hash = "sha256:7e8c9aa91a9e51199048202e3c54491e0a89fb3ac47dde36ff2964fbcee143a3"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0eaedd3c77824c3762b728c485f91097a58116fa135f3bbc24703621476cd866"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:a14007902fb6565c21815da4177105ec905ef37f0550190c4d1bbeb2928c6560"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df8f098bb92d192230f3b23df514b139f3549e2a4390d1f0f0d8ff89de458c54"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c68642829368f4f83929e0df571dbbc99f1f1553555d8f98d0582da9f6743d9e"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:5fd20110d2c7706dfdd95457807acb8c050253be2e272b9f5fb977e87ea44d86"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:4b16244be4cff92408eb82901b883a70f3dd902fb7c7f66e2a368271be84cde4"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-win32.whl", hash = "sha256:d872ba3bbe9e15b43eeb9310dad5edbf490bb3ab0072a46b3a12fed0234eec23"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-win_amd64.whl", hash = "sha256:a2810921218471aab5c8cd20204d3b1886aa8e13b495e882158bb398982cf18e"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:538eb263b9969e866619775df341307ece0b09afce091ede8141c5bb4d7d8933"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9a68c71bb1358f0994fc7d0f0d70a0d419d57507faa25c982145be401f6aca48"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:1bc41d5b36d414bb0940aa50e30d624903a2538f9387ae730953675adcbe1498"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c43dcd3ee13418545ea10416f46296ddbc7fb355cf136ddebd3b3f881a383168"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc16f9e6baafed315846e79a746513863e6ecbb89e9c98d872834e44f9e87a5"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3655c96eef8aac2a610bbf4cb9c7839fcff09f07a609b74408b3b0a136e1ef57"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:86d971fc64e63642058ac01ce2e484a8340d60a95ead0dc6697ef2aa18a7b936"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-win32.whl", hash = "sha256:c14db004b28ee2adefc6d36107d7fdf770f7509bd1f1ecd195eecb88cdbe5d96"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-win_amd64.whl", hash = "sha256:c65f12474634195ff5ed91b304412b80008c067d28226c26b4e451ea9da16b24"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:75c6a25a5cf729c4606c388013cf7c59dda99cf3718c24fe4fd52b06c19955d0"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5a5146e780ed87348d84b11fc3843741e676b2a84d493363bf0b4ae31c56841b"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c42ba1b24e701544bf08a43bb2d63d56dedd0fd33a5b499c9cf85e15aa154b13"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5daf9807260e172ffcc5dd582c01f60bac820f99f0151a507c8a537f9e6dceb8"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a15a4d0f4eba3773dabe07113b42e018a8fa9a28441483ada111991d5c1468b6"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:cc4f65cd189832676dca16046a4b6247d0bc1fc20648d16ac7fb0b075d1658f4"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ba63dbcbb8ade67e5a04dd3a6c5860efb454bda6d5e8558b17c9a7251339ce36"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-win32.whl", hash = "sha256:c4df0f547f4193dfa6689949b374974f08d81f129174738f0410ba8d45dc63be"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-win_amd64.whl", hash = "sha256:0cad9ffe5df7801201773b91f14923cf3e20ca764e418ae7f8cb75f6045a0aa1"}, + {file = "grpcio_tools-1.66.2.tar.gz", hash = "sha256:4a36e07913d26ba5ccfd2685ba63ca97f26b08c249d2cc9e74dda37efa49d7e4"}, ] [package.dependencies] -grpcio = ">=1.66.1" +grpcio = ">=1.66.2" protobuf = ">=5.26.1,<6.0dev" setuptools = "*" diff --git a/python/examples/minimal/pyproject.toml b/python/examples/simple/pyproject.toml similarity index 88% rename from python/examples/minimal/pyproject.toml rename to python/examples/simple/pyproject.toml index f01d630..63914e5 100644 --- a/python/examples/minimal/pyproject.toml +++ b/python/examples/simple/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "minimal" +name = "simple" version = "0.1.0" description = "" authors = ["Felix Nguyen "] @@ -9,9 +9,10 @@ readme = "README.md" python = ">=3.10, <3.13" arflow = { path = "../.." } + [build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] -minimal = "minimal:main" +simple = "simple:main" diff --git a/python/examples/simple/simple.py b/python/examples/simple/simple.py new file mode 100644 index 0000000..80c4555 --- /dev/null +++ b/python/examples/simple/simple.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +"""A simple example of extending the ARFlow server.""" + +from pathlib import Path + +import arflow + + +class CustomService(arflow.ARFlowServicer): + def on_register(self, request: arflow.RegisterClientRequest): + """Called when a client registers.""" + print("Client registered!") + + def on_frame_received(self, decoded_data_frame: arflow.DecodedDataFrame): + """Called when a frame is received.""" + print("Frame received!") + + +def main(): + arflow.run_server(CustomService, port=8500, path_to_save=Path("./")) + + +if __name__ == "__main__": + main() diff --git a/python/examples/xihe/poetry.lock b/python/examples/xihe/poetry.lock index 2c6ac83..74960cc 100644 --- a/python/examples/xihe/poetry.lock +++ b/python/examples/xihe/poetry.lock @@ -2,8 +2,8 @@ [[package]] name = "arflow" -version = "0.2.0" -description = "" +version = "0.3.0" +description = "ARFlow is a data-sharing layer that enables developer-friendly data streaming, storage, and visualization for augmented reality (AR) device data." optional = false python-versions = ">=3.9,<3.13" files = [] @@ -12,7 +12,7 @@ develop = false [package.dependencies] grpcio = "^1.60.1" grpcio-tools = "^1.60.1" -rerun-sdk = "^0.12.1" +rerun-sdk = "^0.18.2" [package.source] type = "directory" @@ -39,18 +39,18 @@ tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "filelock" -version = "3.16.0" +version = "3.16.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.16.0-py3-none-any.whl", hash = "sha256:f6ed4c963184f4c84dd5557ce8fece759a3724b37b80c6c4f20a2f63a4dc6609"}, - {file = "filelock-3.16.0.tar.gz", hash = "sha256:81de9eb8453c769b63369f87f11131a7ab04e367f8d97ad39dc230daa07e3bec"}, + {file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, + {file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.1.1)", "pytest (>=8.3.2)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.3)"] +docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] [[package]] @@ -555,40 +555,53 @@ files = [ [[package]] name = "pandas" -version = "2.2.2" +version = "2.2.3" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, - {file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, - {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, - {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, - {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, - {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, - {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"}, + {file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"}, + {file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"}, + {file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"}, + {file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"}, + {file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"}, + {file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"}, + {file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"}, + {file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"}, + {file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"}, + {file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"}, + {file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"}, + {file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"}, + {file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"}, + {file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"}, + {file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"}, + {file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"}, + {file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"}, + {file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"}, + {file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"}, + {file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"}, + {file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"}, + {file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"}, + {file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"}, + {file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"}, ] [package.dependencies] @@ -725,72 +738,75 @@ xmp = ["defusedxml"] [[package]] name = "protobuf" -version = "5.28.1" +version = "5.28.2" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.28.1-cp310-abi3-win32.whl", hash = "sha256:fc063acaf7a3d9ca13146fefb5b42ac94ab943ec6e978f543cd5637da2d57957"}, - {file = "protobuf-5.28.1-cp310-abi3-win_amd64.whl", hash = "sha256:4c7f5cb38c640919791c9f74ea80c5b82314c69a8409ea36f2599617d03989af"}, - {file = "protobuf-5.28.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4304e4fceb823d91699e924a1fdf95cde0e066f3b1c28edb665bda762ecde10f"}, - {file = "protobuf-5.28.1-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:0dfd86d2b5edf03d91ec2a7c15b4e950258150f14f9af5f51c17fa224ee1931f"}, - {file = "protobuf-5.28.1-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:51f09caab818707ab91cf09cc5c156026599cf05a4520779ccbf53c1b352fb25"}, - {file = "protobuf-5.28.1-cp38-cp38-win32.whl", hash = "sha256:1b04bde117a10ff9d906841a89ec326686c48ececeb65690f15b8cabe7149495"}, - {file = "protobuf-5.28.1-cp38-cp38-win_amd64.whl", hash = "sha256:cabfe43044ee319ad6832b2fda332646f9ef1636b0130186a3ae0a52fc264bb4"}, - {file = "protobuf-5.28.1-cp39-cp39-win32.whl", hash = "sha256:4b4b9a0562a35773ff47a3df823177ab71a1f5eb1ff56d8f842b7432ecfd7fd2"}, - {file = "protobuf-5.28.1-cp39-cp39-win_amd64.whl", hash = "sha256:f24e5d70e6af8ee9672ff605d5503491635f63d5db2fffb6472be78ba62efd8f"}, - {file = "protobuf-5.28.1-py3-none-any.whl", hash = "sha256:c529535e5c0effcf417682563719e5d8ac8d2b93de07a56108b4c2d436d7a29a"}, - {file = "protobuf-5.28.1.tar.gz", hash = "sha256:42597e938f83bb7f3e4b35f03aa45208d49ae8d5bcb4bc10b9fc825e0ab5e423"}, + {file = "protobuf-5.28.2-cp310-abi3-win32.whl", hash = "sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d"}, + {file = "protobuf-5.28.2-cp310-abi3-win_amd64.whl", hash = "sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132"}, + {file = "protobuf-5.28.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7"}, + {file = "protobuf-5.28.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f"}, + {file = "protobuf-5.28.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f"}, + {file = "protobuf-5.28.2-cp38-cp38-win32.whl", hash = "sha256:87317e9bcda04a32f2ee82089a204d3a2f0d3c8aeed16568c7daf4756e4f1fe0"}, + {file = "protobuf-5.28.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0ea0123dac3399a2eeb1a1443d82b7afc9ff40241433296769f7da42d142ec3"}, + {file = "protobuf-5.28.2-cp39-cp39-win32.whl", hash = "sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36"}, + {file = "protobuf-5.28.2-cp39-cp39-win_amd64.whl", hash = "sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276"}, + {file = "protobuf-5.28.2-py3-none-any.whl", hash = "sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece"}, + {file = "protobuf-5.28.2.tar.gz", hash = "sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0"}, ] [[package]] name = "pyarrow" -version = "14.0.2" +version = "17.0.0" description = "Python library for Apache Arrow" optional = false python-versions = ">=3.8" files = [ - {file = "pyarrow-14.0.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:ba9fe808596c5dbd08b3aeffe901e5f81095baaa28e7d5118e01354c64f22807"}, - {file = "pyarrow-14.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:22a768987a16bb46220cef490c56c671993fbee8fd0475febac0b3e16b00a10e"}, - {file = "pyarrow-14.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dbba05e98f247f17e64303eb876f4a80fcd32f73c7e9ad975a83834d81f3fda"}, - {file = "pyarrow-14.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a898d134d00b1eca04998e9d286e19653f9d0fcb99587310cd10270907452a6b"}, - {file = "pyarrow-14.0.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:87e879323f256cb04267bb365add7208f302df942eb943c93a9dfeb8f44840b1"}, - {file = "pyarrow-14.0.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:76fc257559404ea5f1306ea9a3ff0541bf996ff3f7b9209fc517b5e83811fa8e"}, - {file = "pyarrow-14.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0c4a18e00f3a32398a7f31da47fefcd7a927545b396e1f15d0c85c2f2c778cd"}, - {file = "pyarrow-14.0.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:87482af32e5a0c0cce2d12eb3c039dd1d853bd905b04f3f953f147c7a196915b"}, - {file = "pyarrow-14.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:059bd8f12a70519e46cd64e1ba40e97eae55e0cbe1695edd95384653d7626b23"}, - {file = "pyarrow-14.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f16111f9ab27e60b391c5f6d197510e3ad6654e73857b4e394861fc79c37200"}, - {file = "pyarrow-14.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06ff1264fe4448e8d02073f5ce45a9f934c0f3db0a04460d0b01ff28befc3696"}, - {file = "pyarrow-14.0.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:6dd4f4b472ccf4042f1eab77e6c8bce574543f54d2135c7e396f413046397d5a"}, - {file = "pyarrow-14.0.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:32356bfb58b36059773f49e4e214996888eeea3a08893e7dbde44753799b2a02"}, - {file = "pyarrow-14.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:52809ee69d4dbf2241c0e4366d949ba035cbcf48409bf404f071f624ed313a2b"}, - {file = "pyarrow-14.0.2-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:c87824a5ac52be210d32906c715f4ed7053d0180c1060ae3ff9b7e560f53f944"}, - {file = "pyarrow-14.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a25eb2421a58e861f6ca91f43339d215476f4fe159eca603c55950c14f378cc5"}, - {file = "pyarrow-14.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c1da70d668af5620b8ba0a23f229030a4cd6c5f24a616a146f30d2386fec422"}, - {file = "pyarrow-14.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2cc61593c8e66194c7cdfae594503e91b926a228fba40b5cf25cc593563bcd07"}, - {file = "pyarrow-14.0.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:78ea56f62fb7c0ae8ecb9afdd7893e3a7dbeb0b04106f5c08dbb23f9c0157591"}, - {file = "pyarrow-14.0.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:37c233ddbce0c67a76c0985612fef27c0c92aef9413cf5aa56952f359fcb7379"}, - {file = "pyarrow-14.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:e4b123ad0f6add92de898214d404e488167b87b5dd86e9a434126bc2b7a5578d"}, - {file = "pyarrow-14.0.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:e354fba8490de258be7687f341bc04aba181fc8aa1f71e4584f9890d9cb2dec2"}, - {file = "pyarrow-14.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:20e003a23a13da963f43e2b432483fdd8c38dc8882cd145f09f21792e1cf22a1"}, - {file = "pyarrow-14.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc0de7575e841f1595ac07e5bc631084fd06ca8b03c0f2ecece733d23cd5102a"}, - {file = "pyarrow-14.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e986dc859712acb0bd45601229021f3ffcdfc49044b64c6d071aaf4fa49e98"}, - {file = "pyarrow-14.0.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:f7d029f20ef56673a9730766023459ece397a05001f4e4d13805111d7c2108c0"}, - {file = "pyarrow-14.0.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:209bac546942b0d8edc8debda248364f7f668e4aad4741bae58e67d40e5fcf75"}, - {file = "pyarrow-14.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:1e6987c5274fb87d66bb36816afb6f65707546b3c45c44c28e3c4133c010a881"}, - {file = "pyarrow-14.0.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a01d0052d2a294a5f56cc1862933014e696aa08cc7b620e8c0cce5a5d362e976"}, - {file = "pyarrow-14.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a51fee3a7db4d37f8cda3ea96f32530620d43b0489d169b285d774da48ca9785"}, - {file = "pyarrow-14.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64df2bf1ef2ef14cee531e2dfe03dd924017650ffaa6f9513d7a1bb291e59c15"}, - {file = "pyarrow-14.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c0fa3bfdb0305ffe09810f9d3e2e50a2787e3a07063001dcd7adae0cee3601a"}, - {file = "pyarrow-14.0.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c65bf4fd06584f058420238bc47a316e80dda01ec0dfb3044594128a6c2db794"}, - {file = "pyarrow-14.0.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:63ac901baec9369d6aae1cbe6cca11178fb018a8d45068aaf5bb54f94804a866"}, - {file = "pyarrow-14.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:75ee0efe7a87a687ae303d63037d08a48ef9ea0127064df18267252cfe2e9541"}, - {file = "pyarrow-14.0.2.tar.gz", hash = "sha256:36cef6ba12b499d864d1def3e990f97949e0b79400d08b7cf74504ffbd3eb025"}, + {file = "pyarrow-17.0.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07"}, + {file = "pyarrow-17.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8"}, + {file = "pyarrow-17.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047"}, + {file = "pyarrow-17.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977"}, + {file = "pyarrow-17.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420"}, + {file = "pyarrow-17.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4"}, + {file = "pyarrow-17.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22"}, + {file = "pyarrow-17.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a"}, + {file = "pyarrow-17.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b"}, + {file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"}, + {file = "pyarrow-17.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:af5ff82a04b2171415f1410cff7ebb79861afc5dae50be73ce06d6e870615204"}, + {file = "pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c7916bff914ac5d4a8fe25b7a25e432ff921e72f6f2b7547d1e325c1ad9d155"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f553ca691b9e94b202ff741bdd40f6ccb70cdd5fbf65c187af132f1317de6145"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0cdb0e627c86c373205a2f94a510ac4376fdc523f8bb36beab2e7f204416163c"}, + {file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d7d192305d9d8bc9082d10f361fc70a73590a4c65cf31c3e6926cd72b76bc35c"}, + {file = "pyarrow-17.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:02dae06ce212d8b3244dd3e7d12d9c4d3046945a5933d28026598e9dbbda1fca"}, + {file = "pyarrow-17.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb"}, + {file = "pyarrow-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5"}, + {file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda"}, + {file = "pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204"}, + {file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"}, ] [package.dependencies] numpy = ">=1.16.6" +[package.extras] +test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -818,41 +834,43 @@ files = [ [[package]] name = "rerun-sdk" -version = "0.12.1" +version = "0.18.2" description = "The Rerun Logging SDK" optional = false -python-versions = ">=3.8, <3.13" +python-versions = "<3.13,>=3.8" files = [ - {file = "rerun_sdk-0.12.1-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:00b02adabe1ecf380d4c1fd3ddbfd5347c2eab912e15b5394977f8deff4955f2"}, - {file = "rerun_sdk-0.12.1-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec010caeddba99ab55498680d44d81eb0bab3180db4837b40d0e93519151161b"}, - {file = "rerun_sdk-0.12.1-cp38-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:cebe96871222a433eebd30f46d19ee29dc7f0fe4243e1a271f8fd67fda1dcb0e"}, - {file = "rerun_sdk-0.12.1-cp38-abi3-win_amd64.whl", hash = "sha256:12072ee880cdddbd111b53aac312b5ab61d6deb26e25095adf2caed9d1d0ae60"}, + {file = "rerun_sdk-0.18.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:bc4e73275f428e4e9feb8e85f88db7a9fd18b997b1570de62f949a926978f1b2"}, + {file = "rerun_sdk-0.18.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:efbba40a59710ae83607cb0dc140398a35979c2d2acf5190c9def2ac4697f6a8"}, + {file = "rerun_sdk-0.18.2-cp38-abi3-manylinux_2_31_aarch64.whl", hash = "sha256:2a5e3b618b6d1bfde09bd5614a898995f3c318cc69d8f6d569924a2cd41536ce"}, + {file = "rerun_sdk-0.18.2-cp38-abi3-manylinux_2_31_x86_64.whl", hash = "sha256:8fdfc4c51ef2e75cb68d39e56f0d7c196eff250cb9a0260c07d5e2d6736e31b0"}, + {file = "rerun_sdk-0.18.2-cp38-abi3-win_amd64.whl", hash = "sha256:c929ade91d3be301b26671b25e70fb529524ced915523d266641c6fc667a1eb5"}, ] [package.dependencies] attrs = ">=23.1.0" numpy = ">=1.23,<2" -pillow = "*" -pyarrow = "14.0.2" -typing_extensions = ">=4.5" +pillow = ">=8.0.0" +pyarrow = ">=14.0.2" +typing-extensions = ">=4.5" [package.extras] +notebook = ["rerun-notebook (==0.18.2)"] tests = ["pytest (==7.1.2)"] [[package]] name = "setuptools" -version = "74.1.2" +version = "75.1.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-74.1.2-py3-none-any.whl", hash = "sha256:5f4c08aa4d3ebcb57a50c33b1b07e94315d7fc7230f7115e47fc99776c8ce308"}, - {file = "setuptools-74.1.2.tar.gz", hash = "sha256:95b40ed940a1c67eb70fc099094bd6e99c6ee7c23aa2306f4d2697ba7916f9c6"}, + {file = "setuptools-75.1.0-py3-none-any.whl", hash = "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2"}, + {file = "setuptools-75.1.0.tar.gz", hash = "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538"}, ] [package.extras] check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] enabler = ["pytest-enabler (>=2.2)"] @@ -872,13 +890,13 @@ files = [ [[package]] name = "sympy" -version = "1.13.2" +version = "1.13.3" description = "Computer algebra system (CAS) in Python" optional = false python-versions = ">=3.8" files = [ - {file = "sympy-1.13.2-py3-none-any.whl", hash = "sha256:c51d75517712f1aed280d4ce58506a4a88d635d6b5dd48b39102a7ae1f3fcfe9"}, - {file = "sympy-1.13.2.tar.gz", hash = "sha256:401449d84d07be9d0c7a46a64bd54fe097667d5e7181bfe67ec777be9e01cb13"}, + {file = "sympy-1.13.3-py3-none-any.whl", hash = "sha256:54612cf55a62755ee71824ce692986f23c88ffa77207b30c1368eda4a7060f73"}, + {file = "sympy-1.13.3.tar.gz", hash = "sha256:b27fd2c6530e0ab39e275fc9b683895367e51d5da91baa8d3d64db2565fec4d9"}, ] [package.dependencies] @@ -976,13 +994,13 @@ files = [ [[package]] name = "tzdata" -version = "2024.1" +version = "2024.2" description = "Provider of IANA time zone data" optional = false python-versions = ">=2" files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, + {file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, + {file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, ] [metadata] diff --git a/python/examples/xihe/utils3d/utils/cli.py b/python/examples/xihe/utils3d/utils/cli.py index f35377a..a7e8df9 100644 --- a/python/examples/xihe/utils3d/utils/cli.py +++ b/python/examples/xihe/utils3d/utils/cli.py @@ -13,6 +13,6 @@ def pretty_print_matrix(mat): res_rows.append(res_cols) - df = pd.DataFrame(res_rows) + df = pd.ProcessFrameRequest(res_rows) print(df) diff --git a/python/examples/xihe/xihe.py b/python/examples/xihe/xihe.py old mode 100755 new mode 100644 index 66c1d78..0826e13 --- a/python/examples/xihe/xihe.py +++ b/python/examples/xihe/xihe.py @@ -1,6 +1,10 @@ #!/usr/bin/env python3 +# type: ignore """A demo integration with Xihe. +Note: `# type: ignore` is added to the first line to suppress typecheck errors. +In case you want to copy this code, please remove the first line if you are using typecheck. + Reference: - Yiqin Zhao and Tian Guo. 2021. Xihe: A 3D Vision-Based Lighting Estimation Framework for Mobile Augmented Reality. In Proceedings of @@ -9,25 +13,24 @@ """ from threading import Thread -from typing import Any, Dict import numpy as np +import numpy.typing as npt import pandas as pd import torch -import utils3d as u3d -import xihenet_utils # This import is necessary to avoid an "operator not found" error when loading # DO NOT REMOVE (please) # To install this (workaround currently), enter "poetry shell" and run: # pip install wheel # pip install torch-cluster -f https://data.pyg.org/whl/torch-2.4.0+${CUDA}.html (this will take very long). -import torch_cluster +import utils3d as u3d +import xihenet_utils import arflow -class XiheService(arflow.ARFlowService): +class XiheService(arflow.ARFlowServicer): def __init__(self, *args, **kwargs) -> None: super().__init__() @@ -39,25 +42,28 @@ def __init__(self, *args, **kwargs) -> None: self.calculator = xihenet_utils.JointEntropyCalculator() - def on_register(self, request: arflow.RegisterRequest): + def on_register(self, request: arflow.RegisterClientRequest): self.num_frame = 0 - def on_frame_received(self, frame_data: Dict[str, Any]): + def on_frame_received(self, decoded_data_frame: arflow.DecodedDataFrame): # Run XiheNet inference. - pcd = frame_data["point_cloud_pcd"] - clr = frame_data["point_cloud_clr"] if self.num_frame % 100 == 0: thread = Thread( - target=self.run_xihenet_inference, args=(pcd.copy(), clr.copy()) + target=self.run_xihenet_inference, args=(decoded_data_frame.point_cloud_pcd.copy(), decoded_data_frame.point_cloud_clr.copy()) ) thread.start() self.num_frame = self.num_frame + 1 - def run_xihenet_inference(self, xyz: np.ndarray, rgb: np.ndarray): + def run_xihenet_inference(self, xyz: npt.NDArray[np.float32], rgb: npt.NDArray[np.uint8]): # Log input entropy. entropy = self.calculator.forward(torch.from_numpy(xyz).float()) - self.recorder.log("Xihe/input_entropy", self.recorder.TimeSeriesScalar(entropy)) + + # TODO: FIX THIS https://rerun.io/docs/reference/migration/migration-0-13#timeseriesscalar-deprecated-in-favor-of-scalartypesarchetypesscalarmd--serieslinetypesarchetypesserieslinemdseriespointtypesarchetypesseriespointmd + # self.recorder.log("Xihe/input_entropy", self.recorder.TimeSeriesScalar(entropy)) + self.recorder.log("Xihe/input_entropy", self.recorder.Scalar(entropy)) + # self.recorder.log("Xihe/input_entropy", self.recorder.SeriesPoint(entropy)) + # self.recorder.log("Xihe/input_entropy", self.recorder.SeriesLine(entropy)) # Inference preprocessing code copied from previous # lighting estimation visualization code. @@ -99,7 +105,7 @@ def run_xihenet_inference(self, xyz: np.ndarray, rgb: np.ndarray): def main(): - arflow.create_server(XiheService, port=8500, path_to_save=None) + arflow.run_server(XiheService, port=8500, path_to_save=None) if __name__ == "__main__": diff --git a/python/poetry.lock b/python/poetry.lock index 5893cb5..5fa5fbd 100644 --- a/python/poetry.lock +++ b/python/poetry.lock @@ -41,6 +41,93 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +[[package]] +name = "coverage" +version = "7.6.1" +description = "Code coverage measurement for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "coverage-7.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b06079abebbc0e89e6163b8e8f0e16270124c154dc6e4a47b413dd538859af16"}, + {file = "coverage-7.6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cf4b19715bccd7ee27b6b120e7e9dd56037b9c0681dcc1adc9ba9db3d417fa36"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61c0abb4c85b095a784ef23fdd4aede7a2628478e7baba7c5e3deba61070a02"}, + {file = "coverage-7.6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fd21f6ae3f08b41004dfb433fa895d858f3f5979e7762d052b12aef444e29afc"}, + {file = "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"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a1ac0ae2b8bd743b88ed0502544847c3053d7171a3cff9228af618a068ed9c34"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e6a08c0be454c3b3beb105c0596ebdc2371fab6bb90c0c0297f4e58fd7e1012c"}, + {file = "coverage-7.6.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f5796e664fe802da4f57a168c85359a8fbf3eab5e55cd4e4569fbacecc903959"}, + {file = "coverage-7.6.1-cp310-cp310-win32.whl", hash = "sha256:7bb65125fcbef8d989fa1dd0e8a060999497629ca5b0efbca209588a73356232"}, + {file = "coverage-7.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:3115a95daa9bdba70aea750db7b96b37259a81a709223c8448fa97727d546fe0"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7dea0889685db8550f839fa202744652e87c60015029ce3f60e006f8c4462c93"}, + {file = "coverage-7.6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed37bd3c3b063412f7620464a9ac1314d33100329f39799255fb8d3027da50d3"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d85f5e9a5f8b73e2350097c3756ef7e785f55bd71205defa0bfdaf96c31616ff"}, + {file = "coverage-7.6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bc572be474cafb617672c43fe989d6e48d3c83af02ce8de73fff1c6bb3c198d"}, + {file = "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"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f4aa8219db826ce6be7099d559f8ec311549bfc4046f7f9fe9b5cea5c581c56"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:fc5a77d0c516700ebad189b587de289a20a78324bc54baee03dd486f0855d234"}, + {file = "coverage-7.6.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b48f312cca9621272ae49008c7f613337c53fadca647d6384cc129d2996d1133"}, + {file = "coverage-7.6.1-cp311-cp311-win32.whl", hash = "sha256:1125ca0e5fd475cbbba3bb67ae20bd2c23a98fac4e32412883f9bcbaa81c314c"}, + {file = "coverage-7.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:8ae539519c4c040c5ffd0632784e21b2f03fc1340752af711f33e5be83a9d6c6"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:95cae0efeb032af8458fc27d191f85d1717b1d4e49f7cb226cf526ff28179778"}, + {file = "coverage-7.6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5621a9175cf9d0b0c84c2ef2b12e9f5f5071357c4d2ea6ca1cf01814f45d2391"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:260933720fdcd75340e7dbe9060655aff3af1f0c5d20f46b57f262ab6c86a5e8"}, + {file = "coverage-7.6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07e2ca0ad381b91350c0ed49d52699b625aab2b44b65e1b4e02fa9df0e92ad2d"}, + {file = "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"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877abb17e6339d96bf08e7a622d05095e72b71f8afd8a9fefc82cf30ed944163"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e0cadcf6733c09154b461f1ca72d5416635e5e4ec4e536192180d34ec160f8a"}, + {file = "coverage-7.6.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c3c02d12f837d9683e5ab2f3d9844dc57655b92c74e286c262e0fc54213c216d"}, + {file = "coverage-7.6.1-cp312-cp312-win32.whl", hash = "sha256:e05882b70b87a18d937ca6768ff33cc3f72847cbc4de4491c8e73880766718e5"}, + {file = "coverage-7.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:b5d7b556859dd85f3a541db6a4e0167b86e7273e1cdc973e5b175166bb634fdb"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a4acd025ecc06185ba2b801f2de85546e0b8ac787cf9d3b06e7e2a69f925b106"}, + {file = "coverage-7.6.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a6d3adcf24b624a7b778533480e32434a39ad8fa30c315208f6d3e5542aeb6e9"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d0c212c49b6c10e6951362f7c6df3329f04c2b1c28499563d4035d964ab8e08c"}, + {file = "coverage-7.6.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e81d7a3e58882450ec4186ca59a3f20a5d4440f25b1cff6f0902ad890e6748a"}, + {file = "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"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a78d169acd38300060b28d600344a803628c3fd585c912cacc9ea8790fe96862"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2c09f4ce52cb99dd7505cd0fc8e0e37c77b87f46bc9c1eb03fe3bc9991085388"}, + {file = "coverage-7.6.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6878ef48d4227aace338d88c48738a4258213cd7b74fd9a3d4d7582bb1d8a155"}, + {file = "coverage-7.6.1-cp313-cp313-win32.whl", hash = "sha256:44df346d5215a8c0e360307d46ffaabe0f5d3502c8a1cefd700b34baf31d411a"}, + {file = "coverage-7.6.1-cp313-cp313-win_amd64.whl", hash = "sha256:8284cf8c0dd272a247bc154eb6c95548722dce90d098c17a883ed36e67cdb129"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:d3296782ca4eab572a1a4eca686d8bfb00226300dcefdf43faa25b5242ab8a3e"}, + {file = "coverage-7.6.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:502753043567491d3ff6d08629270127e0c31d4184c4c8d98f92c26f65019962"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a89ecca80709d4076b95f89f308544ec8f7b4727e8a547913a35f16717856cb"}, + {file = "coverage-7.6.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a318d68e92e80af8b00fa99609796fdbcdfef3629c77c6283566c6f02c6d6704"}, + {file = "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"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4421712dbfc5562150f7554f13dde997a2e932a6b5f352edcce948a815efee6f"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:166811d20dfea725e2e4baa71fffd6c968a958577848d2131f39b60043400223"}, + {file = "coverage-7.6.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:225667980479a17db1048cb2bf8bfb39b8e5be8f164b8f6628b64f78a72cf9d3"}, + {file = "coverage-7.6.1-cp313-cp313t-win32.whl", hash = "sha256:170d444ab405852903b7d04ea9ae9b98f98ab6d7e63e1115e82620807519797f"}, + {file = "coverage-7.6.1-cp313-cp313t-win_amd64.whl", hash = "sha256:b9f222de8cded79c49bf184bdbc06630d4c58eec9459b939b4a690c82ed05657"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6db04803b6c7291985a761004e9060b2bca08da6d04f26a7f2294b8623a0c1a0"}, + {file = "coverage-7.6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f1adfc8ac319e1a348af294106bc6a8458a0f1633cc62a1446aebc30c5fa186a"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a95324a9de9650a729239daea117df21f4b9868ce32e63f8b650ebe6cef5595b"}, + {file = "coverage-7.6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b43c03669dc4618ec25270b06ecd3ee4fa94c7f9b3c14bae6571ca00ef98b0d3"}, + {file = "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"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:a09ece4a69cf399510c8ab25e0950d9cf2b42f7b3cb0374f95d2e2ff594478a6"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:9054a0754de38d9dbd01a46621636689124d666bad1936d76c0341f7d71bf569"}, + {file = "coverage-7.6.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0dbde0f4aa9a16fa4d754356a8f2e36296ff4d83994b2c9d8398aa32f222f989"}, + {file = "coverage-7.6.1-cp38-cp38-win32.whl", hash = "sha256:da511e6ad4f7323ee5702e6633085fb76c2f893aaf8ce4c51a0ba4fc07580ea7"}, + {file = "coverage-7.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:3f1156e3e8f2872197af3840d8ad307a9dd18e615dc64d9ee41696f287c57ad8"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abd5fd0db5f4dc9289408aaf34908072f805ff7792632250dcb36dc591d24255"}, + {file = "coverage-7.6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:547f45fa1a93154bd82050a7f3cddbc1a7a4dd2a9bf5cb7d06f4ae29fe94eaf8"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:645786266c8f18a931b65bfcefdbf6952dd0dea98feee39bd188607a9d307ed2"}, + {file = "coverage-7.6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9e0b2df163b8ed01d515807af24f63de04bebcecbd6c3bfeff88385789fdf75a"}, + {file = "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"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:702855feff378050ae4f741045e19a32d57d19f3e0676d589df0575008ea5004"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:2bdb062ea438f22d99cba0d7829c2ef0af1d768d1e4a4f528087224c90b132cb"}, + {file = "coverage-7.6.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9c56863d44bd1c4fe2abb8a4d6f5371d197f1ac0ebdee542f07f35895fc07f36"}, + {file = "coverage-7.6.1-cp39-cp39-win32.whl", hash = "sha256:6e2cd258d7d927d09493c8df1ce9174ad01b381d4729a9d8d4e38670ca24774c"}, + {file = "coverage-7.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:06a737c882bd26d0d6ee7269b20b12f14a8704807a01056c80bb881a4b2ce6ca"}, + {file = "coverage-7.6.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:e9a6e0eb86070e8ccaedfbd9d38fec54864f3125ab95419970575b42af7541df"}, + {file = "coverage-7.6.1.tar.gz", hash = "sha256:953510dfb7b12ab69d20135a0662397f077c59b1e6379a768e97c59d852ee51d"}, +] + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + [[package]] name = "distlib" version = "0.3.8" @@ -52,6 +139,51 @@ files = [ {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, ] +[[package]] +name = "dracopy" +version = "1.4.0" +description = "Python wrapper for Google's Draco Mesh Compression Library" +optional = false +python-versions = "*" +files = [ + {file = "DracoPy-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bffde5eda803f8a2c76ee4fab0e0d40ee7f5939df5f8eed22bdc4e5c6d9f405"}, + {file = "DracoPy-1.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9d3a00c20f829ea2f2a7355b35bd02f62bbafebb39f742929aa498c66c4ae4ef"}, + {file = "DracoPy-1.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf2e0f15145a23a94794272fce2a373ab953fdc75967c0e46ed7ac2687bdcba4"}, + {file = "DracoPy-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f204151f4ca8a10a0945eb9cd86d99746e2fbd6e8cb21511028e9054d9ee6a1"}, + {file = "DracoPy-1.4.0-cp310-cp310-win32.whl", hash = "sha256:004aeb2e24324f7a0e77424ba1116f03c16120a610ebeb8fea5f88077b8330a1"}, + {file = "DracoPy-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:b3142f77670530ae219a591f97db7589a9a47b0bac836da0ce88221555187a1b"}, + {file = "DracoPy-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:934e7927e26a80cef5d7f57de42aa19deefddfe08e3b64e88225d7b4546e8398"}, + {file = "DracoPy-1.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bef6e752b80a5dec169185784ac47dfa4e6418b547410c1be9af5ccbe614e7cf"}, + {file = "DracoPy-1.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8ec3586416a716f03bd78fecb64d77a2be0fd7c08d5e1bf30c3d9633915e7cc9"}, + {file = "DracoPy-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9e0581f6e5bd064ce642e1e1a7850a91a640790e9b9d27834c0d897992192ab"}, + {file = "DracoPy-1.4.0-cp311-cp311-win32.whl", hash = "sha256:f85a7686f31e076d20a36e10d1ad9035bed3ac523bce2274257eb93f17276a5c"}, + {file = "DracoPy-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:7f95d66b69332ad643a6b86fc40f38ff23fa5db9c366842c00eaeb6ca76ed0ec"}, + {file = "DracoPy-1.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c1b1025a012763ab97291b0863e2d7ba5f3c7301102c06ab1ec3c513ca0a9984"}, + {file = "DracoPy-1.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f7d132f7469c782337d84034c3808d8bcb7ab4c28a4fef1b12f717620e18a2ab"}, + {file = "DracoPy-1.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:45efeb617d68beac1712f6ff1aa1a9a86bd110192f1d688ad9cda46f7c8830c7"}, + {file = "DracoPy-1.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc15e5ff9d070d07632dd897fedd8136cd429cb40a838606cc594b9cbdcb8c43"}, + {file = "DracoPy-1.4.0-cp312-cp312-win32.whl", hash = "sha256:7d5bbae855f8ba16148d6de07ca9367ea8960d65cc6911f4069e407193411eb7"}, + {file = "DracoPy-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:ef6e894922c0596374f160d082fdae6d3639466ca4f528a08a145f445b180ee7"}, + {file = "DracoPy-1.4.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ae51f031af22f5b0a5626b30c7e68576166057b901da3b822c8f75420aa96b25"}, + {file = "DracoPy-1.4.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b217267bf05a4ba8e90d5fd85577b958d8dbd133cde1ad1c0c1dba9b712be16f"}, + {file = "DracoPy-1.4.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e6776c773074bfe492d976cd17c25e46ca622e501153d616fec9455ac03f9651"}, + {file = "DracoPy-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:c0b6f8f2c5539c850c6132b2879afe865b3c9754de93e8060b6868b284d556aa"}, + {file = "DracoPy-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:571f783c32f39fb68a7d02fa599330bcfc4bcc2bcac86128ae10539b6fdcb29a"}, + {file = "DracoPy-1.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fabd1e79e24488e9e7aa187594ca043854b516dc5a3c2736c4d13f31057f51bf"}, + {file = "DracoPy-1.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3f3cbba5a534dc3d8427351130f974d7a4782f21b5289097f72bfb37009bb52c"}, + {file = "DracoPy-1.4.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f27505392dddd01cdd66b5cfd96646104dc9afe49f2e7338235cb39b8204963"}, + {file = "DracoPy-1.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4c64f09c886ce9f2e5dba214602d5249a26ae4f478a4cc0ff61b203df407848"}, + {file = "DracoPy-1.4.0-cp38-cp38-win32.whl", hash = "sha256:8ffbb3ccf09920eb103b6d9bbec96105d7b161b06a448c8549952f0b2188fab6"}, + {file = "DracoPy-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5694c18a500d26798bc37baa38a748308a8f497d1e1538e6a48424ee198d25f2"}, + {file = "DracoPy-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a89b07dca2b1c17a68cadf418a5718ad1b4a4c5ffa6d00f5d3c332e0933bef49"}, + {file = "DracoPy-1.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6071ec96ce833a52fbf7d6e491a2aa6bd6d27a11ccdf2c54250dfeaea1c0311c"}, + {file = "DracoPy-1.4.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eaeff76a0b7e7b7c5015970eaaf021b8cfe2df2b15aed9a63be925bad60a67fe"}, + {file = "DracoPy-1.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a63c9f4a86d320393e698e0aaaa90477fa40b96de799672f15f7b4d784f9a5b4"}, + {file = "DracoPy-1.4.0-cp39-cp39-win32.whl", hash = "sha256:3bc04aeb05dd3925841d5370c620a5537a27f22fce9506e6d9146a86ac4be246"}, + {file = "DracoPy-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:2b1e6527b163ff6d87b619bf430089b888ac2fd61b9833992feb850cb334bb26"}, + {file = "DracoPy-1.4.0.tar.gz", hash = "sha256:a889015283e2f74d62912e4b5c9be29947a0be9fb30f9c8ba635c5124966d5a2"}, +] + [[package]] name = "exceptiongroup" version = "1.2.2" @@ -82,121 +214,170 @@ docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2. testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] typing = ["typing-extensions (>=4.12.2)"] +[[package]] +name = "grpc-interceptor" +version = "0.15.4" +description = "Simplifies gRPC interceptors" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "grpc-interceptor-0.15.4.tar.gz", hash = "sha256:1f45c0bcb58b6f332f37c637632247c9b02bc6af0fdceb7ba7ce8d2ebbfb0926"}, + {file = "grpc_interceptor-0.15.4-py3-none-any.whl", hash = "sha256:0035f33228693ed3767ee49d937bac424318db173fef4d2d0170b3215f254d9d"}, +] + +[package.dependencies] +grpcio = ">=1.49.1,<2.0.0" + +[package.extras] +testing = ["protobuf (>=4.21.9)"] + +[[package]] +name = "grpc-stubs" +version = "1.53.0.5" +description = "Mypy stubs for gRPC" +optional = false +python-versions = ">=3.6" +files = [ + {file = "grpc-stubs-1.53.0.5.tar.gz", hash = "sha256:3e1b642775cbc3e0c6332cfcedfccb022176db87e518757bef3a1241397be406"}, + {file = "grpc_stubs-1.53.0.5-py3-none-any.whl", hash = "sha256:04183fb65a1b166a1febb9627e3d9647d3926ccc2dfe049fe7b6af243428dbe1"}, +] + +[package.dependencies] +grpcio = "*" + [[package]] name = "grpcio" -version = "1.66.1" +version = "1.66.2" description = "HTTP/2-based RPC framework" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:4877ba180591acdf127afe21ec1c7ff8a5ecf0fe2600f0d3c50e8c4a1cbc6492"}, - {file = "grpcio-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:3750c5a00bd644c75f4507f77a804d0189d97a107eb1481945a0cf3af3e7a5ac"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a013c5fbb12bfb5f927444b477a26f1080755a931d5d362e6a9a720ca7dbae60"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b1b24c23d51a1e8790b25514157d43f0a4dce1ac12b3f0b8e9f66a5e2c4c132f"}, - {file = "grpcio-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffb8ea674d68de4cac6f57d2498fef477cef582f1fa849e9f844863af50083"}, - {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:307b1d538140f19ccbd3aed7a93d8f71103c5d525f3c96f8616111614b14bf2a"}, - {file = "grpcio-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1c17ebcec157cfb8dd445890a03e20caf6209a5bd4ac5b040ae9dbc59eef091d"}, - {file = "grpcio-1.66.1-cp310-cp310-win32.whl", hash = "sha256:ef82d361ed5849d34cf09105d00b94b6728d289d6b9235513cb2fcc79f7c432c"}, - {file = "grpcio-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:292a846b92cdcd40ecca46e694997dd6b9be6c4c01a94a0dfb3fcb75d20da858"}, - {file = "grpcio-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:c30aeceeaff11cd5ddbc348f37c58bcb96da8d5aa93fed78ab329de5f37a0d7a"}, - {file = "grpcio-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8a1e224ce6f740dbb6b24c58f885422deebd7eb724aff0671a847f8951857c26"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a66fe4dc35d2330c185cfbb42959f57ad36f257e0cc4557d11d9f0a3f14311df"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3ba04659e4fce609de2658fe4dbf7d6ed21987a94460f5f92df7579fd5d0e22"}, - {file = "grpcio-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4573608e23f7e091acfbe3e84ac2045680b69751d8d67685ffa193a4429fedb1"}, - {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7e06aa1f764ec8265b19d8f00140b8c4b6ca179a6dc67aa9413867c47e1fb04e"}, - {file = "grpcio-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3885f037eb11f1cacc41f207b705f38a44b69478086f40608959bf5ad85826dd"}, - {file = "grpcio-1.66.1-cp311-cp311-win32.whl", hash = "sha256:97ae7edd3f3f91480e48ede5d3e7d431ad6005bfdbd65c1b56913799ec79e791"}, - {file = "grpcio-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:cfd349de4158d797db2bd82d2020554a121674e98fbe6b15328456b3bf2495bb"}, - {file = "grpcio-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:a92c4f58c01c77205df6ff999faa008540475c39b835277fb8883b11cada127a"}, - {file = "grpcio-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fdb14bad0835914f325349ed34a51940bc2ad965142eb3090081593c6e347be9"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:f03a5884c56256e08fd9e262e11b5cfacf1af96e2ce78dc095d2c41ccae2c80d"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ca2559692d8e7e245d456877a85ee41525f3ed425aa97eb7a70fc9a79df91a0"}, - {file = "grpcio-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84ca1be089fb4446490dd1135828bd42a7c7f8421e74fa581611f7afdf7ab761"}, - {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d639c939ad7c440c7b2819a28d559179a4508783f7e5b991166f8d7a34b52815"}, - {file = "grpcio-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b9feb4e5ec8dc2d15709f4d5fc367794d69277f5d680baf1910fc9915c633524"}, - {file = "grpcio-1.66.1-cp312-cp312-win32.whl", hash = "sha256:7101db1bd4cd9b880294dec41a93fcdce465bdbb602cd8dc5bd2d6362b618759"}, - {file = "grpcio-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:b0aa03d240b5539648d996cc60438f128c7f46050989e35b25f5c18286c86734"}, - {file = "grpcio-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:ecfe735e7a59e5a98208447293ff8580e9db1e890e232b8b292dc8bd15afc0d2"}, - {file = "grpcio-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4825a3aa5648010842e1c9d35a082187746aa0cdbf1b7a2a930595a94fb10fce"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:f517fd7259fe823ef3bd21e508b653d5492e706e9f0ef82c16ce3347a8a5620c"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1fe60d0772831d96d263b53d83fb9a3d050a94b0e94b6d004a5ad111faa5b5b"}, - {file = "grpcio-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31a049daa428f928f21090403e5d18ea02670e3d5d172581670be006100db9ef"}, - {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f914386e52cbdeb5d2a7ce3bf1fdfacbe9d818dd81b6099a05b741aaf3848bb"}, - {file = "grpcio-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:bff2096bdba686019fb32d2dde45b95981f0d1490e054400f70fc9a8af34b49d"}, - {file = "grpcio-1.66.1-cp38-cp38-win32.whl", hash = "sha256:aa8ba945c96e73de29d25331b26f3e416e0c0f621e984a3ebdb2d0d0b596a3b3"}, - {file = "grpcio-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:161d5c535c2bdf61b95080e7f0f017a1dfcb812bf54093e71e5562b16225b4ce"}, - {file = "grpcio-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:d0cd7050397b3609ea51727b1811e663ffda8bda39c6a5bb69525ef12414b503"}, - {file = "grpcio-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0e6c9b42ded5d02b6b1fea3a25f036a2236eeb75d0579bfd43c0018c88bf0a3e"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c9f80f9fad93a8cf71c7f161778ba47fd730d13a343a46258065c4deb4b550c0"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dd67ed9da78e5121efc5c510f0122a972216808d6de70953a740560c572eb44"}, - {file = "grpcio-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48b0d92d45ce3be2084b92fb5bae2f64c208fea8ceed7fccf6a7b524d3c4942e"}, - {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4d813316d1a752be6f5c4360c49f55b06d4fe212d7df03253dfdae90c8a402bb"}, - {file = "grpcio-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c9bebc6627873ec27a70fc800f6083a13c70b23a5564788754b9ee52c5aef6c"}, - {file = "grpcio-1.66.1-cp39-cp39-win32.whl", hash = "sha256:30a1c2cf9390c894c90bbc70147f2372130ad189cffef161f0432d0157973f45"}, - {file = "grpcio-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:17663598aadbedc3cacd7bbde432f541c8e07d2496564e22b214b22c7523dac8"}, - {file = "grpcio-1.66.1.tar.gz", hash = "sha256:35334f9c9745add3e357e3372756fd32d925bd52c41da97f4dfdafbde0bf0ee2"}, + {file = "grpcio-1.66.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:fe96281713168a3270878255983d2cb1a97e034325c8c2c25169a69289d3ecfa"}, + {file = "grpcio-1.66.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:73fc8f8b9b5c4a03e802b3cd0c18b2b06b410d3c1dcbef989fdeb943bd44aff7"}, + {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:03b0b307ba26fae695e067b94cbb014e27390f8bc5ac7a3a39b7723fed085604"}, + {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d69ce1f324dc2d71e40c9261d3fdbe7d4c9d60f332069ff9b2a4d8a257c7b2b"}, + {file = "grpcio-1.66.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05bc2ceadc2529ab0b227b1310d249d95d9001cd106aa4d31e8871ad3c428d73"}, + {file = "grpcio-1.66.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8ac475e8da31484efa25abb774674d837b343afb78bb3bcdef10f81a93e3d6bf"}, + {file = "grpcio-1.66.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0be4e0490c28da5377283861bed2941d1d20ec017ca397a5df4394d1c31a9b50"}, + {file = "grpcio-1.66.2-cp310-cp310-win32.whl", hash = "sha256:4e504572433f4e72b12394977679161d495c4c9581ba34a88d843eaf0f2fbd39"}, + {file = "grpcio-1.66.2-cp310-cp310-win_amd64.whl", hash = "sha256:2018b053aa15782db2541ca01a7edb56a0bf18c77efed975392583725974b249"}, + {file = "grpcio-1.66.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:2335c58560a9e92ac58ff2bc5649952f9b37d0735608242973c7a8b94a6437d8"}, + {file = "grpcio-1.66.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:45a3d462826f4868b442a6b8fdbe8b87b45eb4f5b5308168c156b21eca43f61c"}, + {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a9539f01cb04950fd4b5ab458e64a15f84c2acc273670072abe49a3f29bbad54"}, + {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ce89f5876662f146d4c1f695dda29d4433a5d01c8681fbd2539afff535da14d4"}, + {file = "grpcio-1.66.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d25a14af966438cddf498b2e338f88d1c9706f3493b1d73b93f695c99c5f0e2a"}, + {file = "grpcio-1.66.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6001e575b8bbd89eee11960bb640b6da6ae110cf08113a075f1e2051cc596cae"}, + {file = "grpcio-1.66.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4ea1d062c9230278793820146c95d038dc0f468cbdd172eec3363e42ff1c7d01"}, + {file = "grpcio-1.66.2-cp311-cp311-win32.whl", hash = "sha256:38b68498ff579a3b1ee8f93a05eb48dc2595795f2f62716e797dc24774c1aaa8"}, + {file = "grpcio-1.66.2-cp311-cp311-win_amd64.whl", hash = "sha256:6851de821249340bdb100df5eacfecfc4e6075fa85c6df7ee0eb213170ec8e5d"}, + {file = "grpcio-1.66.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:802d84fd3d50614170649853d121baaaa305de7b65b3e01759247e768d691ddf"}, + {file = "grpcio-1.66.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:80fd702ba7e432994df208f27514280b4b5c6843e12a48759c9255679ad38db8"}, + {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:12fda97ffae55e6526825daf25ad0fa37483685952b5d0f910d6405c87e3adb6"}, + {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:950da58d7d80abd0ea68757769c9db0a95b31163e53e5bb60438d263f4bed7b7"}, + {file = "grpcio-1.66.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e636ce23273683b00410f1971d209bf3689238cf5538d960adc3cdfe80dd0dbd"}, + {file = "grpcio-1.66.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a917d26e0fe980b0ac7bfcc1a3c4ad6a9a4612c911d33efb55ed7833c749b0ee"}, + {file = "grpcio-1.66.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:49f0ca7ae850f59f828a723a9064cadbed90f1ece179d375966546499b8a2c9c"}, + {file = "grpcio-1.66.2-cp312-cp312-win32.whl", hash = "sha256:31fd163105464797a72d901a06472860845ac157389e10f12631025b3e4d0453"}, + {file = "grpcio-1.66.2-cp312-cp312-win_amd64.whl", hash = "sha256:ff1f7882e56c40b0d33c4922c15dfa30612f05fb785074a012f7cda74d1c3679"}, + {file = "grpcio-1.66.2-cp313-cp313-linux_armv7l.whl", hash = "sha256:3b00efc473b20d8bf83e0e1ae661b98951ca56111feb9b9611df8efc4fe5d55d"}, + {file = "grpcio-1.66.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1caa38fb22a8578ab8393da99d4b8641e3a80abc8fd52646f1ecc92bcb8dee34"}, + {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:c408f5ef75cfffa113cacd8b0c0e3611cbfd47701ca3cdc090594109b9fcbaed"}, + {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c806852deaedee9ce8280fe98955c9103f62912a5b2d5ee7e3eaa284a6d8d8e7"}, + {file = "grpcio-1.66.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f145cc21836c332c67baa6fc81099d1d27e266401565bf481948010d6ea32d46"}, + {file = "grpcio-1.66.2-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:73e3b425c1e155730273f73e419de3074aa5c5e936771ee0e4af0814631fb30a"}, + {file = "grpcio-1.66.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9c509a4f78114cbc5f0740eb3d7a74985fd2eff022971bc9bc31f8bc93e66a3b"}, + {file = "grpcio-1.66.2-cp313-cp313-win32.whl", hash = "sha256:20657d6b8cfed7db5e11b62ff7dfe2e12064ea78e93f1434d61888834bc86d75"}, + {file = "grpcio-1.66.2-cp313-cp313-win_amd64.whl", hash = "sha256:fb70487c95786e345af5e854ffec8cb8cc781bcc5df7930c4fbb7feaa72e1cdf"}, + {file = "grpcio-1.66.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:a18e20d8321c6400185b4263e27982488cb5cdd62da69147087a76a24ef4e7e3"}, + {file = "grpcio-1.66.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:02697eb4a5cbe5a9639f57323b4c37bcb3ab2d48cec5da3dc2f13334d72790dd"}, + {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:99a641995a6bc4287a6315989ee591ff58507aa1cbe4c2e70d88411c4dcc0839"}, + {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ed71e81782966ffead60268bbda31ea3f725ebf8aa73634d5dda44f2cf3fb9c"}, + {file = "grpcio-1.66.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbd27c24a4cc5e195a7f56cfd9312e366d5d61b86e36d46bbe538457ea6eb8dd"}, + {file = "grpcio-1.66.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d9a9724a156c8ec6a379869b23ba3323b7ea3600851c91489b871e375f710bc8"}, + {file = "grpcio-1.66.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d8d4732cc5052e92cea2f78b233c2e2a52998ac40cd651f40e398893ad0d06ec"}, + {file = "grpcio-1.66.2-cp38-cp38-win32.whl", hash = "sha256:7b2c86457145ce14c38e5bf6bdc19ef88e66c5fee2c3d83285c5aef026ba93b3"}, + {file = "grpcio-1.66.2-cp38-cp38-win_amd64.whl", hash = "sha256:e88264caad6d8d00e7913996030bac8ad5f26b7411495848cc218bd3a9040b6c"}, + {file = "grpcio-1.66.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:c400ba5675b67025c8a9f48aa846f12a39cf0c44df5cd060e23fda5b30e9359d"}, + {file = "grpcio-1.66.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:66a0cd8ba6512b401d7ed46bb03f4ee455839957f28b8d61e7708056a806ba6a"}, + {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:06de8ec0bd71be123eec15b0e0d457474931c2c407869b6c349bd9bed4adbac3"}, + {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fb57870449dfcfac428afbb5a877829fcb0d6db9d9baa1148705739e9083880e"}, + {file = "grpcio-1.66.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b672abf90a964bfde2d0ecbce30f2329a47498ba75ce6f4da35a2f4532b7acbc"}, + {file = "grpcio-1.66.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ad2efdbe90c73b0434cbe64ed372e12414ad03c06262279b104a029d1889d13e"}, + {file = "grpcio-1.66.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9c3a99c519f4638e700e9e3f83952e27e2ea10873eecd7935823dab0c1c9250e"}, + {file = "grpcio-1.66.2-cp39-cp39-win32.whl", hash = "sha256:78fa51ebc2d9242c0fc5db0feecc57a9943303b46664ad89921f5079e2e4ada7"}, + {file = "grpcio-1.66.2-cp39-cp39-win_amd64.whl", hash = "sha256:728bdf36a186e7f51da73be7f8d09457a03061be848718d0edf000e709418987"}, + {file = "grpcio-1.66.2.tar.gz", hash = "sha256:563588c587b75c34b928bc428548e5b00ea38c46972181a4d8b75ba7e3f24231"}, ] [package.extras] -protobuf = ["grpcio-tools (>=1.66.1)"] +protobuf = ["grpcio-tools (>=1.66.2)"] [[package]] name = "grpcio-tools" -version = "1.66.1" +version = "1.66.2" description = "Protobuf code generator for gRPC" optional = false python-versions = ">=3.8" files = [ - {file = "grpcio_tools-1.66.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:e0c71405399ef59782600b1f0bdebc69ba12d7c9527cd268162a86273971d294"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:df1a174a6f9d3b4c380f005f33352d2e95464f33f021fb08084735a2eb6e23b1"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:7d789bfe53fce9e87aa80c3694a366258ce4c41b706258e9228ed4994832b780"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:95c44a265ff01fd05166edae9350bc2e7d1d9a95e8f53b8cd04d2ae0a588c583"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b962a8767c3c0f9afe92e0dd6bb0b2305d35195a1053f84d4d31f585b87557ed"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:d8616773126ec3cdf747b06a12e957b43ac15c34e4728def91fa67249a7c689a"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0067e79b6001560ac6acc78cca11fd3504fa27f8af46e3cdbac2f4998505e597"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-win32.whl", hash = "sha256:fa4f95a79a34afc3b5464895d091cd1911227fc3ab0441b9a37cd1817cf7db86"}, - {file = "grpcio_tools-1.66.1-cp310-cp310-win_amd64.whl", hash = "sha256:3acce426f5e643de63019311171f4d31131da8149de518716a95c29a2c12dd38"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:9a07e24feb7472419cf70ebbb38dd4299aea696f91f191b62a99b3ee9ff03f89"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:097a069e7c640043921ecaf3e88d7af78ccd40c25dbddc91db2a4a2adbd0393d"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:016fa273dc696c9d8045091ac50e000bce766183a6b150801f51c2946e33dbe3"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ec9f4f964f8e8ed5e9cc13deb678c83d5597074c256805373220627833bc5ad"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3198815814cdd12bdb69b7580d7770a4ad4c8b2093e0bd6b987bc817618e3eec"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:796620fc41d3fbb566d9614ef22bc55df67fac1f1e19c1e0fb6ec48bc9b6a44b"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:222d8dc218560698e1abf652fb47e4015994ec7a265ef46e012fd9c9e77a4d6b"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-win32.whl", hash = "sha256:56e17a11f34df252b4c6fb8aa8cd7b44d162dba9f3333be87ddf7c8bf496622a"}, - {file = "grpcio_tools-1.66.1-cp311-cp311-win_amd64.whl", hash = "sha256:edd52d667f2aa3c73233be0a821596937f24536647c12d96bfc54aa4cb04747d"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:869b6960d5daffda0dac1a474b44144f0dace0d4336394e499c4f400c5e2f8d9"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:68d9390bf9ba863ac147fc722d6548caa587235e887cac1bc2438212e89d1de7"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:b8660401beca7e3af28722439e07b0bcdca80b4a68f5a5a1138ae7b7780a6abf"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb67b9aa9cd69468bceb933e8e0f89fd13695746c018c4d2e6b3b84e73f3ad97"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5daceb9716e31edc0e1ba0f93303785211438c43502edddad7a919fc4cb3d664"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0a86398a4cd0665bc7f09fa90b89bac592c959d2c895bf3cf5d47a98c0f2d24c"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1b4acb53338072ab3023e418a5c7059cb15686abd1607516fa1453406dd5f69d"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-win32.whl", hash = "sha256:88e04b7546101bc79c868c941777efd5088063a9e4f03b4d7263dde796fbabf7"}, - {file = "grpcio_tools-1.66.1-cp312-cp312-win_amd64.whl", hash = "sha256:5b4fc56abeafae74140f5da29af1093e88ce64811d77f1a81c3146e9e996fb6a"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:d4dd2ff982c1aa328ef47ce34f07af82f1f13599912fb1618ebc5fe1e14dddb8"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:066648543f786cb74b1fef5652359952455dbba37e832642026fd9fd8a219b5f"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:d19d47744c30e6bafa76b3113740e71f382d75ebb2918c1efd62ebe6ba7e20f9"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:739c53571130b359b738ac7d6d0a1f772e15779b66df7e6764bee4071cd38689"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2226ff8d3ecba83b7622946df19d6e8e15cb52f761b8d9e2f807b228db5f1b1e"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2f4b1498cb8b422fbae32a491c9154e8d47650caf5852fbe6b3b34253e824343"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:93d2d9e14e81affdc63d67c42eb16a8da1b6fecc16442a703ca60eb0e7591691"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-win32.whl", hash = "sha256:d761dfd97a10e4aae73628b5120c64e56f0cded88651d0003d2d80e678c3e7c9"}, - {file = "grpcio_tools-1.66.1-cp38-cp38-win_amd64.whl", hash = "sha256:e1c2ac0955f5fb87b8444316e475242d194c3f3cd0b7b6e54b889a7b6f05156f"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:5f1f04578b72c281e39274348a61d240c48d5321ba8d7a8838e194099ecbc322"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:da9b0c08dbbf07535ee1b75a22d0acc5675a808a3a3df9f9b21e0e73ddfbb3a9"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:e302b4e1fa856d74ff65c65888b3a37153287ce6ad5bad80b2fdf95130accec2"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7fc3f62494f238774755ff90f0e66a93ac7972ea1eb7180c45acf4fd53b25cca"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23cad65ff22459aa387f543d293f54834c9aac8f76fb7416a7046556df75b567"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3d17a27c567a5e4d18f487368215cb51b43e2499059fd6113b92f7ae1fee48be"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4df167e67b083f96bc277032a526f6186e98662aaa49baea1dfb8ecfe26ce117"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-win32.whl", hash = "sha256:f94d5193b2f2a9595795b83e7978b2bee1c0399da66f2f24d179c388f81fb99c"}, - {file = "grpcio_tools-1.66.1-cp39-cp39-win_amd64.whl", hash = "sha256:66f527a1e3f063065e29cf6f3e55892434d13a5a51e3b22402e09da9521e98a3"}, - {file = "grpcio_tools-1.66.1.tar.gz", hash = "sha256:5055ffe840ea8f505c30378be02afb4dbecb33480e554debe10b63d6b2f641c3"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-linux_armv7l.whl", hash = "sha256:40b7ad804ff78490408177cfe87427d5a67224f82a2bdfabe9d8d6ac6239733b"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:a886fa2ff9e897b35489557d1c61cbc0e4efc42c4dc0d120a9516f294fefb107"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:1d5e22b2c7f5b453462c85aa66f99961d5c7b275d1c60b84fe847c06c73c9400"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a425b2600ad4fcf887107ee975a9b7c20478c2959c58b12af7f36577d7a7f7b3"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef979af76b0cd3f5235d3ec30e86a4f0acc0eab179e796ddbb481aa351a1e6ca"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:99638043e1a78b8617f31b676f1ecf248d75a45b318776af3acc48a85c8e10a2"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0a465850c7e5c4ab588c7b7275d47781e9c0ee397a8faf4977262592f95e1831"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-win32.whl", hash = "sha256:48997b704d2fcf59d922228c7a79fcd35d52ca8b2202e5cfe193962643b8354f"}, + {file = "grpcio_tools-1.66.2-cp310-cp310-win_amd64.whl", hash = "sha256:ab4eda584ba2e647e9bb5098f5e4e8d370a333761bf33924e9a7c14f069c8b08"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-linux_armv7l.whl", hash = "sha256:007750b4db62018e441f8401fa567aa11174ae0173826cbbe54982fdf2383067"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:18554bc91640b2f1ce18aa5c6bebd51500ca0b43b5df4e700e6f76522e2b0e94"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:3fe2fc2e4a16d745cae01e1348b401378e58ced920ff759a6b4b85a7ad507896"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0933420362621d8792fea9350f0c82c514da5f93888d1476c37d9e3722d260b0"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3aef5abd34bea8ea98448cd58a938992238c4717df93d12f84fa5f56efb11d0"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7afd9eb9be413a731cff7ad638081795a7ed0fec4b23af5cec2099fbd9d742f9"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fd1fa95188ae7d5460a8c4a2abcb2777fdf9c3b80d592a2e8434c52a6eb48e8d"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-win32.whl", hash = "sha256:80c233215cf0f08353b7aac4e86cdedf4d545ed368a7491ccc9996e5a317dce4"}, + {file = "grpcio_tools-1.66.2-cp311-cp311-win_amd64.whl", hash = "sha256:2a9a376b300aa2b4da8e6c4f6f746e824d3f24eefeac2753ffffe2b9f37d156d"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-linux_armv7l.whl", hash = "sha256:d8ca76fc40a7d35ddf1229afd04408e2ff94caf4385068c8b147e064e951e0ba"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6cc3da6994d575c425c74ce33e34b86a975ea7e78bb9c3525e8439a3af3c508f"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:89e437ced43275e7427cc82a837f5cd43ebe18a1080b0e50a47627895b44b0e6"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d95f030e708266d7fd6d3e5d56e30a9bbbe230604856b1fe93edd892e4389aab"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b3cf9ae67f8bb431ab3ff60db75c3586dc5aa993be4b15bd7cad651362563cd"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b4896a0853fc402273e908c0a0710d25242f1ae907efb9d22ba6d82d4ba00ad8"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d31aad10f90fccb0073bc03b4d1b67690ef4f0cd9af96e82944b9cc655d12b6f"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-win32.whl", hash = "sha256:d8f976f35683e49467d0bf2b90c170ac5443cd162d48d8d868801fd0d87a5fa8"}, + {file = "grpcio_tools-1.66.2-cp312-cp312-win_amd64.whl", hash = "sha256:b2c19e5a888a6ee48ba699581a90c04806b2a93f574f37449c359ec17a793669"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-linux_armv7l.whl", hash = "sha256:7e8c9aa91a9e51199048202e3c54491e0a89fb3ac47dde36ff2964fbcee143a3"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0eaedd3c77824c3762b728c485f91097a58116fa135f3bbc24703621476cd866"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:a14007902fb6565c21815da4177105ec905ef37f0550190c4d1bbeb2928c6560"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df8f098bb92d192230f3b23df514b139f3549e2a4390d1f0f0d8ff89de458c54"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c68642829368f4f83929e0df571dbbc99f1f1553555d8f98d0582da9f6743d9e"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:5fd20110d2c7706dfdd95457807acb8c050253be2e272b9f5fb977e87ea44d86"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:4b16244be4cff92408eb82901b883a70f3dd902fb7c7f66e2a368271be84cde4"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-win32.whl", hash = "sha256:d872ba3bbe9e15b43eeb9310dad5edbf490bb3ab0072a46b3a12fed0234eec23"}, + {file = "grpcio_tools-1.66.2-cp313-cp313-win_amd64.whl", hash = "sha256:a2810921218471aab5c8cd20204d3b1886aa8e13b495e882158bb398982cf18e"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-linux_armv7l.whl", hash = "sha256:538eb263b9969e866619775df341307ece0b09afce091ede8141c5bb4d7d8933"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9a68c71bb1358f0994fc7d0f0d70a0d419d57507faa25c982145be401f6aca48"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:1bc41d5b36d414bb0940aa50e30d624903a2538f9387ae730953675adcbe1498"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c43dcd3ee13418545ea10416f46296ddbc7fb355cf136ddebd3b3f881a383168"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc16f9e6baafed315846e79a746513863e6ecbb89e9c98d872834e44f9e87a5"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3655c96eef8aac2a610bbf4cb9c7839fcff09f07a609b74408b3b0a136e1ef57"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:86d971fc64e63642058ac01ce2e484a8340d60a95ead0dc6697ef2aa18a7b936"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-win32.whl", hash = "sha256:c14db004b28ee2adefc6d36107d7fdf770f7509bd1f1ecd195eecb88cdbe5d96"}, + {file = "grpcio_tools-1.66.2-cp38-cp38-win_amd64.whl", hash = "sha256:c65f12474634195ff5ed91b304412b80008c067d28226c26b4e451ea9da16b24"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-linux_armv7l.whl", hash = "sha256:75c6a25a5cf729c4606c388013cf7c59dda99cf3718c24fe4fd52b06c19955d0"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5a5146e780ed87348d84b11fc3843741e676b2a84d493363bf0b4ae31c56841b"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:c42ba1b24e701544bf08a43bb2d63d56dedd0fd33a5b499c9cf85e15aa154b13"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5daf9807260e172ffcc5dd582c01f60bac820f99f0151a507c8a537f9e6dceb8"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a15a4d0f4eba3773dabe07113b42e018a8fa9a28441483ada111991d5c1468b6"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:cc4f65cd189832676dca16046a4b6247d0bc1fc20648d16ac7fb0b075d1658f4"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ba63dbcbb8ade67e5a04dd3a6c5860efb454bda6d5e8558b17c9a7251339ce36"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-win32.whl", hash = "sha256:c4df0f547f4193dfa6689949b374974f08d81f129174738f0410ba8d45dc63be"}, + {file = "grpcio_tools-1.66.2-cp39-cp39-win_amd64.whl", hash = "sha256:0cad9ffe5df7801201773b91f14923cf3e20ca764e418ae7f8cb75f6045a0aa1"}, + {file = "grpcio_tools-1.66.2.tar.gz", hash = "sha256:4a36e07913d26ba5ccfd2685ba63ca97f26b08c249d2cc9e74dda37efa49d7e4"}, ] [package.dependencies] -grpcio = ">=1.66.1" +grpcio = ">=1.66.2" protobuf = ">=5.26.1,<6.0dev" setuptools = "*" @@ -214,6 +395,29 @@ files = [ [package.extras] license = ["ukkonen"] +[[package]] +name = "importlib-metadata" +version = "8.5.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -545,22 +749,22 @@ virtualenv = ">=20.10.0" [[package]] name = "protobuf" -version = "5.28.1" +version = "5.28.2" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.28.1-cp310-abi3-win32.whl", hash = "sha256:fc063acaf7a3d9ca13146fefb5b42ac94ab943ec6e978f543cd5637da2d57957"}, - {file = "protobuf-5.28.1-cp310-abi3-win_amd64.whl", hash = "sha256:4c7f5cb38c640919791c9f74ea80c5b82314c69a8409ea36f2599617d03989af"}, - {file = "protobuf-5.28.1-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4304e4fceb823d91699e924a1fdf95cde0e066f3b1c28edb665bda762ecde10f"}, - {file = "protobuf-5.28.1-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:0dfd86d2b5edf03d91ec2a7c15b4e950258150f14f9af5f51c17fa224ee1931f"}, - {file = "protobuf-5.28.1-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:51f09caab818707ab91cf09cc5c156026599cf05a4520779ccbf53c1b352fb25"}, - {file = "protobuf-5.28.1-cp38-cp38-win32.whl", hash = "sha256:1b04bde117a10ff9d906841a89ec326686c48ececeb65690f15b8cabe7149495"}, - {file = "protobuf-5.28.1-cp38-cp38-win_amd64.whl", hash = "sha256:cabfe43044ee319ad6832b2fda332646f9ef1636b0130186a3ae0a52fc264bb4"}, - {file = "protobuf-5.28.1-cp39-cp39-win32.whl", hash = "sha256:4b4b9a0562a35773ff47a3df823177ab71a1f5eb1ff56d8f842b7432ecfd7fd2"}, - {file = "protobuf-5.28.1-cp39-cp39-win_amd64.whl", hash = "sha256:f24e5d70e6af8ee9672ff605d5503491635f63d5db2fffb6472be78ba62efd8f"}, - {file = "protobuf-5.28.1-py3-none-any.whl", hash = "sha256:c529535e5c0effcf417682563719e5d8ac8d2b93de07a56108b4c2d436d7a29a"}, - {file = "protobuf-5.28.1.tar.gz", hash = "sha256:42597e938f83bb7f3e4b35f03aa45208d49ae8d5bcb4bc10b9fc825e0ab5e423"}, + {file = "protobuf-5.28.2-cp310-abi3-win32.whl", hash = "sha256:eeea10f3dc0ac7e6b4933d32db20662902b4ab81bf28df12218aa389e9c2102d"}, + {file = "protobuf-5.28.2-cp310-abi3-win_amd64.whl", hash = "sha256:2c69461a7fcc8e24be697624c09a839976d82ae75062b11a0972e41fd2cd9132"}, + {file = "protobuf-5.28.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8b9403fc70764b08d2f593ce44f1d2920c5077bf7d311fefec999f8c40f78b7"}, + {file = "protobuf-5.28.2-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:35cfcb15f213449af7ff6198d6eb5f739c37d7e4f1c09b5d0641babf2cc0c68f"}, + {file = "protobuf-5.28.2-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:5e8a95246d581eef20471b5d5ba010d55f66740942b95ba9b872d918c459452f"}, + {file = "protobuf-5.28.2-cp38-cp38-win32.whl", hash = "sha256:87317e9bcda04a32f2ee82089a204d3a2f0d3c8aeed16568c7daf4756e4f1fe0"}, + {file = "protobuf-5.28.2-cp38-cp38-win_amd64.whl", hash = "sha256:c0ea0123dac3399a2eeb1a1443d82b7afc9ff40241433296769f7da42d142ec3"}, + {file = "protobuf-5.28.2-cp39-cp39-win32.whl", hash = "sha256:ca53faf29896c526863366a52a8f4d88e69cd04ec9571ed6082fa117fac3ab36"}, + {file = "protobuf-5.28.2-cp39-cp39-win_amd64.whl", hash = "sha256:8ddc60bf374785fb7cb12510b267f59067fa10087325b8e1855b898a0d81d276"}, + {file = "protobuf-5.28.2-py3-none-any.whl", hash = "sha256:52235802093bd8a2811abbe8bf0ab9c5f54cca0a751fdd3f6ac2a21438bffece"}, + {file = "protobuf-5.28.2.tar.gz", hash = "sha256:59379674ff119717404f7454647913787034f03fe7049cbef1d74a97bb4593f0"}, ] [[package]] @@ -630,21 +834,23 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pyright" -version = "1.1.381" +version = "1.1.382.post1" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.381-py3-none-any.whl", hash = "sha256:5dc0aa80a265675d36abab59c674ae01dbe476714f91845b61b841d34aa99081"}, - {file = "pyright-1.1.381.tar.gz", hash = "sha256:314cf0c1351c189524fb10c7ac20688ecd470e8cc505c394d642c9c80bf7c3a5"}, + {file = "pyright-1.1.382.post1-py3-none-any.whl", hash = "sha256:21a4749dd1740e209f88d3a601e9f40748670d39481ea32b9d77edf7f3f1fb2e"}, + {file = "pyright-1.1.382.post1.tar.gz", hash = "sha256:66a5d4e83be9452853d73e9dd9e95ba0ac3061845270e4e331d0070a597d3445"}, ] [package.dependencies] nodeenv = ">=1.6.0" +typing-extensions = ">=4.1" [package.extras] -all = ["twine (>=3.4.1)"] +all = ["nodejs-wheel-binaries", "twine (>=3.4.1)"] dev = ["twine (>=3.4.1)"] +nodejs = ["nodejs-wheel-binaries"] [[package]] name = "pytest" @@ -668,6 +874,39 @@ tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-cov" +version = "5.0.0" +description = "Pytest plugin for measuring coverage." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-cov-5.0.0.tar.gz", hash = "sha256:5837b58e9f6ebd335b0f8060eecce69b662415b16dc503883a02f45dfeb14857"}, + {file = "pytest_cov-5.0.0-py3-none-any.whl", hash = "sha256:4f0764a1219df53214206bf1feea4633c3b558a2925c8b59f144f682861ce652"}, +] + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "virtualenv"] + +[[package]] +name = "pytest-randomly" +version = "3.15.0" +description = "Pytest plugin to randomly order tests and control random.seed." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest_randomly-3.15.0-py3-none-any.whl", hash = "sha256:0516f4344b29f4e9cdae8bce31c4aeebf59d0b9ef05927c33354ff3859eeeca6"}, + {file = "pytest_randomly-3.15.0.tar.gz", hash = "sha256:b908529648667ba5e54723088edd6f82252f540cc340d748d1fa985539687047"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +pytest = "*" + [[package]] name = "pyyaml" version = "6.0.2" @@ -757,29 +996,29 @@ tests = ["pytest (==7.1.2)"] [[package]] name = "ruff" -version = "0.6.5" +version = "0.6.8" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.5-py3-none-linux_armv6l.whl", hash = "sha256:7e4e308f16e07c95fc7753fc1aaac690a323b2bb9f4ec5e844a97bb7fbebd748"}, - {file = "ruff-0.6.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:932cd69eefe4daf8c7d92bd6689f7e8182571cb934ea720af218929da7bd7d69"}, - {file = "ruff-0.6.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3a8d42d11fff8d3143ff4da41742a98f8f233bf8890e9fe23077826818f8d680"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a50af6e828ee692fb10ff2dfe53f05caecf077f4210fae9677e06a808275754f"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:794ada3400a0d0b89e3015f1a7e01f4c97320ac665b7bc3ade24b50b54cb2972"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:381413ec47f71ce1d1c614f7779d88886f406f1fd53d289c77e4e533dc6ea200"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:52e75a82bbc9b42e63c08d22ad0ac525117e72aee9729a069d7c4f235fc4d276"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09c72a833fd3551135ceddcba5ebdb68ff89225d30758027280968c9acdc7810"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:800c50371bdcb99b3c1551d5691e14d16d6f07063a518770254227f7f6e8c178"}, - {file = "ruff-0.6.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e25ddd9cd63ba1f3bd51c1f09903904a6adf8429df34f17d728a8fa11174253"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7291e64d7129f24d1b0c947ec3ec4c0076e958d1475c61202497c6aced35dd19"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9ad7dfbd138d09d9a7e6931e6a7e797651ce29becd688be8a0d4d5f8177b4b0c"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:005256d977021790cc52aa23d78f06bb5090dc0bfbd42de46d49c201533982ae"}, - {file = "ruff-0.6.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:482c1e6bfeb615eafc5899127b805d28e387bd87db38b2c0c41d271f5e58d8cc"}, - {file = "ruff-0.6.5-py3-none-win32.whl", hash = "sha256:cf4d3fa53644137f6a4a27a2b397381d16454a1566ae5335855c187fbf67e4f5"}, - {file = "ruff-0.6.5-py3-none-win_amd64.whl", hash = "sha256:3e42a57b58e3612051a636bc1ac4e6b838679530235520e8f095f7c44f706ff9"}, - {file = "ruff-0.6.5-py3-none-win_arm64.whl", hash = "sha256:51935067740773afdf97493ba9b8231279e9beef0f2a8079188c4776c25688e0"}, - {file = "ruff-0.6.5.tar.gz", hash = "sha256:4d32d87fab433c0cf285c3683dd4dae63be05fd7a1d65b3f5bf7cdd05a6b96fb"}, + {file = "ruff-0.6.8-py3-none-linux_armv6l.whl", hash = "sha256:77944bca110ff0a43b768f05a529fecd0706aac7bcce36d7f1eeb4cbfca5f0f2"}, + {file = "ruff-0.6.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:27b87e1801e786cd6ede4ada3faa5e254ce774de835e6723fd94551464c56b8c"}, + {file = "ruff-0.6.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd48f945da2a6334f1793d7f701725a76ba93bf3d73c36f6b21fb04d5338dcf5"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:677e03c00f37c66cea033274295a983c7c546edea5043d0c798833adf4cf4c6f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f1476236b3eacfacfc0f66aa9e6cd39f2a624cb73ea99189556015f27c0bdeb"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f5a2f17c7d32991169195d52a04c95b256378bbf0de8cb98478351eb70d526f"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5fd0d4b7b1457c49e435ee1e437900ced9b35cb8dc5178921dfb7d98d65a08d0"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f8034b19b993e9601f2ddf2c517451e17a6ab5cdb1c13fdff50c1442a7171d87"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6cfb227b932ba8ef6e56c9f875d987973cd5e35bc5d05f5abf045af78ad8e098"}, + {file = "ruff-0.6.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef0411eccfc3909269fed47c61ffebdcb84a04504bafa6b6df9b85c27e813b0"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:007dee844738c3d2e6c24ab5bc7d43c99ba3e1943bd2d95d598582e9c1b27750"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:ce60058d3cdd8490e5e5471ef086b3f1e90ab872b548814e35930e21d848c9ce"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:1085c455d1b3fdb8021ad534379c60353b81ba079712bce7a900e834859182fa"}, + {file = "ruff-0.6.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:70edf6a93b19481affd287d696d9e311388d808671bc209fb8907b46a8c3af44"}, + {file = "ruff-0.6.8-py3-none-win32.whl", hash = "sha256:792213f7be25316f9b46b854df80a77e0da87ec66691e8f012f887b4a671ab5a"}, + {file = "ruff-0.6.8-py3-none-win_amd64.whl", hash = "sha256:ec0517dc0f37cad14a5319ba7bba6e7e339d03fbf967a6d69b0907d61be7a263"}, + {file = "ruff-0.6.8-py3-none-win_arm64.whl", hash = "sha256:8d3bb2e3fbb9875172119021a13eed38849e762499e3cfde9588e4b4d70968dc"}, + {file = "ruff-0.6.8.tar.gz", hash = "sha256:a5bf44b1aa0adaf6d9d20f86162b34f7c593bfedabc51239953e446aefc8ce18"}, ] [[package]] @@ -826,13 +1065,13 @@ files = [ [[package]] name = "virtualenv" -version = "20.26.5" +version = "20.26.6" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, - {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, + {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, + {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, ] [package.dependencies] @@ -844,7 +1083,26 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +[[package]] +name = "zipp" +version = "3.20.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [metadata] lock-version = "2.0" python-versions = ">=3.9,<3.13" -content-hash = "daf08d3009c99686f52960089c3d16ff858d9784ad0f065d0473eeca935098bc" +content-hash = "6e12dc20875296cfeff66e245bdabef8dd7569537cfadb4a5c1b5e5634e22e21" diff --git a/python/pyproject.toml b/python/pyproject.toml index 9d16759..b131aa2 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -4,7 +4,7 @@ version = "0.3.0" description = "ARFlow is a data-sharing layer that enables developer-friendly data streaming, storage, and visualization for augmented reality (AR) device data." authors = ["Yiqin Zhao "] readme = "README.md" -packages = [{ include = "arflow" }] +packages = [{ include = "arflow" }, { include = "arflow_grpc" }] license = "GPL-3.0" homepage = "https://cake.wpi.edu/ARFlow/" repository = "https://github.com/cake-lab/ARFlow" @@ -15,15 +15,20 @@ repository = "https://github.com/cake-lab/ARFlow" [tool.poetry.dependencies] python = ">=3.9,<3.13" +rerun-sdk = "^0.18.2" grpcio = "^1.60.1" grpcio-tools = "^1.60.1" -rerun-sdk = "^0.18.2" +grpc-interceptor = "^0.15.4" +dracopy = "^1.4.0" [tool.poetry.group.dev.dependencies] -ruff = "^0.6.4" -pyright = "^1.1.379" +ruff = "^0.6.4" # Sync with `.github/ci.yml` if changed pytest = "^8.3.2" +pytest-randomly = "^3.15.0" +pytest-cov = "^5.0.0" pre-commit = "^3.8.0" +pyright = "^1.1.382" # Sync with `.github/ci.yml` if changed +grpc-stubs = "^1.53.0.5" [tool.poetry.group.docs.dependencies] pdoc = "^14.6.1" @@ -33,30 +38,30 @@ requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" [tool.poetry.scripts] -serve = "arflow.serve:serve" +arflow = "arflow._cli:main" [tool.ruff] # gRPC generated files -extend-exclude = [ - "arflow/service_pb2.py", - "arflow/service_pb2_grpc.py", - "arflow/service_pb2.pyi", -] +extend-exclude = ["examples", "arflow_grpc/**.py", "arflow_grpc/**.pyi"] [tool.ruff.lint] # https://github.com/astral-sh/ruff-vscode/blob/main/README.md#configuring-vs-code extend-select = ["I"] +# Enable all `pydocstyle` rules, limiting to those that adhere to the +# Google convention via `convention = "google"`, below. +select = ["D", "T"] + +# On top of the Google convention, disable `D417`, which requires +# documentation for every function parameter. +ignore = ["D417"] [tool.ruff.lint.pydocstyle] convention = "google" [tool.pyright] -typeCheckingMode = "off" # TODO: Enable strict type checking -ignore = [ - "arflow/service_pb2.py", - "arflow/service_pb2_grpc.py", - "arflow/service_pb2.pyi", -] -# https://github.com/RobertCraigie/pyright-python#pre-commit -venvPath = "." -venv = ".venv" +typeCheckingMode = "strict" +exclude = ["examples", "arflow_grpc/**.py", "arflow_grpc/**.pyi"] + +[tool.pytest.ini_options] +# coverage report for only `arflow` package with missing lines +addopts = "--cov arflow --cov-report term-missing" diff --git a/python/tests/__init__.py b/python/tests/__init__.py index e69de29..6e0e580 100644 --- a/python/tests/__init__.py +++ b/python/tests/__init__.py @@ -0,0 +1 @@ +"""ARFlow tests.""" diff --git a/python/tests/bunny.drc b/python/tests/bunny.drc new file mode 100644 index 0000000..1e80a75 Binary files /dev/null and b/python/tests/bunny.drc differ diff --git a/python/tests/test_cli.py b/python/tests/test_cli.py new file mode 100644 index 0000000..ceaa866 --- /dev/null +++ b/python/tests/test_cli.py @@ -0,0 +1,177 @@ +"""Command line interface tests.""" + +# ruff:noqa: D103 +# pyright: reportPrivateUsage=false + +import argparse +import logging +import shlex +import tempfile +from pathlib import Path +from typing import Literal +from unittest.mock import MagicMock, patch + +import pytest + +from arflow._cli import ( + _validate_dir_path, + _validate_file_path, + parse_args, + replay, + serve, +) + + +@pytest.fixture +def temp_dir(): + with tempfile.TemporaryDirectory() as td: + yield td + + +@pytest.fixture +def temp_file(): + with tempfile.NamedTemporaryFile() as tf: + yield tf.name + + +def test_validate_dir_path_valid(temp_dir: str): + assert _validate_dir_path(temp_dir) == temp_dir + + +def test_validate_dir_path_invalid(): + with pytest.raises(argparse.ArgumentTypeError): + _validate_dir_path("/path/to/nonexistent/directory") + + +def test_validate_dir_path_none(): + assert _validate_dir_path(None) is None + + +def test_validate_file_path_valid(temp_file: str): + assert _validate_file_path(temp_file) == temp_file + + +def test_validate_file_path_invalid(): + with pytest.raises(argparse.ArgumentTypeError): + _validate_file_path("/path/to/nonexistent/file.txt") + + +def test_serve(): + with patch("arflow._cli.run_server") as mock_run_server, patch( + "arflow._cli.ARFlowServicer" + ) as mock_servicer: + args = MagicMock() + args.port = 1234 + args.save_path = "/tmp/save_path" + + serve(args) + + mock_run_server.assert_called_once_with( + mock_servicer, port=1234, path_to_save=Path("/tmp/save_path") + ) + + +def test_replay(): + with patch("arflow._cli.ARFlowPlayer") as mock_player_class, patch( + "arflow._cli.ARFlowServicer" + ) as mock_servicer: + mock_player_instance = MagicMock() + mock_player_class.return_value = mock_player_instance + + args = MagicMock() + args.file_path = "/path/to/data.file" + + replay(args) + + mock_player_class.assert_called_once_with( + mock_servicer, Path("/path/to/data.file") + ) + mock_player_instance.run.assert_called_once() + + +@pytest.mark.parametrize( + "command, subcommand, debug, verbose, port, save_path, file_path", + [ + ("", None, False, False, None, None, None), + ("-d", None, True, False, None, None, None), + ("-v", None, False, True, None, None, None), + ("-d -v", None, True, True, None, None, None), + ("serve", "serve", False, False, 8500, None, None), + ("-d serve", "serve", True, False, 8500, None, None), + ("-d serve -p 1234", "serve", True, False, 1234, None, None), + ( + "-d serve -s /tmp/save_path", + "serve", + True, + False, + 8500, + "/tmp/save_path", + None, + ), + ( + "-d serve -p 1234 -s /tmp/save_path", + "serve", + True, + False, + 1234, + "/tmp/save_path", + None, + ), + ( + "replay /path/to/data.file", + "replay", + False, + False, + None, + None, + "/path/to/data.file", + ), + ( + "-d replay /path/to/data.file", + "replay", + True, + False, + None, + None, + "/path/to/data.file", + ), + ], +) +def test_parse_args( + command: str, + subcommand: Literal["serve", "replay"] | None, + debug: bool, + verbose: bool, + port: int | None, + save_path: str | None, + file_path: str | None, +): + with patch( + "arflow._cli._validate_file_path", return_value="/path/to/data.file" + ), patch("arflow._cli._validate_dir_path", return_value="/tmp/save_path"): + if debug and verbose: + with pytest.raises(SystemExit): + parse_args(shlex.split(command)) + return + + _, args = parse_args(shlex.split(command)) + + if not debug and not verbose: + assert args.loglevel == logging.WARNING + elif debug: + assert args.loglevel == logging.DEBUG + elif verbose: + assert args.loglevel == logging.INFO + + if subcommand == "serve": + assert args.func == serve + assert args.port == port + assert args.save_path == save_path + elif subcommand == "replay": + assert args.func == replay + assert args.file_path == file_path + else: + assert not hasattr(args, "port") + assert not hasattr(args, "save_path") + assert not hasattr(args, "file_path") + assert not hasattr(args, "func") diff --git a/python/tests/test_decoding.py b/python/tests/test_decoding.py new file mode 100644 index 0000000..20363f5 --- /dev/null +++ b/python/tests/test_decoding.py @@ -0,0 +1,285 @@ +"""Data deserialization tests.""" + +# ruff:noqa: D103,D107 +# pyright: reportPrivateUsage=false + +from typing import Literal + +import numpy as np +import pytest + +from arflow._decoding import ( + convert_2d_to_3d_boundary_points, + decode_depth_image, + decode_intrinsic, + decode_point_cloud, + decode_rgb_image, + decode_transform, +) +from arflow._types import ( + PlaneBoundaryPoints2D, + PlaneCenter, + PlaneNormal, +) + + +@pytest.mark.parametrize( + "resolution_y,resolution_x,resize_factor_y,resize_factor_x,data_type,buffer_length,should_pass", + [ + (4, 4, 1.0, 1.0, "RGB24", 4 * 4 * 3, True), # Valid RGB24 case + (4, 4, 1.0, 1.0, "YCbCr420", 4 * 4 * 3 // 2, True), # Valid YCbCr420 case + (4, 4, 1.0, 1.0, "Invalid", 4 * 4 * 3, False), # Invalid data type + (4, 4, 1.0, 1.0, "RGB24", 1, False), # Buffer too small + ], +) +def test_decode_rgb_image( + resolution_y: int, + resolution_x: int, + resize_factor_y: float, + resize_factor_x: float, + data_type: Literal["RGB24", "YCbCr420"], + buffer_length: int, + should_pass: bool, +): + buffer = np.random.randint(0, 255, buffer_length, dtype=np.uint8).tobytes() # pyright: ignore [reportUnknownMemberType] + if should_pass: + assert decode_rgb_image( + resolution_y, + resolution_x, + resize_factor_y, + resize_factor_x, + data_type, + buffer, + ).shape == ( + resolution_y, + resolution_x, + 3, + ) + assert ( + decode_rgb_image( + resolution_y, + resolution_x, + resize_factor_y, + resize_factor_x, + data_type, + buffer, + ).dtype + == np.uint8 + ) + else: + with pytest.raises(ValueError): + decode_rgb_image( + resolution_y, + resolution_x, + resize_factor_y, + resize_factor_x, + data_type, + buffer, + ) + + +@pytest.mark.parametrize( + "resolution_y,resolution_x,data_type,buffer_dtype,should_pass", + [ + (4, 4, "f32", np.float32, True), # Valid float32 depth case + (4, 4, "u16", np.uint16, True), # Valid uint16 depth case + (4, 4, "Invalid", np.uint16, False), # Invalid data type + (4, 4, "f32", np.float32, False), # Buffer too small + ], +) +def test_decode_depth_image( + resolution_y: int, + resolution_x: int, + data_type: Literal["f32", "u16"], + buffer_dtype: np.float32 | np.uint16, + should_pass: bool, +): + buffer = np.random.rand(resolution_y * resolution_x).astype(buffer_dtype).tobytes() + + if should_pass: + result = decode_depth_image(resolution_y, resolution_x, data_type, buffer) + assert result.shape == (resolution_y, resolution_x) + assert result.dtype == np.float32 + else: + with pytest.raises(ValueError): + decode_depth_image(resolution_y, resolution_x, data_type, buffer[:1]) + + +@pytest.mark.parametrize( + "buffer_length,should_pass", + [ + (12 * 4, True), # Correct size for 3x4 matrix + (8, False), # Incorrect buffer size + ], +) +def test_decode_transform(buffer_length: int, should_pass: bool): + buffer = np.random.rand(buffer_length // 4).astype(np.float32).tobytes() + + if should_pass: + result = decode_transform(buffer) + assert result.shape == (4, 4) + assert result.dtype == np.float32 + else: + with pytest.raises(ValueError): + decode_transform(buffer) + + +@pytest.mark.parametrize( + "resize_factor_y,resize_factor_x,focal_length_y,focal_length_x,principal_point_y,principal_point_x,should_pass", + [ + (1.0, 1.0, 2.0, 2.0, 1.0, 1.0, True), # Valid intrinsic matrix + # TODO: Really no error cases? + ], +) +def test_decode_intrinsic( + resize_factor_y: float, + resize_factor_x: float, + focal_length_y: float, + focal_length_x: float, + principal_point_y: float, + principal_point_x: float, + should_pass: bool, +): + if should_pass: + result = decode_intrinsic( + resize_factor_y, + resize_factor_x, + focal_length_y, + focal_length_x, + principal_point_y, + principal_point_x, + ) + assert result.shape == (3, 3) + assert result.dtype == np.float32 + else: + with pytest.raises(ValueError): + decode_intrinsic( + resize_factor_y, + resize_factor_x, + focal_length_y, + focal_length_x, + principal_point_y, + principal_point_x, + ) + + +@pytest.mark.parametrize( + "resolution_y,resolution_x,resize_factor_y,resize_factor_x,should_pass", + [ + (4, 4, 1.0, 1.0, True), # Valid point cloud case + # TODO: Really no error cases? Because we can assume color_rgb, depth_img, and k are valid + ], +) +def test_decode_point_cloud( + resolution_y: int, + resolution_x: int, + resize_factor_y: float, + resize_factor_x: float, + should_pass: bool, +): + color_rgb = np.random.randint( # pyright: ignore [reportUnknownMemberType] + 0, 255, (resolution_y, resolution_x, 3), dtype=np.uint8 + ) + depth_img = np.random.rand(resolution_y, resolution_x).astype(np.float32) + k = np.array([[2.0, 0, 1.0], [0, 2.0, 1.0], [0, 0, 1.0]], dtype=np.float32) + transform = np.eye(4, dtype=np.float32) + + if should_pass: + pcd, clr = decode_point_cloud( + resolution_y, + resolution_x, + resize_factor_y, + resize_factor_x, + k, + color_rgb, + depth_img, + transform, + ) + assert pcd.shape == (resolution_y * resolution_x, 3) + assert pcd.dtype == np.float32 + assert clr.shape == (resolution_y * resolution_x, 3) + assert clr.dtype == np.uint8 + else: + with pytest.raises(ValueError): + decode_point_cloud( + resolution_y, + resolution_x, + resize_factor_y, + resize_factor_x, + k, + color_rgb, + depth_img, + transform, + ) + + +@pytest.mark.parametrize( + "boundary_points_2d,normal,center,should_pass", + [ + ( + np.array([[1, 2], [2, 3], [1, 3]]), + np.array([4, 5, 6]), + np.array([2, 3, 4]), + True, + ), # Valid 2D points, normal, and center + ( + np.array([[1, 2, 3], [2, 3, 4], [1, 3, 4]]), + np.array([4, 5, 6]), + np.array([2, 3, 4]), + False, + ), # Boundary points not in 2D + ( + np.array([[1, 2], [2, 3]]), + np.array([4, 5, 6]), + np.array([2, 3, 4]), + False, + ), # Only 2 boundary points + ( + np.array([[1, 2], [2, 3], [1, 3]]), + np.array([[2, 3, 4], [4, 5, 6]]), + np.array([2, 3, 4]), + False, + ), # More than 1 normal + ( + np.array([[1, 2], [2, 3], [1, 3]]), + np.array([2, 3]), + np.array([2, 3, 4]), + False, + ), # Normal not in 3D + ( + np.array([[1, 2], [2, 3], [1, 3]]), + np.array([0, 0, 0]), + np.array([2, 3, 4]), + False, + ), # Normal is zero + ( + np.array([[1, 2], [2, 3], [1, 3]]), + np.array([4, 5, 6]), + np.array([[2, 3, 4], [2, 3, 4]]), + False, + ), # More than 1 center + ( + np.array([[1, 2], [2, 3], [1, 3]]), + np.array([4, 5, 6]), + np.array([2, 3, 4, 5]), + False, + ), # Center not in 3D + ], +) +def test_convert_2d_to_3d_boundary_points( + boundary_points_2d: PlaneBoundaryPoints2D, + normal: PlaneNormal, + center: PlaneCenter, + should_pass: bool, +): + if should_pass: + result = convert_2d_to_3d_boundary_points(boundary_points_2d, normal, center) + assert result.shape[0] == (boundary_points_2d.shape[0]) + assert result.shape[1] == 3 + assert result.dtype == np.float32 + + else: + with pytest.raises(ValueError): + result = convert_2d_to_3d_boundary_points( + boundary_points_2d, normal, center + ) diff --git a/python/tests/test_hooks.py b/python/tests/test_hooks.py new file mode 100644 index 0000000..990abde --- /dev/null +++ b/python/tests/test_hooks.py @@ -0,0 +1,72 @@ +"""User-extension hooks tests.""" + +# ruff:noqa: D101,D102,D103,D107 +# pyright: reportPrivateUsage=false + +import tempfile +from pathlib import Path + +import pytest + +from arflow import ARFlowServicer, DecodedDataFrame, RegisterClientRequest +from arflow._types import EnrichedARFlowRequest, HashableClientIdentifier +from arflow_grpc.service_pb2 import ProcessFrameRequest + + +class UserExtendedService(ARFlowServicer): + def __init__(self): + super().__init__() + self.num_clients = 0 + self.num_frames = 0 + + def on_register(self, request: RegisterClientRequest) -> None: + self.num_clients += 1 + + def on_frame_received(self, decoded_data_frame: DecodedDataFrame) -> None: + self.num_frames += 1 + + +@pytest.fixture +def user_service(): + """A user-extended ARFlow service that can be shared across tests.""" + return UserExtendedService() + + +def test_on_register(user_service: UserExtendedService): + request = RegisterClientRequest() + for i in range(3): + assert user_service.num_clients == i + user_service.RegisterClient(request) + + +def test_on_frame_received(user_service: UserExtendedService): + config = RegisterClientRequest() + response = user_service.RegisterClient(config) + request = ProcessFrameRequest(uid=response.uid) + for i in range(3): + assert user_service.num_frames == i + user_service.ProcessFrame(request) + + +def test_on_program_exit(user_service: UserExtendedService): + # Add some mock data to the service + enriched_request = EnrichedARFlowRequest(timestamp=1, data=ProcessFrameRequest()) + user_service._requests_history.append(enriched_request) + client_id = HashableClientIdentifier("test_client") + user_service._client_configurations[client_id] = RegisterClientRequest() + + # Use tempfile to create a temporary directory + with tempfile.TemporaryDirectory() as temp_dir: + temp_path = Path(temp_dir) + + # Call on_program_exit + user_service.on_program_exit(temp_path) + + # Check the results + pkl_files = list(temp_path.glob("*.pkl")) + assert len(pkl_files) == 1 + pkl_file = pkl_files[0] + assert pkl_file.exists() + assert pkl_file.stat().st_size > 0 + + # No need for manual cleanup - the TemporaryDirectory context manager handles it diff --git a/python/tests/test_interceptor.py b/python/tests/test_interceptor.py new file mode 100644 index 0000000..c8b0d22 --- /dev/null +++ b/python/tests/test_interceptor.py @@ -0,0 +1,71 @@ +"""Server-side interceptors tests.""" + +# ruff:noqa: D102,D103,D107 + +from typing import Any + +import grpc +import pytest +from grpc_interceptor.testing import ( + DummyRequest, + dummy_client, # pyright: ignore [reportUnknownVariableType] + raises, # pyright: ignore [reportUnknownVariableType] +) + +from arflow._error_interceptor import ErrorInterceptor + + +class MockErrorLogger(ErrorInterceptor): + """Mock error logger that stores the last exception. + + You don’t actually want the logging side effect to happen. You just want to make sure it’s called. + """ + + def __init__(self): + self.logged_exception = None + + def log_error(self, e: Exception) -> None: + self.logged_exception = e + + +def test_log_error(): + """Test that the error logger catches exceptions. + + Use the `dummy_client()` context manager to create a client that’s connected to a real gRPC + microservice. You send `DummyRequest` to the microservice, and it replies with `DummyResponse`. + By default, the input of `DummyRequest` is echoed to the output of `DummyResponse`. However, you can + pass `dummy_client()` a dictionary of special cases, and if input matches one of them, then it will + call a function you provide and return the result. + """ + mock = MockErrorLogger() + ex = Exception() + special_cases: dict[str, Any] = {"error": raises(ex)} + + with dummy_client(special_cases=special_cases, interceptors=[mock]) as client: + # Test no exception + assert client.Execute(DummyRequest(input="foo")).output == "foo" # pyright: ignore [reportUnknownMemberType] + assert mock.logged_exception is None + + # Test exception + with pytest.raises(grpc.RpcError): + client.Execute(DummyRequest(input="error")) # pyright: ignore [reportUnknownMemberType] + assert mock.logged_exception is ex + + +def test_log_error_multiple_cases(): + mock = MockErrorLogger() + ex1 = Exception("Error 1") + ex2 = Exception("Error 2") + special_cases: dict[str, Any] = { + "error1": raises(ex1), + "error2": raises(ex2), + } + + with dummy_client(special_cases=special_cases, interceptors=[mock]) as client: + with pytest.raises(grpc.RpcError): + client.Execute(DummyRequest(input="error1")) # pyright: ignore [reportUnknownMemberType] + assert mock.logged_exception is ex1 + + with pytest.raises(grpc.RpcError): + client.Execute(DummyRequest(input="error2")) # pyright: ignore [reportUnknownMemberType] + assert mock.logged_exception is ex2 diff --git a/python/tests/test_replay.py b/python/tests/test_replay.py new file mode 100644 index 0000000..a45870a --- /dev/null +++ b/python/tests/test_replay.py @@ -0,0 +1,25 @@ +"""Replay tests.""" + +# ruff:noqa: D103 + +# from unittest.mock import MagicMock, patch + +# from arflow import ARFlowPlayer, RegisterClientRequest +# from arflow._types import EnrichedARFlowRequest + + +# def test_replay_data(): +# mock_service = MagicMock() + +# # Mock the pickle load to simulate the requests history +# mock_requests_history = [ +# EnrichedARFlowRequest(timestamp=1, data=RegisterClientRequest()), +# EnrichedARFlowRequest(timestamp=2, data=RegisterClientRequest()), +# ] + +# with patch("pickle.load", return_value=mock_requests_history): +# player = ARFlowPlayer(mock_service, "dummy_path") +# player.run() + +# # Verify the service processes the requests correctly +# assert mock_service.RegisterClient.call_count == 2 diff --git a/python/tests/test_server.py b/python/tests/test_server.py new file mode 100644 index 0000000..7d40d8c --- /dev/null +++ b/python/tests/test_server.py @@ -0,0 +1,258 @@ +"""gRPC server tests with an end-to-end fashion.""" + +# ruff:noqa: D103 +# pyright: reportUnknownMemberType=false, reportUnknownVariableType=false, reportUnknownArgumentType=false +# We have to do the above because of the typelessness of the grpc stub +from concurrent import futures +from typing import Any, Generator + +import grpc +import numpy as np +import pytest + +from arflow import ARFlowServicer +from arflow._error_interceptor import ErrorInterceptor +from arflow_grpc import service_pb2, service_pb2_grpc +from arflow_grpc.service_pb2 import ( + ProcessFrameRequest, + RegisterClientRequest, + RegisterClientResponse, +) +from arflow_grpc.service_pb2_grpc import ARFlowServiceStub + + +@pytest.fixture(scope="function") +def stub() -> Generator[ARFlowServiceStub, Any, None]: + servicer = ARFlowServicer() + interceptors = [ErrorInterceptor()] + server = grpc.server( + futures.ThreadPoolExecutor( + max_workers=10, + ), + interceptors=interceptors, # pyright: ignore [reportArgumentType] + options=[ + ("grpc.max_send_message_length", -1), + ("grpc.max_receive_message_length", -1), + ], + ) + service_pb2_grpc.add_ARFlowServiceServicer_to_server(servicer, server) + port = server.add_insecure_port("[::]:0") + server.start() + + try: + with grpc.insecure_channel(f"localhost:{port}") as channel: + yield ARFlowServiceStub(channel) + finally: + server.stop(None) + + +def test_register_client(stub: ARFlowServiceStub): + request = RegisterClientRequest() + + response: RegisterClientResponse = stub.RegisterClient(request) + assert len(response.uid) == 36 + + +# def test_register_client_with_init_uid(stub: ARFlowServiceStub): +# request = RegisterClientRequest() + +# response: RegisterClientResponse = stub.RegisterClient(request, init_uid="1234") +# assert response.uid == "1234" + + +def test_multiple_clients(stub: ARFlowServiceStub): + """Flaky since UUIDs might collide.""" + uids = [] + for _ in range(3): + request = RegisterClientRequest() + response = stub.RegisterClient(request) + assert len(response.uid) == 36 + assert response.uid not in uids + uids.append(response.uid) + assert len(uids) == 3 + + +# def test_register_same_client_twice(stub: ARFlowServiceStub): +# request = RegisterClientRequest() +# response = stub.RegisterClient(request) +# uid = response.uid + +# response = stub.RegisterClient(request, init_uid=uid) +# assert response.uid == uid + + +def test_process_frame(stub: ARFlowServiceStub): + client_config = RegisterClientRequest( + camera_color=RegisterClientRequest.CameraColor( + enabled=True, data_type="RGB24", resize_factor_x=1.0, resize_factor_y=1.0 + ), + camera_depth=RegisterClientRequest.CameraDepth( + enabled=True, data_type="f32", resolution_x=4, resolution_y=4 + ), + camera_transform=RegisterClientRequest.CameraTransform(enabled=True), + camera_point_cloud=RegisterClientRequest.CameraPointCloud(enabled=True), + camera_intrinsics=RegisterClientRequest.CameraIntrinsics( + resolution_x=4, + resolution_y=4, + focal_length_x=1.0, + focal_length_y=1.0, + principal_point_x=1.0, + principal_point_y=1.0, + ), + ) + response = stub.RegisterClient(client_config) + client_id = response.uid + + frame = ProcessFrameRequest( + uid=client_id, + color=np.random.randint(0, 255, 4 * 4 * 3, dtype=np.uint8).tobytes(), # pyright: ignore [reportUnknownMemberType] + depth=np.random.rand(4, 4).astype(np.float32).tobytes(), + transform=np.random.rand(12).astype(np.float32).tobytes(), + ) + + response = stub.ProcessFrame(frame) + assert response.message == "OK" + + +def test_process_frame_with_unregistered_client(stub: ARFlowServiceStub): + invalid_frame = ProcessFrameRequest(uid="invalid_id") + + with pytest.raises(grpc.RpcError) as excinfo: + stub.ProcessFrame(invalid_frame) + assert excinfo.value.code() == grpc.StatusCode.NOT_FOUND + + +@pytest.mark.parametrize( + "client_config", + [ + RegisterClientRequest( + camera_color=( + RegisterClientRequest.CameraColor( + enabled=True, + data_type="unknown", + ) + ) + ), + RegisterClientRequest( + camera_depth=( + RegisterClientRequest.CameraDepth( + enabled=True, + data_type="unknown", + ) + ) + ), + ], +) +def test_process_frame_with_invalid_data_types( + client_config: RegisterClientRequest, stub: ARFlowServiceStub +): + response = stub.RegisterClient(client_config) + client_id = response.uid + invalid_frame = ProcessFrameRequest( + uid=client_id, + ) + with pytest.raises(grpc.RpcError) as excinfo: + stub.ProcessFrame(invalid_frame) + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT + + +@pytest.mark.parametrize( + "client_config, corrupted_frame", + [ + ( + RegisterClientRequest( + camera_color=RegisterClientRequest.CameraColor( + enabled=True, + data_type="RGB24", + resize_factor_x=1.0, + resize_factor_y=1.0, + ), + camera_intrinsics=RegisterClientRequest.CameraIntrinsics( + resolution_x=4, + resolution_y=4, + ), + ), + ProcessFrameRequest( + color=np.random.randint( # pyright: ignore [reportUnknownMemberType] + 0, 255, (4, 4, 2), dtype=np.uint8 + ).tobytes(), # Incorrect size + ), + ), + ( + RegisterClientRequest( + camera_color=RegisterClientRequest.CameraColor( + enabled=False, + ), + camera_depth=RegisterClientRequest.CameraDepth( + enabled=True, resolution_x=4, resolution_y=4, data_type="f32" + ), + ), + ProcessFrameRequest( + depth=np.random.rand(4 * 4) + .astype(np.float32) + .tobytes()[:1], # Incorrect size + ), + ), + ( + RegisterClientRequest( + camera_color=RegisterClientRequest.CameraColor( + enabled=False, + resize_factor_x=1.0, + resize_factor_y=1.0, + ), + camera_transform=RegisterClientRequest.CameraTransform(enabled=True), + camera_intrinsics=RegisterClientRequest.CameraIntrinsics( + focal_length_x=1.0, + focal_length_y=1.0, + principal_point_x=1.0, + principal_point_y=1.0, + ), + ), + ProcessFrameRequest( + transform=np.random.rand(8 // 4) + .astype(np.float32) + .tobytes(), # Incorrect size + ), + ), + ( + RegisterClientRequest( + camera_plane_detection=RegisterClientRequest.CameraPlaneDetection( + enabled=True + ), + ), + ProcessFrameRequest( + plane_detection=[ + service_pb2.ProcessFrameRequest.Plane( + center=service_pb2.ProcessFrameRequest.Vector3( + x=1.0, y=2.0, z=3.0 + ), + normal=service_pb2.ProcessFrameRequest.Vector3( + x=1.0, y=2.0, z=3.0 + ), + size=service_pb2.ProcessFrameRequest.Vector2(x=1.0, y=2.0), + boundary_points=[ + service_pb2.ProcessFrameRequest.Vector2(x=1.0, y=2.0), + service_pb2.ProcessFrameRequest.Vector2(x=2.0, y=3.0), + # Missing one point + ], + ) + ], + ), + ), + ], +) +def test_process_frame_with_corrupted_data( + client_config: RegisterClientRequest, + corrupted_frame: ProcessFrameRequest, + stub: ARFlowServiceStub, +): + response = stub.RegisterClient( + client_config, + ) + corrupted_frame.uid = response.uid + + with pytest.raises(grpc.RpcError) as excinfo: + stub.ProcessFrame( + corrupted_frame, + ) + assert excinfo.value.code() == grpc.StatusCode.INVALID_ARGUMENT diff --git a/python/tests/test_service.py b/python/tests/test_service.py new file mode 100644 index 0000000..c49b77e --- /dev/null +++ b/python/tests/test_service.py @@ -0,0 +1,329 @@ +"""gRPC service tests.""" + +# ruff:noqa: D103 +# pyright: reportPrivateUsage=false + +from pathlib import Path +from unittest.mock import patch + +import DracoPy +import grpc +import grpc_interceptor +import grpc_interceptor.exceptions +import numpy as np +import pytest + +from arflow import ARFlowServicer, DecodedDataFrame, RegisterClientRequest +from arflow._types import HashableClientIdentifier +from arflow_grpc import service_pb2 +from arflow_grpc.service_pb2 import ProcessFrameRequest + + +@pytest.fixture +def default_service(): + """A default ARFlow service that can be shared across tests.""" + return ARFlowServicer() + + +def test_save_request(default_service: ARFlowServicer): + request = RegisterClientRequest() + default_service._save_request(request) + + assert len(default_service._requests_history) == 1 + enriched_request = default_service._requests_history[0] + assert enriched_request.timestamp >= 0 + assert enriched_request.data == request + + request = ProcessFrameRequest() + default_service._save_request(request) + + assert len(default_service._requests_history) == 2 + enriched_request = default_service._requests_history[1] + assert enriched_request.timestamp >= 0 + assert enriched_request.data == request + assert ( + default_service._requests_history[0].timestamp + <= default_service._requests_history[1].timestamp + ) + + +def test_register_client(default_service: ARFlowServicer): + request = RegisterClientRequest() + + response = default_service.RegisterClient(request) + assert len(response.uid) == 36 + + +def test_register_client_with_init_uid(default_service: ARFlowServicer): + request = RegisterClientRequest() + + response = default_service.RegisterClient(request, init_uid="1234") + assert response.uid == "1234" + + +def test_multiple_clients(default_service: ARFlowServicer): + """Flaky since UUIDs might collide.""" + # Register multiple clients + for _ in range(3): + request = RegisterClientRequest() + response = default_service.RegisterClient(request) + assert len(response.uid) == 36 + + assert len(default_service._client_configurations) == 3 + + +def test_register_same_client_twice(default_service: ARFlowServicer): + request = RegisterClientRequest() + response1 = default_service.RegisterClient(request) + response2 = default_service.RegisterClient(request, init_uid=response1.uid) + + assert response1.uid == response2.uid + + +@pytest.mark.parametrize( + "client_config,expected_enabled", + [ + ( + RegisterClientRequest( + camera_color=RegisterClientRequest.CameraColor(enabled=True) + ), + True, + ), + ( + RegisterClientRequest( + camera_color=RegisterClientRequest.CameraColor(enabled=False) + ), + False, + ), + ], +) +def test_ensure_correct_config( + default_service: ARFlowServicer, + client_config: RegisterClientRequest, + expected_enabled: bool, +): + response = default_service.RegisterClient(client_config) + client_id = HashableClientIdentifier(response.uid) + assert ( + default_service._client_configurations[client_id].camera_color.enabled + == expected_enabled + ) + + +def test_process_frame(default_service: ARFlowServicer): + client_config = RegisterClientRequest( + camera_color=RegisterClientRequest.CameraColor( + enabled=True, data_type="RGB24", resize_factor_x=1.0, resize_factor_y=1.0 + ), + camera_depth=RegisterClientRequest.CameraDepth( + enabled=True, data_type="f32", resolution_x=4, resolution_y=4 + ), + camera_transform=RegisterClientRequest.CameraTransform(enabled=True), + camera_point_cloud=RegisterClientRequest.CameraPointCloud(enabled=True), + camera_intrinsics=RegisterClientRequest.CameraIntrinsics( + resolution_x=4, + resolution_y=4, + focal_length_x=1.0, + focal_length_y=1.0, + principal_point_x=1.0, + principal_point_y=1.0, + ), + camera_plane_detection=RegisterClientRequest.CameraPlaneDetection(enabled=True), + gyroscope=RegisterClientRequest.Gyroscope(enabled=True), + audio=RegisterClientRequest.Audio(enabled=True), + meshing=RegisterClientRequest.Meshing(enabled=True), + ) + response = default_service.RegisterClient(client_config) + client_id = response.uid + + with (Path(__file__).parent / "bunny.drc").open("rb") as draco_file: + mesh = DracoPy.decode(draco_file.read()) # pyright: ignore [reportUnknownMemberType, reportUnknownVariableType] + mock_frame = ProcessFrameRequest( + uid=client_id, + color=np.random.randint(0, 255, 4 * 4 * 3, dtype=np.uint8).tobytes(), # pyright: ignore [reportUnknownMemberType] + depth=np.random.rand(4, 4).astype(np.float32).tobytes(), + transform=np.random.rand(12).astype(np.float32).tobytes(), + plane_detection=[ + service_pb2.ProcessFrameRequest.Plane( + center=service_pb2.ProcessFrameRequest.Vector3(x=1.0, y=2.0, z=3.0), + normal=service_pb2.ProcessFrameRequest.Vector3(x=1.0, y=2.0, z=3.0), + size=service_pb2.ProcessFrameRequest.Vector2(x=1.0, y=2.0), + boundary_points=[ + service_pb2.ProcessFrameRequest.Vector2(x=1.0, y=2.0), + service_pb2.ProcessFrameRequest.Vector2(x=2.0, y=3.0), + service_pb2.ProcessFrameRequest.Vector2(x=1.0, y=3.0), + ], + ) + ], + gyroscope=service_pb2.ProcessFrameRequest.GyroscopeData( + attitude=service_pb2.ProcessFrameRequest.Quaternion( + x=1.0, y=2.0, z=3.0, w=4.0 + ), + rotation_rate=service_pb2.ProcessFrameRequest.Vector3( + x=1.0, y=2.0, z=3.0 + ), + gravity=service_pb2.ProcessFrameRequest.Vector3(x=1.0, y=2.0, z=3.0), + acceleration=service_pb2.ProcessFrameRequest.Vector3( + x=1.0, y=2.0, z=3.0 + ), + ), + audio_data=np.random.rand(4).astype(np.float32), + meshes=[ + service_pb2.ProcessFrameRequest.Mesh( + data=DracoPy.encode( # pyright: ignore [reportUnknownMemberType, reportUnknownArgumentType] + mesh.points, # pyright: ignore [reportUnknownMemberType] + faces=mesh.faces, # pyright: ignore [reportUnknownMemberType] + colors=mesh.colors, # pyright: ignore [reportUnknownMemberType] + ) + ) + ], + ) + + with patch.object(default_service, "on_frame_received") as mock_on_frame: + response = default_service.ProcessFrame(mock_frame) + assert response.message == "OK" + mock_on_frame.assert_called_once() + assert isinstance(mock_on_frame.call_args[0][0], DecodedDataFrame) + + +def test_process_frame_with_unregistered_client(default_service: ARFlowServicer): + invalid_frame = ProcessFrameRequest(uid="invalid_id") + with pytest.raises(grpc_interceptor.exceptions.GrpcException) as excinfo: + default_service.ProcessFrame(invalid_frame) + assert excinfo.value.status_code == grpc.StatusCode.NOT_FOUND + + +@pytest.mark.parametrize( + "client_config", + [ + RegisterClientRequest( + camera_color=( + RegisterClientRequest.CameraColor( + enabled=True, + data_type="unknown", + ) + ) + ), + RegisterClientRequest( + camera_depth=( + RegisterClientRequest.CameraDepth( + enabled=True, + data_type="unknown", + ) + ) + ), + ], +) +def test_process_frame_with_invalid_data_types( + client_config: RegisterClientRequest, default_service: ARFlowServicer +): + response = default_service.RegisterClient(client_config) + client_id = response.uid + invalid_frame = ProcessFrameRequest( + uid=client_id, + ) + with pytest.raises(grpc_interceptor.exceptions.GrpcException) as excinfo: + default_service.ProcessFrame(invalid_frame) + assert excinfo.value.status_code == grpc.StatusCode.INVALID_ARGUMENT + + +@pytest.mark.parametrize( + "client_config, corrupted_frame", + [ + ( + RegisterClientRequest( + camera_color=RegisterClientRequest.CameraColor( + enabled=True, + data_type="RGB24", + resize_factor_x=1.0, + resize_factor_y=1.0, + ), + camera_intrinsics=RegisterClientRequest.CameraIntrinsics( + resolution_x=4, + resolution_y=4, + ), + ), + ProcessFrameRequest( + uid="1234", + color=np.random.randint( # pyright: ignore [reportUnknownMemberType] + 0, 255, (4, 4, 2), dtype=np.uint8 + ).tobytes(), # Incorrect size + ), + ), + ( + RegisterClientRequest( + camera_depth=RegisterClientRequest.CameraDepth( + enabled=True, resolution_x=4, resolution_y=4, data_type="f32" + ), + ), + ProcessFrameRequest( + uid="1234", + depth=np.random.rand(4 * 4) + .astype(np.float32) + .tobytes()[:1], # Incorrect size + ), + ), + ( + RegisterClientRequest( + camera_color=RegisterClientRequest.CameraColor( + resize_factor_x=1.0, + resize_factor_y=1.0, + ), + camera_transform=RegisterClientRequest.CameraTransform(enabled=True), + camera_intrinsics=RegisterClientRequest.CameraIntrinsics( + focal_length_x=1.0, + focal_length_y=1.0, + principal_point_x=1.0, + principal_point_y=1.0, + ), + ), + ProcessFrameRequest( + uid="1234", + transform=np.random.rand(8 // 4) + .astype(np.float32) + .tobytes(), # Incorrect size + ), + ), + ( + RegisterClientRequest( + camera_plane_detection=RegisterClientRequest.CameraPlaneDetection( + enabled=True + ), + ), + ProcessFrameRequest( + uid="1234", + plane_detection=[ + service_pb2.ProcessFrameRequest.Plane( + center=service_pb2.ProcessFrameRequest.Vector3( + x=1.0, y=2.0, z=3.0 + ), + normal=service_pb2.ProcessFrameRequest.Vector3( + x=1.0, y=2.0, z=3.0 + ), + size=service_pb2.ProcessFrameRequest.Vector2(x=1.0, y=2.0), + boundary_points=[ + service_pb2.ProcessFrameRequest.Vector2(x=1.0, y=2.0), + service_pb2.ProcessFrameRequest.Vector2(x=2.0, y=3.0), + # Missing one point + ], + ) + ], + ), + ), + ], +) +def test_process_frame_with_corrupted_data( + client_config: RegisterClientRequest, + corrupted_frame: ProcessFrameRequest, + default_service: ARFlowServicer, +): + default_service.RegisterClient( + client_config, + init_uid="1234", + ) + + with pytest.raises(grpc_interceptor.exceptions.InvalidArgument) as excinfo: + default_service.ProcessFrame( + corrupted_frame, + ) + assert excinfo.value.status_code == grpc.StatusCode.INVALID_ARGUMENT diff --git a/python/tools/make_docs_cli.py b/python/tools/make_docs_cli.py index db31143..c335e5c 100644 --- a/python/tools/make_docs_cli.py +++ b/python/tools/make_docs_cli.py @@ -6,6 +6,7 @@ def make_docs(): + """Generate documentation for the `arflow` package and `examples` package.""" pdoc.pdoc( "arflow", "examples", output_directory=Path(__file__).parent.parent / "docs" ) diff --git a/scripts/compile.sh b/scripts/compile.sh new file mode 100644 index 0000000..54946b1 --- /dev/null +++ b/scripts/compile.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +# Generate Python gRPC files +python -m grpc_tools.protoc --proto_path=../protos --python_out=../python --pyi_out=../python --grpc_python_out=../python ../protos/arflow_grpc/*.proto + +# Generate C# gRPC files +protoc --csharp_out=../unity/Assets/Scripts/ARFlow --grpc_out=../unity/Assets/Scripts/ARFlow --plugin=protoc-gen-grpc=grpc_csharp_plugin ../protos/arflow_grpc/*.proto + +# protoc --csharp_out=unity/Assets/Scripts/ARFlow --grpc_out=unity/Assets/Scripts/ARFlow --plugin=protoc-gen-grpc=./.grpc_tools/grpc_csharp_plugin.exe protos/arflow_grpc/*.proto \ No newline at end of file diff --git a/unity/.gitignore b/unity/.gitignore index 50878a6..c6cf8ce 100644 --- a/unity/.gitignore +++ b/unity/.gitignore @@ -1,7 +1,10 @@ +# Created by https://www.toptal.com/developers/gitignore/api/unity +# Edit at https://www.toptal.com/developers/gitignore?templates=unity + +### Unity ### # This .gitignore file should be placed at the root of your Unity project directory # -# Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore -# +# Get latest from https://github.com/github/gitignore/blob/main/Unity.gitignore /[Ll]ibrary/ /[Tt]emp/ /[Oo]bj/ @@ -9,13 +12,14 @@ /[Bb]uilds/ /[Ll]ogs/ /[Uu]ser[Ss]ettings/ +/ContentPackages # MemoryCaptures can get excessive in size. # They also could contain extremely sensitive data /[Mm]emoryCaptures/ -# Asset meta data should only be ignored when the corresponding asset is also ignored -!/[Aa]ssets/**/*.meta +# Recordings can get excessive in size +/[Rr]ecordings/ # Uncomment this line if you wish to ignore the asset store tools plugin # /[Aa]ssets/AssetStoreTools* @@ -34,7 +38,6 @@ ExportedObj/ .consulo/ *.csproj *.unityproj -# solution file is uploaded for doc building *.sln *.suo *.tmp @@ -60,6 +63,7 @@ sysinfo.txt *.apk *.aab *.unitypackage +*.app # Crashlytics generated file crashlytics-build.properties @@ -71,142 +75,13 @@ crashlytics-build.properties /[Aa]ssets/[Ss]treamingAssets/aa.meta /[Aa]ssets/[Ss]treamingAssets/aa/* -# Created by https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode -# Edit at https://www.toptal.com/developers/gitignore?templates=macos,visualstudiocode - -### macOS ### -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -*.code-workspace - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history - -# End of https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode - -# Created by https://www.toptal.com/developers/gitignore/api/intellij+all -# Edit at https://www.toptal.com/developers/gitignore?templates=intellij+all - -### Intellij+all ### -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -# .idea/artifacts -# .idea/compiler.xml -# .idea/jarRepositories.xml -# .idea/modules.xml -# .idea/*.iml -# .idea/modules -# *.iml -# *.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# SonarLint plugin -.idea/sonarlint/ - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser - -### Intellij+all Patch ### -# Ignore everything but code style settings and run configurations -# that are supposed to be shared within teams. - -.idea/* - -!.idea/codeStyles -!.idea/runConfigurations +# End of https://www.toptal.com/developers/gitignore/api/unity -# End of https://www.toptal.com/developers/gitignore/api/intellij+all +### ARFlow ### +# ignore files moved by build script and generated by Docfx +Documentation/index.md +Documentation/api -XcodeBuild +# Client docs directory inside documentation is for local environment. +# When running github workflows the files will be moved into the website/docs directory (in root) +Documentation/clientHTMLOutput diff --git a/unity/Assets/.gitignore b/unity/Assets/.gitignore new file mode 100644 index 0000000..b0ceea0 --- /dev/null +++ b/unity/Assets/.gitignore @@ -0,0 +1,2 @@ +ARFoundation/SimulationEnvironments +/UnityXRContent \ No newline at end of file diff --git a/unity/Assets/ARFlowPackage.meta b/unity/Assets/ARFlowPackage.meta new file mode 100644 index 0000000..813315f --- /dev/null +++ b/unity/Assets/ARFlowPackage.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cd100aca6d90f7e4c9e9ee9f72bf04fe +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Scripts/ARFlow.meta b/unity/Assets/ARFlowPackage/ARFlow.meta similarity index 100% rename from unity/Assets/Scripts/ARFlow.meta rename to unity/Assets/ARFlowPackage/ARFlow.meta diff --git a/unity/Assets/ARFlowPackage/ARFlow/ARFlow.asmdef b/unity/Assets/ARFlowPackage/ARFlow/ARFlow.asmdef new file mode 100644 index 0000000..47538ae --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/ARFlow.asmdef @@ -0,0 +1,22 @@ +{ + "name": "ARFlow", + "rootNamespace": "", + "references": [ + "GUID:a9420e37d7990b54abdef6688edbe313", + "GUID:c3bc0afe4a069b54aa23296f3c18a871", + "GUID:21b0c8d1703a94250bfac916590cea4f", + "GUID:75469ad4d38634e559750d17036d5f7c", + "GUID:92703082f92b41ba80f0d6912de66115", + "GUID:8a68004c08665e8449ab651ffdc7bee0", + "GUID:1504f36c4fe570349afad1ff2291fbd0" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/unity/Assets/ARFlowPackage/ARFlow/ARFlow.asmdef.meta b/unity/Assets/ARFlowPackage/ARFlow/ARFlow.asmdef.meta new file mode 100644 index 0000000..60ad30e --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/ARFlow.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 32105e0090382574e9f663b4802eb134 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Scripts/ARFlow/ARFlowClient.cs b/unity/Assets/ARFlowPackage/ARFlow/ARFlowClient.cs similarity index 75% rename from unity/Assets/Scripts/ARFlow/ARFlowClient.cs rename to unity/Assets/ARFlowPackage/ARFlow/ARFlowClient.cs index 232e8cf..826dbe3 100644 --- a/unity/Assets/Scripts/ARFlow/ARFlowClient.cs +++ b/unity/Assets/ARFlowPackage/ARFlow/ARFlowClient.cs @@ -3,6 +3,8 @@ using Grpc.Net.Client; using UnityEngine; +using static ARFlow.OtherUtils; + namespace ARFlow { /// @@ -21,7 +23,7 @@ public class ARFlowClient /// The address (AKA server URL) to connect to public ARFlowClient(string address) { - Debug.Log("Initialize client for " + address); + PrintDebug("Initialize client for " + address); var handler = new YetAnotherHttpHandler() { Http2Only = true }; _channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions() { @@ -38,30 +40,22 @@ public ARFlowClient(string address) /// /// Connect to the server with a request that contain register data of about the camera. + /// This will throw an error if the connection is not established. /// /// Register data (AKA metadata) of the camera. The typing of this is generated by Protobuf. - public void Connect(RegisterRequest requestData) + public void Connect(RegisterClientRequest requestData) { - try - { - var response = _client.register(requestData); - _sessionId = response.Uid; + var response = _client.RegisterClient(requestData); + _sessionId = response.Uid; - Debug.Log(response.Uid); - } - catch (Exception e) - { - // Try to catch any exceptions. - // Network, device image, camera intrinsics - Debug.LogError(e); - } + PrintDebug(response.Uid); } /// /// Send a data of a frame to the server. /// /// Data of the frame. The typing of this is generated by Protobuf. - public string SendFrame(DataFrameRequest frameData) + public string SendFrame(ProcessFrameRequest frameData) { string res = ""; frameData.Uid = _sessionId; @@ -70,16 +64,16 @@ public string SendFrame(DataFrameRequest frameData) // _client.data_frameAsync(frameData) // .ResponseAsync.ContinueWith(response => // { - // Debug.Log(response); + // PrintDebug(response); // }); - var response = _client.data_frame(frameData); + var response = _client.ProcessFrame(frameData); res = response.Message; } catch (Exception e) { // Try to catch any exceptions. // Network, device image, camera intrinsics - Debug.LogError(e); + PrintDebug(e.Message); } return res; diff --git a/unity/Assets/Scripts/ARFlow/ARFlowClient.cs.meta b/unity/Assets/ARFlowPackage/ARFlow/ARFlowClient.cs.meta similarity index 100% rename from unity/Assets/Scripts/ARFlow/ARFlowClient.cs.meta rename to unity/Assets/ARFlowPackage/ARFlow/ARFlowClient.cs.meta diff --git a/unity/Assets/ARFlowPackage/ARFlow/ARFlowClientManager.cs b/unity/Assets/ARFlowPackage/ARFlow/ARFlowClientManager.cs new file mode 100644 index 0000000..198f350 --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/ARFlowClientManager.cs @@ -0,0 +1,494 @@ +using System; +using System.Net; +using System.Runtime.CompilerServices; +using System.Threading.Tasks; +using Cysharp.Net.Http; +using Google.Protobuf; +using Grpc.Net.Client; +using Unity.VisualScripting.FullSerializer; +using UnityEngine; +using UnityEngine.XR.ARFoundation; +using System.Collections.Generic; +using UnityEngine.InputSystem; +using Google.Protobuf.WellKnownTypes; +using Unity.Collections; +using System.Linq; +using UnityEngine.Android; + +using static ARFlow.OtherUtils; + +namespace ARFlow +{ + /// + /// This class represent the implementation for the client manager + /// The client manager is an abstraction layer (for hopefully cleaner code) that collects and send data to the client. + /// The Unity Scene only needs to input AR managers and modalities options. + /// + public class ARFlowClientManager + { + private ARFlowClient _client; + private ARCameraManager _cameraManager; + private AROcclusionManager _occlusionManager; + private Vector2Int _sampleSize; + private Dictionary _activatedDataModalities; + private ARMeshManager _meshManager; + private ARPlaneManager _planeManager; + + private Task oldTask = null; + + private bool _isStreaming = false; + + // Interfaces for implementations using other packages + private AudioStreaming _audioStreaming; + private MeshEncoder _meshEncoder; + + //TODO + //private Dictionary> _modalityConfig + + private readonly Dictionary DEFAULT_MODALITIES = new() + { + ["CameraColor"] = false, + ["CameraDepth"] = false, + ["CameraTransform"] = false, + ["CameraPointCloud"] = false, + ["PlaneDetection"] = false, + ["Gyroscope"] = false, + ["Audio"] = false, + ["Meshing"] = false + }; + + public static readonly List MODALITIES = new() + { + "CameraColor", + "CameraDepth", + "CameraTransform", + "CameraPointCloud", + "PlaneDetection", + "Gyroscope", + "Audio", + "Meshing" + }; + + /// + /// Initialize the client manager + /// + public ARFlowClientManager( + ARCameraManager cameraManager = null, + AROcclusionManager occlusionManager = null, + ARPlaneManager planeManager = null, + ARMeshManager meshManager = null + ) + { + if (UnityEngine.InputSystem.Gyroscope.current != null) + { + InputSystem.EnableDevice(UnityEngine.InputSystem.Gyroscope.current); + } + if (AttitudeSensor.current != null) + { + InputSystem.EnableDevice(AttitudeSensor.current); + } + if (Accelerometer.current != null) + { + InputSystem.EnableDevice(Accelerometer.current); + } + if (GravitySensor.current != null) + { + InputSystem.EnableDevice(GravitySensor.current); + } + _cameraManager = cameraManager; + _occlusionManager = occlusionManager; + + _planeManager = planeManager; + _meshManager = meshManager; + + _audioStreaming = new AudioStreaming(); + _meshEncoder = new MeshEncoder(); + +#if UNITY_ANDROID + if (!Permission.HasUserAuthorizedPermission(Permission.Microphone)) + { + Permission.RequestUserPermission(Permission.Microphone); + } +#endif +#if UNITY_IOS + if (Application.HasUserAuthorization(UserAuthorization.Microphone)) + { + Application.RequestUserAuthorization(UserAuthorization.Microphone); + } +#endif + } + + /// + /// A Task wrapper for the connect method, to avoid blocking the main thread. + /// Only the connect method is sent to another thread. + /// The methods to collect client configurations is required to be in the main thread. + /// + /// The method will spawn a new task, so the usage of this is only calling "ConnectTask(args)" + /// + /// Server address + /// Dictionary of all data modalities, either activated or not + public Task ConnectTask( + string address, + Dictionary activatedDataModalities = null, + Action taskFinishedHook = null + ) + { + ResetState(); + _client = new ARFlowClient(address); + + _activatedDataModalities = activatedDataModalities; + if (activatedDataModalities == null) + _activatedDataModalities = DEFAULT_MODALITIES; + + // To avoid old method calls to log message + oldTask?.ContinueWith(t => { }); + + var requestData = GetClientConfiguration(); + var task = Task.Run(() => _client.Connect(requestData)); + if (taskFinishedHook is not null) + task.ContinueWith(taskFinishedHook); + + oldTask = task; + + return task; + } + + + private void ResetState() + { + if (_isStreaming) + { + StopDataStreaming(); + } + } + + private RegisterClientRequest GetClientConfiguration() + { + _cameraManager.TryGetIntrinsics(out var k); + _cameraManager.TryAcquireLatestCpuImage(out var colorImage); + _occlusionManager.TryAcquireEnvironmentDepthCpuImage(out var depthImage); + + _sampleSize = depthImage.dimensions; + + var requestData = new RegisterClientRequest() + { + DeviceName = SystemInfo.deviceName, + CameraIntrinsics = new RegisterClientRequest.Types.CameraIntrinsics() + { + FocalLengthX = k.focalLength.x, + FocalLengthY = k.focalLength.y, + ResolutionX = k.resolution.x, + ResolutionY = k.resolution.y, + PrincipalPointX = k.principalPoint.x, + PrincipalPointY = k.principalPoint.y, + } + + }; + if (_activatedDataModalities["CameraColor"]) + { + var CameraColor = new RegisterClientRequest.Types.CameraColor() + { + Enabled = true, + DataType = "YCbCr420", + ResizeFactorX = depthImage.dimensions.x / (float)colorImage.dimensions.x, + ResizeFactorY = depthImage.dimensions.y / (float)colorImage.dimensions.y, + }; + requestData.CameraColor = CameraColor; + } + if (_activatedDataModalities["CameraDepth"]) + { + var CameraDepth = new RegisterClientRequest.Types.CameraDepth() + { + Enabled = true, +#if UNITY_ANDROID + DataType = "u16", // f32 for iOS, u16 for Android +#endif +#if UNITY_IOS + DataType = "f32", +#endif + ConfidenceFilteringLevel = 0, + ResolutionX = depthImage.dimensions.x, + ResolutionY = depthImage.dimensions.y + }; + requestData.CameraDepth = CameraDepth; + } + + if (_activatedDataModalities["CameraTransform"]) + { + var CameraTransform = new RegisterClientRequest.Types.CameraTransform() + { + Enabled = true + }; + requestData.CameraTransform = CameraTransform; + } + + if (_activatedDataModalities["CameraPointCloud"]) + { + var CameraPointCloud = new RegisterClientRequest.Types.CameraPointCloud() + { + Enabled = true, + DepthUpscaleFactor = 1.0f, + }; + requestData.CameraPointCloud = CameraPointCloud; + }; + + if (_activatedDataModalities["PlaneDetection"]) + { + var CameraPlaneDetection = new RegisterClientRequest.Types.CameraPlaneDetection() + { + Enabled = true + }; + requestData.CameraPlaneDetection = CameraPlaneDetection; + } + + if (_activatedDataModalities["Gyroscope"]) + { + var Gyroscope = new RegisterClientRequest.Types.Gyroscope() + { + Enabled = true + }; + requestData.Gyroscope = Gyroscope; + } + + if (_activatedDataModalities["Audio"]) + { + var Audio = new RegisterClientRequest.Types.Audio() + { + Enabled = true + }; + requestData.Audio = Audio; + } + + if (_activatedDataModalities["Meshing"]) + { + var Meshing = new RegisterClientRequest.Types.Meshing() + { + Enabled = true + }; + requestData.Meshing = Meshing; + } + + colorImage.Dispose(); + depthImage.Dispose(); + + return requestData; + + } + + /// + /// Connect to the server at an address, and with data modalities activated or not. + /// + /// Server address + /// Dictionary of all data modalities, either activated or not + public void Connect( + string address, + Dictionary activatedDataModalities = null + ) + { + ResetState(); + _client = new ARFlowClient(address); + + _activatedDataModalities = activatedDataModalities; + if (activatedDataModalities == null) + _activatedDataModalities = DEFAULT_MODALITIES; + + try + { + var requestData = GetClientConfiguration(); + _client.Connect(requestData); + } + catch (Exception e) + { + PrintDebug(e.Message); + } + } + + /// + /// Helper function to convert from unity data types to custom proto types + /// + /// + /// + ProcessFrameRequest.Types.Vector3 UnityVector3ToProto(Vector3 a) + { + return new ProcessFrameRequest.Types.Vector3() + { + X = a.x, + Y = a.y, + Z = a.z + }; + } + + ProcessFrameRequest.Types.Vector2 UnityVector2ToProto(Vector2 a) + { + return new ProcessFrameRequest.Types.Vector2() + { + X = a.x, + Y = a.y, + }; + } + + ProcessFrameRequest.Types.Quaternion UnityQuaternionToProto(Quaternion a) + { + return new ProcessFrameRequest.Types.Quaternion() + { + X = a.x, + Y = a.y, + Z = a.z, + W = a.w + }; + } + + + private const int DEFAULT_SAMPLE_RATE = 10000; + public const int DEFAULT_FRAME_LENGTH = 2000; + /// + /// For streaming data: start streaming allow data to be sent periodically until stop streaming. + /// + public void StartDataStreaming() + { + _isStreaming = true; + if (_activatedDataModalities["Audio"]) + { + _audioStreaming.InitializeAudioRecording(DEFAULT_SAMPLE_RATE, DEFAULT_FRAME_LENGTH); + } + } + + /// + /// For streaming data: stop streaming data so that we don't consume more + /// resource after this point. + /// + public void StopDataStreaming() + { + _isStreaming = false; + if (_activatedDataModalities["Audio"]) + { + _audioStreaming.DisposeAudioRecording(); + } + } + + /// + /// Collect data frame's data for sending to server + /// + /// + public ProcessFrameRequest CollectDataFrame() + { + var dataFrame = new ProcessFrameRequest(); + + if (_activatedDataModalities["CameraColor"]) + { + var colorImage = new XRYCbCrColorImage(_cameraManager, _sampleSize); + dataFrame.Color = ByteString.CopyFrom(colorImage.Encode()); + + colorImage.Dispose(); + } + + if (_activatedDataModalities["CameraDepth"]) + { + var depthImage = new XRConfidenceFilteredDepthImage(_occlusionManager, 0); + dataFrame.Depth = ByteString.CopyFrom(depthImage.Encode()); + + depthImage.Dispose(); + } + + if (_activatedDataModalities["CameraTransform"]) + { + const int transformLength = 3 * 4 * sizeof(float); + var m = Camera.main!.transform.localToWorldMatrix; + var cameraTransformBytes = new byte[transformLength]; + + Buffer.BlockCopy(new[] + { + m.m00, m.m01, m.m02, m.m03, + m.m10, m.m11, m.m12, m.m13, + m.m20, m.m21, m.m22, m.m23 + }, 0, cameraTransformBytes, 0, transformLength); + + dataFrame.Transform = ByteString.CopyFrom(cameraTransformBytes); + } + + if (_activatedDataModalities["PlaneDetection"]) + { + foreach (ARPlane plane in _planeManager.trackables) + { + var protoPlane = new ProcessFrameRequest.Types.Plane(); + protoPlane.Center = UnityVector3ToProto(plane.center); + protoPlane.Normal = UnityVector3ToProto(plane.normal); + protoPlane.Size = UnityVector2ToProto(plane.size); + protoPlane.BoundaryPoints.Add(plane.boundary.Select(point => UnityVector2ToProto(point))); + + dataFrame.PlaneDetection.Add(protoPlane); + } + } + + if (_activatedDataModalities["Gyroscope"]) + { + dataFrame.Gyroscope = new ProcessFrameRequest.Types.GyroscopeData(); + Quaternion attitude = AttitudeSensor.current.attitude.ReadValue(); + Vector3 rotation_rate = UnityEngine.InputSystem.Gyroscope.current.angularVelocity.ReadValue(); + Vector3 gravity = GravitySensor.current.gravity.ReadValue(); + Vector3 acceleration = Accelerometer.current.acceleration.ReadValue(); + + dataFrame.Gyroscope.Attitude = UnityQuaternionToProto(attitude); + dataFrame.Gyroscope.RotationRate = UnityVector3ToProto(rotation_rate); + dataFrame.Gyroscope.Gravity = UnityVector3ToProto(gravity); + dataFrame.Gyroscope.Acceleration = UnityVector3ToProto(acceleration); + } + + if (_activatedDataModalities["Audio"]) + { + PrintDebug("audio"); + dataFrame.AudioData.Add(_audioStreaming.GetFrames()); + _audioStreaming.ClearFrameList(); + } + + if (_activatedDataModalities["Meshing"]) + { + IList meshFilters = _meshManager.meshes; + PrintDebug($"Number of mesh filters: {meshFilters.Count}"); + foreach (MeshFilter meshFilter in meshFilters) + { + Mesh mesh = meshFilter.sharedMesh; + List> encodedMesh = _meshEncoder.EncodeMesh(mesh); + + foreach (var meshElement in encodedMesh) + { + var meshProto = new ProcessFrameRequest.Types.Mesh(); + meshProto.Data = ByteString.CopyFrom(meshElement); + + dataFrame.Meshes.Add(meshProto); + } + } + + } + + return dataFrame; + } + + /// + /// This is a Task wrapper for GetAndSendFrame, to avoid blocking in the main thread. + /// The method will spawn a new task, so the usage of this is only calling "GetAndSendFrameTask()" + /// + /// + public Task GetAndSendFrameTask() + { + var dataFrame = CollectDataFrame(); + + return Task.Run(() => _client.SendFrame(dataFrame)); + } + + /// + /// Send a data of a frame to the server. + /// + /// Data of the frame. The typing of this is generated by Protobuf. + /// A message from the server. + public string GetAndSendFrame() + { + var dataFrame = CollectDataFrame(); + + string serverMessage = _client.SendFrame(dataFrame); + return serverMessage; + } + } +} + + diff --git a/unity/Assets/Scripts/ARFlow/ARFlowClientManager.cs.meta b/unity/Assets/ARFlowPackage/ARFlow/ARFlowClientManager.cs.meta similarity index 100% rename from unity/Assets/Scripts/ARFlow/ARFlowClientManager.cs.meta rename to unity/Assets/ARFlowPackage/ARFlow/ARFlowClientManager.cs.meta diff --git a/unity/Assets/ARFlowPackage/ARFlow/AudioStreaming.cs b/unity/Assets/ARFlowPackage/ARFlow/AudioStreaming.cs new file mode 100644 index 0000000..8418e0a --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/AudioStreaming.cs @@ -0,0 +1,54 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +using Pv.Unity; + +namespace ARFlow +{ + public class AudioStreaming + { + private List _unsentFrames; + + public List GetFrames() + { + return _unsentFrames; + } + + public AudioStreaming() + { + _unsentFrames = new List(); + } + + /// + /// Since Unity's microphone implementation requires calling "start" and "end", we need + /// a call to start microphone recording + /// + public void InitializeAudioRecording(int sampleRate, int frameLength) + { + VoiceProcessor.Instance.StartRecording(frameLength, sampleRate); + VoiceProcessor.Instance.AddFrameListener(UpdateCurrentAudioFrame); + } + + /// + /// Our point is that we only want to send the current frame, not all frames that might be sent. + /// + /// + private void UpdateCurrentAudioFrame(float[] frame) + { + _unsentFrames.AddRange(frame); + } + + public void ClearFrameList() + { + _unsentFrames.Clear(); + } + + public void DisposeAudioRecording() + { + VoiceProcessor.Instance.StopRecording(); + } + } + + +} diff --git a/unity/Assets/ARFlowPackage/ARFlow/AudioStreaming.cs.meta b/unity/Assets/ARFlowPackage/ARFlow/AudioStreaming.cs.meta new file mode 100644 index 0000000..3b2d2aa --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/AudioStreaming.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 35e7c7b9e1e3a9d42ad3d1a461e6e49c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/ARFlowPackage/ARFlow/Interfaces.meta b/unity/Assets/ARFlowPackage/ARFlow/Interfaces.meta new file mode 100644 index 0000000..485afa5 --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/Interfaces.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dc0cea679e23cf242b22104342c95441 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode.meta b/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode.meta new file mode 100644 index 0000000..50e877f --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6a6145503248911438db7b7a49fa5304 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.asmdef b/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.asmdef new file mode 100644 index 0000000..590b365 --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.asmdef @@ -0,0 +1,16 @@ +{ + "name": "MeshingEncoderAssembly", + "rootNamespace": "", + "references": [ + "GUID:411961ebab96043d8a061c656d3a461c" + ], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": true, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.asmdef.meta b/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.asmdef.meta new file mode 100644 index 0000000..0bab74e --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 8a68004c08665e8449ab651ffdc7bee0 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.cs b/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.cs new file mode 100644 index 0000000..a406969 --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.cs @@ -0,0 +1,25 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +using Draco.Encoder; +using Unity.Collections; + +namespace ARFlow +{ + public class MeshEncoder + { + public List> EncodeMesh(Mesh mesh) + { + EncodeResult[] result = DracoEncoder.EncodeMesh(mesh); + List> ret = new(); + foreach (EncodeResult item in result) + { + ret.Add(item.data); + } + return ret; + } + } + +} + diff --git a/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.cs.meta b/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.cs.meta new file mode 100644 index 0000000..eaee770 --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/MeshingEncode/MeshEncoder.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5c9dd96908350bc4db80d334d0807274 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/ARFlowPackage/ARFlow/OtherUtils.cs b/unity/Assets/ARFlowPackage/ARFlow/OtherUtils.cs new file mode 100644 index 0000000..f493ace --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/OtherUtils.cs @@ -0,0 +1,26 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +namespace ARFlow +{ + public static class OtherUtils + { + public static void PrintDebug(object message) + { + try + { + if (Debug.isDebugBuild) + { + Debug.Log(message); + } + } + catch + { + //Debug.isDebugBuild throws error + //not on main thread --> skip logging + } + } + } + +} diff --git a/unity/Assets/ARFlowPackage/ARFlow/OtherUtils.cs.meta b/unity/Assets/ARFlowPackage/ARFlow/OtherUtils.cs.meta new file mode 100644 index 0000000..344774c --- /dev/null +++ b/unity/Assets/ARFlowPackage/ARFlow/OtherUtils.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 52f248f840d6b094cb738b670a322491 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Scripts/ARFlow/Service.cs b/unity/Assets/ARFlowPackage/ARFlow/Service.cs similarity index 84% rename from unity/Assets/Scripts/ARFlow/Service.cs rename to unity/Assets/ARFlowPackage/ARFlow/Service.cs index a212bc1..d597ad3 100644 --- a/unity/Assets/Scripts/ARFlow/Service.cs +++ b/unity/Assets/ARFlowPackage/ARFlow/Service.cs @@ -1,6 +1,6 @@ // // Generated by the protocol buffer compiler. DO NOT EDIT! -// source: protos/arflow/service.proto +// source: protos/arflow_grpc/service.proto // #pragma warning disable 1591, 0612, 3021, 8981 #region Designer generated code @@ -11,11 +11,11 @@ using scg = global::System.Collections.Generic; namespace ARFlow { - /// Holder for reflection information generated from protos/arflow/service.proto + /// Holder for reflection information generated from protos/arflow_grpc/service.proto public static partial class ServiceReflection { #region Descriptor - /// File descriptor for protos/arflow/service.proto + /// File descriptor for protos/arflow_grpc/service.proto public static pbr::FileDescriptor Descriptor { get { return descriptor; } } @@ -24,72 +24,84 @@ public static partial class ServiceReflection { static ServiceReflection() { byte[] descriptorData = global::System.Convert.FromBase64String( string.Concat( - "Chtwcm90b3MvYXJmbG93L3NlcnZpY2UucHJvdG8igwkKD1JlZ2lzdGVyUmVx", - "dWVzdBITCgtkZXZpY2VfbmFtZRgBIAEoCRI8ChFjYW1lcmFfaW50cmluc2lj", - "cxgCIAEoCzIhLlJlZ2lzdGVyUmVxdWVzdC5DYW1lcmFJbnRyaW5zaWNzEjIK", - "DGNhbWVyYV9jb2xvchgDIAEoCzIcLlJlZ2lzdGVyUmVxdWVzdC5DYW1lcmFD", - "b2xvchIyCgxjYW1lcmFfZGVwdGgYBCABKAsyHC5SZWdpc3RlclJlcXVlc3Qu", - "Q2FtZXJhRGVwdGgSOgoQY2FtZXJhX3RyYW5zZm9ybRgFIAEoCzIgLlJlZ2lz", - "dGVyUmVxdWVzdC5DYW1lcmFUcmFuc2Zvcm0SPQoSY2FtZXJhX3BvaW50X2Ns", - "b3VkGAYgASgLMiEuUmVnaXN0ZXJSZXF1ZXN0LkNhbWVyYVBvaW50Q2xvdWQS", - "RQoWY2FtZXJhX3BsYW5lX2RldGVjdGlvbhgHIAEoCzIlLlJlZ2lzdGVyUmVx", - "dWVzdC5DYW1lcmFQbGFuZURldGVjdGlvbhItCglneXJvc2NvcGUYCCABKAsy", - "Gi5SZWdpc3RlclJlcXVlc3QuR3lyb3Njb3BlEiUKBWF1ZGlvGAkgASgLMhYu", - "UmVnaXN0ZXJSZXF1ZXN0LkF1ZGlvEikKB21lc2hpbmcYCiABKAsyGC5SZWdp", - "c3RlclJlcXVlc3QuTWVzaGluZxqkAQoQQ2FtZXJhSW50cmluc2ljcxIWCg5m", - "b2NhbF9sZW5ndGhfeBgBIAEoAhIWCg5mb2NhbF9sZW5ndGhfeRgCIAEoAhIZ", - "ChFwcmluY2lwYWxfcG9pbnRfeBgDIAEoAhIZChFwcmluY2lwYWxfcG9pbnRf", - "eRgEIAEoAhIUCgxyZXNvbHV0aW9uX3gYBSABKAUSFAoMcmVzb2x1dGlvbl95", - "GAYgASgFGmMKC0NhbWVyYUNvbG9yEg8KB2VuYWJsZWQYASABKAgSEQoJZGF0", - "YV90eXBlGAIgASgJEhcKD3Jlc2l6ZV9mYWN0b3JfeBgDIAEoAhIXCg9yZXNp", - "emVfZmFjdG9yX3kYBCABKAIagQEKC0NhbWVyYURlcHRoEg8KB2VuYWJsZWQY", - "ASABKAgSEQoJZGF0YV90eXBlGAIgASgJEiIKGmNvbmZpZGVuY2VfZmlsdGVy", - "aW5nX2xldmVsGAMgASgFEhQKDHJlc29sdXRpb25feBgEIAEoBRIUCgxyZXNv", - "bHV0aW9uX3kYBSABKAUaIgoPQ2FtZXJhVHJhbnNmb3JtEg8KB2VuYWJsZWQY", - "ASABKAgaQQoQQ2FtZXJhUG9pbnRDbG91ZBIPCgdlbmFibGVkGAEgASgIEhwK", - "FGRlcHRoX3Vwc2NhbGVfZmFjdG9yGAIgASgCGicKFENhbWVyYVBsYW5lRGV0", - "ZWN0aW9uEg8KB2VuYWJsZWQYASABKAgaHAoJR3lyb3Njb3BlEg8KB2VuYWJs", - "ZWQYASABKAgaGAoFQXVkaW8SDwoHZW5hYmxlZBgBIAEoCBoaCgdNZXNoaW5n", - "Eg8KB2VuYWJsZWQYASABKAgiHwoQUmVnaXN0ZXJSZXNwb25zZRILCgN1aWQY", - "ASABKAkiuwUKEERhdGFGcmFtZVJlcXVlc3QSCwoDdWlkGAEgASgJEg0KBWNv", - "bG9yGAIgASgMEg0KBWRlcHRoGAMgASgMEhEKCXRyYW5zZm9ybRgEIAEoDBIx", - "Cg9wbGFuZV9kZXRlY3Rpb24YBSADKAsyGC5EYXRhRnJhbWVSZXF1ZXN0LlBs", - "YW5lcxIzCglneXJvc2NvcGUYBiABKAsyIC5EYXRhRnJhbWVSZXF1ZXN0Lmd5", - "cm9zY29wZV9kYXRhEg0KBWF1ZGlvGAggASgMEg8KB21lc2hpbmcYCSABKAwa", - "KgoHVmVjdG9yMxIJCgF4GAEgASgCEgkKAXkYAiABKAISCQoBehgDIAEoAhof", - "CgdWZWN0b3IyEgkKAXgYASABKAISCQoBeRgCIAEoAhqHAQoGUGxhbmVzEikK", - "BmNlbnRlchgBIAEoCzIZLkRhdGFGcmFtZVJlcXVlc3QuVmVjdG9yMxIpCgZu", - "b3JtYWwYAiABKAsyGS5EYXRhRnJhbWVSZXF1ZXN0LlZlY3RvcjMSJwoEc2l6", - "ZRgDIAEoCzIZLkRhdGFGcmFtZVJlcXVlc3QuVmVjdG9yMho4CgpRdWF0ZXJu", - "aW9uEgkKAXgYASABKAISCQoBeRgCIAEoAhIJCgF6GAMgASgCEgkKAXcYBCAB", - "KAIazwEKDmd5cm9zY29wZV9kYXRhEi4KCGF0dGl0dWRlGAEgASgLMhwuRGF0", - "YUZyYW1lUmVxdWVzdC5RdWF0ZXJuaW9uEjAKDXJvdGF0aW9uX3JhdGUYAiAB", - "KAsyGS5EYXRhRnJhbWVSZXF1ZXN0LlZlY3RvcjMSKgoHZ3Jhdml0eRgDIAEo", - "CzIZLkRhdGFGcmFtZVJlcXVlc3QuVmVjdG9yMxIvCgxhY2NlbGVyYXRpb24Y", - "BCABKAsyGS5EYXRhRnJhbWVSZXF1ZXN0LlZlY3RvcjMiJAoRRGF0YUZyYW1l", - "UmVzcG9uc2USDwoHbWVzc2FnZRgBIAEoCTJ1Cg1BUkZsb3dTZXJ2aWNlEi8K", - "CHJlZ2lzdGVyEhAuUmVnaXN0ZXJSZXF1ZXN0GhEuUmVnaXN0ZXJSZXNwb25z", - "ZRIzCgpkYXRhX2ZyYW1lEhEuRGF0YUZyYW1lUmVxdWVzdBoSLkRhdGFGcmFt", - "ZVJlc3BvbnNlQgmqAgZBUkZsb3diBnByb3RvMw==")); + "CiBwcm90b3MvYXJmbG93X2dycGMvc2VydmljZS5wcm90bxIJYXJmbG93LnYx", + "IpkKChVSZWdpc3RlckNsaWVudFJlcXVlc3QSEwoLZGV2aWNlX25hbWUYASAB", + "KAkSTAoRY2FtZXJhX2ludHJpbnNpY3MYAiABKAsyMS5hcmZsb3cudjEuUmVn", + "aXN0ZXJDbGllbnRSZXF1ZXN0LkNhbWVyYUludHJpbnNpY3MSQgoMY2FtZXJh", + "X2NvbG9yGAMgASgLMiwuYXJmbG93LnYxLlJlZ2lzdGVyQ2xpZW50UmVxdWVz", + "dC5DYW1lcmFDb2xvchJCCgxjYW1lcmFfZGVwdGgYBCABKAsyLC5hcmZsb3cu", + "djEuUmVnaXN0ZXJDbGllbnRSZXF1ZXN0LkNhbWVyYURlcHRoEkoKEGNhbWVy", + "YV90cmFuc2Zvcm0YBSABKAsyMC5hcmZsb3cudjEuUmVnaXN0ZXJDbGllbnRS", + "ZXF1ZXN0LkNhbWVyYVRyYW5zZm9ybRJNChJjYW1lcmFfcG9pbnRfY2xvdWQY", + "BiABKAsyMS5hcmZsb3cudjEuUmVnaXN0ZXJDbGllbnRSZXF1ZXN0LkNhbWVy", + "YVBvaW50Q2xvdWQSVQoWY2FtZXJhX3BsYW5lX2RldGVjdGlvbhgHIAEoCzI1", + "LmFyZmxvdy52MS5SZWdpc3RlckNsaWVudFJlcXVlc3QuQ2FtZXJhUGxhbmVE", + "ZXRlY3Rpb24SPQoJZ3lyb3Njb3BlGAggASgLMiouYXJmbG93LnYxLlJlZ2lz", + "dGVyQ2xpZW50UmVxdWVzdC5HeXJvc2NvcGUSNQoFYXVkaW8YCSABKAsyJi5h", + "cmZsb3cudjEuUmVnaXN0ZXJDbGllbnRSZXF1ZXN0LkF1ZGlvEjkKB21lc2hp", + "bmcYCiABKAsyKC5hcmZsb3cudjEuUmVnaXN0ZXJDbGllbnRSZXF1ZXN0Lk1l", + "c2hpbmcapAEKEENhbWVyYUludHJpbnNpY3MSFgoOZm9jYWxfbGVuZ3RoX3gY", + "ASABKAISFgoOZm9jYWxfbGVuZ3RoX3kYAiABKAISGQoRcHJpbmNpcGFsX3Bv", + "aW50X3gYAyABKAISGQoRcHJpbmNpcGFsX3BvaW50X3kYBCABKAISFAoMcmVz", + "b2x1dGlvbl94GAUgASgFEhQKDHJlc29sdXRpb25feRgGIAEoBRpjCgtDYW1l", + "cmFDb2xvchIPCgdlbmFibGVkGAEgASgIEhEKCWRhdGFfdHlwZRgCIAEoCRIX", + "Cg9yZXNpemVfZmFjdG9yX3gYAyABKAISFwoPcmVzaXplX2ZhY3Rvcl95GAQg", + "ASgCGoEBCgtDYW1lcmFEZXB0aBIPCgdlbmFibGVkGAEgASgIEhEKCWRhdGFf", + "dHlwZRgCIAEoCRIiChpjb25maWRlbmNlX2ZpbHRlcmluZ19sZXZlbBgDIAEo", + "BRIUCgxyZXNvbHV0aW9uX3gYBCABKAUSFAoMcmVzb2x1dGlvbl95GAUgASgF", + "GiIKD0NhbWVyYVRyYW5zZm9ybRIPCgdlbmFibGVkGAEgASgIGkEKEENhbWVy", + "YVBvaW50Q2xvdWQSDwoHZW5hYmxlZBgBIAEoCBIcChRkZXB0aF91cHNjYWxl", + "X2ZhY3RvchgCIAEoAhonChRDYW1lcmFQbGFuZURldGVjdGlvbhIPCgdlbmFi", + "bGVkGAEgASgIGhwKCUd5cm9zY29wZRIPCgdlbmFibGVkGAEgASgIGhgKBUF1", + "ZGlvEg8KB2VuYWJsZWQYASABKAgaGgoHTWVzaGluZxIPCgdlbmFibGVkGAEg", + "ASgIIiUKFlJlZ2lzdGVyQ2xpZW50UmVzcG9uc2USCwoDdWlkGAEgASgJIq8H", + "ChNQcm9jZXNzRnJhbWVSZXF1ZXN0EgsKA3VpZBgBIAEoCRINCgVjb2xvchgC", + "IAEoDBINCgVkZXB0aBgDIAEoDBIRCgl0cmFuc2Zvcm0YBCABKAwSPQoPcGxh", + "bmVfZGV0ZWN0aW9uGAUgAygLMiQuYXJmbG93LnYxLlByb2Nlc3NGcmFtZVJl", + "cXVlc3QuUGxhbmUSPwoJZ3lyb3Njb3BlGAYgASgLMiwuYXJmbG93LnYxLlBy", + "b2Nlc3NGcmFtZVJlcXVlc3QuR3lyb3Njb3BlRGF0YRISCgphdWRpb19kYXRh", + "GAggAygCEjMKBm1lc2hlcxgJIAMoCzIjLmFyZmxvdy52MS5Qcm9jZXNzRnJh", + "bWVSZXF1ZXN0Lk1lc2gaKgoHVmVjdG9yMxIJCgF4GAEgASgCEgkKAXkYAiAB", + "KAISCQoBehgDIAEoAhofCgdWZWN0b3IyEgkKAXgYASABKAISCQoBeRgCIAEo", + "AhruAQoFUGxhbmUSNgoGY2VudGVyGAEgASgLMiYuYXJmbG93LnYxLlByb2Nl", + "c3NGcmFtZVJlcXVlc3QuVmVjdG9yMxI2CgZub3JtYWwYAiABKAsyJi5hcmZs", + "b3cudjEuUHJvY2Vzc0ZyYW1lUmVxdWVzdC5WZWN0b3IzEjQKBHNpemUYAyAB", + "KAsyJi5hcmZsb3cudjEuUHJvY2Vzc0ZyYW1lUmVxdWVzdC5WZWN0b3IyEj8K", + "D2JvdW5kYXJ5X3BvaW50cxgEIAMoCzImLmFyZmxvdy52MS5Qcm9jZXNzRnJh", + "bWVSZXF1ZXN0LlZlY3RvcjIaOAoKUXVhdGVybmlvbhIJCgF4GAEgASgCEgkK", + "AXkYAiABKAISCQoBehgDIAEoAhIJCgF3GAQgASgCGoICCg1HeXJvc2NvcGVE", + "YXRhEjsKCGF0dGl0dWRlGAEgASgLMikuYXJmbG93LnYxLlByb2Nlc3NGcmFt", + "ZVJlcXVlc3QuUXVhdGVybmlvbhI9Cg1yb3RhdGlvbl9yYXRlGAIgASgLMiYu", + "YXJmbG93LnYxLlByb2Nlc3NGcmFtZVJlcXVlc3QuVmVjdG9yMxI3CgdncmF2", + "aXR5GAMgASgLMiYuYXJmbG93LnYxLlByb2Nlc3NGcmFtZVJlcXVlc3QuVmVj", + "dG9yMxI8CgxhY2NlbGVyYXRpb24YBCABKAsyJi5hcmZsb3cudjEuUHJvY2Vz", + "c0ZyYW1lUmVxdWVzdC5WZWN0b3IzGhQKBE1lc2gSDAoEZGF0YRgBIAEoDCIn", + "ChRQcm9jZXNzRnJhbWVSZXNwb25zZRIPCgdtZXNzYWdlGAEgASgJMrcBCg1B", + "UkZsb3dTZXJ2aWNlElUKDlJlZ2lzdGVyQ2xpZW50EiAuYXJmbG93LnYxLlJl", + "Z2lzdGVyQ2xpZW50UmVxdWVzdBohLmFyZmxvdy52MS5SZWdpc3RlckNsaWVu", + "dFJlc3BvbnNlEk8KDFByb2Nlc3NGcmFtZRIeLmFyZmxvdy52MS5Qcm9jZXNz", + "RnJhbWVSZXF1ZXN0Gh8uYXJmbG93LnYxLlByb2Nlc3NGcmFtZVJlc3BvbnNl", + "QgmqAgZBUkZsb3diBnByb3RvMw==")); descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, new pbr::FileDescriptor[] { }, new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterRequest), global::ARFlow.RegisterRequest.Parser, new[]{ "DeviceName", "CameraIntrinsics", "CameraColor", "CameraDepth", "CameraTransform", "CameraPointCloud", "CameraPlaneDetection", "Gyroscope", "Audio", "Meshing" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterRequest.Types.CameraIntrinsics), global::ARFlow.RegisterRequest.Types.CameraIntrinsics.Parser, new[]{ "FocalLengthX", "FocalLengthY", "PrincipalPointX", "PrincipalPointY", "ResolutionX", "ResolutionY" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterRequest.Types.CameraColor), global::ARFlow.RegisterRequest.Types.CameraColor.Parser, new[]{ "Enabled", "DataType", "ResizeFactorX", "ResizeFactorY" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterRequest.Types.CameraDepth), global::ARFlow.RegisterRequest.Types.CameraDepth.Parser, new[]{ "Enabled", "DataType", "ConfidenceFilteringLevel", "ResolutionX", "ResolutionY" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterRequest.Types.CameraTransform), global::ARFlow.RegisterRequest.Types.CameraTransform.Parser, new[]{ "Enabled" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterRequest.Types.CameraPointCloud), global::ARFlow.RegisterRequest.Types.CameraPointCloud.Parser, new[]{ "Enabled", "DepthUpscaleFactor" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterRequest.Types.CameraPlaneDetection), global::ARFlow.RegisterRequest.Types.CameraPlaneDetection.Parser, new[]{ "Enabled" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterRequest.Types.Gyroscope), global::ARFlow.RegisterRequest.Types.Gyroscope.Parser, new[]{ "Enabled" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterRequest.Types.Audio), global::ARFlow.RegisterRequest.Types.Audio.Parser, new[]{ "Enabled" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterRequest.Types.Meshing), global::ARFlow.RegisterRequest.Types.Meshing.Parser, new[]{ "Enabled" }, null, null, null, null)}), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterResponse), global::ARFlow.RegisterResponse.Parser, new[]{ "Uid" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.DataFrameRequest), global::ARFlow.DataFrameRequest.Parser, new[]{ "Uid", "Color", "Depth", "Transform", "PlaneDetection", "Gyroscope", "Audio", "Meshing" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.DataFrameRequest.Types.Vector3), global::ARFlow.DataFrameRequest.Types.Vector3.Parser, new[]{ "X", "Y", "Z" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.DataFrameRequest.Types.Vector2), global::ARFlow.DataFrameRequest.Types.Vector2.Parser, new[]{ "X", "Y" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.DataFrameRequest.Types.Planes), global::ARFlow.DataFrameRequest.Types.Planes.Parser, new[]{ "Center", "Normal", "Size" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.DataFrameRequest.Types.Quaternion), global::ARFlow.DataFrameRequest.Types.Quaternion.Parser, new[]{ "X", "Y", "Z", "W" }, null, null, null, null), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.DataFrameRequest.Types.gyroscope_data), global::ARFlow.DataFrameRequest.Types.gyroscope_data.Parser, new[]{ "Attitude", "RotationRate", "Gravity", "Acceleration" }, null, null, null, null)}), - new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.DataFrameResponse), global::ARFlow.DataFrameResponse.Parser, new[]{ "Message" }, null, null, null, null) + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientRequest), global::ARFlow.RegisterClientRequest.Parser, new[]{ "DeviceName", "CameraIntrinsics", "CameraColor", "CameraDepth", "CameraTransform", "CameraPointCloud", "CameraPlaneDetection", "Gyroscope", "Audio", "Meshing" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientRequest.Types.CameraIntrinsics), global::ARFlow.RegisterClientRequest.Types.CameraIntrinsics.Parser, new[]{ "FocalLengthX", "FocalLengthY", "PrincipalPointX", "PrincipalPointY", "ResolutionX", "ResolutionY" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientRequest.Types.CameraColor), global::ARFlow.RegisterClientRequest.Types.CameraColor.Parser, new[]{ "Enabled", "DataType", "ResizeFactorX", "ResizeFactorY" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientRequest.Types.CameraDepth), global::ARFlow.RegisterClientRequest.Types.CameraDepth.Parser, new[]{ "Enabled", "DataType", "ConfidenceFilteringLevel", "ResolutionX", "ResolutionY" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientRequest.Types.CameraTransform), global::ARFlow.RegisterClientRequest.Types.CameraTransform.Parser, new[]{ "Enabled" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientRequest.Types.CameraPointCloud), global::ARFlow.RegisterClientRequest.Types.CameraPointCloud.Parser, new[]{ "Enabled", "DepthUpscaleFactor" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientRequest.Types.CameraPlaneDetection), global::ARFlow.RegisterClientRequest.Types.CameraPlaneDetection.Parser, new[]{ "Enabled" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientRequest.Types.Gyroscope), global::ARFlow.RegisterClientRequest.Types.Gyroscope.Parser, new[]{ "Enabled" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientRequest.Types.Audio), global::ARFlow.RegisterClientRequest.Types.Audio.Parser, new[]{ "Enabled" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientRequest.Types.Meshing), global::ARFlow.RegisterClientRequest.Types.Meshing.Parser, new[]{ "Enabled" }, null, null, null, null)}), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.RegisterClientResponse), global::ARFlow.RegisterClientResponse.Parser, new[]{ "Uid" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.ProcessFrameRequest), global::ARFlow.ProcessFrameRequest.Parser, new[]{ "Uid", "Color", "Depth", "Transform", "PlaneDetection", "Gyroscope", "AudioData", "Meshes" }, null, null, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.ProcessFrameRequest.Types.Vector3), global::ARFlow.ProcessFrameRequest.Types.Vector3.Parser, new[]{ "X", "Y", "Z" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.ProcessFrameRequest.Types.Vector2), global::ARFlow.ProcessFrameRequest.Types.Vector2.Parser, new[]{ "X", "Y" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.ProcessFrameRequest.Types.Plane), global::ARFlow.ProcessFrameRequest.Types.Plane.Parser, new[]{ "Center", "Normal", "Size", "BoundaryPoints" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.ProcessFrameRequest.Types.Quaternion), global::ARFlow.ProcessFrameRequest.Types.Quaternion.Parser, new[]{ "X", "Y", "Z", "W" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.ProcessFrameRequest.Types.GyroscopeData), global::ARFlow.ProcessFrameRequest.Types.GyroscopeData.Parser, new[]{ "Attitude", "RotationRate", "Gravity", "Acceleration" }, null, null, null, null), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.ProcessFrameRequest.Types.Mesh), global::ARFlow.ProcessFrameRequest.Types.Mesh.Parser, new[]{ "Data" }, null, null, null, null)}), + new pbr::GeneratedClrTypeInfo(typeof(global::ARFlow.ProcessFrameResponse), global::ARFlow.ProcessFrameResponse.Parser, new[]{ "Message" }, null, null, null, null) })); } #endregion @@ -97,16 +109,16 @@ static ServiceReflection() { } #region Messages [global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")] - public sealed partial class RegisterRequest : pb::IMessage + public sealed partial class RegisterClientRequest : pb::IMessage #if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE , pb::IBufferMessage #endif { - private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new RegisterRequest()); + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new RegisterClientRequest()); private pb::UnknownFieldSet _unknownFields; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public static pb::MessageParser Parser { get { return _parser; } } + public static pb::MessageParser Parser { get { return _parser; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] @@ -122,7 +134,7 @@ public sealed partial class RegisterRequest : pb::IMessage [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public RegisterRequest() { + public RegisterClientRequest() { OnConstruction(); } @@ -130,7 +142,7 @@ public RegisterRequest() { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public RegisterRequest(RegisterRequest other) : this() { + public RegisterClientRequest(RegisterClientRequest other) : this() { deviceName_ = other.deviceName_; cameraIntrinsics_ = other.cameraIntrinsics_ != null ? other.cameraIntrinsics_.Clone() : null; cameraColor_ = other.cameraColor_ != null ? other.cameraColor_.Clone() : null; @@ -146,13 +158,16 @@ public RegisterRequest(RegisterRequest other) : this() { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public RegisterRequest Clone() { - return new RegisterRequest(this); + public RegisterClientRequest Clone() { + return new RegisterClientRequest(this); } /// Field number for the "device_name" field. public const int DeviceNameFieldNumber = 1; private string deviceName_ = ""; + /// + /// TODO: Add documentation for each field, units of measurement, etc. + /// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public string DeviceName { @@ -164,10 +179,10 @@ public string DeviceName { /// Field number for the "camera_intrinsics" field. public const int CameraIntrinsicsFieldNumber = 2; - private global::ARFlow.RegisterRequest.Types.CameraIntrinsics cameraIntrinsics_; + private global::ARFlow.RegisterClientRequest.Types.CameraIntrinsics cameraIntrinsics_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::ARFlow.RegisterRequest.Types.CameraIntrinsics CameraIntrinsics { + public global::ARFlow.RegisterClientRequest.Types.CameraIntrinsics CameraIntrinsics { get { return cameraIntrinsics_; } set { cameraIntrinsics_ = value; @@ -176,10 +191,10 @@ public string DeviceName { /// Field number for the "camera_color" field. public const int CameraColorFieldNumber = 3; - private global::ARFlow.RegisterRequest.Types.CameraColor cameraColor_; + private global::ARFlow.RegisterClientRequest.Types.CameraColor cameraColor_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::ARFlow.RegisterRequest.Types.CameraColor CameraColor { + public global::ARFlow.RegisterClientRequest.Types.CameraColor CameraColor { get { return cameraColor_; } set { cameraColor_ = value; @@ -188,10 +203,10 @@ public string DeviceName { /// Field number for the "camera_depth" field. public const int CameraDepthFieldNumber = 4; - private global::ARFlow.RegisterRequest.Types.CameraDepth cameraDepth_; + private global::ARFlow.RegisterClientRequest.Types.CameraDepth cameraDepth_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::ARFlow.RegisterRequest.Types.CameraDepth CameraDepth { + public global::ARFlow.RegisterClientRequest.Types.CameraDepth CameraDepth { get { return cameraDepth_; } set { cameraDepth_ = value; @@ -200,10 +215,10 @@ public string DeviceName { /// Field number for the "camera_transform" field. public const int CameraTransformFieldNumber = 5; - private global::ARFlow.RegisterRequest.Types.CameraTransform cameraTransform_; + private global::ARFlow.RegisterClientRequest.Types.CameraTransform cameraTransform_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::ARFlow.RegisterRequest.Types.CameraTransform CameraTransform { + public global::ARFlow.RegisterClientRequest.Types.CameraTransform CameraTransform { get { return cameraTransform_; } set { cameraTransform_ = value; @@ -212,10 +227,10 @@ public string DeviceName { /// Field number for the "camera_point_cloud" field. public const int CameraPointCloudFieldNumber = 6; - private global::ARFlow.RegisterRequest.Types.CameraPointCloud cameraPointCloud_; + private global::ARFlow.RegisterClientRequest.Types.CameraPointCloud cameraPointCloud_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::ARFlow.RegisterRequest.Types.CameraPointCloud CameraPointCloud { + public global::ARFlow.RegisterClientRequest.Types.CameraPointCloud CameraPointCloud { get { return cameraPointCloud_; } set { cameraPointCloud_ = value; @@ -224,10 +239,10 @@ public string DeviceName { /// Field number for the "camera_plane_detection" field. public const int CameraPlaneDetectionFieldNumber = 7; - private global::ARFlow.RegisterRequest.Types.CameraPlaneDetection cameraPlaneDetection_; + private global::ARFlow.RegisterClientRequest.Types.CameraPlaneDetection cameraPlaneDetection_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::ARFlow.RegisterRequest.Types.CameraPlaneDetection CameraPlaneDetection { + public global::ARFlow.RegisterClientRequest.Types.CameraPlaneDetection CameraPlaneDetection { get { return cameraPlaneDetection_; } set { cameraPlaneDetection_ = value; @@ -236,10 +251,10 @@ public string DeviceName { /// Field number for the "gyroscope" field. public const int GyroscopeFieldNumber = 8; - private global::ARFlow.RegisterRequest.Types.Gyroscope gyroscope_; + private global::ARFlow.RegisterClientRequest.Types.Gyroscope gyroscope_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::ARFlow.RegisterRequest.Types.Gyroscope Gyroscope { + public global::ARFlow.RegisterClientRequest.Types.Gyroscope Gyroscope { get { return gyroscope_; } set { gyroscope_ = value; @@ -248,10 +263,10 @@ public string DeviceName { /// Field number for the "audio" field. public const int AudioFieldNumber = 9; - private global::ARFlow.RegisterRequest.Types.Audio audio_; + private global::ARFlow.RegisterClientRequest.Types.Audio audio_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::ARFlow.RegisterRequest.Types.Audio Audio { + public global::ARFlow.RegisterClientRequest.Types.Audio Audio { get { return audio_; } set { audio_ = value; @@ -260,10 +275,10 @@ public string DeviceName { /// Field number for the "meshing" field. public const int MeshingFieldNumber = 10; - private global::ARFlow.RegisterRequest.Types.Meshing meshing_; + private global::ARFlow.RegisterClientRequest.Types.Meshing meshing_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public global::ARFlow.RegisterRequest.Types.Meshing Meshing { + public global::ARFlow.RegisterClientRequest.Types.Meshing Meshing { get { return meshing_; } set { meshing_ = value; @@ -273,12 +288,12 @@ public string DeviceName { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public override bool Equals(object other) { - return Equals(other as RegisterRequest); + return Equals(other as RegisterClientRequest); } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public bool Equals(RegisterRequest other) { + public bool Equals(RegisterClientRequest other) { if (ReferenceEquals(other, null)) { return false; } @@ -468,7 +483,7 @@ public int CalculateSize() { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] - public void MergeFrom(RegisterRequest other) { + public void MergeFrom(RegisterClientRequest other) { if (other == null) { return; } @@ -477,55 +492,55 @@ public void MergeFrom(RegisterRequest other) { } if (other.cameraIntrinsics_ != null) { if (cameraIntrinsics_ == null) { - CameraIntrinsics = new global::ARFlow.RegisterRequest.Types.CameraIntrinsics(); + CameraIntrinsics = new global::ARFlow.RegisterClientRequest.Types.CameraIntrinsics(); } CameraIntrinsics.MergeFrom(other.CameraIntrinsics); } if (other.cameraColor_ != null) { if (cameraColor_ == null) { - CameraColor = new global::ARFlow.RegisterRequest.Types.CameraColor(); + CameraColor = new global::ARFlow.RegisterClientRequest.Types.CameraColor(); } CameraColor.MergeFrom(other.CameraColor); } if (other.cameraDepth_ != null) { if (cameraDepth_ == null) { - CameraDepth = new global::ARFlow.RegisterRequest.Types.CameraDepth(); + CameraDepth = new global::ARFlow.RegisterClientRequest.Types.CameraDepth(); } CameraDepth.MergeFrom(other.CameraDepth); } if (other.cameraTransform_ != null) { if (cameraTransform_ == null) { - CameraTransform = new global::ARFlow.RegisterRequest.Types.CameraTransform(); + CameraTransform = new global::ARFlow.RegisterClientRequest.Types.CameraTransform(); } CameraTransform.MergeFrom(other.CameraTransform); } if (other.cameraPointCloud_ != null) { if (cameraPointCloud_ == null) { - CameraPointCloud = new global::ARFlow.RegisterRequest.Types.CameraPointCloud(); + CameraPointCloud = new global::ARFlow.RegisterClientRequest.Types.CameraPointCloud(); } CameraPointCloud.MergeFrom(other.CameraPointCloud); } if (other.cameraPlaneDetection_ != null) { if (cameraPlaneDetection_ == null) { - CameraPlaneDetection = new global::ARFlow.RegisterRequest.Types.CameraPlaneDetection(); + CameraPlaneDetection = new global::ARFlow.RegisterClientRequest.Types.CameraPlaneDetection(); } CameraPlaneDetection.MergeFrom(other.CameraPlaneDetection); } if (other.gyroscope_ != null) { if (gyroscope_ == null) { - Gyroscope = new global::ARFlow.RegisterRequest.Types.Gyroscope(); + Gyroscope = new global::ARFlow.RegisterClientRequest.Types.Gyroscope(); } Gyroscope.MergeFrom(other.Gyroscope); } if (other.audio_ != null) { if (audio_ == null) { - Audio = new global::ARFlow.RegisterRequest.Types.Audio(); + Audio = new global::ARFlow.RegisterClientRequest.Types.Audio(); } Audio.MergeFrom(other.Audio); } if (other.meshing_ != null) { if (meshing_ == null) { - Meshing = new global::ARFlow.RegisterRequest.Types.Meshing(); + Meshing = new global::ARFlow.RegisterClientRequest.Types.Meshing(); } Meshing.MergeFrom(other.Meshing); } @@ -550,63 +565,63 @@ public void MergeFrom(pb::CodedInputStream input) { } case 18: { if (cameraIntrinsics_ == null) { - CameraIntrinsics = new global::ARFlow.RegisterRequest.Types.CameraIntrinsics(); + CameraIntrinsics = new global::ARFlow.RegisterClientRequest.Types.CameraIntrinsics(); } input.ReadMessage(CameraIntrinsics); break; } case 26: { if (cameraColor_ == null) { - CameraColor = new global::ARFlow.RegisterRequest.Types.CameraColor(); + CameraColor = new global::ARFlow.RegisterClientRequest.Types.CameraColor(); } input.ReadMessage(CameraColor); break; } case 34: { if (cameraDepth_ == null) { - CameraDepth = new global::ARFlow.RegisterRequest.Types.CameraDepth(); + CameraDepth = new global::ARFlow.RegisterClientRequest.Types.CameraDepth(); } input.ReadMessage(CameraDepth); break; } case 42: { if (cameraTransform_ == null) { - CameraTransform = new global::ARFlow.RegisterRequest.Types.CameraTransform(); + CameraTransform = new global::ARFlow.RegisterClientRequest.Types.CameraTransform(); } input.ReadMessage(CameraTransform); break; } case 50: { if (cameraPointCloud_ == null) { - CameraPointCloud = new global::ARFlow.RegisterRequest.Types.CameraPointCloud(); + CameraPointCloud = new global::ARFlow.RegisterClientRequest.Types.CameraPointCloud(); } input.ReadMessage(CameraPointCloud); break; } case 58: { if (cameraPlaneDetection_ == null) { - CameraPlaneDetection = new global::ARFlow.RegisterRequest.Types.CameraPlaneDetection(); + CameraPlaneDetection = new global::ARFlow.RegisterClientRequest.Types.CameraPlaneDetection(); } input.ReadMessage(CameraPlaneDetection); break; } case 66: { if (gyroscope_ == null) { - Gyroscope = new global::ARFlow.RegisterRequest.Types.Gyroscope(); + Gyroscope = new global::ARFlow.RegisterClientRequest.Types.Gyroscope(); } input.ReadMessage(Gyroscope); break; } case 74: { if (audio_ == null) { - Audio = new global::ARFlow.RegisterRequest.Types.Audio(); + Audio = new global::ARFlow.RegisterClientRequest.Types.Audio(); } input.ReadMessage(Audio); break; } case 82: { if (meshing_ == null) { - Meshing = new global::ARFlow.RegisterRequest.Types.Meshing(); + Meshing = new global::ARFlow.RegisterClientRequest.Types.Meshing(); } input.ReadMessage(Meshing); break; @@ -632,63 +647,63 @@ public void MergeFrom(pb::CodedInputStream input) { } case 18: { if (cameraIntrinsics_ == null) { - CameraIntrinsics = new global::ARFlow.RegisterRequest.Types.CameraIntrinsics(); + CameraIntrinsics = new global::ARFlow.RegisterClientRequest.Types.CameraIntrinsics(); } input.ReadMessage(CameraIntrinsics); break; } case 26: { if (cameraColor_ == null) { - CameraColor = new global::ARFlow.RegisterRequest.Types.CameraColor(); + CameraColor = new global::ARFlow.RegisterClientRequest.Types.CameraColor(); } input.ReadMessage(CameraColor); break; } case 34: { if (cameraDepth_ == null) { - CameraDepth = new global::ARFlow.RegisterRequest.Types.CameraDepth(); + CameraDepth = new global::ARFlow.RegisterClientRequest.Types.CameraDepth(); } input.ReadMessage(CameraDepth); break; } case 42: { if (cameraTransform_ == null) { - CameraTransform = new global::ARFlow.RegisterRequest.Types.CameraTransform(); + CameraTransform = new global::ARFlow.RegisterClientRequest.Types.CameraTransform(); } input.ReadMessage(CameraTransform); break; } case 50: { if (cameraPointCloud_ == null) { - CameraPointCloud = new global::ARFlow.RegisterRequest.Types.CameraPointCloud(); + CameraPointCloud = new global::ARFlow.RegisterClientRequest.Types.CameraPointCloud(); } input.ReadMessage(CameraPointCloud); break; } case 58: { if (cameraPlaneDetection_ == null) { - CameraPlaneDetection = new global::ARFlow.RegisterRequest.Types.CameraPlaneDetection(); + CameraPlaneDetection = new global::ARFlow.RegisterClientRequest.Types.CameraPlaneDetection(); } input.ReadMessage(CameraPlaneDetection); break; } case 66: { if (gyroscope_ == null) { - Gyroscope = new global::ARFlow.RegisterRequest.Types.Gyroscope(); + Gyroscope = new global::ARFlow.RegisterClientRequest.Types.Gyroscope(); } input.ReadMessage(Gyroscope); break; } case 74: { if (audio_ == null) { - Audio = new global::ARFlow.RegisterRequest.Types.Audio(); + Audio = new global::ARFlow.RegisterClientRequest.Types.Audio(); } input.ReadMessage(Audio); break; } case 82: { if (meshing_ == null) { - Meshing = new global::ARFlow.RegisterRequest.Types.Meshing(); + Meshing = new global::ARFlow.RegisterClientRequest.Types.Meshing(); } input.ReadMessage(Meshing); break; @@ -699,7 +714,7 @@ public void MergeFrom(pb::CodedInputStream input) { #endif #region Nested types - /// Container for nested types declared in the RegisterRequest message type. + /// Container for nested types declared in the RegisterClientRequest message type. [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static partial class Types { @@ -718,7 +733,7 @@ public sealed partial class CameraIntrinsics : pb::IMessage [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::ARFlow.RegisterRequest.Descriptor.NestedTypes[0]; } + get { return global::ARFlow.RegisterClientRequest.Descriptor.NestedTypes[0]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -1093,7 +1108,7 @@ public sealed partial class CameraColor : pb::IMessage [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::ARFlow.RegisterRequest.Descriptor.NestedTypes[1]; } + get { return global::ARFlow.RegisterClientRequest.Descriptor.NestedTypes[1]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -1394,7 +1409,7 @@ public sealed partial class CameraDepth : pb::IMessage [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::ARFlow.RegisterRequest.Descriptor.NestedTypes[2]; } + get { return global::ARFlow.RegisterClientRequest.Descriptor.NestedTypes[2]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -1732,7 +1747,7 @@ public sealed partial class CameraTransform : pb::IMessage [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::ARFlow.RegisterRequest.Descriptor.NestedTypes[3]; } + get { return global::ARFlow.RegisterClientRequest.Descriptor.NestedTypes[3]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -1922,7 +1937,7 @@ public sealed partial class CameraPointCloud : pb::IMessage [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::ARFlow.RegisterRequest.Descriptor.NestedTypes[4]; } + get { return global::ARFlow.RegisterClientRequest.Descriptor.NestedTypes[4]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -2149,7 +2164,7 @@ public sealed partial class CameraPlaneDetection : pb::IMessage [global::System.Diagnostics.DebuggerNonUserCodeAttribute] [global::System.CodeDom.Compiler.GeneratedCode("protoc", null)] public static pbr::MessageDescriptor Descriptor { - get { return global::ARFlow.RegisterRequest.Descriptor.NestedTypes[6]; } + get { return global::ARFlow.RegisterClientRequest.Descriptor.NestedTypes[6]; } } [global::System.Diagnostics.DebuggerNonUserCodeAttribute] @@ -2529,7 +2544,7 @@ public sealed partial class Audio : pb::IMessage private void OnConnectButtonClick() { + var serverURL = _defaultConnection; - if (validIP(ipField.text) && validPort(portField.text)) + if (IsIpValid(ipField.text) && IsPortValid(portField.text)) { serverURL = "http://" + ipField.text + ":" + portField.text; } - var modalities = modalityOptions(); - prettyPrintDictionary(modalities); + // To update status of task to user + Toast.Show($"Connecting to {serverURL}", 3f, ToastColor.Yellow); + + // Since toast can only be called from main thread (we cannot use the hook to display toast) + // these flags are updated and signals connection result to display to user. + connectTask = _clientManager.ConnectTask( + serverURL, + GetModalityOptions(), + t => + { + }); + _isConnected = false; + } - _clientManager.Connect(serverURL, modalityOptions()); + private void UpdateConnectionStatus() + { + if (connectTask is not null && connectTask.IsCompleted) + { + if (connectTask.IsFaulted) + { + PrintDebug(connectTask.Exception); + connectTask = null; + Toast.Show("Connection failed.", ToastColor.Red); + } + else if (connectTask.IsCompletedSuccessfully) + { + _isConnected = true; + Toast.Show("Connected successfully.", ToastColor.Green); + } + } } - public static void prettyPrintDictionary(Dictionary dict) + public static void PrettyPrintDictionary(Dictionary dict) { string log = ""; foreach (var kvp in dict) @@ -135,7 +184,7 @@ public static void prettyPrintDictionary(Dictionary dict) //textBox3.Text += ("Key = {0}, Value = {1}", kvp.Key, kvp.Value); log += string.Format("Key = {0}, Value = {1}", kvp.Key, kvp.Value); } - Debug.Log(log); + PrintDebug(log); } /// @@ -144,26 +193,36 @@ public static void prettyPrintDictionary(Dictionary dict) /// private void OnStartPauseButtonClick() { - Debug.Log($"Current framerate: {Application.targetFrameRate}"); + PrintDebug($"Current framerate: {Application.targetFrameRate}"); + if (enabled) + { + if (!_isConnected) + { + Toast.Show("Connnection not established. Cannot send dataframe."); + _enabled = false; + return; + } + + _clientManager.StartDataStreaming(); + } + else + { + _clientManager.StopDataStreaming(); + } _enabled = !_enabled; startPauseButton.GetComponentInChildren().text = _enabled ? "Pause" : "Start"; } // Update is called once per frame - void Update() + void FixedUpdate() { + if (!_isConnected) + { + UpdateConnectionStatus(); + } if (!_enabled) return; - UploadFrame(); - } - - /// - /// Get color image and depth information, and copy camera's transform from float to bytes. - /// This data is sent over the server. - /// - private void UploadFrame() - { - _clientManager.GetAndSendFrame(); + _clientManager.GetAndSendFrameTask(); } } diff --git a/unity/Assets/Scripts/ARFlowMockDataSample.cs b/unity/Assets/Scripts/ARFlowMockDataSample.cs index 3168e91..9d85e24 100644 --- a/unity/Assets/Scripts/ARFlowMockDataSample.cs +++ b/unity/Assets/Scripts/ARFlowMockDataSample.cs @@ -3,6 +3,11 @@ using Google.Protobuf; using UnityEngine.UI; using TMPro; +using System.Collections.Generic; +using Unity.Collections; +using static ARFlow.ProcessFrameRequest.Types.Mesh; + +using static ARFlow.OtherUtils; /// /// Class for sending mock data to the server. @@ -14,6 +19,8 @@ public class ARFlowMockDataSample : MonoBehaviour public Button connectButton; public Button sendButton; + public GameObject testBunny; + private ARFlowClient _client; /// /// Size of mock data generated to send to server, in width (x) and length (y). @@ -39,10 +46,10 @@ private void OnConnectButtonClick() _sampleSize = new Vector2Int(256, 192); - _client.Connect(new RegisterRequest() + _client.Connect(new RegisterClientRequest() { DeviceName = "MockDataTestbed", - CameraIntrinsics = new RegisterRequest.Types.CameraIntrinsics() + CameraIntrinsics = new RegisterClientRequest.Types.CameraIntrinsics() { FocalLengthX = 128, FocalLengthY = 96, @@ -51,22 +58,30 @@ private void OnConnectButtonClick() PrincipalPointX = 128, PrincipalPointY = 96 }, - CameraColor = new RegisterRequest.Types.CameraColor() + CameraColor = new RegisterClientRequest.Types.CameraColor() { Enabled = true, DataType = "YCbCr420", ResizeFactorX = 1.0f, ResizeFactorY = 1.0f, }, - CameraDepth = new RegisterRequest.Types.CameraDepth() + CameraDepth = new RegisterClientRequest.Types.CameraDepth() { Enabled = false, }, - CameraTransform = new RegisterRequest.Types.CameraTransform() + CameraTransform = new RegisterClientRequest.Types.CameraTransform() { Enabled = false }, - Gyroscope = new RegisterRequest.Types.Gyroscope() + Gyroscope = new RegisterClientRequest.Types.Gyroscope() + { + Enabled = true, + }, + Meshing = new RegisterClientRequest.Types.Meshing() + { + Enabled = true, + }, + CameraPlaneDetection = new RegisterClientRequest.Types.CameraPlaneDetection() { Enabled = true, } @@ -84,32 +99,58 @@ private void OnSendButtonClick() var colorBytes = new byte[size]; _rnd.NextBytes(colorBytes); - var dataFrameRequest = new DataFrameRequest() + var dataFrame = new ProcessFrameRequest() { Color = ByteString.CopyFrom(colorBytes) }; - dataFrameRequest.Gyroscope = new DataFrameRequest.Types.gyroscope_data(); + dataFrame.Gyroscope = new ProcessFrameRequest.Types.GyroscopeData(); Quaternion attitude = Input.gyro.attitude; Vector3 rotation_rate = Input.gyro.rotationRateUnbiased; Vector3 gravity = Input.gyro.gravity; Vector3 acceleration = Input.gyro.userAcceleration; - dataFrameRequest.Gyroscope.Attitude = unityQuaternionToProto(attitude); - dataFrameRequest.Gyroscope.RotationRate = unityVector3ToProto(rotation_rate); - dataFrameRequest.Gyroscope.Gravity = unityVector3ToProto(gravity); - dataFrameRequest.Gyroscope.Acceleration = unityVector3ToProto(acceleration); + dataFrame.Gyroscope.Attitude = unityQuaternionToProto(attitude); + dataFrame.Gyroscope.RotationRate = unityVector3ToProto(rotation_rate); + dataFrame.Gyroscope.Gravity = unityVector3ToProto(gravity); + dataFrame.Gyroscope.Acceleration = unityVector3ToProto(acceleration); - _client.SendFrame(new DataFrameRequest() + // Test meshing data encode + test server handling + Mesh meshdata = testBunny.GetComponent().sharedMesh; + + var meshEncoder = new MeshEncoder(); + List> encodedMesh = meshEncoder.EncodeMesh(meshdata); + for (int i = 0; i < 20; i++) { - Color = ByteString.CopyFrom(colorBytes) + foreach (var meshElement in encodedMesh) + { + var meshProto = new ProcessFrameRequest.Types.Mesh(); + meshProto.Data = ByteString.CopyFrom(meshElement); - }); + dataFrame.Meshes.Add(meshProto); + } + } + + // Test plane + var plane = new ProcessFrameRequest.Types.Plane(); + plane.Center = unityVector3ToProto(new Vector3(1, 2, 3)); + plane.Normal = unityVector3ToProto(new Vector3(0, 2, 5)); + plane.Size = unityVector2ToProto(new Vector2(5, 5)); + plane.BoundaryPoints.Add(new[] + { unityVector2ToProto(new Vector2(0, 2)), + unityVector2ToProto(new Vector2(1, 3)), + unityVector2ToProto(new Vector2(2, 4)), + unityVector2ToProto(new Vector2(1, 5)), + unityVector2ToProto(new Vector2(2, 1)) } + ); + dataFrame.PlaneDetection.Add(plane); + + _client.SendFrame(dataFrame); } - DataFrameRequest.Types.Vector3 unityVector3ToProto(Vector3 a) + ProcessFrameRequest.Types.Vector3 unityVector3ToProto(Vector3 a) { - return new DataFrameRequest.Types.Vector3() + return new ProcessFrameRequest.Types.Vector3() { X = a.x, Y = a.y, @@ -117,9 +158,9 @@ DataFrameRequest.Types.Vector3 unityVector3ToProto(Vector3 a) }; } - DataFrameRequest.Types.Quaternion unityQuaternionToProto(Quaternion a) + ProcessFrameRequest.Types.Quaternion unityQuaternionToProto(Quaternion a) { - return new DataFrameRequest.Types.Quaternion() + return new ProcessFrameRequest.Types.Quaternion() { X = a.x, Y = a.y, @@ -128,6 +169,15 @@ DataFrameRequest.Types.Quaternion unityQuaternionToProto(Quaternion a) }; } + ProcessFrameRequest.Types.Vector2 unityVector2ToProto(Vector2 a) + { + return new ProcessFrameRequest.Types.Vector2() + { + X = a.x, + Y = a.y, + }; + } + // Update is called once per frame void Update() { diff --git a/unity/Assets/Scripts/ARFlowUnityDataSample.cs b/unity/Assets/Scripts/ARFlowUnityDataSample.cs index cd19ce8..fc59b96 100644 --- a/unity/Assets/Scripts/ARFlowUnityDataSample.cs +++ b/unity/Assets/Scripts/ARFlowUnityDataSample.cs @@ -7,6 +7,8 @@ using TMPro; using Unity.Profiling; +using static ARFlow.OtherUtils; + public class ARFlowUnityDataSample : MonoBehaviour { private bool _enabled; @@ -86,10 +88,10 @@ private void OnConnectButtonClick() // _sampleSize = new Vector2Int(screenWidth, screenHeight); - _client.Connect(new RegisterRequest() + _client.Connect(new RegisterClientRequest() { DeviceName = "UnityDataTestbed", - CameraIntrinsics = new RegisterRequest.Types.CameraIntrinsics() + CameraIntrinsics = new RegisterClientRequest.Types.CameraIntrinsics() { FocalLengthX = focalLengthX, FocalLengthY = focalLengthY, @@ -98,21 +100,21 @@ private void OnConnectButtonClick() PrincipalPointX = principalPointX, PrincipalPointY = principalPointY }, - CameraColor = new RegisterRequest.Types.CameraColor() + CameraColor = new RegisterClientRequest.Types.CameraColor() { Enabled = true, DataType = "RGB24", ResizeFactorX = 1.0f, ResizeFactorY = 1.0f, }, - CameraDepth = new RegisterRequest.Types.CameraDepth() + CameraDepth = new RegisterClientRequest.Types.CameraDepth() { Enabled = true, DataType = "f32", ResolutionX = screenWidth, ResolutionY = screenHeight }, - CameraTransform = new RegisterRequest.Types.CameraTransform() + CameraTransform = new RegisterClientRequest.Types.CameraTransform() { Enabled = false } @@ -152,8 +154,8 @@ private void UploadFrame() _depthTexture.Apply(); var depthBytes = _depthTexture.GetRawTextureData(); - Debug.Log($"pixelBytes length: {pixelBytes.Length}, depthBytes length: {depthBytes.Length}"); - _client.SendFrame(new DataFrameRequest() + PrintDebug($"pixelBytes length: {pixelBytes.Length}, depthBytes length: {depthBytes.Length}"); + _client.SendFrame(new ProcessFrameRequest() { Color = ByteString.CopyFrom(pixelBytes), Depth = ByteString.CopyFrom(depthBytes) diff --git a/unity/Assets/Scripts/ARFlowXiheDemo.cs b/unity/Assets/Scripts/ARFlowXiheDemo.cs index 2437c33..52a4831 100644 --- a/unity/Assets/Scripts/ARFlowXiheDemo.cs +++ b/unity/Assets/Scripts/ARFlowXiheDemo.cs @@ -9,6 +9,8 @@ using UnityEngine.XR.ARSubsystems; using UnityEngine.Rendering; +using static ARFlow.OtherUtils; + public class ARFlowXiheDemo : MonoBehaviour { public TMP_InputField addressInput; @@ -105,7 +107,7 @@ private void OnConnectButtonClick() private void OnStartPauseButtonClick() { - Debug.Log($"Current framerate: {Application.targetFrameRate}"); + PrintDebug($"Current framerate: {Application.targetFrameRate}"); _enabled = !_enabled; startPauseButton.GetComponentInChildren().text = _enabled ? "Pause" : "Start"; diff --git a/unity/Assets/Scripts/Utils.meta b/unity/Assets/Scripts/Utils.meta new file mode 100644 index 0000000..9338de1 --- /dev/null +++ b/unity/Assets/Scripts/Utils.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5b3e9f289a9ce734f85d333613c170b4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/TextMesh Pro.meta b/unity/Assets/TextMesh Pro.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Documentation.meta b/unity/Assets/TextMesh Pro/Documentation.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Documentation/TextMesh Pro User Guide 2016.pdf b/unity/Assets/TextMesh Pro/Documentation/TextMesh Pro User Guide 2016.pdf old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Documentation/TextMesh Pro User Guide 2016.pdf.meta b/unity/Assets/TextMesh Pro/Documentation/TextMesh Pro User Guide 2016.pdf.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Fonts.meta b/unity/Assets/TextMesh Pro/Fonts.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Fonts/LiberationSans - OFL.txt b/unity/Assets/TextMesh Pro/Fonts/LiberationSans - OFL.txt old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Fonts/LiberationSans - OFL.txt.meta b/unity/Assets/TextMesh Pro/Fonts/LiberationSans - OFL.txt.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Fonts/LiberationSans.ttf b/unity/Assets/TextMesh Pro/Fonts/LiberationSans.ttf old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Fonts/LiberationSans.ttf.meta b/unity/Assets/TextMesh Pro/Fonts/LiberationSans.ttf.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources.meta b/unity/Assets/TextMesh Pro/Resources.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Fonts & Materials.meta b/unity/Assets/TextMesh Pro/Resources/Fonts & Materials.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Drop Shadow.mat b/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Drop Shadow.mat old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Drop Shadow.mat.meta b/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Drop Shadow.mat.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Fallback.asset b/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Fallback.asset old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Fallback.asset.meta b/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Fallback.asset.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Outline.mat b/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Outline.mat old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Outline.mat.meta b/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF - Outline.mat.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF.asset b/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF.asset old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF.asset.meta b/unity/Assets/TextMesh Pro/Resources/Fonts & Materials/LiberationSans SDF.asset.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/LineBreaking Following Characters.txt b/unity/Assets/TextMesh Pro/Resources/LineBreaking Following Characters.txt old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/LineBreaking Following Characters.txt.meta b/unity/Assets/TextMesh Pro/Resources/LineBreaking Following Characters.txt.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/LineBreaking Leading Characters.txt b/unity/Assets/TextMesh Pro/Resources/LineBreaking Leading Characters.txt old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/LineBreaking Leading Characters.txt.meta b/unity/Assets/TextMesh Pro/Resources/LineBreaking Leading Characters.txt.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Sprite Assets.meta b/unity/Assets/TextMesh Pro/Resources/Sprite Assets.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Sprite Assets/EmojiOne.asset b/unity/Assets/TextMesh Pro/Resources/Sprite Assets/EmojiOne.asset old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Sprite Assets/EmojiOne.asset.meta b/unity/Assets/TextMesh Pro/Resources/Sprite Assets/EmojiOne.asset.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Style Sheets.meta b/unity/Assets/TextMesh Pro/Resources/Style Sheets.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Style Sheets/Default Style Sheet.asset b/unity/Assets/TextMesh Pro/Resources/Style Sheets/Default Style Sheet.asset old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/Style Sheets/Default Style Sheet.asset.meta b/unity/Assets/TextMesh Pro/Resources/Style Sheets/Default Style Sheet.asset.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/TMP Settings.asset b/unity/Assets/TextMesh Pro/Resources/TMP Settings.asset old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Resources/TMP Settings.asset.meta b/unity/Assets/TextMesh Pro/Resources/TMP Settings.asset.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders.meta b/unity/Assets/TextMesh Pro/Shaders.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap-Custom-Atlas.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap-Custom-Atlas.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap-Custom-Atlas.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap-Custom-Atlas.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap-Mobile.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap-Mobile.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap-Mobile.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap-Mobile.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_Bitmap.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF Overlay.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF Overlay.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF Overlay.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF Overlay.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF SSD.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF SSD.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF SSD.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF SSD.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile Masking.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile Masking.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile Masking.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile Masking.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile Overlay.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile Overlay.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile Overlay.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile Overlay.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile SSD.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile SSD.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile SSD.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile SSD.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Mobile.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Surface-Mobile.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Surface-Mobile.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Surface-Mobile.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Surface-Mobile.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Surface.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Surface.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Surface.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF-Surface.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_SDF.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_SDF.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_Sprite.shader b/unity/Assets/TextMesh Pro/Shaders/TMP_Sprite.shader old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMP_Sprite.shader.meta b/unity/Assets/TextMesh Pro/Shaders/TMP_Sprite.shader.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMPro.cginc b/unity/Assets/TextMesh Pro/Shaders/TMPro.cginc old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMPro.cginc.meta b/unity/Assets/TextMesh Pro/Shaders/TMPro.cginc.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMPro_Mobile.cginc b/unity/Assets/TextMesh Pro/Shaders/TMPro_Mobile.cginc old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMPro_Mobile.cginc.meta b/unity/Assets/TextMesh Pro/Shaders/TMPro_Mobile.cginc.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMPro_Properties.cginc b/unity/Assets/TextMesh Pro/Shaders/TMPro_Properties.cginc old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMPro_Properties.cginc.meta b/unity/Assets/TextMesh Pro/Shaders/TMPro_Properties.cginc.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMPro_Surface.cginc b/unity/Assets/TextMesh Pro/Shaders/TMPro_Surface.cginc old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Shaders/TMPro_Surface.cginc.meta b/unity/Assets/TextMesh Pro/Shaders/TMPro_Surface.cginc.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Sprites.meta b/unity/Assets/TextMesh Pro/Sprites.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt b/unity/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt old mode 100755 new mode 100644 index 10c4be3..384180a --- a/unity/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt +++ b/unity/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt @@ -1,3 +1,3 @@ -This sample of beautiful emojis are provided by EmojiOne https://www.emojione.com/ - +This sample of beautiful emojis are provided by EmojiOne https://www.emojione.com/ + Please visit their website to view the complete set of their emojis and review their licensing terms. \ No newline at end of file diff --git a/unity/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt.meta b/unity/Assets/TextMesh Pro/Sprites/EmojiOne Attribution.txt.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Sprites/EmojiOne.json b/unity/Assets/TextMesh Pro/Sprites/EmojiOne.json old mode 100755 new mode 100644 index 16c800d..6c4e50b --- a/unity/Assets/TextMesh Pro/Sprites/EmojiOne.json +++ b/unity/Assets/TextMesh Pro/Sprites/EmojiOne.json @@ -1,156 +1,156 @@ -{"frames": [ - -{ - "filename": "1f60a.png", - "frame": {"x":0,"y":0,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f60b.png", - "frame": {"x":128,"y":0,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f60d.png", - "frame": {"x":256,"y":0,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f60e.png", - "frame": {"x":384,"y":0,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f600.png", - "frame": {"x":0,"y":128,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f601.png", - "frame": {"x":128,"y":128,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f602.png", - "frame": {"x":256,"y":128,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f603.png", - "frame": {"x":384,"y":128,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f604.png", - "frame": {"x":0,"y":256,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f605.png", - "frame": {"x":128,"y":256,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f606.png", - "frame": {"x":256,"y":256,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f609.png", - "frame": {"x":384,"y":256,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f618.png", - "frame": {"x":0,"y":384,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "1f923.png", - "frame": {"x":128,"y":384,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "263a.png", - "frame": {"x":256,"y":384,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}, -{ - "filename": "2639.png", - "frame": {"x":384,"y":384,"w":128,"h":128}, - "rotated": false, - "trimmed": false, - "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, - "sourceSize": {"w":128,"h":128}, - "pivot": {"x":0.5,"y":0.5} -}], -"meta": { - "app": "http://www.codeandweb.com/texturepacker", - "version": "1.0", - "image": "EmojiOne.png", - "format": "RGBA8888", - "size": {"w":512,"h":512}, - "scale": "1", - "smartupdate": "$TexturePacker:SmartUpdate:196a26a2e149d875b91ffc8fa3581e76:fc928c7e275404b7e0649307410475cb:424723c3774975ddb2053fd5c4b85f6e$" -} -} +{"frames": [ + +{ + "filename": "1f60a.png", + "frame": {"x":0,"y":0,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f60b.png", + "frame": {"x":128,"y":0,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f60d.png", + "frame": {"x":256,"y":0,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f60e.png", + "frame": {"x":384,"y":0,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f600.png", + "frame": {"x":0,"y":128,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f601.png", + "frame": {"x":128,"y":128,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f602.png", + "frame": {"x":256,"y":128,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f603.png", + "frame": {"x":384,"y":128,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f604.png", + "frame": {"x":0,"y":256,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f605.png", + "frame": {"x":128,"y":256,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f606.png", + "frame": {"x":256,"y":256,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f609.png", + "frame": {"x":384,"y":256,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f618.png", + "frame": {"x":0,"y":384,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "1f923.png", + "frame": {"x":128,"y":384,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "263a.png", + "frame": {"x":256,"y":384,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}, +{ + "filename": "2639.png", + "frame": {"x":384,"y":384,"w":128,"h":128}, + "rotated": false, + "trimmed": false, + "spriteSourceSize": {"x":0,"y":0,"w":128,"h":128}, + "sourceSize": {"w":128,"h":128}, + "pivot": {"x":0.5,"y":0.5} +}], +"meta": { + "app": "http://www.codeandweb.com/texturepacker", + "version": "1.0", + "image": "EmojiOne.png", + "format": "RGBA8888", + "size": {"w":512,"h":512}, + "scale": "1", + "smartupdate": "$TexturePacker:SmartUpdate:196a26a2e149d875b91ffc8fa3581e76:fc928c7e275404b7e0649307410475cb:424723c3774975ddb2053fd5c4b85f6e$" +} +} diff --git a/unity/Assets/TextMesh Pro/Sprites/EmojiOne.json.meta b/unity/Assets/TextMesh Pro/Sprites/EmojiOne.json.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Sprites/EmojiOne.png b/unity/Assets/TextMesh Pro/Sprites/EmojiOne.png old mode 100755 new mode 100644 diff --git a/unity/Assets/TextMesh Pro/Sprites/EmojiOne.png.meta b/unity/Assets/TextMesh Pro/Sprites/EmojiOne.png.meta old mode 100755 new mode 100644 diff --git a/unity/Assets/Toast UI.meta b/unity/Assets/Toast UI.meta new file mode 100644 index 0000000..fa505c0 --- /dev/null +++ b/unity/Assets/Toast UI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 843996f0f5bce764bb6803a04e1102b6 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Toast UI/Resources.meta b/unity/Assets/Toast UI/Resources.meta new file mode 100644 index 0000000..99e6e25 --- /dev/null +++ b/unity/Assets/Toast UI/Resources.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: e101ec0bdde418143857f3bf5df01632 +folderAsset: yes +timeCreated: 1608726934 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Toast UI/Resources/ToastUI.prefab b/unity/Assets/Toast UI/Resources/ToastUI.prefab new file mode 100644 index 0000000..b31ad6a --- /dev/null +++ b/unity/Assets/Toast UI/Resources/ToastUI.prefab @@ -0,0 +1,431 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &1119175726458820 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 224187762307540902} + - component: {fileID: 222656141654398366} + - component: {fileID: 114027073042206576} + m_Layer: 0 + m_Name: Container + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &224187762307540902 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1119175726458820} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 224597220361603002} + m_Father: {fileID: 224664132396536646} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &222656141654398366 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1119175726458820} + m_CullTransparentMesh: 1 +--- !u!114 &114027073042206576 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1119175726458820} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 20 + m_Right: 20 + m_Top: 20 + m_Bottom: 20 + m_ChildAlignment: 7 + m_Spacing: 20 + m_ChildForceExpandWidth: 0 + m_ChildForceExpandHeight: 0 + m_ChildControlWidth: 0 + m_ChildControlHeight: 0 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!1 &1172958986161510 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 224195315142845230} + - component: {fileID: 222511039075034948} + - component: {fileID: 114009775724315652} + m_Layer: 0 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &224195315142845230 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1172958986161510} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: [] + m_Father: {fileID: 224597220361603002} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 358, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &222511039075034948 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1172958986161510} + m_CullTransparentMesh: 1 +--- !u!114 &114009775724315652 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1172958986161510} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 30 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 0 + m_MaxSize: 180 + m_Alignment: 4 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1.3 + m_Text: Hello GameDevs, Unity is an amazing game + engine. +--- !u!1 &1262329948662846 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4649504104831760} + - component: {fileID: 114939976373272096} + m_Layer: 0 + m_Name: ToastUI + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4649504104831760 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1262329948662846} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 224664132396536646} + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &114939976373272096 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1262329948662846} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9849e4280a058e148bdec13b05ce3a43, type: 3} + m_Name: + m_EditorClassIdentifier: + uiCanvasGroup: {fileID: 225432418422502172} + uiRectTransform: {fileID: 224597220361603002} + uiContentVerticalLayoutGroup: {fileID: 114027073042206576} + uiImage: {fileID: 114659833635478522} + uiText: {fileID: 114009775724315652} + colors: + - {r: 0.09558821, g: 0.09558821, b: 0.09558821, a: 1} + - {r: 0.9264706, g: 0.06812285, b: 0.263471, a: 1} + - {r: 0.83823526, g: 0.09861591, b: 0.75662273, a: 1} + - {r: 0.5484829, g: 0.12121541, b: 0.86764705, a: 1} + - {r: 0.08223397, g: 0.6027296, b: 0.8602941, a: 1} + - {r: 0.08969508, g: 0.5808823, b: 0.29633242, a: 1} + - {r: 0.6802499, g: 0.71323526, b: 0.11537627, a: 1} + - {r: 0.9264706, g: 0.4801487, b: 0.07493514, a: 1} + fadeDuration: 0.15 +--- !u!1 &1379282666370740 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 224664132396536646} + - component: {fileID: 223334170058356020} + - component: {fileID: 114620511354508300} + - component: {fileID: 225432418422502172} + m_Layer: 0 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &224664132396536646 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1379282666370740} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 224187762307540902} + m_Father: {fileID: 4649504104831760} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!223 &223334170058356020 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1379282666370740} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 32000 + m_TargetDisplay: 0 +--- !u!114 &114620511354508300 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1379282666370740} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 1 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 640, y: 960} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0.5 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!225 &225432418422502172 +CanvasGroup: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1379282666370740} + m_Enabled: 1 + m_Alpha: 1 + m_Interactable: 0 + m_BlocksRaycasts: 0 + m_IgnoreParentGroups: 0 +--- !u!1 &1635921763246106 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 224597220361603002} + - component: {fileID: 222180899360473682} + - component: {fileID: 114659833635478522} + - component: {fileID: 114990127041106248} + - component: {fileID: 114314708490085082} + m_Layer: 0 + m_Name: Toast + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &224597220361603002 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1635921763246106} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 + m_Children: + - {fileID: 224195315142845230} + m_Father: {fileID: 224187762307540902} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 297.35678, y: 0} + m_SizeDelta: {x: 397, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &222180899360473682 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1635921763246106} + m_CullTransparentMesh: 1 +--- !u!114 &114659833635478522 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1635921763246106} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.066176474, g: 0.066176474, b: 0.066176474, a: 1} + m_RaycastTarget: 0 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 21300000, guid: 28b08fa84201bf74f815e602f1963808, type: 3} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!114 &114990127041106248 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1635921763246106} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 59f8146938fff824cb5fd77236b75775, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: + m_Left: 20 + m_Right: 20 + m_Top: 20 + m_Bottom: 20 + m_ChildAlignment: 4 + m_Spacing: 10 + m_ChildForceExpandWidth: 1 + m_ChildForceExpandHeight: 1 + m_ChildControlWidth: 0 + m_ChildControlHeight: 1 + m_ChildScaleWidth: 0 + m_ChildScaleHeight: 0 + m_ReverseArrangement: 0 +--- !u!114 &114314708490085082 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1635921763246106} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalFit: 0 + m_VerticalFit: 2 diff --git a/unity/Assets/Toast UI/Resources/ToastUI.prefab.meta b/unity/Assets/Toast UI/Resources/ToastUI.prefab.meta new file mode 100644 index 0000000..1a7127f --- /dev/null +++ b/unity/Assets/Toast UI/Resources/ToastUI.prefab.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: c310b787f758e3a4db55284145e55bc8 +timeCreated: 1608726922 +licenseType: Pro +NativeFormatImporter: + mainObjectFileID: 100100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Toast UI/Scripts.meta b/unity/Assets/Toast UI/Scripts.meta new file mode 100644 index 0000000..9ddc9eb --- /dev/null +++ b/unity/Assets/Toast UI/Scripts.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 71b8ccabcd0218f4e80edf4930aac815 +folderAsset: yes +timeCreated: 1609002110 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Toast UI/Scripts/Toast.cs b/unity/Assets/Toast UI/Scripts/Toast.cs new file mode 100644 index 0000000..ceb8122 --- /dev/null +++ b/unity/Assets/Toast UI/Scripts/Toast.cs @@ -0,0 +1,119 @@ +using UnityEngine ; +using EasyUI.Helpers ; + +/* ------------------------------- + Created by : Hamza Herbou + hamza95herbou@gmail.com +---------------------------------- */ + +namespace EasyUI.Toast { + + public enum ToastColor { + Black, + Red, + Purple, + Magenta, + Blue, + Green, + Yellow, + Orange + } + + public enum ToastPosition { + TopLeft, + TopCenter, + TopRight, + MiddleLeft, + MiddleCenter, + MiddleRight, + BottomLeft, + BottomCenter, + BottomRight + } + + public static class Toast { + public static bool isLoaded = false ; + + private static ToastUI toastUI ; + + private static void Prepare () { + if (!isLoaded) { + GameObject instance = MonoBehaviour.Instantiate (Resources.Load ("ToastUI")) ; + instance.name = "[ TOAST UI ]" ; + toastUI = instance.GetComponent () ; + isLoaded = true ; + } + } + + + + public static void Show (string text) { + Prepare () ; + toastUI.Init (text, 2F, ToastColor.Black, ToastPosition.BottomCenter) ; + } + + + public static void Show (string text, float duration) { + Prepare () ; + toastUI.Init (text, duration, ToastColor.Black, ToastPosition.BottomCenter) ; + } + + public static void Show (string text, float duration, ToastPosition position) { + Prepare () ; + toastUI.Init (text, duration, ToastColor.Black, position) ; + } + + + public static void Show (string text, ToastColor color) { + Prepare () ; + toastUI.Init (text, 2F, color, ToastPosition.BottomCenter) ; + } + + public static void Show (string text, ToastColor color, ToastPosition position) { + Prepare () ; + toastUI.Init (text, 2F, color, position) ; + } + + + public static void Show (string text, Color color) { + Prepare () ; + toastUI.Init (text, 2F, color, ToastPosition.BottomCenter) ; + } + + public static void Show (string text, Color color, ToastPosition position) { + Prepare () ; + toastUI.Init (text, 2F, color, position) ; + } + + + public static void Show (string text, float duration, ToastColor color) { + Prepare () ; + toastUI.Init (text, duration, color, ToastPosition.BottomCenter) ; + } + + public static void Show (string text, float duration, ToastColor color, ToastPosition position) { + Prepare () ; + toastUI.Init (text, duration, color, position) ; + } + + + public static void Show (string text, float duration, Color color) { + Prepare () ; + toastUI.Init (text, duration, color, ToastPosition.BottomCenter) ; + } + + public static void Show (string text, float duration, Color color, ToastPosition position) { + Prepare () ; + toastUI.Init (text, duration, color, position) ; + } + + + + public static void Dismiss () { + if (isLoaded) + toastUI.Dismiss () ; + } + + } + +} diff --git a/unity/Assets/Toast UI/Scripts/Toast.cs.meta b/unity/Assets/Toast UI/Scripts/Toast.cs.meta new file mode 100644 index 0000000..ff22e1f --- /dev/null +++ b/unity/Assets/Toast UI/Scripts/Toast.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 4289cb162818a214b923de0a2b451a9b +timeCreated: 1609008056 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Toast UI/Scripts/ToastUI.cs b/unity/Assets/Toast UI/Scripts/ToastUI.cs new file mode 100644 index 0000000..0827874 --- /dev/null +++ b/unity/Assets/Toast UI/Scripts/ToastUI.cs @@ -0,0 +1,98 @@ +using UnityEngine ; +using System.Collections ; +using UnityEngine.UI ; +using EasyUI.Toast ; + +/* ------------------------------- + Created by : Hamza Herbou + hamza95herbou@gmail.com +---------------------------------- */ + +namespace EasyUI.Helpers { + + public class ToastUI : MonoBehaviour { + [Header ("UI References :")] + [SerializeField] private CanvasGroup uiCanvasGroup ; + [SerializeField] private RectTransform uiRectTransform ; + [SerializeField] private VerticalLayoutGroup uiContentVerticalLayoutGroup ; + [SerializeField] private Image uiImage ; + [SerializeField] private Text uiText ; + + [Header ("Toast Colors :")] + [SerializeField] private Color[] colors ; + + [Header ("Toast Fade In/Out Duration :")] + [Range (.1f, .8f)] + [SerializeField] private float fadeDuration = .3f ; + + + private int maxTextLength = 300 ; + + + void Awake () { + uiCanvasGroup.alpha = 0f ; + } + + public void Init (string text, float duration, ToastColor color, ToastPosition position) { + Show (text, duration, colors [ (int)color ], position) ; + } + + public void Init (string text, float duration, Color color, ToastPosition position) { + Show (text, duration, color, position) ; + } + + + + private void Show (string text, float duration, Color color, ToastPosition position) { + uiText.text = (text.Length > maxTextLength) ? text.Substring (0, maxTextLength) + "..." : text ; + uiImage.color = color ; + + uiContentVerticalLayoutGroup.childAlignment = (TextAnchor)((int)position) ; + + + Dismiss () ; + StartCoroutine (FadeInOut (duration, fadeDuration)) ; + } + + private IEnumerator FadeInOut (float toastDuration, float fadeDuration) { + yield return null ; + uiContentVerticalLayoutGroup.CalculateLayoutInputHorizontal () ; + uiContentVerticalLayoutGroup.CalculateLayoutInputVertical () ; + uiContentVerticalLayoutGroup.SetLayoutHorizontal () ; + uiContentVerticalLayoutGroup.SetLayoutVertical () ; + yield return null ; + // Anim start + yield return Fade (uiCanvasGroup, 0f, 1f, fadeDuration) ; + yield return new WaitForSeconds (toastDuration) ; + yield return Fade (uiCanvasGroup, 1f, 0f, fadeDuration) ; + // Anim end + } + + private IEnumerator Fade (CanvasGroup cGroup, float startAlpha, float endAlpha, float fadeDuration) { + float startTime = Time.time ; + float alpha = startAlpha ; + + if (fadeDuration > 0f) { + //Anim start + while (alpha != endAlpha) { + alpha = Mathf.Lerp (startAlpha, endAlpha, (Time.time - startTime) / fadeDuration) ; + cGroup.alpha = alpha ; + + yield return null ; + } + } + + cGroup.alpha = endAlpha ; + } + + public void Dismiss () { + StopAllCoroutines () ; + uiCanvasGroup.alpha = 0f ; + } + + private void OnDestroy () { + EasyUI.Toast.Toast.isLoaded = false ; + } + } + +} diff --git a/unity/Assets/Toast UI/Scripts/ToastUI.cs.meta b/unity/Assets/Toast UI/Scripts/ToastUI.cs.meta new file mode 100644 index 0000000..8257d21 --- /dev/null +++ b/unity/Assets/Toast UI/Scripts/ToastUI.cs.meta @@ -0,0 +1,12 @@ +fileFormatVersion: 2 +guid: 9849e4280a058e148bdec13b05ce3a43 +timeCreated: 1608726990 +licenseType: Pro +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Toast UI/Sprites.meta b/unity/Assets/Toast UI/Sprites.meta new file mode 100644 index 0000000..a92bd73 --- /dev/null +++ b/unity/Assets/Toast UI/Sprites.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 89c2e77622af86641bf81361d1a2d7b1 +folderAsset: yes +timeCreated: 1609002326 +licenseType: Pro +DefaultImporter: + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Toast UI/Sprites/square_r.png b/unity/Assets/Toast UI/Sprites/square_r.png new file mode 100644 index 0000000..bc97443 --- /dev/null +++ b/unity/Assets/Toast UI/Sprites/square_r.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1856a53b59f4948c06cf10e1173af9a60ca75c0d6ab07a4caadd13097af698ba +size 1300 diff --git a/unity/Assets/Toast UI/Sprites/square_r.png.meta b/unity/Assets/Toast UI/Sprites/square_r.png.meta new file mode 100644 index 0000000..177ee09 --- /dev/null +++ b/unity/Assets/Toast UI/Sprites/square_r.png.meta @@ -0,0 +1,135 @@ +fileFormatVersion: 2 +guid: 28b08fa84201bf74f815e602f1963808 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 12 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 1 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 1 + spriteExtrude: 1 + spriteMeshType: 0 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 160 + spriteBorder: {x: 45, y: 45, z: 45, w: 45} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 8 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 1 + cookieLightType: 1 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 1 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 1 + - serializedVersion: 3 + buildTarget: Android + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 1 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 1 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: 5e97eb03825dee720800000000000000 + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/UnityVoiceProcessor.meta b/unity/Assets/UnityVoiceProcessor.meta new file mode 100644 index 0000000..d433740 --- /dev/null +++ b/unity/Assets/UnityVoiceProcessor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0979be8284022dd40b41436688588942 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/UnityXRContent.meta b/unity/Assets/UnityXRContent.meta new file mode 100644 index 0000000..38e1f39 --- /dev/null +++ b/unity/Assets/UnityXRContent.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 81fbb07bfae2206489da2bdf0c57cc51 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/XR/.gitignore b/unity/Assets/XR/.gitignore new file mode 100644 index 0000000..421cd20 --- /dev/null +++ b/unity/Assets/XR/.gitignore @@ -0,0 +1 @@ +**/MockHMD* \ No newline at end of file diff --git a/unity/Assets/XR/Resources/XRSimulationRuntimeSettings.asset b/unity/Assets/XR/Resources/XRSimulationRuntimeSettings.asset index c6d555b..0a73675 100644 --- a/unity/Assets/XR/Resources/XRSimulationRuntimeSettings.asset +++ b/unity/Assets/XR/Resources/XRSimulationRuntimeSettings.asset @@ -12,7 +12,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: e2b12afd4d27418a9cfb2823fe2b9ff3, type: 3} m_Name: XRSimulationRuntimeSettings m_EditorClassIdentifier: - m_EnvironmentLayer: 30 + m_EnvironmentLayer: 1 m_EnvironmentScanParams: m_MinimumRescanTime: 0.1 m_DeltaCameraDistanceToRescan: 0.025 diff --git a/unity/Assets/XR/UserSimulationSettings/Resources/XRSimulationPreferences.asset b/unity/Assets/XR/UserSimulationSettings/Resources/XRSimulationPreferences.asset index bffc7c7..d586ec8 100644 --- a/unity/Assets/XR/UserSimulationSettings/Resources/XRSimulationPreferences.asset +++ b/unity/Assets/XR/UserSimulationSettings/Resources/XRSimulationPreferences.asset @@ -12,6 +12,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: b2f528b98f844ed8b6b2d5fdf90b40e6, type: 3} m_Name: XRSimulationPreferences m_EditorClassIdentifier: - m_EnvironmentPrefab: {fileID: 0} + m_EnvironmentPrefab: {fileID: 4033279824830409645, guid: f1fbb93a309712c40992b49e13b0e872, type: 3} m_FallbackEnvironmentPrefab: {fileID: 7576867131100388943, guid: c7b92c392902f4043a03a64032c02fe1, type: 3} m_EnableNavigation: 1 diff --git a/unity/Assets/XR/UserSimulationSettings/SimulationEnvironmentAssetsManager.asset b/unity/Assets/XR/UserSimulationSettings/SimulationEnvironmentAssetsManager.asset index 117c021..3fa89ea 100644 --- a/unity/Assets/XR/UserSimulationSettings/SimulationEnvironmentAssetsManager.asset +++ b/unity/Assets/XR/UserSimulationSettings/SimulationEnvironmentAssetsManager.asset @@ -12,5 +12,26 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 378fb4eec0f59ac4c95c0d5b227aa85e, type: 3} m_Name: SimulationEnvironmentAssetsManager m_EditorClassIdentifier: - m_EnvironmentPrefabPaths: [] - m_FallbackAtEndOfList: 0 + m_EnvironmentPrefabPaths: + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Backyard/Backyard_45ftx40ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Backyard/Backyard_55ftx40ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Bedroom/Bedroom_12ftx12ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Bedroom/Bedroom_20ftx14ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Bedroom/Bedroom_20ftx17ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Billboard/Billboard_City.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/DiningRoom/DiningRoom_15ftx12.5ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/DiningRoom/DiningRoom_18ftx16ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Factory/Factory_180ftx101ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Kitchen/Kitchen_13ftx9ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Kitchen/Kitchen_17ftx16ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Kitchen/Kitchen_25ftx16ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/LivingRoom/LivingRoom_17.5ftx16.5ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/LivingRoom/LivingRoom_18ftx13ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/LivingRoom/LivingRoom_20ftx18.5ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Museum/Museum_69ftx48ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Office/Office_12ftx12ft.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Office/Office_36ftx16.5ft_001.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Office/Office_36ftx16.5ft_002.prefab + - Assets/UnityXRContent/ARFoundation/SimulationEnvironments/Park/Park_322ftx300ft.prefab + - Packages/com.unity.xr.arfoundation/Assets/Prefabs/DefaultSimulationEnvironment.prefab + m_FallbackAtEndOfList: 1 diff --git a/unity/Assets/XR/XRGeneralSettingsPerBuildTarget.asset b/unity/Assets/XR/XRGeneralSettingsPerBuildTarget.asset index 8ff5e90..a6603ab 100644 --- a/unity/Assets/XR/XRGeneralSettingsPerBuildTarget.asset +++ b/unity/Assets/XR/XRGeneralSettingsPerBuildTarget.asset @@ -1,5 +1,21 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: +--- !u!114 &-9103902354846192695 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f4c3631f5e58749a59194e0cf6baf6d5, type: 3} + m_Name: Standalone Providers + m_EditorClassIdentifier: + m_RequiresSettingsUpdate: 0 + m_AutomaticLoading: 0 + m_AutomaticRunning: 0 + m_Loaders: [] --- !u!114 &-9054060240666304766 MonoBehaviour: m_ObjectHideFlags: 0 @@ -57,10 +73,11 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d2dc886499c26824283350fa532d087d, type: 3} m_Name: XRGeneralSettingsPerBuildTarget m_EditorClassIdentifier: - Keys: 0400000007000000 + Keys: 040000000700000001000000 Values: - {fileID: -9054060240666304766} - {fileID: -4019099003003015272} + - {fileID: 6700612043900912164} --- !u!114 &5537603414789288036 MonoBehaviour: m_ObjectHideFlags: 0 @@ -78,3 +95,17 @@ MonoBehaviour: m_AutomaticRunning: 0 m_Loaders: - {fileID: 11400000, guid: a69a83de527d749caa73d26427c2baff, type: 2} +--- !u!114 &6700612043900912164 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d236b7d11115f2143951f1e14045df39, type: 3} + m_Name: Standalone Settings + m_EditorClassIdentifier: + m_LoaderManagerInstance: {fileID: -9103902354846192695} + m_InitManagerOnStart: 1 diff --git a/unity/Documentation/.gitignore b/unity/Documentation/.gitignore deleted file mode 100644 index 5f076af..0000000 --- a/unity/Documentation/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# ignore files moved by build script and generated by Docfx -index.md -/api - -# Client docs directory inside documentation is for local environment. -# When running github workflows the files will be moved into the website/docs directory (in root) -/clientHTMLOutput \ No newline at end of file diff --git a/unity/Packages/manifest.json b/unity/Packages/manifest.json index 291a14f..b4a2a50 100644 --- a/unity/Packages/manifest.json +++ b/unity/Packages/manifest.json @@ -1,17 +1,21 @@ { "dependencies": { + "com.atteneder.draco": "4.1.0", "com.cysharp.yetanotherhttphandler": "https://github.com/Cysharp/YetAnotherHttpHandler.git?path=src/YetAnotherHttpHandler#v1.0.0", "com.github-glitchenzo.nugetforunity": "https://github.com/GlitchEnzo/NuGetForUnity.git?path=/src/NuGetForUnity", "com.unity.collab-proxy": "2.4.4", "com.unity.feature.development": "1.0.1", + "com.unity.mobile.android-logcat": "1.4.3", "com.unity.textmeshpro": "3.0.8", "com.unity.timeline": "1.7.6", "com.unity.toolchain.linux-x86_64": "2.0.6", "com.unity.ugui": "1.0.0", "com.unity.visualscripting": "1.9.4", + "com.unity.xr-content.xr-sim-environments": "file:../ContentPackages\\com.unity.xr-content.xr-sim-environments-1.0.0.tgz", "com.unity.xr.arcore": "5.1.5", "com.unity.xr.arfoundation": "5.1.5", "com.unity.xr.arkit": "5.1.5", + "com.unity.xr.mock-hmd": "1.4.0-preview.2", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", @@ -43,5 +47,14 @@ "com.unity.modules.vr": "1.0.0", "com.unity.modules.wind": "1.0.0", "com.unity.modules.xr": "1.0.0" - } + }, + "scopedRegistries": [ + { + "name": "OpenUPM", + "url": "https://package.openupm.com", + "scopes": [ + "com.atteneder" + ] + } + ] } diff --git a/unity/Packages/packages-lock.json b/unity/Packages/packages-lock.json index 0a6a982..a819c45 100644 --- a/unity/Packages/packages-lock.json +++ b/unity/Packages/packages-lock.json @@ -1,5 +1,14 @@ { "dependencies": { + "com.atteneder.draco": { + "version": "4.1.0", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.burst": "1.4.11" + }, + "url": "https://package.openupm.com" + }, "com.cysharp.yetanotherhttphandler": { "version": "https://github.com/Cysharp/YetAnotherHttpHandler.git?path=src/YetAnotherHttpHandler#v1.0.0", "depth": 0, @@ -14,6 +23,16 @@ "dependencies": {}, "hash": "75f68222d0a4bd2b468dbf3e6a17a191d28041ab" }, + "com.unity.burst": { + "version": "1.8.17", + "depth": 1, + "source": "registry", + "dependencies": { + "com.unity.mathematics": "1.2.1", + "com.unity.modules.jsonserialize": "1.0.0" + }, + "url": "https://packages.unity.com" + }, "com.unity.collab-proxy": { "version": "2.4.4", "depth": 0, @@ -90,6 +109,13 @@ "dependencies": {}, "url": "https://packages.unity.com" }, + "com.unity.mobile.android-logcat": { + "version": "1.4.3", + "depth": 0, + "source": "registry", + "dependencies": {}, + "url": "https://packages.unity.com" + }, "com.unity.performance.profile-analyzer": { "version": "1.2.2", "depth": 1, @@ -191,6 +217,14 @@ }, "url": "https://packages.unity.com" }, + "com.unity.xr-content.xr-sim-environments": { + "version": "file:../ContentPackages\\com.unity.xr-content.xr-sim-environments-1.0.0.tgz", + "depth": 0, + "source": "local-tarball", + "dependencies": { + "com.unity.xr.arfoundation": "5.0.0" + } + }, "com.unity.xr.arcore": { "version": "5.1.5", "depth": 0, @@ -265,6 +299,15 @@ }, "url": "https://packages.unity.com" }, + "com.unity.xr.mock-hmd": { + "version": "1.4.0-preview.2", + "depth": 0, + "source": "registry", + "dependencies": { + "com.unity.xr.management": "4.0.1" + }, + "url": "https://packages.unity.com" + }, "com.unity.modules.ai": { "version": "1.0.0", "depth": 0, diff --git a/unity/ProjectSettings/EditorBuildSettings.asset b/unity/ProjectSettings/EditorBuildSettings.asset index 263b7b4..ff00d1c 100644 --- a/unity/ProjectSettings/EditorBuildSettings.asset +++ b/unity/ProjectSettings/EditorBuildSettings.asset @@ -18,3 +18,4 @@ EditorBuildSettings: com.unity.xr.arfoundation.simulation_settings: {fileID: 11400000, guid: 8d33ccf7e1a574b7a8329f7d9563a589, type: 2} com.unity.xr.management.loader_settings: {fileID: 11400000, guid: 20526f0dba1114536b1e928dbe0eca0a, type: 2} com.unity.xr.openxr.settings4: {fileID: 11400000, guid: fc3d0292db9f649d29cdd25eb39ffe7c, type: 2} + xr.sdk.mock-hmd.settings: {fileID: 11400000, guid: 6ba7822580889c04dac4128b1ad2d3de, type: 2} diff --git a/unity/ProjectSettings/PackageManagerSettings.asset b/unity/ProjectSettings/PackageManagerSettings.asset index fb3b38c..52e0a0d 100644 --- a/unity/ProjectSettings/PackageManagerSettings.asset +++ b/unity/ProjectSettings/PackageManagerSettings.asset @@ -26,11 +26,19 @@ MonoBehaviour: m_IsDefault: 1 m_Capabilities: 7 m_ConfigSource: 0 - m_UserSelectedRegistryName: + - m_Id: scoped:project:OpenUPM + m_Name: OpenUPM + m_Url: https://package.openupm.com + m_Scopes: + - com.atteneder + m_IsDefault: 0 + m_Capabilities: 0 + m_ConfigSource: 4 + m_UserSelectedRegistryName: OpenUPM m_UserAddingNewScopedRegistry: 0 m_RegistryInfoDraft: m_Modified: 0 m_ErrorMessage: - m_UserModificationsInstanceId: -840 - m_OriginalInstanceId: -842 + m_UserModificationsInstanceId: -834 + m_OriginalInstanceId: -836 m_LoadAssets: 0 diff --git a/unity/ProjectSettings/ProjectSettings.asset b/unity/ProjectSettings/ProjectSettings.asset index 44e39cc..665bc30 100644 --- a/unity/ProjectSettings/ProjectSettings.asset +++ b/unity/ProjectSettings/ProjectSettings.asset @@ -140,7 +140,12 @@ PlayerSettings: visionOSBundleVersion: 1.0 tvOSBundleVersion: 1.0 bundleVersion: 0.1 - preloadedAssets: [] + preloadedAssets: + - {fileID: -4019099003003015272, guid: 20526f0dba1114536b1e928dbe0eca0a, type: 2} + - {fileID: 11400000, guid: 8d33ccf7e1a574b7a8329f7d9563a589, type: 2} + - {fileID: 11400000, guid: 6ba7822580889c04dac4128b1ad2d3de, type: 2} + - {fileID: 4800000, guid: c9f956787b1d945e7b36e0516201fc76, type: 3} + - {fileID: 4800000, guid: 0945859e5a1034c2cb6dce53cb4fb899, type: 3} metroInputSource: 0 wsaTransparentSwapchain: 0 m_HolographicPauseOnTrackingLoss: 1 diff --git a/unity/ProjectSettings/TimeManager.asset b/unity/ProjectSettings/TimeManager.asset index 558a017..baa38d2 100644 --- a/unity/ProjectSettings/TimeManager.asset +++ b/unity/ProjectSettings/TimeManager.asset @@ -3,7 +3,7 @@ --- !u!5 &1 TimeManager: m_ObjectHideFlags: 0 - Fixed Timestep: 0.02 - Maximum Allowed Timestep: 0.33333334 + Fixed Timestep: 0.1 + Maximum Allowed Timestep: 0.5 m_TimeScale: 1 - Maximum Particle Timestep: 0.03 + Maximum Particle Timestep: 0.5 diff --git a/unity/README.md b/unity/README.md index df01f66..bd1f104 100644 --- a/unity/README.md +++ b/unity/README.md @@ -1,7 +1,30 @@ # ARFlow Client +[![release](https://img.shields.io/badge/release-0.0.4-blue)]() +[![license](https://img.shields.io/badge/License-GNU%20GPL-green)](https://www.gnu.org/licenses/gpl-3.0.html) The ARFlow client is responsible for on-device AR data collection and high-performance AR data streaming. We implement the ARFlow client as a Unity application that can be easily ported to different platforms and devices. +## Installation +This package can be installed with Unity Package Manager's Install from Git feature. This package has some dependencies that must be installed seperately. + +1. Install these dependency packages by specifying the following URL in `Add package from git URL...` +``` +https://github.com/Cysharp/YetAnotherHttpHandler.git?path=src/YetAnotherHttpHandler#{1.0.0} +``` +``` +https://github.com/atteneder/DracoUnity.git +``` +2. Install the Unity Voice Processor package by importing the following `.unitypackage` into your Unity Project (dragging and dropping) +``` +https://github.com/Picovoice/unity-voice-processor/blob/main/unity-voice-processor-1.0.0.unitypackage +``` + +3. To install the latest package version, specify the following URL in `Add package from git URL...` of Package Manager on Unity +``` +https://github.com/cake-lab/ARFlow.git?path=unity/Assets/ARFlowPackage/ARFlow +``` +## Implementation details + The core functions are implemented in `unity/Assets/Scripts/ARFlow`. We show three example ARFlow integration of three different data sources: - Mock data: inside ./Assets/Scripts/ARFlowMockDataSample.cs diff --git a/website/static/images/favicon.svg b/website/static/images/favicon.svg index 98d2546..ec23dce 100644 --- a/website/static/images/favicon.svg +++ b/website/static/images/favicon.svg @@ -1,37 +1,37 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +